view artifact-database/src/main/java/de/intevation/artifactdatabase/FactoryBootstrap.java @ 313:ddc35c950e97

Backend: Handle forgotten store/create event source. artifacts/trunk@2440 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Mon, 01 Aug 2011 15:43:44 +0000
parents 1d517e051e95
children f2277458823e
line wrap: on
line source
/*
 * Copyright (c) 2010 by Intevation GmbH
 *
 * This program is free software under the LGPL (>=v2.1)
 * Read the file LGPL.txt coming with the software for details
 * or visit http://www.gnu.org/licenses/ if it does not exist.
 */

package de.intevation.artifactdatabase;

import de.intevation.artifacts.common.utils.Config;

import de.intevation.artifacts.ArtifactCollectionFactory;
import de.intevation.artifacts.ArtifactContextFactory;
import de.intevation.artifacts.ArtifactFactory;
import de.intevation.artifacts.CallContext;
import de.intevation.artifacts.GlobalContext;
import de.intevation.artifacts.Hook;
import de.intevation.artifacts.ServiceFactory;
import de.intevation.artifacts.UserFactory;

import de.intevation.artifacts.common.utils.StringUtils;

import de.intevation.artifactdatabase.rest.HTTPServer;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * Bootstrap facility for the global context and the artifact factories.
 *
 * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a>
 */
public class FactoryBootstrap
{
    private static Logger logger = Logger.getLogger(FactoryBootstrap.class);

    /**
     * XPath to figure out the class name of the context factory from
     * the global configuration.
     */
    public static final String CONTEXT_FACTORY =
        "/artifact-database/factories/context-factory/text()";

    /**
     * The name of the default context factory.
     */
    public static final String DEFAULT_CONTEXT_FACTORY =
        "de.intevation.artifactdatabase.DefaultArtifactContextFactory";

    /**
     * XPath to figure out the names of the artifact factories from
     * the global configuration to be exposed by the artifact database.
     */
    public static final String ARTIFACT_FACTORIES =
        "/artifact-database/factories/artifact-factories/artifact-factory";

    /**
     * XPath to figure out the names of the service factories from
     * the global configuration to build the services offered by the
     * artifact database.
     */
    public static final String SERVICE_FACTORIES =
        "/artifact-database/factories/service-factories/service-factory";

    /**
     * XPath to figure out the class name of the user factory from global
     * configuration.
     */
    public static final String USER_FACTORY =
        "/artifact-database/factories/user-factory";

    /**
     * The name of the default user factory.
     */
    public static final String DEFAULT_USER_FACTORY =
        "de.intevation.artifactdatabase.DefaultUserFactory";

    /**
     * XPath to figure out the class name of the collection factory from global
     * configuration.
     */
    public static final String COLLECTION_FACTORY =
        "/artifact-database/factories/collection-factory";

    /**
     * The name of the default user factory.
     */
    public static final String DEFAULT_COLLECTION_FACTORY =
        "de.intevation.artifactdatabase.DefaultArtifactCollectionFactory";

    /**
     * XPath to figure out the secret used to sign the artifact exports
     * made by the artfifact database server.
     */
    public static final String EXPORT_SECRET =
        "/artifact-database/export-secret/text()";

    /**
     * XPAth that points to a configuration node for a CallContext.Listener.
     */
    public static final String CALLCONTEXT_LISTENER =
        "/artifact-database/callcontext-listener";

    /**
     * XPath that points to configuration nodes for hooks.
     */
    public static final String HOOKS =
        "/artifact-database/hooks/hook";

    public static final String HTTP_SERVER =
        "/artifact-database/rest-server/http-server/text()";

    public static final String DEFAULT_HTTP_SERVER =
        "de.intevation.artifactdatabase.rest.Standalone";

    public static final String LIFETIME_LISTENERS =
        "/artifact-database/lifetime-listeners/listener";

    public static final String BACKEND_LISTENERS =
        "/artifact-database/backend-listeners/listener";

