info.magnolia.cms.module.ModuleUtil.java Source code

Java tutorial

Introduction

Here is the source code for info.magnolia.cms.module.ModuleUtil.java

Source

/**
 *
 * Magnolia and its source-code is licensed under the LGPL.
 * You may copy, adapt, and redistribute this file for commercial or non-commercial use.
 * When copying, adapting, or redistributing this document in keeping with the guidelines above,
 * you are required to provide proper attribution to obinary.
 * If you reproduce or distribute the document without making any substantive modifications to its content,
 * please use the following attribution line:
 *
 * Copyright 1993-2006 obinary Ltd. (http://www.obinary.com) All rights reserved.
 *
 */
package info.magnolia.cms.module;

import info.magnolia.cms.beans.config.ConfigurationException;
import info.magnolia.cms.beans.config.ContentRepository;
import info.magnolia.cms.core.Content;
import info.magnolia.cms.core.HierarchyManager;
import info.magnolia.cms.core.ItemType;
import info.magnolia.cms.core.Path;
import info.magnolia.cms.core.SystemProperty;
import info.magnolia.cms.core.ie.DataTransporter;
import info.magnolia.cms.security.AccessDeniedException;
import info.magnolia.cms.security.Permission;
import info.magnolia.cms.security.Role;
import info.magnolia.cms.security.Security;
import info.magnolia.cms.util.ClasspathResourcesUtil;
import info.magnolia.cms.util.ContentUtil;
import info.magnolia.context.MgnlContext;
import info.magnolia.repository.Provider;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.jcr.ImportUUIDBehavior;
import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;

import org.apache.commons.collections.map.ListOrderedMap;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.jdom.Comment;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.jdom.xpath.XPath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This is a util providing some methods for the registration process of a module.
 * @author philipp
 * @version $Revision: 3331 $ ($Author: philipp $)
 */
public final class ModuleUtil {

    /**
     * Logger
     */

    private static Logger log = LoggerFactory.getLogger(ModuleUtil.class);

    /**
     * Util has no public constructor
     */
    private ModuleUtil() {
    }

    /**
     * registers the properties in the repository
     * @param hm
     * @param name
     * @throws IOException
     * @throws RepositoryException
     * @throws PathNotFoundException
     * @throws AccessDeniedException
     */
    public static void registerProperties(HierarchyManager hm, String name)
            throws IOException, AccessDeniedException, PathNotFoundException, RepositoryException {
        Map map = new ListOrderedMap();

        // not using properties since they are not ordered
        // Properties props = new Properties();
        // props.load(ModuleUtil.class.getResourceAsStream("/" + name.replace('.', '/') + ".properties"));
        InputStream stream = ModuleUtil.class.getResourceAsStream("/" + name.replace('.', '/') + ".properties"); //$NON-NLS-1$ //$NON-NLS-2$
        LineNumberReader lines = new LineNumberReader(new InputStreamReader(stream));

        String line = lines.readLine();
        while (line != null) {
            line = line.trim();
            if (line.length() > 0 && !line.startsWith("#")) { //$NON-NLS-1$
                String key = StringUtils.substringBefore(line, "=").trim(); //$NON-NLS-1$
                String value = StringUtils.substringAfter(line, "=").trim(); //$NON-NLS-1$
                map.put(key, value);
            }
            line = lines.readLine();
        }
        IOUtils.closeQuietly(lines);
        IOUtils.closeQuietly(stream);
        registerProperties(hm, map);
    }

    public static void registerProperties(HierarchyManager hm, Map map)
            throws AccessDeniedException, PathNotFoundException, RepositoryException {
        for (Iterator iter = map.keySet().iterator(); iter.hasNext();) {
            String key = (String) iter.next();
            String value = (String) map.get(key);

            String name = StringUtils.substringAfterLast(key, "."); //$NON-NLS-1$
            String path = StringUtils.substringBeforeLast(key, ".").replace('.', '/'); //$NON-NLS-1$
            Content node = ContentUtil.createPath(hm, path);
            node.createNodeData(name).setValue(value);
        }
    }

