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