Package org.apache.jackrabbit.core.config

Source Code of org.apache.jackrabbit.core.config.RepositoryConfigurationParser

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.core.config;

import org.apache.commons.io.FileUtils;
import org.apache.jackrabbit.core.cluster.ClusterNode;
import org.apache.jackrabbit.core.data.DataStore;
import org.apache.jackrabbit.core.data.DataStoreFactory;
import org.apache.jackrabbit.core.fs.FileSystem;
import org.apache.jackrabbit.core.fs.FileSystemException;
import org.apache.jackrabbit.core.fs.FileSystemFactory;
import org.apache.jackrabbit.core.journal.AbstractJournal;
import org.apache.jackrabbit.core.journal.Journal;
import org.apache.jackrabbit.core.journal.JournalException;
import org.apache.jackrabbit.core.journal.JournalFactory;
import org.apache.jackrabbit.core.query.QueryHandler;
import org.apache.jackrabbit.core.query.QueryHandlerContext;
import org.apache.jackrabbit.core.query.QueryHandlerFactory;
import org.apache.jackrabbit.core.state.DefaultISMLocking;
import org.apache.jackrabbit.core.state.ISMLocking;
import org.apache.jackrabbit.core.state.ISMLockingFactory;
import org.apache.jackrabbit.core.util.RepositoryLock;
import org.apache.jackrabbit.core.util.RepositoryLockMechanism;
import org.apache.jackrabbit.core.util.RepositoryLockMechanismFactory;
import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import java.io.File;
import java.io.IOException;
import java.util.Properties;
import java.util.UUID;
import java.util.List;
import java.util.ArrayList;

import javax.jcr.RepositoryException;

/**
* Configuration parser. This class is used to parse the repository and
* workspace configuration files.
* <p>
* The following code sample outlines the usage of this class:
* <pre>
*     Properties variables = ...; // parser variables
*     RepositoryConfigurationParser parser =
*         new RepositoryConfigurationParser(variables);
*     RepositoryConfig rc = parser.parseRepositoryConfig(...);
*     WorkspaceConfig wc = parser.parseWorkspaceConfig(...);
* </pre>
* <p>
* Note that the configuration objects returned by this parser are not
* initialized. The caller needs to initialize the configuration objects
* before using them.
*/
public class RepositoryConfigurationParser extends ConfigurationParser {

    /** Name of the repository home directory parser variable. */
    public static final String REPOSITORY_HOME_VARIABLE = "rep.home";

    /** Name of the workspace home directory parser variable. */
    public static final String WORKSPACE_HOME_VARIABLE = "wsp.home";

    /** Name of the repository name parser variable. */
    public static final String WORKSPACE_NAME_VARIABLE = "wsp.name";

    /** Name of the security configuration element. */
    public static final String SECURITY_ELEMENT = "Security";

    /** Name of the security manager configuration element. */
    public static final String SECURITY_MANAGER_ELEMENT = "SecurityManager";

    /** Name of the access manager configuration element. */
    public static final String ACCESS_MANAGER_ELEMENT = "AccessManager";

    /** Name of the login module configuration element. */
    public static final String LOGIN_MODULE_ELEMENT = "LoginModule";

    /**
     * Name of the optional WorkspaceAccessManager element defining which
     * implementation of WorkspaceAccessManager to be used.
     */
    private static final String WORKSPACE_ACCESS_ELEMENT = "WorkspaceAccessManager";

    /**
     * Name of the optional UserManagerConfig element that defines the
     * configuration options for the user manager.
     */
    private static final String USER_MANAGER_ELEMENT = "UserManager";

    /** Name of the general workspace configuration element. */
    public static final String WORKSPACES_ELEMENT = "Workspaces";

    /** Name of the workspace configuration element. */
    public static final String WORKSPACE_ELEMENT = "Workspace";

    /** Name of the versioning configuration element. */
    public static final String VERSIONING_ELEMENT = "Versioning";

    /** Name of the file system configuration element. */
    public static final String FILE_SYSTEM_ELEMENT = "FileSystem";

    /** Name of the cluster configuration element. */
    public static final String CLUSTER_ELEMENT = "Cluster";

    /** Name of the journal configuration element. */
    public static final String JOURNAL_ELEMENT = "Journal";

    /** Name of the data store configuration element. */
    public static final String DATA_STORE_ELEMENT = "DataStore";

    /** Name of the repository lock mechanism configuration element. */
    public static final String REPOSITORY_LOCK_MECHANISM_ELEMENT =
        "RepositoryLockMechanism";

