org.nuxeo.runtime.jetty.JettyComponent.java Source code

Java tutorial

Introduction

Here is the source code for org.nuxeo.runtime.jetty.JettyComponent.java

Source

/*
 * (C) Copyright 2006-2008 Nuxeo SA (http://nuxeo.com/) and others.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Contributors:
 *     bstefanescu
 *
 * $Id$
 */

package org.nuxeo.runtime.jetty;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mortbay.jetty.NCSARequestLog;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.handler.ContextHandlerCollection;
import org.mortbay.jetty.handler.HandlerCollection;
import org.mortbay.jetty.handler.RequestLogHandler;
import org.mortbay.jetty.webapp.Configuration;
import org.mortbay.jetty.webapp.WebAppContext;
import org.mortbay.jetty.webapp.WebInfConfiguration;
import org.mortbay.jetty.webapp.WebXmlConfiguration;
import org.mortbay.xml.XmlConfiguration;
import org.nuxeo.common.Environment;
import org.nuxeo.common.server.WebApplication;
import org.nuxeo.common.utils.ExceptionUtils;
import org.nuxeo.runtime.api.Framework;
import org.nuxeo.runtime.model.ComponentContext;
import org.nuxeo.runtime.model.ComponentInstance;
import org.nuxeo.runtime.model.ComponentName;
import org.nuxeo.runtime.model.DefaultComponent;
import org.xml.sax.SAXException;

/**
 * This component registers and configures an embedded Jetty server.
 * <p>
 * Contexts are registered like this:
 * <p>
 * First, if there is a {@code jetty.xml} config file, the contexts defined there are registered first; if there is no
 * {@code jetty.xml}, a log context will be create programatically and registered first.
 * <p>
 * Second an empty collection context is registered. Here will be registered all regular war contexts.
 * <p>
 * Third, the root collection is registered. This way all requests not handled by regular wars are directed to the root
 * war, which usually is the webengine war in a nxserver application.
 *
 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
 */
public class JettyComponent extends DefaultComponent {

    public static final ComponentName NAME = new ComponentName("org.nuxeo.runtime.server");

    public static final String XP_WEB_APP = "webapp";

    public static final String XP_SERVLET = "servlet";

    public static final String XP_FILTER = "filter";

    public static final String XP_LISTENERS = "listeners";

    public static final String P_SCAN_WEBDIR = "org.nuxeo.runtime.jetty.scanWebDir";

    protected Server server;

    protected ContextManager ctxMgr;

    // here we are putting all regular war contexts
    // the root context will be appended after this context collection to be
    // sure the regular contexts are checked first
    // This is because the root context is bound to / so if it is checked first
    // it will consume
    // all requests even if there is a context that is the target of the request
    protected ContextHandlerCollection warContexts;

    protected File config;

    protected File log;

    private static final Log logger = LogFactory.getLog(JettyComponent.class);

    public Server getServer() {
        return server;
    }