    /**
     * Default export signing secret.
     * <strong>PLEASE CHANGE THE SECRET VIA THE XPATH EXPORT_SECRET
     * IN THE CONFIGURATION.</strong>.
     */
    public static final String DEFAULT_EXPORT_SECRET =
        "!!!CHANGE ME! I'M NO SECRET!!!";

    /**
     * Reference to the global context build by the global context factory.
     */
    protected GlobalContext context;

    /**
     * List of the artifact factories to be exposed by the
     * artifact database.
     */
    protected ArtifactFactory [] artifactFactories;

    /**
     * List of service factories which creates services that are
     * exposed by the artifact database.
     */
    protected ServiceFactory [] serviceFactories;

    /**
     * The factory that is used to create and list users.
     */
    protected UserFactory userFactory;

    /**
     * The factory that is used to create new artifact collections.
     */
    protected ArtifactCollectionFactory collectionFactory;

    /**
     * The CallContext.Listener.
     */
    protected CallContext.Listener callContextListener;

    protected List<Hook> postFeedHooks;

    protected List<Hook> postAdvanceHooks;

    protected List<LifetimeListener> lifetimeListeners;

    protected List<BackendListener> backendListeners;

    /**
     * byte array holding the export signing secret.
     */
    protected byte [] exportSecret;

    protected HTTPServer httpServer;


    /**
     * Default constructor
     */
    public FactoryBootstrap() {
    }

    void buildContext() {
        String className = Config.getStringXPath(
            CONTEXT_FACTORY, DEFAULT_CONTEXT_FACTORY);

        ArtifactContextFactory factory = null;

        try {
            Class clazz = Class.forName(className);
            factory = (ArtifactContextFactory)clazz.newInstance();
        }
        catch (ClassNotFoundException cnfe) {
            logger.error(cnfe.getLocalizedMessage(), cnfe);
        }
        catch (InstantiationException ie) {
            logger.error(ie.getLocalizedMessage(), ie);
        }
        catch (ClassCastException cce) {
            logger.error(cce.getLocalizedMessage(), cce);
        }
        catch (IllegalAccessException iae) {
            logger.error(iae.getLocalizedMessage(), iae);
        }

        if (factory == null) {
            factory = new DefaultArtifactContextFactory();
        }

        logger.info("Using class '" + factory.getClass().getName()
            + "' for context creation.");

        context = factory.createArtifactContext(Config.getConfig());
    }


    /**
     * Scans the global configuration to load the configured collection factory
     * and sets it up.
     */
    protected void loadCollectionFactory() {

        logger.info("loading collection factory.");

        Node factory = Config.getNodeXPath(COLLECTION_FACTORY);

        String className = Config.getStringXPath(
            factory, "text()", DEFAULT_COLLECTION_FACTORY);

        try {
            Class clazz       = Class.forName(className);
            collectionFactory = (ArtifactCollectionFactory) clazz.newInstance();

            collectionFactory.setup(Config.getConfig(), factory);
        }
        catch (ClassNotFoundException cnfe) {
            logger.error(cnfe.getLocalizedMessage(), cnfe);
        }
        catch (InstantiationException ie) {
            logger.error(ie.getLocalizedMessage(), ie);
        }
        catch (ClassCastException cce) {
            logger.error(cce.getLocalizedMessage(), cce);
        }
        catch (IllegalAccessException iae) {
            logger.error(iae.getLocalizedMessage(), iae);
        }
    }

