com.esri.gpt.framework.context.ApplicationConfigurationLoader.java Source code

Java tutorial

Introduction

Here is the source code for com.esri.gpt.framework.context.ApplicationConfigurationLoader.java

Source

/* See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Esri Inc. 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 com.esri.gpt.framework.context;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.lucene.search.BooleanQuery;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.esri.gpt.catalog.arcims.ImsService;
import com.esri.gpt.catalog.context.CatalogConfiguration;
import com.esri.gpt.catalog.lucene.LuceneIndexObserver;
import com.esri.gpt.catalog.lucene.LuceneIndexObserverInfo;
import com.esri.gpt.catalog.lucene.ParserAdaptorInfo;
import com.esri.gpt.catalog.lucene.ParserAdaptorInfos;
import com.esri.gpt.catalog.search.MapViewerConfigs;
import com.esri.gpt.catalog.search.SearchConfig;
import com.esri.gpt.control.download.DownloadConfiguration;
import com.esri.gpt.control.download.ItemInfo;
import com.esri.gpt.control.webharvest.engine.DataProcessorFactory;
import com.esri.gpt.control.webharvest.engine.HarvesterConfiguration;
import com.esri.gpt.control.webharvest.engine.LocalDataProcessorFactory;
import com.esri.gpt.control.webharvest.protocol.ProtocolFactories;
import com.esri.gpt.control.webharvest.protocol.ProtocolFactory;
import com.esri.gpt.control.webharvest.protocol.ProtocolInitializer;
import com.esri.gpt.framework.collection.StringAttribute;
import com.esri.gpt.framework.collection.StringAttributeMap;
import com.esri.gpt.framework.http.HttpClientRequest;
import com.esri.gpt.framework.mail.MailConfiguration;
import com.esri.gpt.framework.scheduler.ThreadSchedulerConfiguration;
import com.esri.gpt.framework.security.codec.PC1_Encryptor;
import com.esri.gpt.framework.security.credentials.ProxyAuthenticator;
import com.esri.gpt.framework.security.credentials.UsernamePasswordCredentials;
import com.esri.gpt.framework.security.identity.IdentityConfiguration;
import com.esri.gpt.framework.security.identity.IdentitySupport;
import com.esri.gpt.framework.security.identity.SingleSignOnMechanism;
import com.esri.gpt.framework.security.identity.ldap.LdapConfiguration;
import com.esri.gpt.framework.security.identity.ldap.LdapConnectionProperties;
import com.esri.gpt.framework.security.identity.ldap.LdapGroupProperties;
import com.esri.gpt.framework.security.identity.ldap.LdapUserProperties;
import com.esri.gpt.framework.security.identity.open.OpenProvider;
import com.esri.gpt.framework.security.metadata.MetadataAccessPolicy;
import com.esri.gpt.framework.security.principal.Group;
import com.esri.gpt.framework.security.principal.Role;
import com.esri.gpt.framework.security.principal.Roles;
import com.esri.gpt.framework.security.principal.UserAttribute;
import com.esri.gpt.framework.security.principal.UserAttributeMap;
import com.esri.gpt.framework.sql.DatabaseReference;
import com.esri.gpt.framework.util.LogUtil;
import com.esri.gpt.framework.util.TimePeriod;
import com.esri.gpt.framework.util.Val;
import com.esri.gpt.framework.xml.DomUtil;
import com.esri.gpt.framework.xml.NodeListAdapter;
import java.util.List;

/**
 * Application configuration loader.
 * <p>
 * Loads the primary configuration for an application based upon the XML content
 * defined by the primary configuration source.
 * 
 * @see ApplicationConfiguration
 */
public class ApplicationConfigurationLoader {

    /** Main XML configuration file location. */
    private static final String MAIN_FILE = "gpt/config/gpt.xml";

    /** Default constructor. */
    public ApplicationConfigurationLoader() {
    }

    // properties ==================================================================
    /**
     * Gets the logger.
     * 
     * @return the logger
     */
    private Logger getLogger() {
        return LogUtil.getLogger();
    }

    // methods =====================================================================
    /**
     * Starts the configuration loading process.
     * 
     * @param appConfig
     *          the primary application configuration
     * @throws Exception
     */
    public void load(ApplicationConfiguration appConfig) throws Exception {

        // load the dom
        String sConfigFile = MAIN_FILE;
        getLogger().log(Level.FINE, "Loading configuration file: {0}", sConfigFile);
        Document dom = DomUtil.makeDomFromResourcePath(sConfigFile, false);
        XPath xpath = XPathFactory.newInstance().newXPath();

        try {
            Node root = (Node) xpath.evaluate("/gptConfig", dom, XPathConstants.NODE);
            appConfig.setVersion(xpath.evaluate("@version", root));

            // load configurations
            loadDatabase(appConfig, dom, root);
            loadIdentity(appConfig, dom, root);
            loadMail(appConfig, dom, root);
            loadInteractiveMap(appConfig, dom, root);
            loadCatalog(appConfig, dom, root);
            loadScheduler(appConfig, dom, root);
            loadDownloadData(appConfig, dom, root);
            loadHarvesterConfiguration(appConfig, dom, root);
            loadProtocolFactories(appConfig, dom, root);

            // forward proxy authentication
            Node ndProxyAuth = (Node) xpath.evaluate("forwardProxyAuth", root, XPathConstants.NODE);
            if (ndProxyAuth != null) {
                String sUser = xpath.evaluate("@username", ndProxyAuth);
                String sPwd = xpath.evaluate("@password", ndProxyAuth);
                boolean bEncrypted = Val.chkBool(xpath.evaluate("@encrypted", ndProxyAuth), false);
                boolean bSetSystemProperties = Val.chkBool(xpath.evaluate("@setSystemProperties", ndProxyAuth),
                        true);
                if (bEncrypted) {
                    try {
                        String sDecrypted = PC1_Encryptor.decrypt(sPwd);
                        sPwd = sDecrypted;
                    } catch (Exception e) {
                        this.getLogger().log(Level.SEVERE, "The forwardProxyAuth password failed to decrypt.", e);
                    }
                }
                if ((sUser != null) && (sUser.length() > 0) && (sPwd != null) && (sPwd.length() > 0)) {
                    ProxyAuthenticator.setDefault(sUser, sPwd);

                    // set system properties
                    if (bSetSystemProperties) {
                        try {
                            if (System.getProperty("http.proxyUser") == null) {
                                System.setProperty("http.proxyUser", sUser);
                                System.setProperty("http.proxyPassword", sPwd);
                            }
                            if (System.getProperty("https.proxyUser") == null) {
                                System.setProperty("https.proxyUser", sUser);
                                System.setProperty("https.proxyPassword", sPwd);
                            }
                        } catch (Exception e) {
                            this.getLogger().log(Level.SEVERE,
                                    "Error setting system properties for forward proxy authentication.", e);
                        }
                    }
                }
            }

        } catch (XPathExpressionException e) {
            e.printStackTrace(System.err);
        }

        getLogger().info(appConfig.toString());
    }

