/*
 * RHQ Management Platform
 * Copyright (C) 2005-2008 Red Hat, Inc.
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
package org.rhq.enterprise.gui.startup;

import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Properties;
import java.util.logging.LogManager;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.enterprise.gui.legacy.AttrConstants;
import org.rhq.enterprise.gui.legacy.Constants;
import org.rhq.enterprise.server.util.LookupUtil;

/**
 * This is a servlet context listener defined in web.xml that loads several files and puts data in the servlet context:
 *
 * <ul>
 *   <li>the <code>/WEB-INF/rhq-server-internals.properties</code> properties which configures things like how to
 *     connect to EJBs</li>
 *   <li>configures a user's default preferences, for those users that have no persisted preferences already</li>
 *   <li>the build version number</li>
 *   <li>taglib properties</li>
 * </ul>
 */
public final class Configurator implements ServletContextListener {
    private Log log = LogFactory.getLog(Configurator.class.getName());

    private static final String SERVER_INTERNALS_FILE = "/WEB-INF/rhq-server-internals.properties";
    private static final String DEFAULT_USER_PREFERENCES_FILE = "/WEB-INF/DefaultUserPreferences.properties";
    private static final String TAGLIB_PROPERTIES_FILE = "/WEB-INF/taglib.properties";
    private static final String LOGGING_PROPERTIES_RESOURCE = "logging.properties";

    // If any name in the server internals properties file starts with this, it defines a system property.
    // Therefore, the name/value will not go into the servlet context, but will be a system property.
    private static final String SYSPROP_KEY = "org.jboss.on.sysprop";

    /**
     * Creates a new {@link Configurator} object.
     */
    public Configurator() {
    }

    /**
     * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)
     */
    public void contextInitialized(ServletContextEvent sce) {
        ServletContext ctx = sce.getServletContext();
        loadConfig(ctx);
        loadPreferences(ctx);
        loadTablePreferences(ctx);
        loadBuildVersion(ctx);
        loadJavaLoggingConfiguration();
    }

    /**
     * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent)
     */
    public void contextDestroyed(ServletContextEvent sce) {
    }

    /**
     * Load the internals properties file and configures the web application. All properties in the file are exposed as
     * servlet context attributes.
     */
    private void loadConfig(ServletContext ctx) {
        Properties props;

        try {
            props = loadProperties(ctx, SERVER_INTERNALS_FILE);
        } catch (Exception e) {
            log.error("unable to load server internals file [" + SERVER_INTERNALS_FILE + "]", e);
            return;
        }

        if (props == null) {
            log.debug("server internals file [" + SERVER_INTERNALS_FILE + "] does not exist");
            return;
        }

        Enumeration names = props.propertyNames();

        while (names.hasMoreElements()) {
            String name = (String) names.nextElement();

            // this is not currently used, but is interesting, it lets you define system properties in rhq-server-internals.properties
            if (name.startsWith(SYSPROP_KEY)) {
                System.setProperty(name, props.getProperty(name));
            } else {
                ctx.setAttribute(name, props.getProperty(name));
            }
        }

        log.debug("loaded server internals [" + SERVER_INTERNALS_FILE + "]");

        return;
    }

    /**
     * There is a single properties file in the web application that defines the default user properties. That
     * properties file is loaded in and stored in the servlet context so a user that logs in but doesn't have
     * preferences set yet can obtain these default settings.
     */
    private void loadPreferences(ServletContext ctx) {
        try {
            Configuration userPrefs = new Configuration();
            Properties userProps = loadProperties(ctx, DEFAULT_USER_PREFERENCES_FILE);
            Enumeration keys = userProps.keys();
            while (keys.hasMoreElements()) {
                String key = (String) keys.nextElement();
                userPrefs.put(new PropertySimple(key, userProps.getProperty(key)));
            }

            ctx.setAttribute(Constants.DEF_USER_PREFS, userPrefs);
        } catch (Exception e) {
            log.error("failed to load user preferences at [" + DEFAULT_USER_PREFERENCES_FILE + "]: ", e);
        }
    }

    /**
     * The table tag GUI component needs the taglib definitions. This loads those definitions.
     */
    private void loadTablePreferences(ServletContext ctx) {
        try {
            Properties tableProps = loadProperties(ctx, TAGLIB_PROPERTIES_FILE);
            ctx.setAttribute(Constants.PROPS_TAGLIB_NAME, tableProps);
        } catch (Exception e) {
            log.error("failed to load the taglib properties at [" + TAGLIB_PROPERTIES_FILE + "]: ", e);
        }
    }

    /**
     * Determines the internal build/version number and puts it in a context attribute.
     */
    private void loadBuildVersion(ServletContext servletContext) {
        String version = LookupUtil.getCoreServer().getVersion();
        servletContext.setAttribute(AttrConstants.RHQ_VERSION_ATTR, version);
    }

    /**
     * Load the specified properties file and return the properties.
     *
     * @param  context  the <code>ServletContext</code>
     * @param  filename the fully qualifed name of the properties file
     *
     * @return the contents of the properties file
     *
     * @throws Exception if a problem occurs while loading the file
     */
    private Properties loadProperties(ServletContext context, String filename) throws Exception {
        Properties props = new Properties();
        InputStream is = context.getResourceAsStream(filename);
        if (is != null) {
            props.load(is);
            is.close();
        }

        return props;
    }

    private void loadJavaLoggingConfiguration() {
        InputStream loggingPropertiesStream = Configurator.class.getClassLoader().getResourceAsStream(
            LOGGING_PROPERTIES_RESOURCE);
        if (loggingPropertiesStream != null) {
            try {
                LogManager.getLogManager().readConfiguration(loggingPropertiesStream);
            } catch (IOException e) {
                log.error("Failed to load '" + LOGGING_PROPERTIES_RESOURCE + "' from webapp classloader.");
            }
        }
    }
}