    /** Name of the persistence manager configuration element. */
    public static final String PERSISTENCE_MANAGER_ELEMENT =
        "PersistenceManager";

    /** Name of the search index configuration element. */
    public static final String SEARCH_INDEX_ELEMENT = "SearchIndex";

    /** Name of the ism locking configuration element. */
    public static final String ISM_LOCKING_ELEMENT = "ISMLocking";

    /** Name of the application name configuration attribute. */
    public static final String APP_NAME_ATTRIBUTE = "appName";

    /** Name of the workspace conaining security data. */
    public static final String WSP_NAME_ATTRIBUTE = "workspaceName";

    /** Name of the root path configuration attribute. */
    public static final String ROOT_PATH_ATTRIBUTE = "rootPath";

    /** Name of the config root path configuration attribute. */
    public static final String CONFIG_ROOT_PATH_ATTRIBUTE = "configRootPath";

    /** Name of the maximum idle time configuration attribute. */
    public static final String MAX_IDLE_TIME_ATTRIBUTE = "maxIdleTime";

    /** Name of the default workspace configuration attribute. */
    public static final String DEFAULT_WORKSPACE_ATTRIBUTE =
        "defaultWorkspace";

    /** Name of the id configuration attribute. */
    public static final String ID_ATTRIBUTE = "id";

    /** Name of the syncDelay configuration attribute. */
    public static final String SYNC_DELAY_ATTRIBUTE = "syncDelay";

    /** Name of the default search index implementation class. */
    public static final String DEFAULT_QUERY_HANDLER =
        "org.apache.jackrabbit.core.query.lucene.SearchIndex";

    /** Name of the clustered configuration attribute. */
    public static final String CLUSTERED_ATTRIBUTE = "clustered";

    /** Default synchronization delay, in milliseconds. */
    public static final String DEFAULT_SYNC_DELAY = "5000";

    /** Name of the workspace specific security configuration element */
    private static final String WSP_SECURITY_ELEMENT = "WorkspaceSecurity";

    /**
     * Name of the optional AccessControlProvider element defining which
     * implementation of AccessControlProvider should be used.
     */
    private static final String AC_PROVIDER_ELEMENT = "AccessControlProvider";

    /**
     * Element specifying the class of principals used to retrieve the userID
     * in the 'class' attribute.
     */
    private static final String USERID_CLASS = "UserIdClass";

    /**
     * Name of the optional XmlImport config entry inside the workspace configuration.
     */
    private static final String IMPORT_ELEMENT = "Import";
    private static final String IMPORT_PNI_ELEMENT = "ProtectedNodeImporter";
    private static final String IMPORT_PPI_ELEMENT = "ProtectedPropertyImporter";

    /**
     * Name of the cluster node id file.
     */
    private static final String CLUSTER_NODE_ID_FILE = "cluster_node.id";

    /**
     * Creates a new configuration parser with the given parser variables.
     *
     * @param variables parser variables
     */
    public RepositoryConfigurationParser(Properties variables) {
        super(variables);
    }

