Package org.apache.sling.jcr.contentloader.internal

Source Code of org.apache.sling.jcr.contentloader.internal.Loader$Descriptor

/*
* 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.sling.jcr.contentloader.internal;

import static javax.jcr.ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.jcr.InvalidSerializedDataException;
import javax.jcr.Item;
import javax.jcr.Node;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Session;

import org.osgi.framework.Bundle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* The <code>Loader</code> loads initial content from the bundle.
*/
public class Loader {

    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 ROOT_DESCRIPTOR = "/ROOT";

    // default content type for createFile()
    private static final String DEFAULT_CONTENT_TYPE = "application/octet-stream";

    /** default log */
    private final Logger log = LoggerFactory.getLogger(Loader.class);

    private ContentLoaderService jcrContentHelper;

    private Map<String, ImportProvider> importProviders;

    private Map<String, List<String>> delayedReferences;

    // bundles whose registration failed and should be retried
    private List<Bundle> delayedBundles;

    public Loader(ContentLoaderService jcrContentHelper) {
        this.jcrContentHelper = jcrContentHelper;
        this.delayedReferences = new HashMap<String, List<String>>();
        this.delayedBundles = new LinkedList<Bundle>();

        importProviders = new LinkedHashMap<String, ImportProvider>();
        importProviders.put(EXT_JCR_XML, null);
        importProviders.put(EXT_JSON, JsonReader.PROVIDER);
        importProviders.put(EXT_XML, XmlReader.PROVIDER);
    }

    public void dispose() {
        delayedReferences = null;
        if (delayedBundles != null) {
            delayedBundles.clear();
            delayedBundles = null;
        }
        jcrContentHelper = null;
        importProviders.clear();
    }

    /**
     * Register a bundle and install its content.
     *
     * @param session
     * @param bundle
     */
    public void registerBundle(final Session session, final Bundle bundle,
            final boolean isUpdate) {

        log.debug("Registering bundle {} for content loading.",
            bundle.getSymbolicName());

        if (registerBundleInternal(session, bundle, false, isUpdate)) {

            // handle delayed bundles, might help now
            int currentSize = -1;
            for (int i = delayedBundles.size(); i > 0
                && currentSize != delayedBundles.size()
                && !delayedBundles.isEmpty(); i--) {

                for (Iterator<Bundle> di = delayedBundles.iterator(); di.hasNext();) {

                    Bundle delayed = di.next();
                    if (registerBundleInternal(session, delayed, true, false)) {
                        di.remove();
                    }

                }

                currentSize = delayedBundles.size();
            }

        } else if (!isUpdate) {
            // add to delayed bundles - if this is not an update!
            delayedBundles.add(bundle);
        }
    }