    public static void bootstrap(String[] resourceNames) throws IOException, RegisterException {

        HierarchyManager hm = MgnlContext.getHierarchyManager(ContentRepository.CONFIG);

        // sort by length --> import parent node first
        List list = new ArrayList(Arrays.asList(resourceNames));

        Collections.sort(list, new Comparator() {

            public int compare(Object name1, Object name2) {
                return ((String) name1).length() - ((String) name2).length();
            }
        });

        for (Iterator iter = list.iterator(); iter.hasNext();) {
            String resourceName = (String) iter.next();

            // windows again
            resourceName = StringUtils.replace(resourceName, "\\", "/");

            String name = StringUtils.removeEnd(StringUtils.substringAfterLast(resourceName, "/"), ".xml");

            String repository = StringUtils.substringBefore(name, ".");
            String pathName = StringUtils.substringAfter(StringUtils.substringBeforeLast(name, "."), "."); //$NON-NLS-1$
            String nodeName = StringUtils.substringAfterLast(name, ".");
            String fullPath;
            if (StringUtils.isEmpty(pathName)) {
                pathName = "/";
                fullPath = "/" + nodeName;
            } else {
                pathName = "/" + StringUtils.replace(pathName, ".", "/");
                fullPath = pathName + "/" + nodeName;
            }

            // if the path already exists --> delete it
            try {
                if (hm.isExist(fullPath)) {
                    hm.delete(fullPath);
                    if (log.isDebugEnabled())
                        log.debug("already existing node [{}] deleted", fullPath);
                }

                // if the parent path not exists just create it
                if (!pathName.equals("/")) {
                    ContentUtil.createPath(hm, pathName, ItemType.CONTENT);
                }
            } catch (Exception e) {
                throw new RegisterException("can't register bootstrap file: [" + name + "]", e);
            }
            InputStream stream = ModuleUtil.class.getResourceAsStream(resourceName);
            DataTransporter.executeImport(pathName, repository, stream, name, false,
                    ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING, true, true);
        }
    }

    /**
     * Extracts files of a jar and stores them in the magnolia file structure
     * @param names a list of resource names
     * @param prefix prefix which is not part of the magolia path (in common 'mgnl-files')
     * @throws Exception io exception
     */
    public static void installFiles(String[] names, String prefix) throws Exception {

        String root = null;
        // Try to get root
        try {
            File f = new File(SystemProperty.getProperty(SystemProperty.MAGNOLIA_APP_ROOTDIR));
            if (f.isDirectory()) {
                root = f.getAbsolutePath();
            }
        } catch (Exception e) {
            // nothing
        }

        if (root == null) {
            throw new Exception("Invalid magnolia " + SystemProperty.MAGNOLIA_APP_ROOTDIR + " path"); //$NON-NLS-1$ //$NON-NLS-2$
        }

        // Loop throgh files and check writeable
        String error = StringUtils.EMPTY;
        for (int j = 0; j < names.length; j++) {
            String name = names[j];

            InputStream resourceStream = ClasspathResourcesUtil.getStream(name, false);

            File targetFile = new File(Path.getAbsoluteFileSystemPath(StringUtils.removeStart(name, prefix)));

            String s = StringUtils.EMPTY;
            if (!targetFile.getParentFile().exists() && !targetFile.getParentFile().mkdirs()) {
                s = "Can't create directories for " + targetFile.getAbsolutePath(); //$NON-NLS-1$
            } else if (!targetFile.getParentFile().canWrite()) {
                s = "Can't write to " + targetFile.getAbsolutePath(); //$NON-NLS-1$
            }
            if (s.length() > 0) {
                if (error.length() > 0) {
                    error += "\r\n"; //$NON-NLS-1$
                }
                error += s;
            }

            OutputStream out = new FileOutputStream(targetFile);
            IOUtils.copy(resourceStream, out);

            IOUtils.closeQuietly(resourceStream);
            IOUtils.closeQuietly(out);
        }

        if (error.length() > 0) {
            throw new Exception("Errors while installing files: " + error); //$NON-NLS-1$
        }

    }

    /**
     * Create a minimal module configuration
     * @param node the module node
     * @param name module name
     * @param className the class used
     * @param version version number of the module
     * @return the modified node (not yet stored)
     * @throws AccessDeniedException exception
     * @throws PathNotFoundException exception
     * @throws RepositoryException exception
     */
    public static Content createMinimalConfiguration(Content node, String name, String displayName,
            String className, String version)
            throws AccessDeniedException, PathNotFoundException, RepositoryException {
        node.createNodeData("version").setValue(version); //$NON-NLS-1$
        node.createNodeData("name").setValue(name); //$NON-NLS-1$
        node.createNodeData("displayName").setValue(displayName); //$NON-NLS-1$
        node.createNodeData("class").setValue(className); //$NON-NLS-1$
        node.createContent("config"); //$NON-NLS-1$
        Content license = node.createContent("license", ItemType.CONTENTNODE); //$NON-NLS-1$
        license.createNodeData("key"); //$NON-NLS-1$
        license.createNodeData("owner"); //$NON-NLS-1$

        return node;
    }