    /**
     * Loads the catalog configuration.
     * 
     * @param appConfig
     *          the primary application configuration
     * @param dom
     *          the configuration document
     * @param root
     *          the root node for the document
     * @throws Exception 
     */
    private void loadCatalog(ApplicationConfiguration appConfig, Document dom, Node root) throws Exception {
        XPath xpath = XPathFactory.newInstance().newXPath();

        // catalog configuration
        Node ndCat = (Node) xpath.evaluate("catalog", root, XPathConstants.NODE);
        if (ndCat != null) {
            CatalogConfiguration cfg = appConfig.getCatalogConfiguration();
            cfg.getParameters().clear();
            ImsService publish = cfg.getArcImsCatalog().getPublishService();
            ImsService browse = cfg.getArcImsCatalog().getBrowseService();
            cfg.setTablePrefix(Val.chkStr(xpath.evaluate("@gptTablePrefix", ndCat), "GPT_"));
            cfg.setMvsTablePrefix(Val.chkStr(xpath.evaluate("@mvsTablePrefix", ndCat), "MVS_"));

            publish.setServerUrl(xpath.evaluate("@metadataServerUrl", ndCat));
            publish.setServiceName(
                    Val.chkStr(xpath.evaluate("@metadataServerPublishService", ndCat), "GPT_Publish_Metadata"));
            publish.setTimeoutMillisecs(Val.chkInt(xpath.evaluate("@metadataServerTimeoutMillisecs", ndCat), 0));

            browse.setServerUrl(publish.getServerUrl());
            browse.setServiceName(
                    Val.chkStr(xpath.evaluate("@metadataServerBrowseService", ndCat), "GPT_Browse_Metadata"));
            browse.setTimeoutMillisecs(publish.getTimeoutMillisecs());

            // additional parameters
            populateParameters(cfg.getParameters(), ndCat);

            // parse http timeouts
            String connectionTimeout = cfg.getParameters().getValue("httpClientRequest.connectionTimeout");
            String responseTimeout = cfg.getParameters().getValue("httpClientRequest.responseTimeout");

            // set http timeouts
            cfg.setConnectionTimeMs(
                    (int) parsePeriod(connectionTimeout, HttpClientRequest.DEFAULT_CONNECTION_TIMEOUT).getValue());
            cfg.setResponseTimeOutMs(
                    (int) parsePeriod(responseTimeout, HttpClientRequest.DEFAULT_RESPONSE_TIMEOUT).getValue());
        }

        // search configuration
        Node ndSearch = (Node) xpath.evaluate("catalog/search", root, XPathConstants.NODE);
        SearchConfig sCfg = appConfig.getCatalogConfiguration().getSearchConfig();
        sCfg.setSearchConfigNode(ndSearch);
        if (ndSearch != null) {
            sCfg.setResultsReviewsShown(Val.chkStr(xpath.evaluate("@searchResultsReviewsShown", ndSearch)));
            sCfg.setResultsPerPage(xpath.evaluate("@searchResultsPerPage", ndSearch));
            sCfg.setMaxSavedSearches(xpath.evaluate("@maxSavedSearches", ndSearch));
            sCfg.setCswProfile(Val.chkStr(xpath.evaluate("@cswServletUrlProfile", ndSearch),
                    "urn:ogc:CSW:2.0.2:HTTP:OGCCORE:ESRI:GPT"));
            sCfg.setSearchUri(xpath.evaluate("@cswServletUrl", ndSearch));
            sCfg.setTimeOut(xpath.evaluate("@searchTimeoutMillisecs", ndSearch));
            sCfg.setDistributedSearchMaxSelectedSites(
                    Val.chkStr(xpath.evaluate("@distributedSearchMaxSelectedSites", ndSearch)));
            sCfg.setDistributedSearchTimeoutMillisecs(
                    Val.chkStr(xpath.evaluate("@distributedSearchTimeoutMillisecs", ndSearch)));

            sCfg.setAllowExternalSearch(Val.chkBool(xpath.evaluate("@allowExternalSiteSearch", ndSearch), false));
            sCfg.setJsfSuffix(Val.chkStr(xpath.evaluate("@jsfSuffix", ndSearch)));
            sCfg.setGptToCswXsltPath(xpath.evaluate("@gpt2cswXslt", ndSearch));
            sCfg.setMapViewerUrl(Val.chkStr(xpath.evaluate("@mapViewerUrl", ndSearch), ""));
            sCfg.validate();
        }

        NodeList nodes = (NodeList) xpath.evaluate("catalog/search/repositories/repository", root,
                XPathConstants.NODESET);
        NodeList nodeList = nodes;
        LinkedHashMap<String, Map<String, String>> sFactory = new LinkedHashMap<String, Map<String, String>>();
        Map<String, String> attributes = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
        /*attributes.put("key", "local");
        attributes.put("class", "com.esri.gpt.catalog.search.SearchEngineLocal");
        attributes.put("resourceKey", "catalog.search.searchSite.defaultsite");
        attributes.put("labelResourceKey", "catalog.search.searchSite.defaultsite");
        attributes.put("abstractResourceKey", "catalog.search.searchSite.defaultsite.abstract");
        sFactory.put("local", attributes);*/
        for (int i = 0; nodeList != null && i < nodeList.getLength(); i++) {
            ndSearch = nodeList.item(i);
            attributes = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
            NamedNodeMap nnm = ndSearch.getAttributes();
            for (int j = 0; nnm != null && j < nnm.getLength(); j++) {
                Node nd = nnm.item(j);
                String key = Val.chkStr(nd.getNodeName());
                String value = Val.chkStr(nd.getNodeValue());
                attributes.put(key, value);
                if (key.equalsIgnoreCase("RESOURCEKEY")) {
                    attributes.put("RESOURCEKEY", value);
                }
                if (key.equalsIgnoreCase("labelResourceKey")) {
                    attributes.put("labelResourceKey", value);
                }
                if (key.equalsIgnoreCase("abstractResourceKey")) {
                    attributes.put("abstractResourceKey", value);
                }
            }
            NodeList params = (NodeList) xpath.evaluate("parameter", ndSearch, XPathConstants.NODESET);
            for (int k = 0; params != null && k < params.getLength(); k++) {
                String key = xpath.evaluate("@key", params.item(k));
                String value = xpath.evaluate("@value", params.item(k));
                attributes.put(Val.chkStr(key), Val.chkStr(value));
            }

            String key = Val.chkStr(xpath.evaluate("@key", ndSearch));
            sFactory.put(key, attributes);

        }
        sCfg.setSearchFactoryRepos(sFactory);

        // Mapviewer
        ArrayList<MapViewerConfigs> mapViewerConfigs = new ArrayList<MapViewerConfigs>();
        nodes = (NodeList) xpath.evaluate("catalog/mapViewer/instance", root, XPathConstants.NODESET);
        for (int j = 0; nodes != null && j < nodes.getLength(); j++) {
            MapViewerConfigs mvConfigs = new MapViewerConfigs();
            Node nd = nodes.item(j);
            mvConfigs.setClassName(
                    Val.chkStr(xpath.evaluate("@className", nd), "com.esri.gpt.catalog.search.MapViewerFlex"));
            mvConfigs.setUrl(xpath.evaluate("@url", nd));
            NodeList pNodeList = (NodeList) xpath.evaluate("parameter", nd, XPathConstants.NODESET);
            for (int k = 0; pNodeList != null && k < pNodeList.getLength(); k++) {
                String key = xpath.evaluate("@key", pNodeList.item(k));
                String value = xpath.evaluate("@value", pNodeList.item(k));
                if (key != null || value != null) {
                    mvConfigs.addParameter(key, value);
                }
            }
            mapViewerConfigs.add(mvConfigs);

        }
        sCfg.setMapViewerInstances(mapViewerConfigs);

        // Lucene configuration
        Node ndLucene = (Node) xpath.evaluate("catalog/lucene", root, XPathConstants.NODE);
        if (ndLucene != null) {
            CatalogConfiguration cfg = appConfig.getCatalogConfiguration();
            cfg.getLuceneConfig().setIndexLocation(xpath.evaluate("@indexLocation", ndLucene));
            cfg.getLuceneConfig()
                    .setWriteLockTimeout(Val.chkInt(xpath.evaluate("@writeLockTimeout", ndLucene), -1));
            cfg.getLuceneConfig().setUseNativeFSLockFactory(
                    Val.chkStr(xpath.evaluate("@useNativeFSLockFactory", ndLucene)).equalsIgnoreCase("true"));
            cfg.getLuceneConfig().setAnalyzerClassName(xpath.evaluate("@analyzerClassName", ndLucene));
            cfg.getLuceneConfig().setUseConstantScoreQuery(
                    Val.chkBool(xpath.evaluate("@useConstantScoreQuery", ndLucene), false));
            cfg.getLuceneConfig().setMaxClauseCount(
                    Val.chkInt(xpath.evaluate("@maxClauseCount", ndLucene), BooleanQuery.getMaxClauseCount()));

            ParserAdaptorInfos infos = new ParserAdaptorInfos();
            NodeList ndLstProxies = (NodeList) xpath.evaluate("adaptor", ndLucene, XPathConstants.NODESET);
            for (int i = 0; i < ndLstProxies.getLength(); i++) {
                Node ndProxy = ndLstProxies.item(i);
                String proxyName = xpath.evaluate("@name", ndProxy);
                String proxyClassName = xpath.evaluate("@className", ndProxy);

                ParserAdaptorInfo info = new ParserAdaptorInfo();
                info.setName(proxyName);
                info.setClassName(proxyClassName);

                NodeList ndListProps = (NodeList) xpath.evaluate("attribute", ndProxy, XPathConstants.NODESET);
                for (int p = 0; p < ndListProps.getLength(); p++) {
                    Node ndProp = ndListProps.item(p);
                    String key = xpath.evaluate("@key", ndProp);
                    String value = xpath.evaluate("@value", ndProp);
                    info.getAttributes().set(key, value);
                }

                infos.add(info);
            }

            cfg.getLuceneConfig().setParserProxies(infos.createParserProxies());

            NodeList ndObservers = (NodeList) xpath.evaluate("observer", ndLucene, XPathConstants.NODESET);
            for (Node ndObserver : new NodeListAdapter(ndObservers)) {
                LuceneIndexObserverInfo info = new LuceneIndexObserverInfo();
                info.setClassName(Val.chkStr(xpath.evaluate("@className", ndObserver)));
                NodeList ndListProps = (NodeList) xpath.evaluate("attribute", ndObserver, XPathConstants.NODESET);
                for (Node ndAttribute : new NodeListAdapter(ndListProps)) {
                    String key = xpath.evaluate("@key", ndAttribute);
                    String value = xpath.evaluate("@value", ndAttribute);
                    info.getAttributes().set(key, value);
                }
                LuceneIndexObserver observer = info.createObserver();
                if (observer != null) {
                    cfg.getLuceneConfig().getObservers().add(observer);
                }
            }

        }

        loadMetadataAccessPolicyConfiguration(appConfig, root);

    }