    /**
     * Parses repository configuration. Repository configuration uses the
     * following format:
     * <pre>
     *   &lt;Repository&gt;
     *     &lt;FileSystem ...&gt;
     *     &lt;Security appName="..."&gt;
     *       &lt;SecurityManager ...&gt;
     *       &lt;AccessManager ...&gt;
     *       &lt;LoginModule ... (optional)&gt;
     *     &lt;/Security&gt;
     *     &lt;Workspaces rootPath="..." defaultWorkspace="..."/&gt;
     *     &lt;Workspace ...&gt;
     *     &lt;Versioning ...&gt;
     *   &lt;/Repository&gt;
     * </pre>
     * <p>
     * The <code>FileSystem</code> element is a
     * {@link #parseBeanConfig(Element,String) bean configuration} element,
     * that specifies the file system implementation for storing global
     * repository information. The <code>Security</code> element contains
     * an <code>AccessManager</code> bean configuration element and the
     * JAAS name of the repository application. The <code>Workspaces</code>
     * element contains general workspace parameters, and the
     * <code>Workspace</code> element is a template for the individual
     * workspace configuration files. The <code>Versioning</code> element
     * contains
     * {@link #parseVersioningConfig(Element) versioning configuration} for
     * the repository.
     * <p>
     * In addition to the configured information, the returned repository
     * configuration object also contains the repository home directory path
     * that is given as the ${rep.home} parser variable. Note that the
     * variable <em>must</em> be available for the configuration document to
     * be correctly parsed.
     * <p>
     * {@link #replaceVariables(String) Variable replacement} is performed
     * on the security application name attribute, the general workspace
     * configuration attributes, and on the file system, access manager,
     * and versioning configuration information.
     * <p>
     * Note that the returned repository configuration object has not been
     * initialized.
     *
     * @param xml repository configuration document
     * @return repository configuration
     * @throws ConfigurationException if the configuration is broken
     * @see #parseBeanConfig(Element, String)
     * @see #parseVersioningConfig(Element)
     */
    public RepositoryConfig parseRepositoryConfig(InputSource xml)
            throws ConfigurationException {
        Element root = parseXML(xml, true);

        // Repository home directory
        String home = getVariables().getProperty(REPOSITORY_HOME_VARIABLE);

        // File system implementation
        FileSystemFactory fsf = getFileSystemFactory(root, FILE_SYSTEM_ELEMENT);

        // Security configuration and access manager implementation
        Element security = getElement(root, SECURITY_ELEMENT);
        SecurityConfig securityConfig = parseSecurityConfig(security);

        // General workspace configuration
        Element workspaces = getElement(root, WORKSPACES_ELEMENT);
        String workspaceDirectory = replaceVariables(
                getAttribute(workspaces, ROOT_PATH_ATTRIBUTE));

        String workspaceConfigDirectory =
                getAttribute(workspaces, CONFIG_ROOT_PATH_ATTRIBUTE, null);

        String defaultWorkspace = replaceVariables(
                getAttribute(workspaces, DEFAULT_WORKSPACE_ATTRIBUTE));

        int maxIdleTime = Integer.parseInt(
                getAttribute(workspaces, MAX_IDLE_TIME_ATTRIBUTE, "0"));

        // Workspace configuration template
        Element template = getElement(root, WORKSPACE_ELEMENT);

        // Versioning configuration
        VersioningConfig vc = parseVersioningConfig(root);

        // Query handler implementation
        QueryHandlerFactory qhf = getQueryHandlerFactory(root);

        // Optional journal configuration
        ClusterConfig cc = parseClusterConfig(root, new File(home));

        // Optional data store factory
        DataStoreFactory dsf = getDataStoreFactory(root, home);

        RepositoryLockMechanismFactory rlf = getRepositoryLockMechanismFactory(root);

        return new RepositoryConfig(home, securityConfig, fsf,
                workspaceDirectory, workspaceConfigDirectory, defaultWorkspace,
                maxIdleTime, template, vc, qhf, cc, dsf, rlf, this);
    }

    /**
     * Parses security configuration. Security configuration
     * uses the following format:
     * <pre>
     *   &lt;Security appName="..."&gt;
     *     &lt;SecurityManager ...&gt;
     *     &lt;AccessManager ...&gt;
     *     &lt;LoginModule ... (optional)&gt;
     *   &lt;/Security&gt;
     * </pre>
     * <p/>
     * The <code>SecurityManager</code>, the <code>AccessManager</code>
     * and <code>LoginModule</code> are all
     * {@link #parseBeanConfig(Element,String) bean configuration}
     * elements.
     * <p/>
     * The login module is an optional feature of repository configuration.
     *
     * @param security the &lt;security> element.
     * @return the security configuration.
     * @throws ConfigurationException if the configuration is broken
     */
    public SecurityConfig parseSecurityConfig(Element security)
            throws ConfigurationException {
        String appName = getAttribute(security, APP_NAME_ATTRIBUTE);

        SecurityManagerConfig smc = parseSecurityManagerConfig(security);
        AccessManagerConfig amc = parseAccessManagerConfig(security);
        LoginModuleConfig lmc = parseLoginModuleConfig(security);

        return new SecurityConfig(appName, smc, amc, lmc);
    }

    /**
     * Parses the security manager configuration.
     *
     * @param security the &lt;security> element.
     * @return the security manager configuration or <code>null</code>.
     * @throws ConfigurationException if the configuration is broken
     */
    public SecurityManagerConfig parseSecurityManagerConfig(Element security)
            throws ConfigurationException {
        // Optional security manager config entry
        Element smElement = getElement(security, SECURITY_MANAGER_ELEMENT, false);
        if (smElement != null) {
            BeanConfig bc = parseBeanConfig(smElement);
            String wspAttr = getAttribute(smElement, WSP_NAME_ATTRIBUTE, null);

            BeanConfig wac = null;
            Element element = getElement(smElement, WORKSPACE_ACCESS_ELEMENT, false);
            if (element != null) {
                wac = parseBeanConfig(smElement, WORKSPACE_ACCESS_ELEMENT);
            }

            UserManagerConfig umc = null;
            element = getElement(smElement, USER_MANAGER_ELEMENT, false);
            if (element != null) {
                umc = new UserManagerConfig(parseBeanConfig(smElement, USER_MANAGER_ELEMENT));
            }

            BeanConfig uidcc = null;
            element = getElement(smElement, USERID_CLASS, false);
            if (element != null) {
                uidcc = parseBeanConfig(element);
            }

            return new SecurityManagerConfig(bc, wspAttr, wac, umc, uidcc);
        } else {
            return null;
        }
    }