    /**
     * Scans the global configuration to load the configured
     * artifact factories and sets them up.
     */
    protected void loadArtifactFactories() {

        logger.info("loading artifact factories");

        ArrayList loadedFactories = new ArrayList();

        NodeList nodes = Config.getNodeSetXPath(ARTIFACT_FACTORIES);

        if (nodes == null) {
            logger.warn("No factories found");
        }

        Document config = Config.getConfig();

        for (int i = 0, N = nodes != null ? nodes.getLength() : 0; i < N; ++i) {
            String className = nodes.item(i).getTextContent().trim();

            ArtifactFactory factory = null;

            try {
                Class clazz = Class.forName(className);
                factory = (ArtifactFactory)clazz.newInstance();
            }
            catch (ClassNotFoundException cnfe) {
                logger.error(cnfe.getLocalizedMessage(), cnfe);
            }
            catch (InstantiationException ie) {
                logger.error(ie.getLocalizedMessage(), ie);
            }
            catch (ClassCastException cce) {
                logger.error(cce.getLocalizedMessage(), cce);
            }
            catch (IllegalAccessException iae) {
                logger.error(iae.getLocalizedMessage(), iae);
            }

            if (factory != null) {
                factory.setup(config, nodes.item(i));
                loadedFactories.add(factory);
                logger.info("Registering '"
                    + factory.getName() + "' as artifact factory.");
            }
        }

        artifactFactories = (ArtifactFactory [])loadedFactories.toArray(
            new ArtifactFactory[loadedFactories.size()]);
    }

    /**
     * Scans the global configuration for the configured service factories
     * and sets them up.
     */
    protected void loadServiceFactories() {

        logger.info("loading service factories");

        ArrayList loadedFactories = new ArrayList();

        NodeList nodes = Config.getNodeSetXPath(SERVICE_FACTORIES);

        if (nodes == null) {
            logger.warn("No factories found");
        }

        Document config = Config.getConfig();

        for (int i = 0, N = nodes != null ? nodes.getLength() : 0; i < N; ++i) {
            String className = nodes.item(i).getTextContent().trim();

            ServiceFactory factory = null;

            try {
                Class clazz = Class.forName(className);
                factory = (ServiceFactory)clazz.newInstance();
            }
            catch (ClassNotFoundException cnfe) {
                logger.error(cnfe.getLocalizedMessage(), cnfe);
            }
            catch (InstantiationException ie) {
                logger.error(ie.getLocalizedMessage(), ie);
            }
            catch (ClassCastException cce) {
                logger.error(cce.getLocalizedMessage(), cce);
            }
            catch (IllegalAccessException iae) {
                logger.error(iae.getLocalizedMessage(), iae);
            }

            if (factory != null) {
                factory.setup(config, nodes.item(i));
                loadedFactories.add(factory);
                logger.info( "Registering '" + factory.getName()
                    + "' as service factory.");
            }
        }

        serviceFactories = (ServiceFactory [])loadedFactories.toArray(
            new ServiceFactory[loadedFactories.size()]);
    }


    /**
     * Scans the global configuration for the configured user factory.
     */
    protected void loadUserFactory() {
        logger.info("loading user factory");

        Node factory = Config.getNodeXPath(USER_FACTORY);

        String className = Config.getStringXPath(
            factory, "text()", DEFAULT_USER_FACTORY);

        try {
            Class clazz = Class.forName(className);
            userFactory = (UserFactory) clazz.newInstance();

            userFactory.setup(Config.getConfig(), factory);
        }
        catch (ClassNotFoundException cnfe) {
                logger.error(cnfe.getLocalizedMessage(), cnfe);
        }
        catch (InstantiationException ie) {
            logger.error(ie.getLocalizedMessage(), ie);
        }
        catch (ClassCastException cce) {
            logger.error(cce.getLocalizedMessage(), cce);
        }
        catch (IllegalAccessException iae) {
            logger.error(iae.getLocalizedMessage(), iae);
        }
    }


    protected void loadCallContextListener() {
        logger.info("loading CallContext.Listener");

        Node listener = Config.getNodeXPath(CALLCONTEXT_LISTENER);

        if (listener == null) {
            return;
        }

        String className = Config.getStringXPath(listener, "text()");

        try {
            Class clazz         = Class.forName(className);
            callContextListener = (CallContext.Listener) clazz.newInstance();

            callContextListener.setup(Config.getConfig(), listener);
        }
        catch (ClassNotFoundException cnfe) {
            logger.error(cnfe.getLocalizedMessage(), cnfe);
        }
        catch (InstantiationException ie) {
            logger.error(ie.getLocalizedMessage(), ie);
        }
        catch (ClassCastException cce) {
            logger.error(cce.getLocalizedMessage(), cce);
        }
        catch (IllegalAccessException iae) {
            logger.error(iae.getLocalizedMessage(), iae);
        }
    }

