it.openutils.mgnlaws.magnolia.init.ClasspathProviderImpl.java Source code

Java tutorial

Introduction

Here is the source code for it.openutils.mgnlaws.magnolia.init.ClasspathProviderImpl.java

Source

/**
 *
 * Amazon AWS consciousness for Magnolia CMS (http://www.openmindlab.com/lab/products/mgnlaws.html)
 * Copyright(C) 2013-2012, Openmind S.r.l. http://www.openmindonline.it
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package it.openutils.mgnlaws.magnolia.init;

import info.magnolia.cms.beans.config.ContentRepository;
import info.magnolia.cms.core.Path;
import info.magnolia.cms.core.SystemProperty;
import info.magnolia.jackrabbit.MissingNodetypesException;
import info.magnolia.repository.Provider;
import info.magnolia.repository.RepositoryMapping;
import info.magnolia.repository.RepositoryNotInitializedException;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;

import javax.jcr.NamespaceException;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.jcr.Workspace;
import javax.jcr.nodetype.NoSuchNodeTypeException;
import javax.jcr.nodetype.NodeTypeManager;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
import javax.xml.transform.TransformerFactoryConfigurationError;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.SystemUtils;
import org.apache.jackrabbit.core.WorkspaceImpl;
import org.apache.jackrabbit.core.jndi.RegistryHelper;
import org.apache.jackrabbit.core.nodetype.NodeTypeManagerImpl;
import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
import org.apache.jackrabbit.core.nodetype.xml.NodeTypeReader;
import org.apache.jackrabbit.spi.Name;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Provider implementation for Apache JackRabbit JCR repository.
 * @author Sameer Charles
 * @author Fabrizio Giustina
 * @version $Id: ClasspathProviderImpl.java 10433 2012-07-23 11:04:39Z fabian.necci $
 */
public class ClasspathProviderImpl implements Provider {

    /**
     * Magnolia property (entry in magnolia.properties) with the cluster id, which will be passed to jackrabbit.
     */
    private static final String MAGNOLIA_CLUSTERID_PROPERTY = "magnolia.clusterid";

    /**
     * Jackrabbit system property for cluster node id.
     */
    private static final String JACKRABBIT_CLUSTER_ID_PROPERTY = "org.apache.jackrabbit.core.cluster.node_id";

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

    private static final String CONFIG_FILENAME_KEY = "configFile"; //$NON-NLS-1$

    private static final String REPOSITORY_HOME_KEY = "repositoryHome"; //$NON-NLS-1$

    private static final String CONTEXT_FACTORY_CLASS_KEY = "contextFactoryClass"; //$NON-NLS-1$

    private static final String PROVIDER_URL_KEY = "providerURL"; //$NON-NLS-1$

    private static final String BIND_NAME_KEY = "bindName"; //$NON-NLS-1$

    private static final String MGNL_NODETYPES = "/mgnl-nodetypes/magnolia-nodetypes.xml"; //$NON-NLS-1$

    private static final String CUSTOM_NODETYPES = "customNodeTypes"; //$NON-NLS-1$

    private RepositoryMapping repositoryMapping;

    private Repository repository;

    private String bindName;

    private Hashtable<String, Object> jndiEnv;

    private static final String REPO_HOME_PREFIX = "${repository.home}";

    private static final int REPO_HOME_SUFIX_LEN = REPO_HOME_PREFIX.length();

    private static final String sysRepositoryHome = System.getProperty("repository.home");

    private static final String sysRepositoryHomes = System.getProperty("repository.homes");

    /**
     * Finds the physical path to the repository folder.
     * @param repositoryHome the property set in the repository.xml file
     * @return the full path resolved to the repository dir
     */
    private String getRepositoryHome(final String repositoryHome) {
        boolean relocate = false;
        String tmp = repositoryHome;
        if (repositoryHome.startsWith(REPOSITORY_HOME_KEY)) {
            tmp = repositoryHome.substring(REPO_HOME_SUFIX_LEN);
            relocate = true;
        }
        /*
         * Resolve if the path started with the suffix
         */
        if (sysRepositoryHome != null && relocate) {
            return sysRepositoryHome + File.separator + tmp;
        }

        /*
         * This is apply to all repositories if the java property is set
         */
        if (sysRepositoryHomes != null) {
            return sysRepositoryHomes + File.separator + tmp;
        }

        /*
         * Return the same value as before if neither of the above applied
         */
        return Path.getAbsoluteFileSystemPath(tmp);
    }