    /**
     * Loads the acsess policies.
     * 
     * @param appConfig
     *          the primary application configuration
     * @param dom
     *          the configuration document
     * @param root
     *          the root node for the document
     * @throws Exception
     * @throws Exception
     */
    private void loadMetadataAccessPolicyConfiguration(ApplicationConfiguration appConfig, Node root)
            throws Exception {
        XPath xpath = XPathFactory.newInstance().newXPath();
        MetadataAccessPolicy aclCfg = appConfig.getMetadataAccessPolicy();
        Node ndPolicy = (Node) xpath.evaluate("catalog/metadataAccessPolicy", root, XPathConstants.NODE);
        if (ndPolicy != null) {
            String type = xpath.evaluate("@type", ndPolicy);
            aclCfg.setAccessPolicyType(type);
            String accessToGroupDN = xpath.evaluate("@protectedGroupDN", ndPolicy);
            if (accessToGroupDN != null && accessToGroupDN.trim().length() > 0) {
                aclCfg.setAccessToGroupDN(accessToGroupDN);
            }
        }
    }

    /**
     * Loads the database references.
     * 
     * @param appConfig
     *          the primary application configuration
     * @param dom
     *          the configuration document
     * @param root
     *          the root node for the document
     * @throws XPathExpressionException
     *           indicates a programming error, bad XPath
     */
    private void loadDatabase(ApplicationConfiguration appConfig, Document dom, Node root)
            throws XPathExpressionException {
        XPath xpath = XPathFactory.newInstance().newXPath();
        Node ndDb = (Node) xpath.evaluate("databaseReference", root, XPathConstants.NODE);
        if (ndDb != null) {
            DatabaseReference dbRef = new DatabaseReference();
            dbRef.setReferenceName(xpath.evaluate("@name", ndDb));
            dbRef.setJndiName(xpath.evaluate("@jndiName", ndDb));
            dbRef.setDirectDriverClassName(xpath.evaluate("@driver", ndDb));
            dbRef.setDirectUrl(xpath.evaluate("@url", ndDb));
            dbRef.setDirectUsername(xpath.evaluate("@username", ndDb));
            dbRef.setDirectPassword(xpath.evaluate("@password", ndDb));
            dbRef.getTags().add("default");
            appConfig.getDatabaseReferences().add(dbRef, true);
        }
    }

