001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH & Co. KG, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.main;
029
030import java.lang.reflect.Method;
031import java.sql.Driver;
032import java.sql.DriverManager;
033import java.util.Enumeration;
034
035import javax.servlet.ServletContextEvent;
036import javax.servlet.ServletContextListener;
037import javax.servlet.http.HttpSessionEvent;
038import javax.servlet.http.HttpSessionListener;
039
040import org.apache.commons.logging.Log;
041
042/**
043 * Provides the OpenCms system with information from the servlet context.<p>
044 *
045 * Used for the following purposes:<ul>
046 * <li>Starting up OpenCms when the servlet container is started.</li>
047 * <li>Shutting down OpenCms when the servlet container is shut down.</li>
048 * <li>Informing the <code>{@link org.opencms.main.CmsSessionManager}</code> if a new session is created.</li>
049 * <li>Informing the <code>{@link org.opencms.main.CmsSessionManager}</code> session is destroyed or invalidated.</li>
050 * </ul>
051 *
052 * @since 6.0.0
053 */
054public class OpenCmsListener implements ServletContextListener, HttpSessionListener {
055
056    /** The log object for this class. */
057    private static final Log LOG = CmsLog.getLog(OpenCmsListener.class);
058
059    /**
060     * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent)
061     */
062    public void contextDestroyed(ServletContextEvent event) {
063
064        try {
065            // destroy the OpenCms instance
066            OpenCmsCore.getInstance().shutDown();
067            shutDownSqlDrivers();
068        } catch (CmsInitException e) {
069            if (e.isNewError()) {
070                LOG.error(e.getLocalizedMessage(), e);
071            }
072        } catch (Throwable t) {
073            // make sure all other errors are displayed in the OpenCms log
074            LOG.error(Messages.get().getBundle().key(Messages.LOG_ERROR_GENERIC_0), t);
075        }
076    }
077
078    /**
079     * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)
080     */
081    public void contextInitialized(ServletContextEvent event) {
082
083        String basePath = event.getServletContext().getRealPath("/");
084        String path = basePath + "/WEB-INF/logs/startup-stacktraces.zip";
085        String summaryPath = basePath + "/WEB-INF/logs/startup-summary.xml";
086        CmsSingleThreadDumperThread dumpThread = new CmsSingleThreadDumperThread(
087            path,
088            summaryPath,
089            Thread.currentThread().getId());
090        boolean enableThreadDumps = "true".equalsIgnoreCase(System.getProperty("opencms.profile.startup.stacktraces"));
091        try {
092            if (enableThreadDumps) {
093                dumpThread.start();
094            }
095            // upgrade the OpenCms runlevel
096            OpenCmsCore.getInstance().upgradeRunlevel(event.getServletContext());
097        } catch (CmsInitException e) {
098            if (e.isNewError()) {
099                // only log new init errors
100                LOG.error(e.getLocalizedMessage(), e);
101            }
102        } catch (Throwable t) {
103            // make sure all other errors are displayed in the OpenCms log
104            LOG.error(Messages.get().getBundle().key(Messages.LOG_ERROR_GENERIC_0), t);
105            // throw a new init Exception to make sure a "context destroyed" event is triggered
106            throw new CmsInitException(Messages.get().container(Messages.ERR_CRITICAL_INIT_GENERIC_1, t.getMessage()));
107        } finally {
108            // Signal the thread to finish its business.
109            // This doesn't do anything if the thread isn't running.
110            dumpThread.interrupt();
111        }
112    }
113
114    /**
115     * @see javax.servlet.http.HttpSessionListener#sessionCreated(javax.servlet.http.HttpSessionEvent)
116     */
117    public void sessionCreated(HttpSessionEvent event) {
118
119        try {
120            // inform the OpenCms session manager
121            OpenCmsCore.getInstance().getSessionManager().sessionCreated(event);
122        } catch (CmsInitException e) {
123            if (e.isNewError()) {
124                LOG.error(e.getLocalizedMessage(), e);
125            }
126        } catch (Throwable t) {
127            // make sure all other errors are displayed in the OpenCms log
128            LOG.error(Messages.get().getBundle().key(Messages.LOG_ERROR_GENERIC_0), t);
129        }
130    }
131
132    /**
133     * @see javax.servlet.http.HttpSessionListener#sessionDestroyed(javax.servlet.http.HttpSessionEvent)
134     */
135    public void sessionDestroyed(HttpSessionEvent event) {
136
137        try {
138            // inform the OpenCms session manager
139            OpenCmsCore.getInstance().getSessionManager().sessionDestroyed(event);
140        } catch (CmsInitException e) {
141            if (e.isNewError()) {
142                LOG.error(e.getLocalizedMessage(), e);
143            }
144        } catch (Throwable t) {
145            // make sure all other errors are displayed in the OpenCms log
146            LOG.error(Messages.get().getBundle().key(Messages.LOG_ERROR_GENERIC_0), t);
147        }
148    }
149
150    /**
151     * De-registers the SQL drivers in order to prevent potential memory leaks.<p>
152     */
153    private void shutDownSqlDrivers() {
154
155        // This manually deregisters JDBC driver, which prevents Tomcat 7 from complaining about memory leaks
156        Enumeration<Driver> drivers = DriverManager.getDrivers();
157        while (drivers.hasMoreElements()) {
158            Driver driver = drivers.nextElement();
159            try {
160                DriverManager.deregisterDriver(driver);
161            } catch (Throwable e) {
162                System.out.println(
163                    Messages.get().getBundle().key(
164                        Messages.ERR_DEREGISTERING_JDBC_DRIVER_1,
165                        driver.getClass().getName()));
166                e.printStackTrace(System.out);
167            }
168        }
169
170        try {
171            Class<?> cls = Class.forName("com.mysql.jdbc.AbandonedConnectionCleanupThread");
172            Method shutdownMethod = (cls == null ? null : cls.getMethod("shutdown"));
173            if (shutdownMethod != null) {
174                shutdownMethod.invoke(null);
175            }
176        } catch (Throwable e) {
177            System.out.println("Failed to shutdown MySQL connection cleanup thread: " + e.getMessage());
178        }
179    }
180}