    /**
     * @see info.magnolia.repository.Provider#init(info.magnolia.repository.RepositoryMapping)
     */
    public void init(RepositoryMapping repositoryMapping) throws RepositoryNotInitializedException {
        checkXmlSettings();

        this.repositoryMapping = repositoryMapping;
        /* connect to repository */
        Map params = this.repositoryMapping.getParameters();
        String configFile = (String) params.get(CONFIG_FILENAME_KEY);
        if (!StringUtils.startsWith(configFile, ClasspathPropertiesInitializer.CLASSPATH_PREFIX)) {
            configFile = Path.getAbsoluteFileSystemPath(configFile);
        }
        String repositoryHome = (String) params.get(REPOSITORY_HOME_KEY);
        repositoryHome = getRepositoryHome(repositoryHome);

        // cleanup the path, to remove eventual ../.. and make it absolute
        try {
            File repoHomeFile = new File(repositoryHome);
            repositoryHome = repoHomeFile.getCanonicalPath();
        } catch (IOException e1) {
            // should never happen and it's not a problem at this point, just pass it to jackrabbit and see
        }

        String clusterid = SystemProperty.getProperty(MAGNOLIA_CLUSTERID_PROPERTY);
        if (StringUtils.isNotBlank(clusterid)) {
            System.setProperty(JACKRABBIT_CLUSTER_ID_PROPERTY, clusterid);
        }

        // get it back from system properties, if it has been set elsewhere
        clusterid = System.getProperty(JACKRABBIT_CLUSTER_ID_PROPERTY);

        log.info("Loading repository at {} (config file: {}) - cluster id: \"{}\"",
                new Object[] { repositoryHome, configFile, StringUtils.defaultString(clusterid, "<unset>") });

        bindName = (String) params.get(BIND_NAME_KEY);
        jndiEnv = new Hashtable<String, Object>();
        jndiEnv.put(Context.INITIAL_CONTEXT_FACTORY, params.get(CONTEXT_FACTORY_CLASS_KEY));
        jndiEnv.put(Context.PROVIDER_URL, params.get(PROVIDER_URL_KEY));

        try {
            InitialContext ctx = new InitialContext(jndiEnv);
            // first try to find the existing object if any
            try {
                this.repository = (Repository) ctx.lookup(bindName);
            } catch (NameNotFoundException ne) {
                log.debug("No JNDI bound Repository found with name {}, trying to initialize a new Repository",
                        bindName);
                ClasspathRegistryHelper.registerRepository(ctx, bindName, configFile, repositoryHome, true);
                this.repository = (Repository) ctx.lookup(bindName);
            }
            this.validateWorkspaces();
        } catch (NamingException e) {
            log.error("Unable to initialize repository: " + e.getMessage(), e);
            throw new RepositoryNotInitializedException(e);
        } catch (RepositoryException e) {
            log.error("Unable to initialize repository: " + e.getMessage(), e);
            throw new RepositoryNotInitializedException(e);
        } catch (TransformerFactoryConfigurationError e) {
            log.error("Unable to initialize repository: " + e.getMessage(), e);
            throw new RepositoryNotInitializedException(e);
        }
    }

    public void shutdownRepository() {
        log.info("Shutting down repository bound to '{}'", bindName);

        try {
            Context ctx = new InitialContext(jndiEnv);
            RegistryHelper.unregisterRepository(ctx, bindName);
        } catch (NamingException e) {
            log.warn("Unable to shutdown repository " + bindName + ": " + e.getMessage(), e);
        } catch (Throwable e) {
            log.error("Failed to shutdown repository " + bindName + ": " + e.getMessage(), e);
        }
    }

    /**
     * @deprecated typo - use get #getUnderlyingRepository() - since 4.0
     */
    public Repository getUnderlineRepository() throws RepositoryNotInitializedException {
        return getUnderlyingRepository();
    }

    public Repository getUnderlyingRepository() throws RepositoryNotInitializedException {
        if (this.repository == null) {
            throw new RepositoryNotInitializedException("Null repository"); //$NON-NLS-1$
        }
        return this.repository;
    }