    protected void loadHTTPServer() {
        logger.info("loading HTTPServer");

        String className = Config.getStringXPath(
            HTTP_SERVER, DEFAULT_HTTP_SERVER);

        logger.info("using HTTP server: " + className);

        try {
            Class clazz = Class.forName(className);
            httpServer  = (HTTPServer)clazz.newInstance();

            httpServer.setup(Config.getConfig());
        }
        catch (ClassNotFoundException cnfe) {
            logger.error(cnfe.getLocalizedMessage(), cnfe);
        }
        catch (InstantiationException ie) {
            logger.error(ie.getLocalizedMessage(), ie);
        }
        catch (ClassCastException cce) {
            logger.error(cce.getLocalizedMessage(), cce);
        }
        catch (IllegalAccessException iae) {
            logger.error(iae.getLocalizedMessage(), iae);
        }
    }

    protected void loadLifetimeListeners() {
        logger.info("loading lifetime listeners");

        NodeList nodes = Config.getNodeSetXPath(LIFETIME_LISTENERS);

        if (nodes == null) {
            logger.debug("no lifetime listeners configure");
            return;
        }

        List<LifetimeListener> ltls = new ArrayList<LifetimeListener>();

        for (int i = 0, N = nodes.getLength(); i < N; ++i) {
            Node node = nodes.item(i);
            String className = node.getTextContent();
            if (className == null
            || (className = className.trim()).length() == 0) {
                continue;
            }
            try {
                Class clazz = Class.forName(className);
                LifetimeListener listener =
                    (LifetimeListener)clazz.newInstance();

                listener.setup(Config.getConfig());

                ltls.add(listener);
            }
            catch (ClassNotFoundException cnfe) {
                logger.error(cnfe.getLocalizedMessage(), cnfe);
            }
            catch (InstantiationException ie) {
                logger.error(ie.getLocalizedMessage(), ie);
            }
            catch (ClassCastException cce) {
                logger.error(cce.getLocalizedMessage(), cce);
            }
            catch (IllegalAccessException iae) {
                logger.error(iae.getLocalizedMessage(), iae);
            }
        }

        lifetimeListeners = ltls;
    }

    protected void loadBackendListeners() {
        logger.info("loading backend listeners");

        NodeList nodes = Config.getNodeSetXPath(BACKEND_LISTENERS);

        if (nodes == null) {
            logger.debug("no backend listeners configure");
            return;
        }

        List<BackendListener> bls = new ArrayList<BackendListener>();

        for (int i = 0, N = nodes.getLength(); i < N; ++i) {
            Node node = nodes.item(i);
            String className = node.getTextContent();
            if (className == null
            || (className = className.trim()).length() == 0) {
                continue;
            }
            try {
                Class clazz = Class.forName(className);
                BackendListener listener =
                    (BackendListener)clazz.newInstance();

                bls.add(listener);
            }
            catch (ClassNotFoundException cnfe) {
                logger.error(cnfe.getLocalizedMessage(), cnfe);
            }
            catch (InstantiationException ie) {
                logger.error(ie.getLocalizedMessage(), ie);
            }
            catch (ClassCastException cce) {
                logger.error(cce.getLocalizedMessage(), cce);
            }
            catch (IllegalAccessException iae) {
                logger.error(iae.getLocalizedMessage(), iae);
            }
        }

        backendListeners = bls;
    }