    /**
     * Parses the access manager configuration.
     *
     * @param security the &lt;security> element.
     * @return the access manager configuration or <code>null</code>.
     * @throws ConfigurationException if the configuration is broken
     */
    public AccessManagerConfig parseAccessManagerConfig(Element security)
            throws ConfigurationException {
        // Optional access manager config entry
        Element accessMgr = getElement(security, ACCESS_MANAGER_ELEMENT, false);
        if (accessMgr != null) {
            return new AccessManagerConfig(parseBeanConfig(accessMgr));
        } else {
            return null;
        }
    }

    /**
     * Parses the login module configuration.
     *
     * @param security the &lt;security> element.
     * @return the login module configuration or <code>null</code>.
     * @throws ConfigurationException if the configuration is broken
     */
    public LoginModuleConfig parseLoginModuleConfig(Element security)
            throws ConfigurationException {
        // Optional login module
        Element loginModule = getElement(security, LOGIN_MODULE_ELEMENT, false);

        if (loginModule != null) {
            return new LoginModuleConfig(parseBeanConfig(security, LOGIN_MODULE_ELEMENT));
        } else {
            return null;
        }
    }

    /**
     * Parses workspace configuration. Workspace configuration uses the
     * following format:
     * <pre>
     *   &lt;Workspace name="..."&gt;
     *     &lt;FileSystem ...&gt;
     *     &lt;PersistenceManager ...&gt;
     *     &lt;SearchIndex ...&gt;
     *     &lt;ISMLocking ...&gt;
     *     &lt;WorkspaceSecurity ...&gt;
     *     &lt;ISMLocking ...&gt;
     *   &lt;/Workspace&gt;
     * </pre>
     * <p>
     * All the child elements (<code>FileSystem</code>,
     * <code>PersistenceManager</code>, and <code>SearchIndex</code>) are
     * {@link #parseBeanConfig(Element,String) bean configuration} elements.
     * In addition to bean configuration, the
     * {@link #parseSearchConfig(Element) search element} also contains
     * configuration for the search file system.
     * <p>
     * In addition to the configured information, the returned workspace
     * configuration object also contains the workspace home directory path
     * that is given as the ${wsp.home} parser variable. Note that the
     * variable <em>must</em> be available for the configuration document to
     * be correctly parsed.
     * <p>
     * Variable replacement is performed on the optional workspace name
     * attribute. If the name is not given, then the name of the workspace
     * home directory is used as the workspace name. Once the name has been
     * determined, it will be added as the ${wsp.name} variable in a temporary
     * configuration parser that is used to parse the contained configuration
     * elements.
     * <p>
     * The search index configuration element is optional. If it is not given,
     * then the workspace will not have search capabilities.
     * <p>
     * The ism locking configuration element is optional. If it is not given,
     * then a default implementation is used.
     * <p>
     * Note that the returned workspace configuration object has not been
     * initialized.
     *
     * @param xml workspace configuration document
     * @return workspace configuration
     * @throws ConfigurationException if the configuration is broken
     * @see #parseBeanConfig(Element, String)
     * @see #parseWorkspaceSecurityConfig(Element)
     */
    public WorkspaceConfig parseWorkspaceConfig(InputSource xml)
            throws ConfigurationException {

        Element root = parseXML(xml);
        return parseWorkspaceConfig(root);
    }

