001/*
002 * (C) Copyright 2011-2014 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl-2.1.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Florent Guillaume
016 *     Julien Carsique
017 */
018package org.nuxeo.runtime.deployment;
019
020import static org.nuxeo.common.Environment.JBOSS_HOST;
021import static org.nuxeo.common.Environment.NUXEO_CONFIG_DIR;
022import static org.nuxeo.common.Environment.NUXEO_DATA_DIR;
023import static org.nuxeo.common.Environment.NUXEO_LOG_DIR;
024import static org.nuxeo.common.Environment.NUXEO_RUNTIME_HOME;
025import static org.nuxeo.common.Environment.NUXEO_TMP_DIR;
026import static org.nuxeo.common.Environment.NUXEO_WEB_DIR;
027import static org.nuxeo.common.Environment.TOMCAT_HOST;
028
029import java.io.BufferedReader;
030import java.io.File;
031import java.io.IOException;
032import java.io.InputStream;
033import java.io.InputStreamReader;
034import java.sql.Driver;
035import java.sql.DriverManager;
036import java.sql.SQLException;
037import java.util.ArrayList;
038import java.util.Arrays;
039import java.util.Enumeration;
040import java.util.HashMap;
041import java.util.List;
042import java.util.Map;
043import java.util.Set;
044
045import javax.servlet.ServletContext;
046import javax.servlet.ServletContextEvent;
047import javax.servlet.ServletContextListener;
048
049import org.apache.commons.logging.Log;
050import org.apache.commons.logging.LogFactory;
051import org.nuxeo.osgi.application.loader.FrameworkLoader;
052import org.osgi.framework.BundleException;
053
054/**
055 * This is called at WAR startup and starts the Nuxeo OSGi runtime and registers the Nuxeo bundles with it.
056 * <p>
057 * This class must be configured as a {@code <listener>/<listener-class>} in {@code META-INF/web.xml}.
058 * <p>
059 * It uses servlet init parameters defined through {@code <context-param>/<param-name>/<param-value>} in web.xml.
060 * Allowable parameter names come from {@link org.nuxeo.common.Environment}, mainly
061 * {@link org.nuxeo.common.Environment#NUXEO_RUNTIME_HOME NUXEO_RUNTIME_HOME} and
062 * {@link org.nuxeo.common.Environment#NUXEO_CONFIG_DIR NUXEO_CONFIG_DIR}, but also
063 * {@link org.nuxeo.common.Environment#NUXEO_DATA_DIR NUXEO_DATA_DIR},
064 * {@link org.nuxeo.common.Environment#NUXEO_LOG_DIR NUXEO_LOG_DIR}, {@link org.nuxeo.common.Environment#NUXEO_TMP_DIR
065 * NUXEO_TMP_DIR} and {@link org.nuxeo.common.Environment#NUXEO_WEB_DIR NUXEO_WEB_DIR}.
066 */
067public class NuxeoStarter implements ServletContextListener {
068
069    private static final Log log = LogFactory.getLog(NuxeoStarter.class);
070
071    /** Default location of the home in the server current directory. */
072    private static final String DEFAULT_HOME = "nuxeo";
073
074    /**
075     * Name of the file listing Nuxeo bundles. If existing, this file will be used at start, else
076     * {@code "/WEB-INF/lib/"} will be scanned.
077     *
078     * @since 5.9.3
079     * @see #findBundles(ServletContext)
080     */
081    public static final String NUXEO_BUNDLES_LIST = ".nuxeo-bundles";
082
083    protected final Map<String, Object> env = new HashMap<>();
084
085    protected List<File> bundleFiles = new ArrayList<>();
086
087    @Override
088    public void contextInitialized(ServletContextEvent event) {
089        try {
090            long startTime = System.currentTimeMillis();
091            start(event);
092            long finishedTime = System.currentTimeMillis();
093            @SuppressWarnings("boxing")
094            Double duration = (finishedTime - startTime) / 1000.0;
095            log.info(String.format("Nuxeo framework started in %.1f sec.", duration));
096        } catch (IOException | BundleException e) {
097            throw new RuntimeException(e);
098        }
099    }
100
101    @Override
102    public void contextDestroyed(ServletContextEvent event) {
103        try {
104            stop();
105        } catch (BundleException e) {
106            throw new RuntimeException(e);
107        }
108    }
109
110    protected void start(ServletContextEvent event) throws IOException, BundleException {
111        ServletContext servletContext = event.getServletContext();
112        findBundles(servletContext);
113        findEnv(servletContext);
114
115        ClassLoader cl = getClass().getClassLoader();
116        File home = new File((String) env.get(NUXEO_RUNTIME_HOME));
117        FrameworkLoader.initialize(cl, home, bundleFiles, env);
118        FrameworkLoader.start();
119    }
120
121    protected void stop() throws BundleException {
122        FrameworkLoader.stop();
123        Enumeration<Driver> drivers = DriverManager.getDrivers();
124        while (drivers.hasMoreElements()) {
125            Driver driver = drivers.nextElement();
126            try {
127                DriverManager.deregisterDriver(driver);
128                log.warn(String.format("Deregister JDBC driver: %s", driver));
129            } catch (SQLException e) {
130                log.error(String.format("Error deregistering JDBC driver %s", driver), e);
131            }
132        }
133    }
134
135    protected void findBundles(ServletContext servletContext) throws IOException {
136        InputStream bundlesListStream = servletContext.getResourceAsStream("/WEB-INF/" + NUXEO_BUNDLES_LIST);
137        if (bundlesListStream != null) {
138            File lib = new File(servletContext.getRealPath("/WEB-INF/lib/"));
139            try (BufferedReader reader = new BufferedReader(new InputStreamReader(bundlesListStream))) {
140                String bundleName;
141                while ((bundleName = reader.readLine()) != null) {
142                    bundleFiles.add(new File(lib, bundleName));
143                }
144            }
145        }
146        if (bundleFiles.isEmpty()) { // Fallback on directory scan
147            File root = new File(servletContext.getRealPath("/"));
148            Set<String> ctxpaths = servletContext.getResourcePaths("/WEB-INF/lib/");
149            if (ctxpaths != null) {
150                for (String ctxpath : ctxpaths) {
151                    if (!ctxpath.endsWith(".jar")) {
152                        continue;
153                    }
154                    bundleFiles.add(new File(root, ctxpath));
155                }
156            }
157        }
158    }
159
160    protected void findEnv(ServletContext servletContext) {
161        for (String param : Arrays.asList( //
162                NUXEO_RUNTIME_HOME, //
163                NUXEO_CONFIG_DIR, //
164                NUXEO_DATA_DIR, //
165                NUXEO_LOG_DIR, //
166                NUXEO_TMP_DIR, //
167                NUXEO_WEB_DIR)) {
168            String value = servletContext.getInitParameter(param);
169            if (value != null && !"".equals(value.trim())) {
170                env.put(param, value);
171            }
172        }
173        // default env values
174        if (!env.containsKey(NUXEO_CONFIG_DIR)) {
175            String webinf = servletContext.getRealPath("/WEB-INF");
176            env.put(NUXEO_CONFIG_DIR, webinf);
177        }
178        if (!env.containsKey(NUXEO_RUNTIME_HOME)) {
179            File home = new File(DEFAULT_HOME);
180            env.put(NUXEO_RUNTIME_HOME, home.getAbsolutePath());
181        }
182        // host
183        if (getClass().getClassLoader().getClass().getName().startsWith("org.jboss.classloader")) {
184            env.put(FrameworkLoader.HOST_NAME, JBOSS_HOST);
185        } else if (servletContext.getClass().getName().startsWith("org.apache.catalina")) {
186            env.put(FrameworkLoader.HOST_NAME, TOMCAT_HOST);
187        }
188    }
189
190}