    @Override
    public void activate(ComponentContext context) {

        // apply bundled configuration
        URL cfg = null;

        String cfgName = Framework.getProperty("org.nuxeo.jetty.config");
        if (cfgName != null) {
            if (cfgName.contains(":/")) {
                try {
                    cfg = new URL(cfgName);
                } catch (MalformedURLException e) {
                    throw new RuntimeException(e);
                }
            } else { // assume a file
                File file = new File(cfgName);
                if (file.isFile()) {
                    try {
                        cfg = file.toURI().toURL();
                    } catch (MalformedURLException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        } else {
            File file = new File(Environment.getDefault().getConfig(), "jetty.xml");
            if (file.isFile()) {
                try {
                    cfg = file.toURI().toURL();
                } catch (MalformedURLException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        boolean hasConfigFile = false;
        if (cfg != null) {
            hasConfigFile = true;
            XmlConfiguration configuration;
            try {
                configuration = new XmlConfiguration(cfg);
            } catch (SAXException | IOException e) {
                throw new RuntimeException(e);
            }
            try {
                server = (Server) configuration.configure();
            } catch (Exception e) { // stupid Jetty API throws Exception
                throw ExceptionUtils.runtimeException(e);
            }
        } else {
            int p = 8080;
            String port = Environment.getDefault().getProperty("http_port");
            if (port != null) {
                try {
                    p = Integer.parseInt(port);
                } catch (NumberFormatException e) {
                    // do noting
                }
            }
            server = new Server(p);

        }

        // if a jetty.xml is present we don't configure logging - this should be
        // done in that file.
        if (!hasConfigFile) {
            RequestLogHandler requestLogHandler = new RequestLogHandler();
            File logDir = Environment.getDefault().getLog();
            logDir.mkdirs();
            File logFile = new File(logDir, "jetty.log");
            NCSARequestLog requestLog = new NCSARequestLog(logFile.getAbsolutePath());
            requestLogHandler.setRequestLog(requestLog);
            // handlers = new Handler[] {contexts, new DefaultHandler(),
            // requestLogHandler};
            server.addHandler(requestLogHandler);
            server.setSendServerVersion(true);
            server.setStopAtShutdown(true);

        }

        // create the war context handler if needed
        HandlerCollection hc = (HandlerCollection) server.getHandler();
        warContexts = (ContextHandlerCollection) hc.getChildHandlerByClass(ContextHandlerCollection.class);
        if (warContexts == null) {
            // create the war context
            warContexts = new ContextHandlerCollection();
            server.addHandler(warContexts);
        }

        // scan for WAR files
        // deploy any war found in web directory
        String scanWebDir = Framework.getProperty(P_SCAN_WEBDIR);
        if (scanWebDir != null && scanWebDir.equals("true")) {
            logger.info("Scanning for WARs in web directory");
            File web = Environment.getDefault().getWeb();
            scanForWars(web);
        }

        ctxMgr = new ContextManager(server);

        // start the server
        // server.start(); -> server will be start after frameworks starts to be
        // sure that all services
        // used by web.xml filters are registered.
    }

    @Override
    public void deactivate(ComponentContext context) {
        ctxMgr = null;
        try {
            server.stop();
        } catch (Exception e) { // stupid Jetty API throws Exception
            throw ExceptionUtils.runtimeException(e);
        }
        server = null;
    }

    @Override
    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
        if (XP_WEB_APP.equals(extensionPoint)) {
            File home = Environment.getDefault().getHome();
            WebApplication app = (WebApplication) contribution;
            // TODO preprocessing was removed from this component -
            // preprocessing should be done in another bundle
            // if still required (on equinox distribution)
            // if (app.needsWarPreprocessing()) {
            // logger.info("Starting deployment preprocessing");
            // DeploymentPreprocessor dp = new DeploymentPreprocessor(home);
            // dp.init();
            // dp.predeploy();
            // logger.info("Deployment preprocessing terminated");
            // }

            WebAppContext ctx = new WebAppContext();
            ctx.setContextPath(app.getContextPath());
            String root = app.getWebRoot();
            if (root != null) {
                File file = new File(home, root);
                ctx.setWar(file.getAbsolutePath());
            }
            String webXml = app.getConfigurationFile();
            if (webXml != null) {
                File file = new File(home, root);
                ctx.setDescriptor(file.getAbsolutePath());
            }
            File defWebXml = new File(Environment.getDefault().getConfig(), "default-web.xml");
            if (defWebXml.isFile()) {
                ctx.setDefaultsDescriptor(defWebXml.getAbsolutePath());
            }
            if ("/".equals(app.getContextPath())) { // the root context must be
                                                    // put at the end
                server.addHandler(ctx);
            } else {
                warContexts.addHandler(ctx);
            }
            org.mortbay.log.Log.setLog(new Log4JLogger(logger));
            // ctx.start();
            // HandlerWrapper wrapper = (HandlerWrapper)ctx.getHandler();
            // wrapper = (HandlerWrapper)wrapper.getHandler();
            // wrapper.setHandler(new NuxeoServletHandler());

            if (ctx.isFailed()) {
                logger.error("Error in war deployment");
            }

        } else if (XP_FILTER.equals(extensionPoint)) {
            ctxMgr.addFilter((FilterDescriptor) contribution);
        } else if (XP_SERVLET.equals(extensionPoint)) {
            ctxMgr.addServlet((ServletDescriptor) contribution);
        } else if (XP_LISTENERS.equals(extensionPoint)) {
            ctxMgr.addLifecycleListener((ServletContextListenerDescriptor) contribution);
        }
    }

    public ContextManager getContextManager() {
        return ctxMgr;
    }

    @Override
    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
        if (XP_WEB_APP.equals(extensionPoint)) {

        } else if (XP_FILTER.equals(extensionPoint)) {
            ctxMgr.removeFilter((FilterDescriptor) contribution);
        } else if (XP_SERVLET.equals(extensionPoint)) {
            ctxMgr.removeServlet((ServletDescriptor) contribution);
        } else if (XP_LISTENERS.equals(extensionPoint)) {
            ctxMgr.removeLifecycleListener((ServletContextListenerDescriptor) contribution);
        }
    }

    @Override
    public <T> T getAdapter(Class<T> adapter) {
        if (adapter == org.mortbay.jetty.Server.class) {
            return adapter.cast(server);
        }
        return null;
    }

    // let's nuxeo runtime get access to the JNDI context
    // injected through the JettyTransactionalListener
    protected ClassLoader nuxeoCL;

    public void setNuxeoClassLoader(ClassLoader cl) {
        nuxeoCL = cl;
    }

    protected ClassLoader getClassLoader(ClassLoader cl) {
        if (!Boolean.valueOf(System.getProperty("org.nuxeo.jetty.propagateNaming"))) {
            return cl;
        }
        if (nuxeoCL == null) {
            return cl;
        }
        return nuxeoCL;
    }

    @Override
    public int getApplicationStartedOrder() {
        return -100;
    }

    @Override
    public void applicationStarted(ComponentContext context) {
        if (server == null) {
            return;
        }
        ctxMgr.applyLifecycleListeners();
        Thread t = Thread.currentThread();
        ClassLoader oldcl = t.getContextClassLoader();
        t.setContextClassLoader(getClass().getClassLoader());
        try {
            server.start();
        } catch (Exception e) { // stupid Jetty API throws Exception
            throw ExceptionUtils.runtimeException(e);
        } finally {
            t.setContextClassLoader(getClassLoader(oldcl));
        }
    }

    private void scanForWars(File dir) {
        scanForWars(dir, "");
    }

    private void scanForWars(File dir, String basePath) {
        File[] roots = dir.listFiles();
        if (roots != null) {
            for (File root : roots) {
                String name = root.getName();
                if (name.endsWith(".war")) {
                    logger.info("Found war: " + name);
                    name = name.substring(0, name.length() - 4);
                    boolean isRoot = "root".equals(name);
                    String ctxPath = isRoot ? "/" : basePath + "/" + name;
                    WebAppContext ctx = new WebAppContext(root.getAbsolutePath(), ctxPath);
                    ctx.setConfigurations(
                            new Configuration[] { new WebInfConfiguration(), new WebXmlConfiguration() });
                    if (isRoot) {
                        server.addHandler(ctx);
                    } else {
                        warContexts.addHandler(ctx);
                    }
                } else if (root.isDirectory()) {
                    scanForWars(root, basePath + "/" + name);
                }
            }
        }
    }

}