    /**
     * Loads the identify configuration.
     * 
     * @param appConfig
     *          the primary application configuration
     * @param dom
     *          the configuration document
     * @param root
     *          the root node for the document
     * @throws XPathExpressionException
     *           indicates a programming error, bad XPath
     */
    private void loadIdentity(ApplicationConfiguration appConfig, Document dom, Node root)
            throws XPathExpressionException {

        // prepare
        XPath xpath = XPathFactory.newInstance().newXPath();
        IdentityConfiguration idConfig = appConfig.getIdentityConfiguration();
        LdapConfiguration ldapConfig = idConfig.getLdapConfiguration();
        Node ndIdentity = (Node) xpath.evaluate("identity", root, XPathConstants.NODE);
        if (ndIdentity == null) {
            return;
        }

        // primary parameters
        String sName = Val.chkStr(xpath.evaluate("@name", ndIdentity));
        String sRealm = Val.chkStr(xpath.evaluate("@realm", ndIdentity));
        String sEncKey = Val.chkStr(xpath.evaluate("@encKey", ndIdentity));
        String sAdapterClass = Val.chkStr(xpath.evaluate("@adapterClassName", ndIdentity));
        if (sName.length() == 0) {
            sName = "Identity Configuration";
        }
        if (sRealm.length() == 0) {
            sRealm = "Geoportal";
        }
        if (sAdapterClass.length() == 0) {
            sAdapterClass = "com.esri.gpt.framework.security.identity.ldap.LdapIdentityAdapter";
        }
        idConfig.setName(sName);
        idConfig.setRealm(sRealm);
        idConfig.setEncKey(sEncKey);

        // determine the adapter
        Node ndSimple = (Node) xpath.evaluate("simpleAdapter", ndIdentity, XPathConstants.NODE);
        Node ndLdap = (Node) xpath.evaluate("ldapAdapter", ndIdentity, XPathConstants.NODE);
        if (ndSimple != null) {
            ndLdap = null;
            sAdapterClass = "com.esri.gpt.framework.security.identity.local.SimpleIdentityAdapter";
        }
        idConfig.setAdapterClassName(sAdapterClass);

        // simple adapter configuration
        if (ndSimple != null) {

            // account
            Node ndAccount = (Node) xpath.evaluate("account", ndSimple, XPathConstants.NODE);
            if (ndAccount != null) {
                String sUser = xpath.evaluate("@username", ndAccount);
                String sPwd = xpath.evaluate("@password", ndAccount);
                String sDN = "cn=" + sUser + ",ou=simpleadapter";
                boolean bEncrypted = Val.chkBool(xpath.evaluate("@encrypted", ndAccount), false);
                if (bEncrypted) {
                    try {
                        String sDecrypted = PC1_Encryptor.decrypt(sPwd);
                        sPwd = sDecrypted;
                    } catch (Exception e) {
                        this.getLogger().log(Level.SEVERE, "The simple account password failed to decrypt.", e);
                    }
                }
                UsernamePasswordCredentials creds = new UsernamePasswordCredentials(sUser, sPwd);
                creds.setDistinguishedName(sDN);
                idConfig.setCatalogAdminDN(creds.getDistinguishedName());
                idConfig.getSimpleConfiguration().setServiceAccountCredentials(creds);
            }

            // roles
            Node ndRoles = (Node) xpath.evaluate("roles", ndSimple, XPathConstants.NODE);
            if (ndRoles != null) {
                Roles roles = idConfig.getConfiguredRoles();
                NodeList nlRoles = (NodeList) xpath.evaluate("role", ndRoles, XPathConstants.NODESET);
                for (int i = 0; i < nlRoles.getLength(); i++) {
                    Node ndRole = nlRoles.item(i);
                    Role role = new Role();
                    role.setKey(xpath.evaluate("@key", ndRole));
                    roles.add(role);
                }
                for (Role role : roles.values()) {
                    role.buildFullRoleSet(roles);
                }
            }

        }

        // LDAP adapter configuration
        if (ndLdap != null) {

            // connection properties & service account
            Node ndCon = (Node) xpath.evaluate("ldapConnectionProperties", ndLdap, XPathConstants.NODE);
            if (ndCon != null) {
                LdapConnectionProperties props = ldapConfig.getConnectionProperties();
                props.setProviderUrl(xpath.evaluate("@providerURL", ndCon));
                props.setInitialContextFactoryName(xpath.evaluate("@initialContextFactoryName", ndCon));
                props.setSecurityAuthenticationLevel(xpath.evaluate("@securityAuthentication", ndCon));
                props.setSecurityProtocol(xpath.evaluate("@securityProtocol", ndCon));

                Node ndService = (Node) xpath.evaluate("ldapServiceAccount", ndCon, XPathConstants.NODE);
                if (ndService != null) {
                    String sUser = xpath.evaluate("@securityPrincipal", ndService);
                    String sPwd = xpath.evaluate("@securityCredentials", ndService);
                    boolean bEncrypted = Val.chkBool(xpath.evaluate("@encrypted", ndService), false);
                    if (bEncrypted) {
                        try {
                            String sDecrypted = PC1_Encryptor.decrypt(sPwd);
                            sPwd = sDecrypted;
                        } catch (Exception e) {
                            this.getLogger().log(Level.SEVERE, "The securityCredentials failed to decrypt.", e);
                        }
                    }
                    UsernamePasswordCredentials creds = new UsernamePasswordCredentials(sUser, sPwd);
                    props.setServiceAccountCredentials(creds);
                    idConfig.setCatalogAdminDN(xpath.evaluate("@catalogAdminDN", ndService));
                }
            }

            // single sign-on mechanism
            Node ndSSO = (Node) xpath.evaluate("singleSignOn", ndLdap, XPathConstants.NODE);
            if (ndSSO != null) {
                SingleSignOnMechanism sso = idConfig.getSingleSignOnMechanism();
                sso.setActive(Val.chkBool(xpath.evaluate("@active", ndSSO), false));
                sso.setCredentialLocation(xpath.evaluate("@credentialLocation", ndSSO));
                sso.setAnonymousValue(xpath.evaluate("@anonymousValue", ndSSO));
            }

            // self care support
            Node ndSupport = (Node) xpath.evaluate("selfCareSupport", ndLdap, XPathConstants.NODE);
            if (ndSupport != null) {
                IdentitySupport support = idConfig.getSupportedFunctions();
                support.setSupportsLogin(Val.chkBool(xpath.evaluate("@supportsLogin", ndSupport), true));
                support.setSupportsLogout(Val.chkBool(xpath.evaluate("@supportsLogout", ndSupport), true));
                support.setSupportsUserRegistration(
                        Val.chkBool(xpath.evaluate("@supportsUserRegistration", ndSupport), false));
                support.setSupportsUserProfileManagement(
                        Val.chkBool(xpath.evaluate("@supportsUserProfileManagement", ndSupport), false));
                support.setSupportsPasswordChange(
                        Val.chkBool(xpath.evaluate("@supportsPasswordChange", ndSupport), false));
                support.setSupportsPasswordRecovery(
                        Val.chkBool(xpath.evaluate("@supportsPasswordRecovery", ndSupport), false));
            }

            // roles
            Node ndRoles = (Node) xpath.evaluate("roles", ndLdap, XPathConstants.NODE);
            if (ndRoles != null) {
                Roles roles = idConfig.getConfiguredRoles();
                String sRegUserKey = Val.chkStr(xpath.evaluate("@registeredUserRoleKey", ndRoles));
                if (sRegUserKey.length() == 0) {
                    sRegUserKey = "gptRegisteredUser";
                }
                roles.setAuthenticatedUserRequiresRole(
                        Val.chkBool(xpath.evaluate("@authenticatedUserRequiresRole", ndRoles), true));
                roles.setRegisteredUserRoleKey(sRegUserKey);

                NodeList nlRoles = (NodeList) xpath.evaluate("role", ndRoles, XPathConstants.NODESET);
                for (int i = 0; i < nlRoles.getLength(); i++) {
                    Node ndRole = nlRoles.item(i);
                    Role role = new Role();
                    role.setKey(xpath.evaluate("@key", ndRole));
                    role.setInherits(xpath.evaluate("@inherits", ndRole));
                    role.setResKey(xpath.evaluate("@resKey", ndRole));
                    role.setManage(Val.chkBool(xpath.evaluate("@manage", ndRole), true));
                    role.setForbidden(Val.chkBool(xpath.evaluate("@forbidden", ndRole), false));
                    role.setDistinguishedName(xpath.evaluate("@groupDN", ndRole));
                    roles.add(role);
                }
                for (Role role : roles.values()) {
                    role.buildFullRoleSet(roles);
                }
            }

            // user properties
            Node ndUser = (Node) xpath.evaluate("users", ndLdap, XPathConstants.NODE);
            if (ndUser != null) {
                LdapUserProperties props = ldapConfig.getUserProperties();
                props.setUserDisplayNameAttribute(xpath.evaluate("@displayNameAttribute", ndUser));
                props.setPasswordEncryptionAlgorithm(xpath.evaluate("@passwordEncryptionAlgorithm", ndUser));
                props.setUserDNPattern(xpath.evaluate("@newUserDNPattern", ndUser));
                props.setUsernameSearchPattern(xpath.evaluate("@usernameSearchPattern", ndUser));

                props.setUserRequestsSearchPattern(xpath.evaluate("@newUserRequestSearchPattern", ndUser));

                props.setUserSearchDIT(xpath.evaluate("@searchDIT", ndUser));
                NodeList nlObj = (NodeList) xpath.evaluate("requiredObjectClasses/objectClass/@name", ndUser,
                        XPathConstants.NODESET);
                for (int i = 0; i < nlObj.getLength(); i++) {
                    props.addUserObjectClass(nlObj.item(i).getNodeValue());
                }
            }

            // user profile parameters
            UserAttributeMap uaMap = idConfig.getUserAttributeMap();
            NodeList nlUserAttr = (NodeList) xpath.evaluate("users/userAttributeMap/attribute", ndLdap,
                    XPathConstants.NODESET);
            for (int i = 0; i < nlUserAttr.getLength(); i++) {
                UserAttribute attr = new UserAttribute();
                attr.setKey(xpath.evaluate("@key", nlUserAttr.item(i)));
                attr.setLdapName(xpath.evaluate("@ldapName", nlUserAttr.item(i)));

                // TODO: need to do a better check to filter out badly defined
                // parameters
                boolean bIsLdap = (idConfig.getAdapterClassName().indexOf("Ldap") != -1);
                if (bIsLdap && (attr.getLdapName().length() > 0)) {
                    uaMap.add(attr);
                }
            }
            ldapConfig.getUserProperties().getUserProfileMapping().configureFromUserAttributes(uaMap);

            // group properties
            Node ndGroup = (Node) xpath.evaluate("groups", ndLdap, XPathConstants.NODE);
            if (ndGroup != null) {
                LdapGroupProperties props = ldapConfig.getGroupProperties();
                props.setGroupDisplayNameAttribute(xpath.evaluate("@displayNameAttribute", ndGroup));
                props.setGroupDynamicMemberAttribute(xpath.evaluate("@dynamicMemberOfGroupsAttribute", ndGroup));
                props.setGroupDynamicMembersAttribute(xpath.evaluate("@dynamicMembersAttribute", ndGroup));
                props.setGroupMemberAttribute(xpath.evaluate("@memberAttribute", ndGroup));
                props.setGroupMemberSearchPattern(xpath.evaluate("@memberSearchPattern", ndGroup));
                props.setGroupNameSearchPattern(xpath.evaluate("@nameSearchPattern", ndGroup));
                props.setGroupSearchDIT(xpath.evaluate("@searchDIT", ndGroup));
            }

            // metadata management groups
            NodeList nlMmg = (NodeList) xpath.evaluate("groups/metadataManagementGroup", ndLdap,
                    XPathConstants.NODESET);
            for (int i = 0; i < nlMmg.getLength(); i++) {
                Node ndMmg = nlMmg.item(i);
                Group group = new Group();
                group.setDistinguishedName(xpath.evaluate("@groupDN", ndMmg));
                group.setKey(group.getDistinguishedName());
                group.setName(xpath.evaluate("@name", ndMmg));
                idConfig.getMetadataManagementGroups().add(group);
            }

        }

        // open providers
        NodeList nlOpenProviders = (NodeList) xpath.evaluate("openProviders/openProvider", ndIdentity,
                XPathConstants.NODESET);
        for (int i = 0; i < nlOpenProviders.getLength(); i++) {
            try {
                OpenProvider op = new OpenProvider();
                op.processConfgurationNode(idConfig.getOpenProviders(), nlOpenProviders.item(i));
            } catch (Exception e) {
                this.getLogger().log(Level.SEVERE, "Error while configuring openProvider.", e);
            }
        }
    }