    /**
     * Parse workspace config.
     *
     * @param root root element of the workspace configuration
     *
     * @see #parseWorkspaceConfig(InputSource)
     */
    protected WorkspaceConfig parseWorkspaceConfig(Element root)
            throws ConfigurationException {

        // Workspace home directory
        String home = getVariables().getProperty(WORKSPACE_HOME_VARIABLE);

        // Workspace name
        String name =
            getAttribute(root, NAME_ATTRIBUTE, new File(home).getName());

        // Clustered attribute
        boolean clustered = Boolean.valueOf(
                getAttribute(root, CLUSTERED_ATTRIBUTE, "true"));

        // Create a temporary parser that contains the ${wsp.name} variable
        Properties tmpVariables = (Properties) getVariables().clone();
        tmpVariables.put(WORKSPACE_NAME_VARIABLE, name);
        RepositoryConfigurationParser tmpParser = createSubParser(tmpVariables);

        // File system implementation
        FileSystemFactory fsf =
            tmpParser.getFileSystemFactory(root, FILE_SYSTEM_ELEMENT);

        // Persistence manager implementation
        PersistenceManagerConfig pmc = tmpParser.parsePersistenceManagerConfig(root);

        // Query handler implementation
        QueryHandlerFactory qhf = tmpParser.getQueryHandlerFactory(root);

        // Item state manager locking configuration (optional)
        ISMLockingFactory ismLockingFactory =
            tmpParser.getISMLockingFactory(root);

        // workspace specific security configuration
        WorkspaceSecurityConfig workspaceSecurityConfig = tmpParser.parseWorkspaceSecurityConfig(root);

        // optinal config for import handling
        ImportConfig importConfig = tmpParser.parseImportConfig(root);

        return new WorkspaceConfig(
                home, name, clustered, fsf, pmc, qhf,
                ismLockingFactory, workspaceSecurityConfig, importConfig);
    }

