org.sindice.analytics.servlet.ServletConfigurationContextListener.java Source code

Java tutorial

Introduction

Here is the source code for org.sindice.analytics.servlet.ServletConfigurationContextListener.java

Source

/*******************************************************************************
 * Copyright (c) 2012 National University of Ireland, Galway. All Rights Reserved.
 *
 *
 * This project is a free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 *
 * This project 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with this project. If not, see <http://www.gnu.org/licenses/>.
 *******************************************************************************/
package org.sindice.analytics.servlet;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Map;

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

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.XMLConfiguration;
import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
import org.apache.commons.lang.text.StrLookup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.util.StatusPrinter;

/**
 * --- Changed for the ASE use case ---</br></br>
 * 
 * Configures the servlet based upon configuration in sindice.home/{appname}
 * folder, or if that folder is not defined then ~/sindice/{appname}. Creates
 * the folder if needed using default logging and configuration provided with
 * the web application. This context listener should be moved into a shared
 * sindice package.
 * 
 * @author robful modified by szymon danielczyk (made it platform independent)
 *         If application deployed in ROOT context tries get the applicationName
 *         from web.xml <context-param> <param-name>applicationName</param-name>
 *         <param-value>xxx</param-value> </context-param>
 * 
 *         if present will use ROOT_xxx in not will use ROOT as contextPath
 * 
 */
public class ServletConfigurationContextListener implements ServletContextListener {