    /**
     * Loads the interactive map configuration.
     * 
     * @param appConfig
     *          the primary application configuration
     * @param dom
     *          the configuration document
     * @param root
     *          the root node for the document
     * @throws XPathExpressionException
     *           indicates a programming error, bad XPath
     */
    private void loadInteractiveMap(ApplicationConfiguration appConfig, Document dom, Node root)
            throws XPathExpressionException {
        XPath xpath = XPathFactory.newInstance().newXPath();
        appConfig.getInteractiveMap().setJsapiUrl(xpath.evaluate("interactiveMap/@jsapiUrl", root));
        appConfig.getInteractiveMap().setMapServiceUrl(xpath.evaluate("interactiveMap/@mapServiceUrl", root));
        appConfig.getInteractiveMap().setMapServiceType(xpath.evaluate("interactiveMap/@mapServiceType", root));
        appConfig.getInteractiveMap()
                .setGeometryServiceUrl(xpath.evaluate("interactiveMap/@geometryServiceUrl", root));
        appConfig.getInteractiveMap().setLocatorUrl(xpath.evaluate("interactiveMap/@locatorUrl", root));
        appConfig.getInteractiveMap().setLocatorSingleFieldParameter(
                xpath.evaluate("interactiveMap/@locatorSingleFieldParameter", root));
        appConfig.getInteractiveMap().setMapVisibleLayers(xpath.evaluate("interactiveMap/@mapVisibleLayers", root));
        appConfig.getInteractiveMap().setMapInitialExtent(xpath.evaluate("interactiveMap/@mapInitialExtent", root));
    }