    /**
     * Register a servlet based on the definition of the modules xml descriptor
     * @param servlet
     * @throws JDOMException
     * @throws IOException
     */
    public static boolean registerServlet(ServletDefinition servlet) throws JDOMException, IOException {
        String[] urlPatterns = (String[]) servlet.getMappings().toArray(new String[servlet.getMappings().size()]);
        Hashtable params = new Hashtable();
        for (Iterator iter = servlet.getParams().iterator(); iter.hasNext();) {
            ServletParameterDefinition param = (ServletParameterDefinition) iter.next();
            params.put(param.getName(), param.getValue());
        }
        return registerServlet(servlet.getName(), servlet.getClassName(), urlPatterns, servlet.getComment(),
                params);
    }

    /**
     * Register a servlet in the web.xml. The code checks if the servlet already exists
     * @param name
     * @param className
     * @param urlPatterns
     * @param comment
     * @throws JDOMException
     * @throws IOException
     */
    public static boolean registerServlet(String name, String className, String[] urlPatterns, String comment)
            throws JDOMException, IOException {
        return registerServlet(name, className, urlPatterns, comment, null);
    }

    /**
     * Register a servlet in the web.xml including init parameters. The code checks if the servlet already exists
     * @param name
     * @param className
     * @param urlPatterns
     * @param comment
     * @param initParams
     * @throws JDOMException
     * @throws IOException
     */
    public static boolean registerServlet(String name, String className, String[] urlPatterns, String comment,
            Hashtable initParams) throws JDOMException, IOException {

        boolean changed = false;

        // get the web.xml
        File source = new File(Path.getAppRootDir() + "/WEB-INF/web.xml");
        if (!source.exists()) {
            throw new FileNotFoundException("Failed to locate web.xml " //$NON-NLS-1$
                    + source.getAbsolutePath());
        }
        SAXBuilder builder = new SAXBuilder();
        Document doc = builder.build(source);

        // check if there already registered
        XPath xpath = XPath.newInstance("/webxml:web-app/webxml:servlet[webxml:servlet-name='" + name + "']");
        // must add the namespace and use it: there is no default namespace elsewise
        xpath.addNamespace("webxml", doc.getRootElement().getNamespace().getURI());
        Element node = (Element) xpath.selectSingleNode(doc);

        if (node == null) {
            log.info("register servlet " + name);

            // make a nice comment
            doc.getRootElement().addContent(new Comment(comment));

            // the same name space must be used
            Namespace ns = doc.getRootElement().getNamespace();

            node = new Element("servlet", ns);
            node.addContent(new Element("servlet-name", ns).addContent(name));
            node.addContent(new Element("servlet-class", ns).addContent(className));

            if (initParams != null && !(initParams.isEmpty())) {
                Enumeration params = initParams.keys();
                while (params.hasMoreElements()) {
                    String paramName = params.nextElement().toString();
                    String paramValue = (String) initParams.get(paramName);
                    Element initParam = new Element("init-param", ns);
                    initParam.addContent(new Element("param-name", ns).addContent(paramName));
                    initParam.addContent(new Element("param-value", ns).addContent(paramValue));
                    node.addContent(initParam);
                }
            }

            doc.getRootElement().addContent(node);
            changed = true;
        } else {
            log.info("servlet {} already registered", name);
        }
        for (int i = 0; i < urlPatterns.length; i++) {
            String urlPattern = urlPatterns[i];
            changed = changed | registerServletMapping(doc, name, urlPattern, comment);
        }

        if (changed) {
            XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
            outputter.output(doc, new FileWriter(source));
        }
        return changed;
    }

    public static boolean registerServletMapping(Document doc, String name, String urlPattern, String comment)
            throws JDOMException {
        XPath xpath = XPath.newInstance("/webxml:web-app/webxml:servlet-mapping[webxml:servlet-name='" + name
                + "' and webxml:url-pattern='" + urlPattern + "']");
        // must add the namespace and use it: there is no default namespace elsewise
        xpath.addNamespace("webxml", doc.getRootElement().getNamespace().getURI());
        Element node = (Element) xpath.selectSingleNode(doc);

        if (node == null) {
            log.info("register servlet mapping [{}] for servlet [{}]", urlPattern, name);

            // make a nice comment
            doc.getRootElement().addContent(new Comment(comment));

            // the same name space must be used
            Namespace ns = doc.getRootElement().getNamespace();

            // create the mapping
            node = new Element("servlet-mapping", ns);
            node.addContent(new Element("servlet-name", ns).addContent(name));
            node.addContent(new Element("url-pattern", ns).addContent(urlPattern));
            doc.getRootElement().addContent(node);
            return true;

        }

        log.info("servlet mapping [{}] for servlet [{}] allready registered", urlPattern, name);
        return false;
    }