    /**
     * @see info.magnolia.repository.Provider#registerNamespace(java.lang.String, java.lang.String, javax.jcr.Workspace)
     */
    public void registerNamespace(String namespacePrefix, String uri, Workspace workspace)
            throws RepositoryException {
        try {
            workspace.getNamespaceRegistry().getURI(namespacePrefix);
        } catch (NamespaceException e) {
            if (log.isDebugEnabled()) {
                log.debug(e.getMessage());
            }
            log.info("Registering prefix [{}] with URI {}", namespacePrefix, uri); //$NON-NLS-1$
            workspace.getNamespaceRegistry().registerNamespace(namespacePrefix, uri);
        }
    }

    /**
     * @see info.magnolia.repository.Provider#unregisterNamespace(java.lang.String, javax.jcr.Workspace)
     */
    public void unregisterNamespace(String prefix, Workspace workspace) throws RepositoryException {
        workspace.getNamespaceRegistry().unregisterNamespace(prefix);
    }

    /**
     * @see info.magnolia.repository.Provider#registerNodeTypes(String)
     */
    public void registerNodeTypes() throws RepositoryException {
        registerNodeTypes(StringUtils.EMPTY);
    }

    /**
     * @see info.magnolia.repository.Provider#registerNodeTypes(java.lang.String)
     */
    public void registerNodeTypes(String configuration) throws RepositoryException {
        if (StringUtils.isEmpty(configuration)) {
            configuration = (String) this.repositoryMapping.getParameters().get(CUSTOM_NODETYPES);
        }

        InputStream xml = getNodeTypeDefinition(configuration);
        this.registerNodeTypes(xml);
    }

    /**
     * @see info.magnolia.repository.Provider#registerNodeTypes(java.io.InputStream)
     */
    public void registerNodeTypes(InputStream xmlStream) throws RepositoryException {
        SimpleCredentials credentials = new SimpleCredentials(ContentRepository.REPOSITORY_USER,
                ContentRepository.REPOSITORY_PSWD.toCharArray());
        Session jcrSession = this.repository.login(credentials);

        try {

            Workspace workspace = jcrSession.getWorkspace();

            // should never happen
            if (xmlStream == null) {
                throw new MissingNodetypesException();
            }

            // Use Objects so that it works both with jackrabbit 1.x (NodeTypeDef) and jackrabbit 2
            // (QNodeTypeDefinition)
            Object[] types;

            try {
                types = (Object[]) NodeTypeReader.class.getMethod("read", new Class[] { InputStream.class })
                        .invoke(null, new Object[] { xmlStream });
            } catch (Exception e) {
                throw new RepositoryException(e.getMessage(), e);
            } finally {
                IOUtils.closeQuietly(xmlStream);
            }

            NodeTypeManager ntMgr = workspace.getNodeTypeManager();
            NodeTypeRegistry ntReg;
            try {
                ntReg = ((NodeTypeManagerImpl) ntMgr).getNodeTypeRegistry();
            } catch (ClassCastException e) {
                // this could happen if the repository provider does not have proper Shared API for the
                // application server like at the moment in Jackrabbit
                log.debug("Failed to get NodeTypeRegistry: ", e);
                return;
            }

            for (int j = 0; j < types.length; j++) {
                Object def = types[j];

                Name ntname;
                try {
                    ntname = (Name) PropertyUtils.getProperty(def, "name");
                } catch (Exception e) {
                    throw new RepositoryException(e.getMessage(), e);
                }

                try {

                    // return value has changed in jackrabbit 2, we still have to use reflection here
                    // ntReg.getNodeTypeDef(ntname);

                    Method method = ntReg.getClass().getMethod("getNodeTypeDef", Name.class);
                    method.invoke(ntReg, ntname);
                } catch (IllegalArgumentException e) {
                    throw new RepositoryException(e.getMessage(), e);
                } catch (IllegalAccessException e) {
                    throw new RepositoryException(e.getMessage(), e);
                } catch (SecurityException e) {
                    throw new RepositoryException(e.getMessage(), e);
                } catch (NoSuchMethodException e) {
                    throw new RepositoryException(e.getMessage(), e);
                } catch (InvocationTargetException ite) {
                    if (ite.getTargetException() instanceof NoSuchNodeTypeException) {
                        log.info("Registering nodetype {} on repository {}", ntname, repositoryMapping.getName()); //$NON-NLS-1$

                        try {
                            // reflection for jackrabbit 1+2 compatibility
                            getMethod(NodeTypeRegistry.class, "registerNodeType").invoke(ntReg,
                                    new Object[] { def });
                        } catch (Exception e) {
                            throw new RepositoryException(e.getMessage(), e);
                        }
                    }
                }
            }

        } finally {
            jcrSession.logout();
        }
    }

