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            log.error("Exception during startup", e);
100            throw new RuntimeException(e);
101        } catch (Throwable e) { // NOSONAR
102            log.error("Exception during startup", e);
103            throw e;
104        }
105    }
106
107    @Override
108    public void contextDestroyed(ServletContextEvent event) {
109        try {
110            stop();
111        } catch (BundleException e) {
112            throw new RuntimeException(e);
113        }
114    }
115
116    protected void start(ServletContextEvent event) throws IOException, BundleException {
117        ServletContext servletContext = event.getServletContext();
118        findBundles(servletContext);
119        findEnv(servletContext);
120
121        ClassLoader cl = getClass().getClassLoader();
122        File home = new File((String) env.get(NUXEO_RUNTIME_HOME));
123        FrameworkLoader.initialize(cl, home, bundleFiles, env);
124        FrameworkLoader.start();
125    }
126
127    protected void stop() throws BundleException {
128        FrameworkLoader.stop();
129        Enumeration<Driver> drivers = DriverManager.getDrivers();
130        while (drivers.hasMoreElements()) {
131            Driver driver = drivers.nextElement();
132            try {
133                DriverManager.deregisterDriver(driver);
134                log.info(String.format("Deregister JDBC driver: %s", driver));
135            } catch (SQLException e) {
136                log.error(String.format("Error deregistering JDBC driver %s", driver), e);
137            }
138        }
139    }
140
141    protected void findBundles(ServletContext servletContext) throws IOException {
142        InputStream bundlesListStream = servletContext.getResourceAsStream("/WEB-INF/" + NUXEO_BUNDLES_LIST);
143        if (bundlesListStream != null) {
144            File lib = new File(servletContext.getRealPath("/WEB-INF/lib/"));
145            try (BufferedReader reader = new BufferedReader(new InputStreamReader(bundlesListStream))) {
146                String bundleName;
147                while ((bundleName = reader.readLine()) != null) {
148                    bundleFiles.add(new File(lib, bundleName));
149                }
150            }
151        }
152        if (bundleFiles.isEmpty()) { // Fallback on directory scan
153            File root = new File(servletContext.getRealPath("/"));
154            Set<String> ctxpaths = servletContext.getResourcePaths("/WEB-INF/lib/");
155            if (ctxpaths != null) {
156                for (String ctxpath : ctxpaths) {
157                    if (!ctxpath.endsWith(".jar")) {
158                        continue;
159                    }
160                    bundleFiles.add(new File(root, ctxpath));
161                }
162            }
163        }
164    }
165
166    protected void findEnv(ServletContext servletContext) {
167        for (String param : Arrays.asList( //
168                NUXEO_RUNTIME_HOME, //
169                NUXEO_CONFIG_DIR, //
170                NUXEO_DATA_DIR, //
171                NUXEO_LOG_DIR, //
172                NUXEO_TMP_DIR, //
173                NUXEO_WEB_DIR)) {
174            String value = servletContext.getInitParameter(param);
175            if (value != null && !"".equals(value.trim())) {
176                env.put(param, value);
177            }
178        }
179        // default env values
180        if (!env.containsKey(NUXEO_CONFIG_DIR)) {
181            String webinf = servletContext.getRealPath("/WEB-INF");
182            env.put(NUXEO_CONFIG_DIR, webinf);
183        }
184        if (!env.containsKey(NUXEO_RUNTIME_HOME)) {
185            File home = new File(DEFAULT_HOME);
186            env.put(NUXEO_RUNTIME_HOME, home.getAbsolutePath());
187        }
188        // host
189        if (getClass().getClassLoader().getClass().getName().startsWith("org.jboss.classloader")) {
190            env.put(FrameworkLoader.HOST_NAME, JBOSS_HOST);
191        } else if (servletContext.getClass().getName().startsWith("org.apache.catalina")) {
192            env.put(FrameworkLoader.HOST_NAME, TOMCAT_HOST);
193        }
194    }
195
196}