    /**
     * Loads the mail configuration.
     * 
     * @param appConfig
     *          the primary application configuration
     * @param dom
     *          the configuration document
     * @param root
     *          the root node for the document
     * @throws XPathExpressionException
     *           indicates a programming error, bad XPath
     */
    private void loadMail(ApplicationConfiguration appConfig, Document dom, Node root)
            throws XPathExpressionException {
        XPath xpath = XPathFactory.newInstance().newXPath();
        MailConfiguration mcfg = appConfig.getMailConfiguration();
        mcfg.put(xpath.evaluate("mail/@smtpHost", root), xpath.evaluate("mail/@smtpPort", root),
                xpath.evaluate("mail/@siteEmailAddress", root), xpath.evaluate("mail/@siteEmailAddress", root));
        mcfg.setEmailAddressRegexp(xpath.evaluate("mail/@emailAddressRegexp", root));

        Node ndAuth = (Node) xpath.evaluate("mail/smtpAuth", root, XPathConstants.NODE);
        if (ndAuth != null) {
            String sUser = Val.chkStr(xpath.evaluate("@username", ndAuth));
            String sPwd = xpath.evaluate("@password", ndAuth);
            boolean bEncrypted = Val.chkBool(xpath.evaluate("@encrypted", ndAuth), false);
            if (bEncrypted) {
                try {
                    String sDecrypted = PC1_Encryptor.decrypt(sPwd);
                    sPwd = sDecrypted;
                } catch (Exception e) {
                    this.getLogger().log(Level.SEVERE, "The smptAuth password failed to decrypt.", e);
                }
            }
            if ((sUser != null) && (sUser.length() > 0) && (sPwd != null) && (sPwd.length() > 0)) {
                mcfg.setPasswordAuthentication(new javax.mail.PasswordAuthentication(sUser, sPwd));
            }
        }
    }

    /**
     * Loads the scheduler configuration.
     * 
     * @param appConfig
     *          the primary application configuration
     * @param dom
     *          the configuration document
     * @param root
     *          the root node for the document
     * @throws XPathExpressionException
     *           indicates a programming error, bad XPath
     */
    private void loadScheduler(ApplicationConfiguration appConfig, Document dom, Node root)
            throws XPathExpressionException {

        XPath xpath = XPathFactory.newInstance().newXPath();
        ThreadSchedulerConfiguration tsConfig = appConfig.getThreadSchedulerConfiguration();
        Node ndScheduler = (Node) xpath.evaluate("scheduler", root, XPathConstants.NODE);
        if (ndScheduler == null) {
            return;
        }

        // primary parameters
        tsConfig.setActive(Val.chkBool(xpath.evaluate("@active", ndScheduler), false));
        tsConfig.setCorePoolSize(Val.chkInt(xpath.evaluate("@corePoolSize", ndScheduler), 0));

        // threads
        NodeList nlThreads = (NodeList) xpath.evaluate("thread", ndScheduler, XPathConstants.NODESET);
        for (int i = 0; i < nlThreads.getLength(); i++) {
            Node ndThread = nlThreads.item(i);
            String sClass = Val.chkStr(xpath.evaluate("@class", ndThread));
            String sDelay = Val.chkStr(xpath.evaluate("@delay", ndThread));
            String sPeriod = Val.chkStr(xpath.evaluate("@period", ndThread));
            String sAt = Val.chkStr(xpath.evaluate("@at", ndThread));

            // read parameters
            StringAttributeMap parameters = new StringAttributeMap();
            populateParameters(parameters, ndThread);

            // add definition
            tsConfig.addDefinition(sClass, sDelay, sPeriod, sAt, parameters);
        }
    }

    /**
     * Loads the download data configuration.
     * 
     * @param appConfig
     *          the primary application configuration
     * @param dom
     *          the configuration document
     * @param root
     *          the root node for the document
     * @throws XPathExpressionException
     *           indicates a programming error, bad XPath
     */
    private void loadDownloadData(ApplicationConfiguration appConfig, Document dom, Node root)
            throws XPathExpressionException {

        // download configuration
        XPath xpath = XPathFactory.newInstance().newXPath();
        Node ndDownload = (Node) xpath.evaluate("downloadData", root, XPathConstants.NODE);
        if (ndDownload != null) {
            DownloadConfiguration cfg = appConfig.getDownloadDataConfiguration();
            cfg.setTaskUrl(xpath.evaluate("@taskUrl", ndDownload));
            cfg.setMapServiceUrl(xpath.evaluate("@mapServiceUrl", ndDownload));
            cfg.setMapServiceType(xpath.evaluate("@mapServiceType", ndDownload));
            cfg.setMapInitialExtent(xpath.evaluate("@mapInitialExtent", ndDownload));

            // load projections
            NodeList ndProjections = (NodeList) xpath.evaluate("projections/projection", ndDownload,
                    XPathConstants.NODESET);
            for (int i = 0; i < ndProjections.getLength(); i++) {
                Node node = ndProjections.item(i);
                String key = xpath.evaluate("@key", node);
                String alias = xpath.evaluate("@alias", node);
                String resKey = xpath.evaluate("@resKey", node);
                ItemInfo ii = new ItemInfo(key, alias, resKey);
                cfg.getProjectionInfo().add(ii);
            }

            // load output format
            NodeList ndFormats = (NodeList) xpath.evaluate("formats/format", ndDownload, XPathConstants.NODESET);
            for (int i = 0; i < ndFormats.getLength(); i++) {
                Node node = ndFormats.item(i);
                String key = xpath.evaluate("@key", node);
                String alias = xpath.evaluate("@alias", node);
                String resKey = xpath.evaluate("@resKey", node);
                ItemInfo ii = new ItemInfo(key, alias, resKey);
                cfg.getOutputFormatInfo().add(ii);
            }

            // load feature formats
            NodeList ndFeatureFormats = (NodeList) xpath.evaluate("features/feature", ndDownload,
                    XPathConstants.NODESET);
            for (int i = 0; i < ndFeatureFormats.getLength(); i++) {
                Node node = ndFeatureFormats.item(i);
                String key = xpath.evaluate("@key", node);
                String alias = xpath.evaluate("@alias", node);
                String resKey = xpath.evaluate("@resKey", node);
                ItemInfo ii = new ItemInfo(key, alias, resKey);
                cfg.getFeatureFormatInfo().add(ii);
            }

            // load raster formats
            NodeList ndRasterFormats = (NodeList) xpath.evaluate("rasters/raster", ndDownload,
                    XPathConstants.NODESET);
            for (int i = 0; i < ndRasterFormats.getLength(); i++) {
                Node node = ndRasterFormats.item(i);
                String key = xpath.evaluate("@key", node);
                String alias = xpath.evaluate("@alias", node);
                String resKey = xpath.evaluate("@resKey", node);
                ItemInfo ii = new ItemInfo(key, alias, resKey);
                cfg.getRasterFormatInfo().add(ii);
            }
        }
    }