    private boolean registerBundleInternal(final Session session,
            final Bundle bundle, final boolean isRetry, final boolean isUpdate) {

        // check if bundle has initial content
        final Iterator<PathEntry> pathIter = PathEntry.getContentPaths(bundle);
        if (pathIter == null) {
            log.debug("Bundle {} has no initial content",
                bundle.getSymbolicName());
            return true;
        }

        try {
           
            // check if the content has already been loaded
            final Map<String, Object> bundleContentInfo = jcrContentHelper.getBundleContentInfo(
                session, bundle);
           
            // if we don't get an info, someone else is currently loading
            if (bundleContentInfo == null) {
                return false;
            }

            boolean success = false;
            try {
               
                final boolean contentAlreadyLoaded = ((Boolean) bundleContentInfo.get(ContentLoaderService.PROPERTY_CONTENT_LOADED)).booleanValue();
               
                if (!isUpdate && contentAlreadyLoaded) {
                   
                    log.info("Content of bundle already loaded {}.",
                        bundle.getSymbolicName());
                   
                } else {
                   
                    installContent(session, bundle, pathIter,
                        contentAlreadyLoaded);
                   
                    if (isRetry) {
                        // log success of retry
                        log.info(
                            "Retrytring to load initial content for bundle {} succeeded.",
                            bundle.getSymbolicName());
                    }
                   
                }
               
                success = true;
                return true;
               
            } finally {
                jcrContentHelper.unlockBundleContentInfo(session, bundle,
                    success);
            }
           
        } 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);
            }
        }

        return false;
    }

    /**
     * Unregister a bundle. Remove installed content.
     *
     * @param bundle The bundle.
     */
    public void unregisterBundle(final Session session, final Bundle bundle) {
       
        // check if bundle has initial content
        final Iterator<PathEntry> pathIter = PathEntry.getContentPaths(bundle);
        if (delayedBundles.contains(bundle)) {
           
            delayedBundles.remove(bundle);
           
        } else {
           
            if (pathIter != null) {
                uninstallContent(session, bundle, pathIter);
                jcrContentHelper.contentIsUninstalled(session, bundle);
            }
           
        }
    }

    // ---------- internal -----------------------------------------------------

    private void installContent(final Session session, final Bundle bundle,
            final Iterator<PathEntry> pathIter,
            final boolean contentAlreadyLoaded) throws RepositoryException {
        log.debug("Installing initial content from bundle {}",
            bundle.getSymbolicName());
        try {

            // the nodes marked to be checked-in after import
            List<Node> versionables = new ArrayList<Node>();

            while (pathIter.hasNext()) {
                final PathEntry entry = pathIter.next();
                if (!contentAlreadyLoaded || entry.isOverwrite()) {
                   
                    Node targetNode = getTargetNode(session, entry.getTarget());

                    if (targetNode != null) {
                        installFromPath(bundle, entry.getPath(),
                            entry.isOverwrite(), versionables,
                            entry.isCheckin(), targetNode);
                    }
                }
            }

            // persist modifications now
            session.save();

            // finally checkin versionable nodes
            for (Node versionable : versionables) {
                versionable.checkin();
            }

        } finally {
            try {
                if (session.hasPendingChanges()) {
                    session.refresh(false);
                }
            } catch (RepositoryException re) {
                log.warn(
                    "Failure to rollback partial initial content for bundle {}",
                    bundle.getSymbolicName(), re);
            }
        }
        log.debug("Done installing initial content from bundle {}",
            bundle.getSymbolicName());

    }

    /**
     * Handle content installation for a single path.
     *
     * @param bundle The bundle containing the content.
     * @param path The path
     * @param overwrite Should the content be overwritten.
     * @param parent The parent node.
     * @throws RepositoryException
     */
    private void installFromPath(final Bundle bundle, final String path,
            final boolean overwrite, List<Node> versionables,
            final boolean checkin, final Node parent)
            throws RepositoryException {
       
        @SuppressWarnings("unchecked")
        Enumeration<String> entries = bundle.getEntryPaths(path);
        if (entries == null) {
            log.info("install: No initial content entries at {}", path);
            return;
        }

        Set<URL> ignoreEntry = new HashSet<URL>();

        // potential root node import/extension
        URL rootNodeDescriptor = importRootNode(parent.getSession(), bundle,
            path, versionables, checkin);
        if (rootNodeDescriptor != null) {
            ignoreEntry.add(rootNodeDescriptor);
        }

        while (entries.hasMoreElements()) {
            final String entry = entries.nextElement();
            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 name = getName(base);

                URL nodeDescriptor = null;
                for (String ext : importProviders.keySet()) {
                    nodeDescriptor = bundle.getEntry(base + ext);
                    if (nodeDescriptor != null) {
                        break;
                    }
                }

                // if we have a descriptor, which has not been processed yet,
                // otherwise call createFolder, which creates an nt:folder or
                // returns an existing node (created by a descriptor)
                Node node = null;
                if (nodeDescriptor != null
                    && !ignoreEntry.contains(nodeDescriptor)) {
                    node = createNode(parent, name, nodeDescriptor, overwrite,
                        versionables, checkin);
                    ignoreEntry.add(nodeDescriptor);
                } else {
                    node = createFolder(parent, name, overwrite);
                }

                // walk down the line
                if (node != null) {
                    installFromPath(bundle, entry, overwrite, versionables,
                        checkin, node);
                }

            } else {
               
                // file => create file
                URL file = bundle.getEntry(entry);
                if (ignoreEntry.contains(file)) {
                    // this is a consumed node descriptor
                    continue;
                }

                // install if it is a descriptor
                boolean foundProvider = false;
                final Iterator<String> ipIter = importProviders.keySet().iterator();
                while (!foundProvider && ipIter.hasNext()) {
                    final String ext = ipIter.next();
                    if (entry.endsWith(ext)) {
                        foundProvider = true;
                    }
                }
                if (foundProvider) {
                    if (createNode(parent, getName(entry), file, overwrite,
                        versionables, checkin) != null) {
                        ignoreEntry.add(file);
                        continue;
                    }
                }

                // otherwise just place as file
                try {
                    createFile(parent, file);
                } catch (IOException ioe) {
                    log.warn("Cannot create file node for {}", file, ioe);
                }
            }
        }
    }

    private Node createNode(Node parent, String name, URL nodeXML,
            boolean overwrite, List<Node> versionables, boolean checkin)
            throws RepositoryException {

        InputStream ins = null;
        try {
            // special treatment for system view imports
            if (nodeXML.getPath().toLowerCase().endsWith(EXT_JCR_XML)) {
                return importSystemView(parent, name, nodeXML);
            }

            NodeReader nodeReader = null;
            for (Map.Entry<String, ImportProvider> e : importProviders.entrySet()) {
                if (nodeXML.getPath().toLowerCase().endsWith(e.getKey())) {
                    nodeReader = e.getValue().getReader();
                    break;
                }
            }

            // cannot find out the type
            if (nodeReader == null) {
                return null;
            }

            ins = nodeXML.openStream();
            NodeDescription clNode = nodeReader.parse(ins);

            // nothing has been parsed
            if (clNode == null) {
                return null;
            }

            if (clNode.getName() == null) {
                // set the name without the [last] extension (xml or json)
                clNode.setName(toPlainName(name));
            }

            return createNode(parent, clNode, overwrite, versionables, checkin);
        } catch (RepositoryException re) {
            throw re;
        } catch (Throwable t) {
            throw new RepositoryException(t.getMessage(), t);
        } finally {
            if (ins != null) {
                try {
                    ins.close();
                } catch (IOException ignore) {
                }
            }
        }
    }

    /**
     * Delete the node from the initial content.
     *
     * @param parent
     * @param name
     * @param nodeXML
     * @throws RepositoryException
     */
    private void deleteNode(Node parent, String name)
            throws RepositoryException {
        if (parent.hasNode(name)) {
            parent.getNode(name).remove();
        }
    }

    private Node createNode(Node parentNode, NodeDescription clNode,
            final boolean overwrite, List<Node> versionables, boolean checkin)
            throws RepositoryException {
       
        // if node already exists but should be overwritten, delete it
        if (overwrite && parentNode.hasNode(clNode.getName())) {
            parentNode.getNode(clNode.getName()).remove();
        }

        // ensure repository node
        Node node;
        if (parentNode.hasNode(clNode.getName())) {

            // use existing node
            node = parentNode.getNode(clNode.getName());

        } else if (clNode.getPrimaryNodeType() == null) {

            // node explicit node type, use repository default
            node = parentNode.addNode(clNode.getName());

        } else {

            // explicit primary node type
            node = parentNode.addNode(clNode.getName(),
                clNode.getPrimaryNodeType());
        }

        return setupNode(node, clNode, versionables, checkin);
    }

    private Node setupNode(Node node, NodeDescription clNode,
            List<Node> versionables, boolean checkin)
            throws RepositoryException {

        // ammend mixin node types
        if (clNode.getMixinNodeTypes() != null) {
            for (String mixin : clNode.getMixinNodeTypes()) {
                if (!node.isNodeType(mixin)) {
                    node.addMixin(mixin);
                }
            }
        }

        // check if node is versionable
        boolean addToVersionables = checkin
            && node.isNodeType("mix:versionable");

        if (clNode.getProperties() != null) {
            for (PropertyDescription prop : clNode.getProperties()) {
                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()]);
                    node.setProperty(prop.getName(), values, type);

                } else if (type == PropertyType.REFERENCE) {

                    // need to resolve the reference
                    String propPath = node.getPath() + "/" + prop.getName();
                    String uuid = getUUID(node.getSession(), propPath,
                        prop.getValue());
                    if (uuid != null) {
                        node.setProperty(prop.getName(), uuid, type);
                    }

                } else if ("jcr:isCheckedOut".equals(prop.getName())) {

                    // don't try to write the property but record its state
                    // for later checkin if set to false
                    boolean checkedout = Boolean.valueOf(prop.getValue());
                    if (!checkedout) {
                        addToVersionables = true;
                    }

                } else {

                    node.setProperty(prop.getName(), prop.getValue(), type);

                }
            }
        }

        // add the current node to the list of versionables to be checked
        // in at the end. This is done if checkin is true and the node is
        // versionable or if the jcr:isCheckedOut property is false
        if (addToVersionables) {
            versionables.add(node);
        }

        // create child nodes from the descriptor
        if (clNode.getChildren() != null) {
            for (NodeDescription child : clNode.getChildren()) {
                createNode(node, child, false, versionables, checkin);
            }
        }

        // resolve REFERENCE property values pointing to this node
        resolveReferences(node);

        return node;
    }

    /**
     * Create a folder
     *
     * @param parent The parent node.
     * @param name The name of the folder
     * @param overwrite If set to true, an existing folder is removed first.
     * @return The node pointing to the folder.
     * @throws RepositoryException
     */
    private Node createFolder(Node parent, String name, final boolean overwrite)
            throws RepositoryException {
        if (parent.hasNode(name)) {
            if (overwrite) {
                parent.getNode(name).remove();
            } else {
                return parent.getNode(name);
            }
        }

        return parent.addNode(name, "nt:folder");
    }

    /**
     * Create a file from the given url.
     *
     * @param parent
     * @param source
     * @throws IOException
     * @throws RepositoryException
     */
    private void createFile(Node parent, URL source) throws IOException,
            RepositoryException {
        String name = getName(source.getPath());
        if (parent.hasNode(name)) {
            return;
        }

        URLConnection conn = source.openConnection();
        long lastModified = conn.getLastModified();
        String type = conn.getContentType();
        InputStream data = conn.getInputStream();

        // ensure content type
        if (type == null) {
            type = jcrContentHelper.getMimeType(name);
            if (type == null) {
                log.info(
                    "createFile: Cannot find content type for {}, using {}",
                    source.getPath(), DEFAULT_CONTENT_TYPE);
                type = DEFAULT_CONTENT_TYPE;
            }
        }

        // ensure sensible last modification date
        if (lastModified <= 0) {
            lastModified = System.currentTimeMillis();
        }

        Node file = parent.addNode(name, "nt:file");
        Node content = file.addNode("jcr:content", "nt:resource");
        content.setProperty("jcr:mimeType", type);
        content.setProperty("jcr:lastModified", lastModified);
        content.setProperty("jcr:data", data);
    }

    /**
     * Delete the file from the given url.
     *
     * @param parent
     * @param source
     * @throws IOException
     * @throws RepositoryException
     */
    private void deleteFile(Node parent, URL source) throws IOException,
            RepositoryException {
        String name = getName(source.getPath());
        if (parent.hasNode(name)) {
            parent.getNode(name).remove();
        }
    }

    private String getUUID(Session session, String propPath,
            String referencePath) throws RepositoryException {
        if (session.itemExists(referencePath)) {
            Item item = session.getItem(referencePath);
            if (item.isNode()) {
                Node refNode = (Node) item;
                if (refNode.isNodeType("mix:referenceable")) {
                    return refNode.getUUID();
                }
            }
        } else {
            // not existing yet, keep for delayed setting
            List<String> current = delayedReferences.get(referencePath);
            if (current == null) {
                current = new ArrayList<String>();
                delayedReferences.put(referencePath, current);
            }
            current.add(propPath);
        }

        // no UUID found
        return null;
    }

    private void resolveReferences(Node node) throws RepositoryException {
        List<String> props = delayedReferences.remove(node.getPath());
        if (props == null || props.size() == 0) {
            return;
        }

        // check whether we can set at all
        if (!node.isNodeType("mix:referenceable")) {
            return;
        }

        Session session = node.getSession();
        String uuid = node.getUUID();

        for (String property : props) {
            String name = getName(property);
            Node parentNode = getParentNode(session, property);
            if (parentNode != null) {
                parentNode.setProperty(name, uuid, PropertyType.REFERENCE);
            }
        }
    }

    /**
     * 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) {
        int lastSlash = path.lastIndexOf('/');
        String name = (lastSlash < 0) ? path : path.substring(lastSlash + 1);

        // check for encoded characters (%xx)
        // has encoded characters, need to decode
        if (name.indexOf('%') >= 0) {
            try {
                return URLDecoder.decode(name, "UTF-8");
            } catch (UnsupportedEncodingException uee) {
                // actually unexpected because UTF-8 is required by the spec
                log.error("Cannot decode "
                    + name
                    + " beause the platform has no support for UTF-8, using undecoded");
            } catch (Exception e) {
                // IllegalArgumentException or failure to decode
                log.error("Cannot decode " + name + ", using undecoded", e);
            }
        }

        // not encoded or problems decoding, return the name unmodified
        return name;
    }

    private Node getParentNode(Session session, String path)
            throws RepositoryException {
        int lastSlash = path.lastIndexOf('/');

        // not an absolute path, cannot find parent
        if (lastSlash < 0) {
            return null;
        }

        // node below root
        if (lastSlash == 0) {
            return session.getRootNode();
        }

        // item in the hierarchy
        path = path.substring(0, lastSlash);
        if (!session.itemExists(path)) {
            return null;
        }

        Item item = session.getItem(path);
        return (item.isNode()) ? (Node) item : null;
    }

    private Node getTargetNode(Session session, String path)
            throws RepositoryException {

        // not specyfied path directive
        if (path == null) return session.getRootNode();

        int firstSlash = path.indexOf("/");

        // it´s a relative path
        if (firstSlash != 0) path = "/" + path;

        Item item = session.getItem(path);
        return (item.isNode()) ? (Node) item : null;
    }

    private void uninstallContent(final Session session, final Bundle bundle,
            final Iterator<PathEntry> pathIter) {
        try {
            log.debug("Uninstalling initial content from bundle {}",
                bundle.getSymbolicName());
            while (pathIter.hasNext()) {
                final PathEntry entry = pathIter.next();
                if (entry.isUninstall()) {
                    Node targetNode = getTargetNode(session, entry.getTarget());
                    if (targetNode != null)
                        uninstallFromPath(bundle, entry.getPath(), targetNode);
                } else {
                    log.debug(
                        "Ignoring to uninstall content at {}, uninstall directive is not set.",
                        entry.getPath());
                }
            }

            // persist modifications now
            session.save();
            log.debug("Done uninstalling initial content from bundle {}",
                bundle.getSymbolicName());
        } catch (RepositoryException re) {
            log.error("Unable to uninstall initial content from bundle "
                + bundle.getSymbolicName(), re);
        } finally {
            try {
                if (session.hasPendingChanges()) {
                    session.refresh(false);
                }
            } catch (RepositoryException re) {
                log.warn(
                    "Failure to rollback uninstaling initial content for bundle {}",
                    bundle.getSymbolicName(), re);
            }
        }
    }

    /**
     * Handle content uninstallation for a single path.
     *
     * @param bundle The bundle containing the content.
     * @param path The path
     * @param parent The parent node.
     * @throws RepositoryException
     */
    private void uninstallFromPath(final Bundle bundle, final String path,
            final Node parent) throws RepositoryException {
        @SuppressWarnings("unchecked")
        Enumeration<String> entries = bundle.getEntryPaths(path);
        if (entries == null) {
            return;
        }

        Set<URL> ignoreEntry = new HashSet<URL>();

        // potential root node import/extension
        Descriptor rootNodeDescriptor = getRootNodeDescriptor(bundle, path);
        if (rootNodeDescriptor != null) {
            ignoreEntry.add(rootNodeDescriptor.rootNodeDescriptor);
        }

        while (entries.hasMoreElements()) {
            final String entry = entries.nextElement();
            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 name = getName(base);

                URL nodeDescriptor = null;
                for (String ext : importProviders.keySet()) {
                    nodeDescriptor = bundle.getEntry(base + ext);
                    if (nodeDescriptor != null) {
                        break;
                    }
                }

                final Node node;
                boolean delete = false;
                if (nodeDescriptor != null
                    && !ignoreEntry.contains(nodeDescriptor)) {
                    node = (parent.hasNode(toPlainName(name))
                            ? parent.getNode(toPlainName(name))
                            : null);
                    delete = true;
                } else {
                    node = (parent.hasNode(name) ? parent.getNode(name) : null);
                }

                if (node != null) {
                    // walk down the line
                    uninstallFromPath(bundle, entry, node);
                }

                if (delete) {
                    deleteNode(parent, toPlainName(name));
                    ignoreEntry.add(nodeDescriptor);
                }

            } else {
                // file => create file
                URL file = bundle.getEntry(entry);
                if (ignoreEntry.contains(file)) {
                    // this is a consumed node descriptor
                    continue;
                }

                // uninstall if it is a descriptor
                boolean foundProvider = false;
                final Iterator<String> ipIter = importProviders.keySet().iterator();
                while (!foundProvider && ipIter.hasNext()) {
                    final String ext = ipIter.next();
                    if (entry.endsWith(ext)) {
                        foundProvider = true;
                    }
                }
                if (foundProvider) {
                    deleteNode(parent, toPlainName(getName(entry)));
                    ignoreEntry.add(file);
                    continue;
                }

                // otherwise just delete the file
                try {
                    deleteFile(parent, file);
                } catch (IOException ioe) {
                    log.warn("Cannot delete file node for {}", file, ioe);
                }
            }
        }
    }

    /**
     * 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 {

        InputStream ins = null;
        try {

            // check whether we have the content already, nothing to do then
            name = toPlainName(name);
            if (parent.hasNode(name)) {
                log.debug(
                    "importSystemView: Node {} for XML {} already exists, nothing to to",
                    name, nodeXML);
                return parent.getNode(name);
            }

            ins = nodeXML.openStream();
            Session session = parent.getSession();
            session.importXML(parent.getPath(), ins, IMPORT_UUID_CREATE_NEW);

            // additionally check whether the expected child node exists
            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
                }
            }
        }

    }

    protected static final class Descriptor {
        public URL rootNodeDescriptor;

        public NodeReader nodeReader;
    }

    /**
     * Return the root node descriptor.
     */
    private Descriptor getRootNodeDescriptor(final Bundle bundle,
            final String path) {
        URL rootNodeDescriptor = null;

        for (Map.Entry<String, ImportProvider> e : importProviders.entrySet()) {
            if (e.getValue() != null) {
                rootNodeDescriptor = bundle.getEntry(path + ROOT_DESCRIPTOR
                    + e.getKey());
                if (rootNodeDescriptor != null) {
                    try {
                        final Descriptor d = new Descriptor();
                        d.rootNodeDescriptor = rootNodeDescriptor;
                        d.nodeReader = e.getValue().getReader();
                        return d;
                    } catch (IOException ioe) {
                        log.error("Unable to setup node reader for "
                            + e.getKey(), ioe);
                        return null;
                    }
                }
            }
        }
        return null;
    }

    /**
     * Imports mixin nodes and properties (and optionally child nodes) of the
     * root node.
     */
    private URL importRootNode(Session session, Bundle bundle, String path,
            List<Node> versionables, boolean checkin)
            throws RepositoryException {
        final Descriptor descriptor = getRootNodeDescriptor(bundle, path);
        // no root descriptor found
        if (descriptor == null) {
            return null;
        }

        InputStream ins = null;
        try {

            ins = descriptor.rootNodeDescriptor.openStream();
            NodeDescription clNode = descriptor.nodeReader.parse(ins);

            setupNode(session.getRootNode(), clNode, versionables, checkin);

            return descriptor.rootNodeDescriptor;
        } catch (RepositoryException re) {
            throw re;
        } catch (Throwable t) {
            throw new RepositoryException(t.getMessage(), t);
        } finally {
            if (ins != null) {
                try {
                    ins.close();
                } catch (IOException ignore) {
                }
            }
        }

    }

    private String toPlainName(String name) {
        String providerExt = null;
        final Iterator<String> ipIter = importProviders.keySet().iterator();
        while (providerExt == null && ipIter.hasNext()) {
            final String ext = ipIter.next();
            if (name.endsWith(ext)) {
                providerExt = ext;
            }
        }
        if (providerExt != null) {
            return name.substring(0, name.length() - providerExt.length());
        }
        return name;

    }
}
TOP

Related Classes of org.apache.sling.jcr.contentloader.internal.Loader$Descriptor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.