    /**
     * Parses search index configuration. Search index configuration
     * uses the following format:
     * <pre>
     *   &lt;SearchIndex class="..."&gt;
     *     &lt;param name="..." value="..."&gt;
     *     ...
     *     &lt;FileSystem ...&gt;
     *   &lt;/Search&gt;
     * </pre>
     * <p/>
     * Both the <code>SearchIndex</code> and <code>FileSystem</code>
     * elements are {@link #parseBeanConfig(Element,String) bean configuration}
     * elements. If the search implementation class is not given, then
     * a default implementation is used.
     * <p/>
     * The search index is an optional feature of workspace configuration.
     * If the search configuration element is not found, then this method
     * returns <code>null</code>.
     * <p/>
     * The FileSystem element in a search index configuration is optional.
     * However some implementations may require a FileSystem.
     *
     * @param parent parent of the <code>SearchIndex</code> element
     * @return query handler factory
     */
    protected QueryHandlerFactory getQueryHandlerFactory(final Element parent) {
        NodeList children = parent.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            final Node child = children.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE
                    && SEARCH_INDEX_ELEMENT.equals(child.getNodeName())) {
                return new QueryHandlerFactory() {
                    public QueryHandler getQueryHandler(QueryHandlerContext context)
                            throws RepositoryException {
                        Element element = (Element) child;

                        // Optional file system implementation
                        FileSystem fs = null;
                        if (getElement(element, FILE_SYSTEM_ELEMENT, false) != null) {
                            fs = getFileSystemFactory(
                                    element, FILE_SYSTEM_ELEMENT).getFileSystem();
                        }

                        // Search implementation class
                        String className = getAttribute(
                                element, CLASS_ATTRIBUTE, DEFAULT_QUERY_HANDLER);
                        BeanConfig config = new BeanConfig(
                                className, parseParameters(element));

                        QueryHandler handler =
                            (QueryHandler) config.newInstance();
                        try {
                            handler.init(fs, context);
                            return handler;
                        } catch (IOException e) {
                            throw new RepositoryException(
                                    "Unable to initialize query handler: " + handler, e);
                        }
                    }
                };
            }
        }
        return null;
    }


    /**
     * Read the WorkspaceSecurity Element of Workspace's configuration. It uses
     * the following format:
     * <pre>
     *   &lt;WorkspaceSecurity&gt;
     *     &lt;AccessControlProvider class="..." (optional)&gt;
     *   &lt;/WorkspaceSecurity&gt;
     * </pre>
     *
     * @param parent Workspace-Root-Element
     * @return a new <code>WorkspaceSecurityConfig</code>
     * @throws ConfigurationException
     */
    public WorkspaceSecurityConfig parseWorkspaceSecurityConfig(Element parent)
        throws ConfigurationException {

        BeanConfig acProviderConfig = null;
        Element element = getElement(parent, WSP_SECURITY_ELEMENT, false);
        if (element != null) {
            Element provFact = getElement(element, AC_PROVIDER_ELEMENT, false);
            if (provFact != null) {
                acProviderConfig = parseBeanConfig(element, AC_PROVIDER_ELEMENT);
                acProviderConfig.setValidate(false); // JCR-1920
            }
        }
        return new WorkspaceSecurityConfig(acProviderConfig);
    }

    /**
     * Read the optional XmlImport Element of Workspace's configuration. It uses
     * the following format:
     * <pre>
     *   &lt;XmlImport&gt;
     *     &lt;ProtectedNodeImporter class="..." (optional)&gt;
     *     &lt;ProtectedNodeImporter class="..." (optional)&gt;
     *     ...
     *     &lt;ProtectedPropertyImporter class="..." (optional)&gt;
     *   &lt;/XmlImport&gt;
     * </pre>
     *
     * @param parent Workspace-Root-Element
     * @return a new <code>XmlImportConfig</code>
     * @throws ConfigurationException
     */
    public ImportConfig parseImportConfig(Element parent) throws ConfigurationException {
        List<BeanConfig> protectedNodeImporters = new ArrayList();
        List<BeanConfig> protectedPropertyImporters = new ArrayList();

        Element element = getElement(parent, IMPORT_ELEMENT, false);
        if (element != null) {
            NodeList children = element.getChildNodes();
            for (int i = 0; i < children.getLength(); i++) {
                Node child = children.item(i);
                if (child.getNodeType() == Node.ELEMENT_NODE) {
                    if (IMPORT_PNI_ELEMENT.equals(child.getNodeName())) {
                        String className = getAttribute((Element) child, CLASS_ATTRIBUTE);
                        BeanConfig bc = new BeanConfig(className, parseParameters((Element) child));
                        bc.setValidate(false);
                        protectedNodeImporters.add(bc);
                    } else if (IMPORT_PPI_ELEMENT.equals(child.getNodeName())) {
                        String className = getAttribute((Element) child, CLASS_ATTRIBUTE);
                        BeanConfig bc = new BeanConfig(className, parseParameters((Element) child));
                        bc.setValidate(false);
                        protectedPropertyImporters.add(bc);
                    } // else: some other entry -> ignore.
                }
            }
        }
        return new ImportConfig(protectedNodeImporters, protectedPropertyImporters);
    }

    /**
     * Returns an ISM locking factory that creates {@link ISMLocking} instances
     * based on the given configuration. ISM locking configuration uses the
     * following format:
     * <pre>
     *   &lt;ISMLocking class="..."&gt;
     *     &lt;param name="..." value="..."&gt;
     *     ...
     *   &lt;/ISMLocking&gt;
     * </pre>
     * <p>
     * The <code>ISMLocking</code> is a
     * {@link #parseBeanConfig(Element,String) bean configuration} element.
     * <p>
     * ISM locking is an optional part of the  workspace configuration. If
     * the ISM locking element is not found, then the returned factory will
     * create instances of the {@link DefaultISMLocking} class.
     *
     * @param parent parent of the <code>ISMLocking</code> element
     * @return ISM locking factory
     */
    protected ISMLockingFactory getISMLockingFactory(final Element parent) {
        return new ISMLockingFactory() {
            public ISMLocking getISMLocking() throws RepositoryException {
                Element element = getElement(parent, ISM_LOCKING_ELEMENT, false);
                if (element != null) {
                    BeanConfig config = parseBeanConfig(element);
                    try {
                        return (ISMLocking) config.newInstance();
                    } catch (ClassCastException e) {
                        throw new RepositoryException(
                                "Invalid ISMLocking class: "
                                + config.getClassName(), e);
                    }
                } else {
                    return new DefaultISMLocking();
                }
            }
        };
    }

    /**
     * Parses versioning configuration. Versioning configuration uses the
     * following format:
     * <pre>
     *   &lt;Versioning rootPath="..."&gt;
     *     &lt;FileSystem ...&gt;
     *     &lt;PersistenceManager ...&gt;
     *   &lt;/Versioning&gt;
     * </pre>
     * <p>
     * Both the <code>FileSystem</code> and <code>PersistenceManager</code>
     * elements are {@link #parseBeanConfig(Element,String) bean configuration}
     * elements. In addition to the bean parameter values,
     * {@link #replaceVariables(String) variable replacement} is performed
     * also on the versioning root path attribute.
     *
     * @param parent parent of the <code>Versioning</code> element
     * @return versioning configuration
     * @throws ConfigurationException if the configuration is broken
     */
    protected VersioningConfig parseVersioningConfig(Element parent)
            throws ConfigurationException {
        Element element = getElement(parent, VERSIONING_ELEMENT);

        // Versioning home directory
        String home =
            replaceVariables(getAttribute(element, ROOT_PATH_ATTRIBUTE));

        // File system implementation
        FileSystemFactory fsf =
            getFileSystemFactory(element, FILE_SYSTEM_ELEMENT);

        // Persistence manager implementation
        PersistenceManagerConfig pmc = parsePersistenceManagerConfig(element);

        // Item state manager locking configuration (optional)
        ISMLockingFactory ismLockingFactory =
            getISMLockingFactory(element);

        return new VersioningConfig(home, fsf, pmc, ismLockingFactory);
    }

    /**
     * Parses cluster configuration. Cluster configuration uses the following format:
     * <pre>
     *   &lt;Cluster&gt;
     *     &lt;Journal ...&gt;
     *   &lt;/Journal&gt;
     * </pre>
     * <p/>
     * <code>Cluster</code> is a {@link #parseBeanConfig(Element,String) bean configuration}
     * element.
     * <p/>
     * Clustering is an optional feature. If the cluster element is not found, then this
     * method returns <code>null</code>.
     *
     * @param parent parent of the <code>Journal</code> element
     * @param home repository home directory
     * @return cluster configuration, or <code>null</code>
     * @throws ConfigurationException if the configuration is broken
     */
    protected ClusterConfig parseClusterConfig(Element parent, File home)
            throws ConfigurationException {

        NodeList children = parent.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            Node child = children.item(i);
            if (child.getNodeType() == Node.ELEMENT_NODE
                    && CLUSTER_ELEMENT.equals(child.getNodeName())) {
                Element element = (Element) child;

                // Find the cluster node id
                String id =
                    System.getProperty(ClusterNode.SYSTEM_PROPERTY_NODE_ID);
                String value = getAttribute(element, ID_ATTRIBUTE, null);
                if (value != null) {
                    id = replaceVariables(value);
                } else if (id == null) {
                    File file = new File(home, CLUSTER_NODE_ID_FILE);
                    try {
                        if (file.exists() && file.canRead()) {
                            id = FileUtils.readFileToString(file);
                        } else {
                            id = UUID.randomUUID().toString();
                            FileUtils.writeStringToFile(file, id);
                        }
                    } catch (IOException e) {
                        throw new ConfigurationException(
                                "Failed to access cluster node id: " + file, e);
                    }
                }

                long syncDelay = Long.parseLong(replaceVariables(getAttribute(
                        element, SYNC_DELAY_ATTRIBUTE, DEFAULT_SYNC_DELAY)));

                JournalFactory jf = getJournalFactory(element, home, id);
                return new ClusterConfig(id, syncDelay, jf);
            }
        }
        return null;
    }

    /**
     * Parses journal configuration. Journal configuration uses the following format:
     * <pre>
     *   &lt;Journal class="..."&gt;
     *     &lt;param name="..." value="..."&gt;
     *     ...
     *   &lt;/Journal&gt;
     * </pre>
     * <p/>
     * <code>Journal</code> is a {@link #parseBeanConfig(Element,String) bean configuration}
     * element.
     *
     * @param cluster parent cluster element
     * @param home repository home directory
     * @param id cluster node id
     * @return journal factory
     * @throws ConfigurationException if the configuration is broken
     */
    protected JournalFactory getJournalFactory(
            final Element cluster, final File home, final String id)
            throws ConfigurationException {
        return new JournalFactory() {
            public Journal getJournal(NamespaceResolver resolver)
                    throws RepositoryException {
                BeanConfig config = parseBeanConfig(cluster, JOURNAL_ELEMENT);
                Object object = config.newInstance();
                if (object instanceof Journal) {
                    Journal journal = (Journal) object;
                    if (journal instanceof AbstractJournal) {
                        ((AbstractJournal) journal).setRepositoryHome(home);
                    }
                    try {
                        journal.init(id, resolver);
                    } catch (JournalException e) {
                        // TODO: Should JournalException extend RepositoryException?
                        throw new RepositoryException(
                                "Journal initialization failed: " + journal, e);
                    }
                    return journal;
                } else {
                    throw new RepositoryException(
                            "Invalid Journal implementation class: "
                            + config.getClassName());
                }
            }
        };
    }

    /**
     * Parses data store configuration. Data store configuration uses the following format:
     * <pre>
     *   &lt;DataStore class="..."&gt;
     *     &lt;param name="..." value="..."&gt;
     *     ...
     *   &lt;/DataStore&gt;
     * </pre>
     * <p/>
     * <code>DataStore</code> is a {@link #parseBeanConfig(Element,String) bean configuration}
     * element.
     *
     * @param parent configuration element
     * @param directory the repository directory
     * @return data store factory
     * @throws ConfigurationException if the configuration is broken
     */
    protected DataStoreFactory getDataStoreFactory(
            final Element parent, final String directory)
            throws ConfigurationException {
        return new DataStoreFactory() {
            public DataStore getDataStore() throws RepositoryException {
                NodeList children = parent.getChildNodes();
                for (int i = 0; i < children.getLength(); i++) {
                    Node child = children.item(i);
                    if (child.getNodeType() == Node.ELEMENT_NODE
                            && DATA_STORE_ELEMENT.equals(child.getNodeName())) {
                        BeanConfig bc =
                            parseBeanConfig(parent, DATA_STORE_ELEMENT);
                        DataStore store = (DataStore) bc.newInstance();
                        store.init(directory);
                        return store;
                    }
                }
                return null;
            }
        };
    }

    /**
     * Parses repository lock mechanism configuration. Repository lock mechanism
     * configuration uses the following format:
     * <pre>
     *   &lt;RepositoryLockMechanism class=&quot;...&quot; &gt;
     *     &lt;param name=&quot;...&quot; value=&quot;...&quot;&gt;
     *     ...
     *   &lt;/RepositoryLockMechanism&gt;
     * </pre>
     * <p/>
     * <code>RepositoryLockMechanism</code> is a
     * {@link #parseBeanConfig(Element,String) bean configuration} element.
     *
     * @param root the root configuration element
     * @return repository lock mechanism factory
     * @throws ConfigurationException if the configuration is broken
     */
    protected RepositoryLockMechanismFactory getRepositoryLockMechanismFactory(final Element root) {
        return new RepositoryLockMechanismFactory() {
            public RepositoryLockMechanism getRepositoryLockMechanism() throws RepositoryException {
                RepositoryLockMechanism lock = null;
                NodeList children = root.getChildNodes();
                for (int i = 0; i < children.getLength(); i++) {
                    Node child = children.item(i);
                    if (child.getNodeType() == Node.ELEMENT_NODE
                            && REPOSITORY_LOCK_MECHANISM_ELEMENT.equals(child.getNodeName())) {
                        BeanConfig bc =
                            parseBeanConfig(root, REPOSITORY_LOCK_MECHANISM_ELEMENT);
                        lock = (RepositoryLockMechanism) bc.newInstance();
                        break;
                    }
                }
                if (lock == null) {
                    lock = new RepositoryLock();
                }
                return lock;
            }
        };
    }

    /**
     * Parses the PersistenceManager config.
     *
     * @param parent parent of the <code>PersistenceManager</code> element
     * @return persistence manager configuration
     * @throws ConfigurationException if the configuration is broken
     */
    protected PersistenceManagerConfig parsePersistenceManagerConfig(
            Element parent) throws ConfigurationException {
        return new PersistenceManagerConfig(
                parseBeanConfig(parent, PERSISTENCE_MANAGER_ELEMENT));
    }

    /**
     * Creates a new instance of a configuration parser but with overlayed
     * variables.
     *
     * @param variables the variables overlay
     * @return a new configuration parser instance
     */
    protected RepositoryConfigurationParser createSubParser(Properties variables) {
        // overlay the properties
        Properties props = new Properties(getVariables());
        props.putAll(variables);
        return new RepositoryConfigurationParser(props);
    }

    /**
     * Creates and returns a factory object that creates {@link FileSystem}
     * instances based on the bean configuration at the named element.
     *
     * @param parent parent element
     * @param name name of the bean configuration element
     * @return file system factory
     * @throws ConfigurationException if the bean configuration is invalid
     */
    protected FileSystemFactory getFileSystemFactory(Element parent, String name)
            throws ConfigurationException {
        final BeanConfig config = parseBeanConfig(parent, name);
        return new FileSystemFactory() {
            public FileSystem getFileSystem() throws RepositoryException {
                try {
                    FileSystem fs = (FileSystem) config.newInstance();
                    fs.init();
                    return fs;
                } catch (ClassCastException e) {
                    throw new RepositoryException(
                            "Invalid file system implementation class: "
                            + config.getClassName(), e);
                } catch (FileSystemException e) {
                    throw new RepositoryException(
                            "File system initialization failure.", e);
                }
            }
        };
    }

}
TOP

Related Classes of org.apache.jackrabbit.core.config.RepositoryConfigurationParser

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.