    /**
     * Loads harvester configuration.
     * @param appConfig application configuration
     */
    private void loadHarvesterConfiguration(ApplicationConfiguration appConfig, Document dom, Node root)
            throws XPathExpressionException {
        StringAttributeMap parameters = appConfig.getCatalogConfiguration().getParameters();
        HarvesterConfiguration cfg = appConfig.getHarvesterConfiguration();

        String active = Val.chkStr(parameters.getValue("webharvester.active"));
        String suspended = Val.chkStr(parameters.getValue("webharvester.suspended"));
        String queueEnabled = Val.chkStr(parameters.getValue("webharvester.queueEnabled"));
        String poolsize = Val.chkStr(parameters.getValue("webharvester.poolSize"));
        String autoselectfrequency = Val.chkStr(parameters.getValue("webharvester.autoSelectFrequency"));
        String watchdogfrequency = Val.chkStr(parameters.getValue("webharvester.watchDogFrequency"));
        String basecontextpath = Val.chkStr(parameters.getValue("webharvester.baseContextPath"));
        String maxRepRecords = Val.chkStr(parameters.getValue("webharvester.maxRepRecords"));
        String maxRepErrors = Val.chkStr(parameters.getValue("webharvester.maxRepErrors"));
        String resourceAutoApprove = Val.chkStr(parameters.getValue("webharvester.resource.autoApprove"));

        Logger logger = getLogger();

        if (Val.chkBool(active, true)) {
            cfg.setActive(true);
            cfg.setQueueEnabled(true);
        } else {
            cfg.setActive(false);
            cfg.setQueueEnabled(false);
        }

        if (Val.chkBool(suspended, false)) {
            cfg.setSuspended(true);
        } else {
            cfg.setSuspended(false);
        }

        if (queueEnabled.length() > 0) {
            cfg.setQueueEnabled(Val.chkBool(queueEnabled, cfg.getQueueEnabled()) || cfg.getActive());
        }

        if (poolsize.length() > 0) {
            try {
                int num = Integer.parseInt(poolsize);
                if (num <= 0) {
                    logger.info(
                            "[SYNCHRONIZER] Parameter \"webharvester.poolSize\" less or equal to zero. No harvestig will be performed.");
                }
                cfg.setPoolSize(num);
            } catch (NumberFormatException ex) {
                logger.log(Level.INFO,
                        "[SYNCHRONIZER] Invalid \"webharvester.poolSize\" parameter. Default {0} will be used instead.",
                        HarvesterConfiguration.DEFAULT_POOL_SIZE);
                cfg.setPoolSize(HarvesterConfiguration.DEFAULT_POOL_SIZE);
            }
        } else {
            logger.log(Level.INFO,
                    "[SYNCHRONIZER] Missing \"webharvester.poolSize\" parameter. Default {0} will be used instead.",
                    HarvesterConfiguration.DEFAULT_POOL_SIZE);
            cfg.setPoolSize(HarvesterConfiguration.DEFAULT_POOL_SIZE);
        }

        if (autoselectfrequency.length() > 0) {
            try {
                TimePeriod tp = TimePeriod.parseValue(autoselectfrequency);
                cfg.setAutoSelectFrequency(tp);
            } catch (NumberFormatException ex) {
                logger.log(Level.INFO,
                        "[SYNCHRONIZER] Invalid \"webharvester.autoSelectFrequency\" parameter. Default {0} will be used instead.",
                        HarvesterConfiguration.AUTOSELECT_FREQUENCY);
                cfg.setAutoSelectFrequency(new TimePeriod(HarvesterConfiguration.AUTOSELECT_FREQUENCY));
            }
        } else {
            logger.log(Level.INFO,
                    "[SYNCHRONIZER] Missing \"webharvester.autoSelectFrequency\" parameter. Default {0} will be used instead.",
                    HarvesterConfiguration.AUTOSELECT_FREQUENCY);
            cfg.setAutoSelectFrequency(new TimePeriod(HarvesterConfiguration.AUTOSELECT_FREQUENCY));
        }

        if (watchdogfrequency.length() > 0) {
            try {
                TimePeriod tp = TimePeriod.parseValue(watchdogfrequency);
                cfg.setWatchDogFrequency(tp);
            } catch (NumberFormatException ex) {
                logger.log(Level.INFO,
                        "[SYNCHRONIZER] Invalid \"webharvester.watchDogFrequency\" parameter. Default {0} will be used instead.",
                        HarvesterConfiguration.WATCHDOG_FREQUENCY);
                cfg.setWatchDogFrequency(new TimePeriod(HarvesterConfiguration.WATCHDOG_FREQUENCY));
            }
        } else {
            logger.log(Level.INFO,
                    "[SYNCHRONIZER] Missing \"webharvester.watchDogFrequency\" parameter. Default {0} will be used instead.",
                    HarvesterConfiguration.WATCHDOG_FREQUENCY);
            cfg.setWatchDogFrequency(new TimePeriod(HarvesterConfiguration.WATCHDOG_FREQUENCY));
        }

        if (basecontextpath.length() > 0) {
            cfg.setBaseContextPath(basecontextpath);
        } else {
            String reverseProxyPath = Val.chkStr(parameters.getValue("reverseProxy.baseContextPath"));
            if (reverseProxyPath.length() > 0) {
                logger.info(
                        "[SYNCHRONIZER] Missing \"webharvester.baseContextPath\" parameter. Value of \"reverseProxy.baseContextPath\" will be used instead.");
            } else {
                logger.info(
                        "[SYNCHRONIZER] Missing \"webharvester.baseContextPath\" parameter. Harvest notification messages will be sent without information about harvest report.");
            }
        }

        if (maxRepRecords.length() > 0) {
            try {
                long num = Long.parseLong(maxRepRecords);
                if (num < 0) {
                    logger.info(
                            "[SYNCHRONIZER] Parameter \"webharvester.maxRepRecords\" less than zero. No limits will be set.");
                }
                cfg.setMaxRepRecords(num);
            } catch (NumberFormatException ex) {
                logger.log(Level.INFO,
                        "[SYNCHRONIZER] Invalid \"webharvester.maxRepRecords\" parameter. Default {0} will be used instead.",
                        HarvesterConfiguration.MAX_REP_RECORDS);
                cfg.setMaxRepRecords(HarvesterConfiguration.MAX_REP_RECORDS);
            }
        } else {
            logger.log(Level.INFO,
                    "[SYNCHRONIZER] Missing \"webharvester.maxRepRecords\" parameter. Default {0} will be used instead.",
                    HarvesterConfiguration.MAX_REP_RECORDS);
            cfg.setMaxRepRecords(HarvesterConfiguration.MAX_REP_RECORDS);
        }

        if (maxRepErrors.length() > 0) {
            try {
                long num = Long.parseLong(maxRepErrors);
                if (num < 0) {
                    logger.info(
                            "[SYNCHRONIZER] Parameter \"webharvester.maxRepErrors\" less than zero. No limits will be set.");
                }
                cfg.setMaxRepErrors(num);
            } catch (NumberFormatException ex) {
                logger.log(Level.INFO,
                        "[SYNCHRONIZER] Invalid \"webharvester.maxRepErrors\" parameter. Default {0} will be used instead.",
                        HarvesterConfiguration.MAX_REP_ERRORS);
                cfg.setMaxRepErrors(HarvesterConfiguration.MAX_REP_ERRORS);
            }
        } else {
            logger.log(Level.INFO,
                    "[SYNCHRONIZER] Missing \"webharvester.maxRepErrors\" parameter. Default {0} will be used instead.",
                    HarvesterConfiguration.MAX_REP_ERRORS);
            cfg.setMaxRepErrors(HarvesterConfiguration.MAX_REP_ERRORS);
        }

        if (resourceAutoApprove.length() > 0) {
            boolean bool = Val.chkBool(resourceAutoApprove, HarvesterConfiguration.RESOURCE_AUTOAPPROVE);
            cfg.setResourceAutoApprove(bool);
        } else {
            logger.log(Level.INFO,
                    "[SYNCHRONIZER] Missing \"webharvester.resource.autoApprove\" parameter. Default {0} will be used instead.",
                    HarvesterConfiguration.RESOURCE_AUTOAPPROVE);
            cfg.setResourceAutoApprove(HarvesterConfiguration.RESOURCE_AUTOAPPROVE);
        }

        // load data processor factories
        XPath xpath = XPathFactory.newInstance().newXPath();

        // add local data processor factory by default
        cfg.getDataProcessorFactories().add(new LocalDataProcessorFactory());

        // get root of webharvester configuration
        Node ndWebHarvester = (Node) xpath.evaluate("webharvester", root, XPathConstants.NODE);
        if (ndWebHarvester != null) {
            // create and initialize data processor for each netry in configuration
            NodeList ndDataProcessorFactories = (NodeList) xpath.evaluate("dataProcessorFactory", ndWebHarvester,
                    XPathConstants.NODESET);
            for (Node ndDataProcessorFactory : new NodeListAdapter(ndDataProcessorFactories)) {
                String className = Val.chkStr(
                        (String) xpath.evaluate("@className", ndDataProcessorFactory, XPathConstants.STRING));
                String name = Val
                        .chkStr((String) xpath.evaluate("@name", ndDataProcessorFactory, XPathConstants.STRING));
                boolean enabled = Val.chkBool(
                        Val.chkStr(
                                (String) xpath.evaluate("@enabled", ndDataProcessorFactory, XPathConstants.STRING)),
                        true);
                if (enabled) {
                    try {
                        Class factoryClass = Class.forName(className);
                        DataProcessorFactory processorFactory = (DataProcessorFactory) factoryClass.newInstance();
                        processorFactory.setName(name);
                        processorFactory.init(ndDataProcessorFactory);
                        cfg.getDataProcessorFactories().add(processorFactory);
                    } catch (Exception ex) {
                        getLogger().log(Level.SEVERE, "Error creating processor factory: " + className, ex);
                    }
                } else {
                    if (LocalDataProcessorFactory.class.getCanonicalName().equals(className)) {
                        removeDataProcessorFactory(cfg.getDataProcessorFactories(), className);
                    }
                }
            }
        }
    }