    private Method getMethod(Class theclass, String methodName) throws NoSuchMethodException {
        Method[] declaredMethods = theclass.getDeclaredMethods();

        for (Method method : declaredMethods) {
            if (method.getName().equals(methodName)) {
                return method;
            }
        }

        throw new NoSuchMethodException(theclass.getName() + "." + methodName + "()");
    }

    /**
     * @param configuration
     * @return InputStream of node type definition file
     */
    private InputStream getNodeTypeDefinition(String configuration) {

        InputStream xml;

        if (StringUtils.isNotEmpty(configuration)) {

            // 1: try to load the configured file from the classpath
            xml = getClass().getResourceAsStream(configuration);
            if (xml != null) {
                log.info("Custom node types registered using {}", configuration);
                return xml;
            }

            // 2: try to load it from the file system
            File nodeTypeDefinition = new File(Path.getAbsoluteFileSystemPath(configuration));
            if (nodeTypeDefinition.exists()) {
                try {
                    return new FileInputStream(nodeTypeDefinition);
                } catch (FileNotFoundException e) {
                    // should never happen
                    log.error("File not found: {}", xml);
                }
            }

            // 3: defaults to standard nodetypes
            log.error("Unable to find node type definition: {} for repository {}", configuration,
                    this.repositoryMapping.getName());
        }

        // initialize default magnolia nodetypes
        xml = getClass().getResourceAsStream(MGNL_NODETYPES);

        return xml;
    }

    /**
     * WORKAROUND for tomcat 5.0/jdk 1.5 problem tomcat\common\endorsed contains an xml-apis.jar needed by tomcat and
     * loaded before all xmsl stuff present in the jdk (1.4 naming problem). In the xml-apis.jar file the
     * TransformerFactoryImpl is set to "org.apache.xalan.processor.TransformerFactoryImpl" instead of
     * "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl". solution: remove the file xml-apis.jar
     * from the directory OR manually change the javax.xml.transform.TransformerFactory system property
     */
    protected void checkXmlSettings() {
        if (SystemUtils.isJavaVersionAtLeast(1.5f) && "org.apache.xalan.processor.TransformerFactoryImpl"
                .equals(System.getProperty("javax.xml.transform.TransformerFactory"))) {

            String transformerClass = "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl";

            try {
                Class.forName(transformerClass);

                System.setProperty("javax.xml.transform.TransformerFactory", transformerClass);

                log.info(
                        "Java 1.5 detected, setting system property \"javax.xml.transform.TransformerFactory\" to \"{}\"",
                        transformerClass);
            } catch (Throwable e) {
                // not in the classpath. We can't assume which one to use, so just go on
            }
        }
    }

    /**
     * Checks if all workspaces are present according to the repository mapping, creates any missing workspace.
     */
    private void validateWorkspaces() throws RepositoryException {
        Iterator<String> configuredNames = repositoryMapping.getWorkspaces().iterator();
        while (configuredNames.hasNext()) {
            registerWorkspace(configuredNames.next());
        }
    }

    /**
     * @see info.magnolia.repository.Provider#registerWorkspace(java.lang.String)
     */
    public boolean registerWorkspace(String workspaceName) throws RepositoryException {
        // check if workspace already exists
        SimpleCredentials credentials = new SimpleCredentials(ContentRepository.REPOSITORY_USER,
                ContentRepository.REPOSITORY_PSWD.toCharArray());
        Session jcrSession = this.repository.login(credentials);

        try {
            WorkspaceImpl defaultWorkspace = (WorkspaceImpl) jcrSession.getWorkspace();
            String[] workspaceNames = defaultWorkspace.getAccessibleWorkspaceNames();

            boolean alreadyExists = ArrayUtils.contains(workspaceNames, workspaceName);
            if (!alreadyExists) {
                defaultWorkspace.createWorkspace(workspaceName);
            }
            jcrSession.logout();

            return !alreadyExists;
        } catch (ClassCastException e) {
            // this could happen if the repository provider does not have proper Shared API for the
            // application server like at the moment in Jackrabbit
            log.debug("Unable to register workspace, will continue", e);
        } catch (Throwable t) {
            log.error("Unable to register workspace, will continue", t);
        }
        return false;
    }

}