    public static boolean registerRepository(String name) throws RegisterException {
        return registerRepository(name, null);
    }

    /**
     * Register a repository
     * @param repositoryName
     * @param nodeTypeFile
     * @return <code>true</code> if a repository is registered or <code>false</code> if it was already existing
     * @throws RegisterException
     */
    public static boolean registerRepository(String repositoryName, String nodeTypeFile) throws RegisterException {

        Document doc;
        try {
            doc = getRepositoryDefinitionDocument();
        } catch (JDOMException e) {
            throw new RegisterException("Failed to read magnolia repositories config file", e);
        } catch (IOException e) {
            throw new RegisterException("Failed to read magnolia repositories config file", e);
        }

        // check if there

        try {
            Element node = (Element) XPath.selectSingleNode(doc, "/JCR/Repository[@name='" + repositoryName + "']");
            if (node == null) {
                // create
                node = new Element("Repository");

                String provider = ((Element) XPath.selectSingleNode(doc, "/JCR/Repository[@name='magnolia']"))
                        .getAttributeValue("provider");
                String configFile = ((Element) XPath.selectSingleNode(doc,
                        "/JCR/Repository[@name='magnolia']/param[@name='configFile']")).getAttributeValue("value");
                String repositoryHome = ((Element) XPath.selectSingleNode(doc,
                        "/JCR/Repository[@name='magnolia']/param[@name='repositoryHome']"))
                                .getAttributeValue("value");
                repositoryHome = StringUtils.substringBeforeLast(repositoryHome, "/") + "/" + repositoryName;
                String contextFactoryClass = ((Element) XPath.selectSingleNode(doc,
                        "/JCR/Repository[@name='magnolia']/param[@name='contextFactoryClass']"))
                                .getAttributeValue("value");
                String providerURL = ((Element) XPath.selectSingleNode(doc,
                        "/JCR/Repository[@name='magnolia']/param[@name='providerURL']")).getAttributeValue("value");
                String bindName = ((Element) XPath.selectSingleNode(doc,
                        "/JCR/Repository[@name='magnolia']/param[@name='bindName']")).getAttributeValue("value");
                bindName = StringUtils.replace(bindName, "magnolia", repositoryName);

                node.setAttribute("name", repositoryName);
                node.setAttribute("id", repositoryName);
                node.setAttribute("loadOnStartup", "true");
                node.setAttribute("provider", provider);

                node.addContent(
                        new Element("param").setAttribute("name", "configFile").setAttribute("value", configFile));
                node.addContent(new Element("param").setAttribute("name", "repositoryHome").setAttribute("value",
                        repositoryHome));
                node.addContent(new Element("param").setAttribute("name", "contextFactoryClass")
                        .setAttribute("value", contextFactoryClass));
                node.addContent(new Element("param").setAttribute("name", "providerURL").setAttribute("value",
                        providerURL));
                node.addContent(
                        new Element("param").setAttribute("name", "bindName").setAttribute("value", bindName));

                if (StringUtils.isNotEmpty(nodeTypeFile)) {
                    node.addContent(new Element("param").setAttribute("name", "customNodeTypes")
                            .setAttribute("value", nodeTypeFile));
                }

                // add a workspace
                node.addContent(new Element("workspace").setAttribute("name", repositoryName));

                doc.getRootElement().addContent(node);

                // make the mapping
                node = new Element("Map");
                node.setAttribute("name", repositoryName).setAttribute("repositoryName", repositoryName);
                // add it
                doc.getRootElement().getChild("RepositoryMapping").addContent(node);

                // save it
                XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
                outputter.output(doc, new FileWriter(getRepositoryDefinitionFile()));

                return true;
            } else if (nodeTypeFile != null) {
                // this never requires a restart
                ModuleUtil.registerNodetypes(repositoryName, nodeTypeFile);
                return false;
            }
        } catch (IOException e) {
            log.error("Can't register repository. Unable to write modified file.", e);
            throw new RegisterException("Can't register repository. Unable to write modified file.", e);
        } catch (JDOMException e) {
            log.error("can't register repository", e);
            throw new RegisterException("can't register repository", e);
        }

        return false;
    }