    private static final Logger logger = LoggerFactory.getLogger(ServletConfigurationContextListener.class);
    private static final String DEFAULT_LOGGING = "default-logback.xml";

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        logger.info("destroying context");
    }

    protected File configFolder;

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        final ServletContext context = sce.getServletContext();
        String contextPath = context.getContextPath().replaceFirst("^/", "");
        if (contextPath.equals("")) {
            // application deployed in ROOT context
            String applicationName = context.getInitParameter("applicationName");
            if (applicationName != null && !applicationName.equals("")) {
                contextPath = "ROOT_" + applicationName;
            } else {
                contextPath = "ROOT";
            }
            logger.warn("Application deployed in ROOT context.");
        } else {
            logger.info("Application deployed in [" + contextPath + "] context.");
        }
        logger.info("Will use [" + contextPath + "] as a part of a path for reading/storing configuration");

        String servletContextName = context.getServletContextName();
        addEnvToContext(context);

        String sindiceHome = null;
        // FOR DEBIAN PACKAGE LOD2 PROJECT INTEGRATION
        // check that following directory exists
        // if yes please use it
        if (new File("/etc/" + contextPath + "/config.xml").exists()) {
            sindiceHome = "/etc";
            logger.info("Found config.xml at [/etc/" + contextPath + "/config.xml]. Setting sindiceHome to : ["
                    + sindiceHome + "]");
        } else {
            logger.info(
                    "File /etc/" + contextPath + "/config.xml does not exists will try to determine sindiceHome");
        }
        // END DEBIAN PACKAGE LOD2 PROJECT INTEGRATION
        if (sindiceHome == null && context.getAttribute("sindice.home") != null) {
            sindiceHome = (String) context.getAttribute("sindice.home");
            logger.info("Setting sindiceHome from sindice.home env variable to [" + sindiceHome + "]");
        }
        if (sindiceHome == null && context.getAttribute("SINDICE_HOME") != null) {
            sindiceHome = (String) context.getAttribute("SINDICE_HOME");
            logger.info("Setting sindiceHome from SINDICE_HOME env variable to [" + sindiceHome + "]");
        }
        if (sindiceHome == null || "".equals(sindiceHome.trim())) {
            String userHome = (String) context.getAttribute("user.home");
            sindiceHome = (userHome == null ? "" : userHome) + File.separatorChar + "sindice";
            logger.warn("Neither sindice.home nor SINDICE_HOME are not defined, assuming {}", sindiceHome);
        }

        logger.info("Looking for configuration in [" + sindiceHome + File.separatorChar + contextPath + "]");

        // important to set these two as they are used later in logback.xml
        context.setAttribute("sindice.home", sindiceHome);
        context.setAttribute("app.name", contextPath);

        configFolder = new File(sindiceHome + File.separatorChar + contextPath);
        if (!(configFolder.exists() && configFolder.isDirectory())) {
            logger.warn("Missing configuration folder {}", configFolder);
            if (configFolder.mkdirs()) {
                logger.warn("Creating default configuration at " + configFolder);

            } else {
                // set logging level to INFO
                configureLogging(context, configFolder);
                return;
            }
        }
        // does a specific folder exist for this servlet context?
        if (servletContextName == null) {
            logger.error("specify display-name element in web.xml !!! ");
        } else {
            File specificFolder = new File(configFolder, servletContextName);
            if (specificFolder.exists() && specificFolder.isDirectory()) {
                configFolder = specificFolder;
            }
        }
        logger.info("loading configuration from folder {}", configFolder);
        configureLogging(context, configFolder);
        final XMLConfiguration config = createXMLConfiguration(context);
        File applicationConfigFile = new File(configFolder, "config.xml");
        if (!applicationConfigFile.exists()) {
            logger.warn("missing application config file {}", applicationConfigFile);
            loadDefaultConfiguration(config, applicationConfigFile);
        } else {
            try {
                config.load(applicationConfigFile);
                logger.info("parsed {}", applicationConfigFile);
            } catch (ConfigurationException e) {
                logger.error("Could not load configuration from {}", applicationConfigFile, e);
                loadDefaultConfiguration(config, null);
            }
        }
        context.setAttribute("config", config);
        logger.info("config now availabe via the following line of code\n"
                + "    XMLConfiguration appConfig = (XMLConfiguration) servletContext.getAttribute(\"config\");");

        logger.info("Starting up {}", servletContextName);
    }

    private XMLConfiguration createXMLConfiguration(final ServletContext context) {
        final XMLConfiguration config = new XMLConfiguration();
        ConfigurationInterpolator interpolator = config.getInterpolator();
        final StrLookup defaultLookup = interpolator.getDefaultLookup();
        interpolator.setDefaultLookup(new StrLookup() {
            @Override
            public String lookup(String key) {
                if (context.getAttribute(key) != null) {
                    return context.getAttribute(key).toString();
                }
                if (context.getInitParameter(key) != null) {
                    return context.getInitParameter(key);
                }
                return defaultLookup.lookup(key);
            }
        });
        return config;
    }

    private void loadDefaultConfiguration(final XMLConfiguration config, File saveAs) {
        logger.info("loading default configuration");
        InputStream in = ServletConfigurationContextListener.class.getClassLoader()
                .getResourceAsStream("default-config.xml");
        if (in == null) {
            logger.error("application is missing default-config.xml from classpath");
        } else {
            try {
                config.load(in);
                if (saveAs != null) {
                    try {
                        saveAs.getParentFile().mkdirs();
                        config.save(saveAs);
                        logger.info("wrote default configuration to {}", saveAs);
                    } catch (ConfigurationException e) {
                        logger.warn("Could not write configuration to {}", saveAs, e);
                    }
                }
            } catch (ConfigurationException e) {
                logger.error("could not load default-config.xml", e);
            } finally {
                try {
                    in.close();
                } catch (IOException e) {
                    logger.warn("problem closing stream", e);
                }
            }
        }
    }

    private void addEnvToContext(ServletContext context) {
        addToContext(context, System.getenv());
        addToContext(context, System.getProperties());
    }

    private void addToContext(ServletContext context, Map map) {
        for (Object key : map.keySet()) {
            if (context.getAttribute(key.toString()) == null) {
                context.setAttribute(key.toString(), map.get(key));
            }
        }
    }

    private void configureLogging(ServletContext context, final File configFolder) {
        InputStream in = openLoggingConfig(configFolder);
        if (in == null) {
            setDefaultLogging();
            return;
        }
        try {
            final LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
            final JoranConfigurator configurator = new JoranConfigurator();
            configurator.setContext(lc);
            lc.reset();
            try {
                Enumeration<String> attributeNames = context.getAttributeNames();
                while (attributeNames.hasMoreElements()) {
                    String key = attributeNames.nextElement();
                    Object value = context.getAttribute(key);
                    if (value != null) {
                        configurator.getContext().putProperty(key, value.toString());
                    }
                }
                configurator.doConfigure(in);
            } catch (JoranException e) {
                lc.reset();
                setDefaultLogging();
                logger.error("logging configuration failed", e);
                logger.warn("using servlet container logging configuration");
            }
            StatusPrinter.printInCaseOfErrorsOrWarnings(lc);
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                logger.warn("problem closing stream", e);
            }
        }
    }

    /**
     * Opens the logging configuration file logback.xml. If that doesn't exist try
     * to create it by copying default-logback.xml which should be provided in the
     * classes folder with the application.
     * 
     * @param configFolder
     * @return
     */
    private InputStream openLoggingConfig(File configFolder) {
        String loggerConfigFileName = configFolder.getAbsolutePath() + File.separatorChar + "logback.xml";
        File logConfigFile = new File(loggerConfigFileName);
        if (!logConfigFile.exists()) {
            logger.warn("Missing logging configuration file " + loggerConfigFileName);
            createLogConfigFile(logConfigFile);
        } else {
            logger.info("Will try to configure logging from:" + logConfigFile.getAbsolutePath());
        }
        if (logConfigFile.exists()) {
            try {
                return new FileInputStream(logConfigFile);
            } catch (Exception e) {
                logger.warn("problem reading log file", e);
            }
        }
        return ServletConfigurationContextListener.class.getClassLoader().getResourceAsStream(DEFAULT_LOGGING);
    }

    private void createLogConfigFile(File logConfigFile) {
        try {
            InputStream in = ServletConfigurationContextListener.class.getClassLoader()
                    .getResourceAsStream(DEFAULT_LOGGING);
            if (in == null) {
                logger.warn("missing default-logging.xml from classpath");
            } else {
                // try to write the application logging file so admin can change it.
                try {
                    logConfigFile.getParentFile().mkdirs();
                    FileOutputStream out = new FileOutputStream(logConfigFile);
                    try {
                        byte[] buff = new byte[1024];
                        int read = 0;
                        while ((read = in.read(buff)) > 0) {
                            out.write(buff, 0, read);
                        }
                    } finally {
                        out.close();
                    }
                } finally {
                    in.close();
                }
            }
        } catch (IOException e) {
            logger.warn("couldn't write logConfigFile {}", logConfigFile, e);
        }
    }

    private void setDefaultLogging() {
        final Logger rootLogger = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
        if (rootLogger instanceof ch.qos.logback.classic.Logger) {
            ((ch.qos.logback.classic.Logger) rootLogger).setLevel(Level.INFO);
        }
        logger.warn("Log level set to INFO, create the logging configuration file to change this.");
    }

    protected static final String getParameterWithLogging(XMLConfiguration config, String name,
            String defaultValue) {
        if (config == null) {
            return defaultValue;
        }

        String value = config.getString(name);
        if (value == null) {
            logger.info("missing init parameter " + name + ", using default value");
            value = defaultValue;
        }
        logger.info("using " + name + "=[" + value + "]");
        return value;
    }

    protected static final String[] getParametersWithLogging(XMLConfiguration config, String name,
            String[] defalt) {
        if (config == null) {
            return defalt;
        }

        String[] value = config.getStringArray(name);
        if (value.length == 0) {
            logger.info("missing init parameter " + name + ", using default value");
            value = defalt;
        }
        StringBuilder sb = new StringBuilder();
        for (String s : value) {
            if (sb.length() > 0) {
                sb.append(',');
            }
            sb.append(s);
        }
        logger.info("using " + name + "=[" + sb + "]");
        return value;
    }

}