    private void removeDataProcessorFactory(List<DataProcessorFactory> factories, String factoryClassName) {
        for (DataProcessorFactory factory : factories) {
            if (factory.getClass().getCanonicalName().equals(factoryClassName)) {
                factories.remove(factory);
                break;
            }
        }
    }

    /**
     * Loads protocol factories.
     * @param appConfig protocol factories
     */
    private void loadProtocolFactories(ApplicationConfiguration appConfig, Document dom, Node root)
            throws XPathExpressionException {
        ProtocolFactories factories = appConfig.getProtocolFactories();
        XPath xpath = XPathFactory.newInstance().newXPath();

        Node ndProtocols = (Node) xpath.evaluate("protocols", root, XPathConstants.NODE);
        if (ndProtocols != null) {
            // check 'default' attribute of the 'protcols' node; if present and true than initialize default factories
            boolean defaultValue = Val
                    .chkBool((String) xpath.evaluate("@default", ndProtocols, XPathConstants.STRING), false);
            if (defaultValue) {
                factories.initDefault();
            }

            // initilaize explicit protocol factories
            NodeList lstProtocol = (NodeList) xpath.evaluate("protocol", ndProtocols, XPathConstants.NODESET);
            for (Node ndProto : new NodeListAdapter(lstProtocol)) {
                String factoryClass = (String) xpath.evaluate("@factoryClass", ndProto, XPathConstants.STRING);
                try {
                    Class fc = Class.forName(factoryClass);
                    ProtocolFactory factory = (ProtocolFactory) fc.newInstance();
                    ProtocolInitializer.init(factory, ndProto);
                    factories.put(factory.getName(), factory);
                } catch (Exception ex) {
                    getLogger().log(Level.WARNING, "Error loading protocol: " + factoryClass, ex);
                }
            }
        } else {
            factories.initDefault();
        }
    }

    /**
     * Reads the parameters under a parent node.
     * 
     * @param parameters
     *          the parameters to populate
     * @param parent
     *          the parent node containing the parameters to read
     * @throws XPathExpressionException
     *           indicates a programming error, bad XPath
     */
    private void populateParameters(StringAttributeMap parameters, Node parent) throws XPathExpressionException {
        XPath xpath = XPathFactory.newInstance().newXPath();
        NodeList nlParameters = (NodeList) xpath.evaluate("parameter", parent, XPathConstants.NODESET);
        for (int i = 0; i < nlParameters.getLength(); i++) {
            Node ndParameter = nlParameters.item(i);
            String sKey = Val.chkStr(xpath.evaluate("@key", ndParameter));
            String sValue = Val.chkStr(xpath.evaluate("@value", ndParameter));
            if (sKey.length() > 0) {
                parameters.add(new StringAttribute(sKey, sValue));
            }
        }
    }

    /**
     * Safely parses time period giving default value if time period can not be parsed.
     * @param periodDef period definition to parse
     * @param defaultValue default value if period definition cannot be parsed
     * @return time period
     */
    private TimePeriod parsePeriod(String periodDef, long defaultValue) {
        try {
            periodDef = Val.chkStr(periodDef);
            if (periodDef.isEmpty()) {
                return new TimePeriod(defaultValue);
            }
            return TimePeriod.parseValue(periodDef);
        } catch (IllegalArgumentException ex) {
            return new TimePeriod(defaultValue);
        }
    }
}