    public static boolean registerNodetypes(String repositoryName, String customNodetypes)
            throws RegisterException {
        Provider provider = ContentRepository.getRepositoryProvider(repositoryName);

        if (provider == null) {
            throw new RegisterException(
                    "before registering nodetypes you need to register the repository [" + repositoryName + "]");
        }

        try {
            provider.registerNodeTypes(customNodetypes);
        } catch (RepositoryException e) {
            throw new RegisterException(
                    "Error registering nodetypes for repository [" + repositoryName + "]: " + e.getMessage(), e);
        }

        // this never requires a restart
        return false;

    }

    /**
     * Read the dom for the repositories.xml
     */
    public static Document getRepositoryDefinitionDocument() throws JDOMException, IOException {
        File source = getRepositoryDefinitionFile();
        SAXBuilder builder = new SAXBuilder();
        return builder.build(source);
    }

    /**
     * @return
     * @throws FileNotFoundException
     */
    public static File getRepositoryDefinitionFile() throws FileNotFoundException {
        File source = Path.getRepositoriesConfigFile();
        if (!source.exists()) {
            throw new FileNotFoundException("Failed to locate magnolia repositories config file at " //$NON-NLS-1$
                    + source.getAbsolutePath());
        }
        return source;
    }

    /**
     * @param repositoryName
     * @param workspaceName
     * @throws RegisterException
     * @throws IOException
     * @throws JDOMException
     */
    public static boolean registerWorkspace(String repositoryName, String workspaceName) throws RegisterException {

        Provider provider = ContentRepository.getRepositoryProvider(repositoryName);

        if (provider != null) {
            try {
                // immediate registration
                provider.registerWorkspace(repositoryName);
                ContentRepository.addMappedRepositoryName(workspaceName, repositoryName);
            } catch (RepositoryException e) {
                throw new RegisterException("Can't register workspace [" + workspaceName + "]", e);
            }
        }

        boolean changed = false;

        try {
            Document doc = getRepositoryDefinitionDocument();
            // check if there
            Element repositoryNode = (Element) XPath.selectSingleNode(doc,
                    "/JCR/Repository[@name='" + repositoryName + "']");
            if (repositoryNode == null) {
                throw new ConfigurationException("before registering a workspace [" + workspaceName
                        + "] you need to register the repository [" + repositoryName + "]");
            }

            // check if existing
            Element workspaceNode = (Element) XPath.selectSingleNode(doc,
                    "/JCR/Repository[@name='" + repositoryName + "']/workspace[@name='" + workspaceName + "']");
            if (workspaceNode == null) {
                workspaceNode = new Element("workspace");
                workspaceNode.setAttribute("name", workspaceName);
                repositoryNode.addContent(workspaceNode);
                changed = true;
            }

            // make the mapping
            Element mappingNode = (Element) XPath.selectSingleNode(doc,
                    "/JCR/RepositoryMapping/Map[@name='" + workspaceName + "']");
            if (mappingNode == null) {
                mappingNode = new Element("Map");
                mappingNode.setAttribute("name", workspaceName).setAttribute("repositoryName", repositoryName);
                // add it
                doc.getRootElement().getChild("RepositoryMapping").addContent(mappingNode);
                changed = true;
            }

            // save it
            if (changed) {
                XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
                outputter.output(doc, new FileWriter(getRepositoryDefinitionFile()));
            }
        } catch (Exception e) {
            log.error("Can't register workspace [" + workspaceName + "]", e);
            throw new RegisterException("can't register workspace [" + workspaceName + "]", e);
        }

        return changed;
    }

    /**
     * Grant the superuser role by default
     */
    public static void grantRepositoryToSuperuser(String repository) {
        Role superuser = Security.getRoleManager().getRole("superuser");
        superuser.addPermission(repository, "/*", Permission.ALL);
    }

    /**
     * Register the repository to get used for activation
     * @param repository
     */
    public static void subscribeRepository(String repository) {
        Content subscribers = ContentUtil.getContent(ContentRepository.CONFIG, "/subscribers/SubscriberConfig");
        if (subscribers == null) {
            return;
        }
        for (Iterator iter = subscribers.getChildren(ItemType.CONTENTNODE).iterator(); iter.hasNext();) {
            Content subscriber = (Content) iter.next();
            Content context = ContentUtil.getCaseInsensitive(subscriber, "Context");
            try {
                if (context != null && !context.hasContent(repository)) {
                    Content rep = context.createContent(repository, ItemType.CONTENTNODE);
                    Content entry = rep.createContent("0001", ItemType.CONTENTNODE);
                    entry.createNodeData("subscribedURI").setValue("/");
                }
            } catch (RepositoryException e) {
                log.error("wasn't able to subscribe repository [" + repository + "]", e);
            }
        }
        try {
            subscribers.save();
        } catch (RepositoryException e) {
        }
    }
}