    protected void loadHooks() {
        logger.info("loading hooks");

        postFeedHooks    = new ArrayList<Hook>();
        postAdvanceHooks = new ArrayList<Hook>();

        NodeList nodes = Config.getNodeSetXPath(HOOKS);

        for (int i = 0, len = nodes.getLength(); i < len; i++) {
            Node   cfg     = nodes.item(i);
            String applies = Config.getStringXPath(cfg, "@applies");

            if (applies == null || applies.length() == 0) {
                continue;
            }

            Hook     hook  = loadHook(cfg);
            String[] apply = applies.split(",");

            for (String a: apply) {
                a = a.trim().toLowerCase();

                if (a.equals("post-feed")) {
                    postFeedHooks.add(hook);
                }
                else if (a.equals("post-advance")) {
                    postAdvanceHooks.add(hook);
                }
            }
        }
    }

    protected Hook loadHook(Node hookCfg) {
        if (hookCfg == null) {
            return null;
        }

        Hook hook = null;

        String className = Config.getStringXPath(hookCfg, "@class");

        try {
            Class clazz = Class.forName(className);
            hook        = (Hook) clazz.newInstance();

            hook.setup(hookCfg);
        }
        catch (ClassNotFoundException cnfe) {
            logger.error(cnfe.getLocalizedMessage(), cnfe);
        }
        catch (InstantiationException ie) {
            logger.error(ie.getLocalizedMessage(), ie);
        }
        catch (ClassCastException cce) {
            logger.error(cce.getLocalizedMessage(), cce);
        }
        catch (IllegalAccessException iae) {
            logger.error(iae.getLocalizedMessage(), iae);
        }

        return hook;
    }

    /**
     * Fetches the export signing secret from the global configuration.
     * If none is found if defaults to the DEFAULT_EXORT_SECRET which
     * is insecure.
     */
    protected void setupExportSecret() {
        String secret = Config.getStringXPath(EXPORT_SECRET);

        if (secret == null) {
            logger.warn("NO EXPORT SECRET SET! USING INSECURE DEFAULT!");
            secret = DEFAULT_EXPORT_SECRET;
        }

        exportSecret = StringUtils.getUTF8Bytes(secret);
    }

    /**
     * Loads all the dynamic classes configured by the global configuration.
     */
    public void boot() {
        setupExportSecret();
        buildContext();
        loadCollectionFactory();
        loadArtifactFactories();
        loadServiceFactories();
        loadUserFactory();
        loadCallContextListener();
        loadHTTPServer();
        loadHooks();
        loadLifetimeListeners();
        loadBackendListeners();
    }

    /**
     * Returns the artifact collection factory.
     *
     * @return the artifact collection factory.
     */
    public ArtifactCollectionFactory getArtifactCollectionFactory() {
        return collectionFactory;
    }

    /**
     * Returns the list of ready to use artifact factories.
     * @return The list of artifact factories.
     */
    public ArtifactFactory [] getArtifactFactories() {
        return artifactFactories;
    }

    /**
     * Returns the ready to use service factories.
     * @return The list of service factories.
     */
    public ServiceFactory [] getServiceFactories() {
        return serviceFactories;
    }

    /**
     * Returns the user factory.
     *
     * @return the user factory.
     */
    public UserFactory getUserFactory() {
        return userFactory;
    }

    /**
     * Returns the global context created by the global context factory.
     * @return The global context.
     */
    public GlobalContext getContext() {
        return context;
    }

    /**
     * Returns the signing secret to be used when ex- and importing
     * artifacts from and into the artifact database.
     * @return the byte array containg the signing secret.
     */
    public byte [] getExportSecret() {
        return exportSecret;
    }

    /**
     * Returns a CallContext.Listener if configured or null.
     *
     * @return a CallContext.Listener.
     */
    public CallContext.Listener getCallContextListener() {
        return callContextListener;
    }

    public List<Hook> getPostFeedHooks() {
        return postFeedHooks;
    }

    public List<Hook> getPostAdvanceHooks() {
        return postAdvanceHooks;
    }

    public HTTPServer getHTTPServer() {
        return httpServer;
    }

    public List<LifetimeListener> getLifetimeListeners() {
        return lifetimeListeners;
    }

    public List<BackendListener> getBackendListeners() {
        return backendListeners;
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org