org.wso2.carbon.tomcat.internal.CarbonTomcat.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.tomcat.internal.CarbonTomcat.java

Source

/*
 * Copyright (c) 2005-2012, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. licenses this file to you 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.
 */
package org.wso2.carbon.tomcat.internal;

import org.apache.catalina.Container;
import org.apache.catalina.Context;
import org.apache.catalina.Engine;
import org.apache.catalina.Host;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.Server;
import org.apache.catalina.Service;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.startup.Catalina;
import org.apache.catalina.startup.Constants;
import org.apache.catalina.startup.Tomcat;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.digester.Digester;
import org.apache.coyote.http11.Http11NioProtocol;
import org.wso2.carbon.tomcat.CarbonTomcatException;
import org.wso2.carbon.tomcat.api.CarbonTomcatService;
import org.xml.sax.SAXException;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLEncoder;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * the extended {@link Tomcat} class, which configures itself using the tomcat {@link Digester}
 */
public class CarbonTomcat extends Tomcat implements CarbonTomcatService {
    private static Log log = LogFactory.getLog(CarbonTomcat.class);
    private ExtendedCatalina catalina = new ExtendedCatalina();
    private String globalWebXml;
    private String globalContextXml;

    /**
     * configuring the {@link CarbonTomcat} using the inbuilt digester mechanism in tomcat
     *
     * @param baseDir     normally we set the catalina.home as the baseDir
     * @param inputStream of catalina-server.xml
     */
    public void configure(String baseDir, InputStream inputStream) {
        this.setBaseDir(baseDir);
        globalWebXml = new File(System.getProperty("carbon.home")).getAbsolutePath() + File.separator + "repository"
                + File.separator + "conf" + File.separator + "tomcat" + File.separator + "web.xml";

        globalContextXml = new File(System.getProperty("carbon.home")).getAbsolutePath() + File.separator
                + "repository" + File.separator + "conf" + File.separator + "tomcat" + File.separator
                + "context.xml";
        //creating a digester to parse our catalina-server.xml
        Digester digester = catalina.createStartDigester();
        digester.push(this);
        try {
            digester.parse(inputStream);
        } catch (IOException e) {
            log.error("error while reading xml stream", e);
        } catch (SAXException e) {
            log.error("error while parsing xml stream", e);
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                log.error("error while closing the inputStream", e);
            }
        }

    }

    /**
     * Start the server.
     *
     * @throws LifecycleException
     */
    @Override
    public void start() throws LifecycleException {
        getServer();
        this.server.start();
    }

    /**
     * This gets called by the tomcat configurator via reflection.
     *
     * @param server reference
     */
    @SuppressWarnings("unused")
    public void setServer(Server server) {
        this.server = server;
    }

    /**
     * the current catalina-server.xml based configuration only allows one service.
     * Even if there are multiple services, we only take first service in to account
     *
     * @return first service found
     */
    @Override
    public Service getService() {
        Server server = getServer();
        Service[] findServices = server.findServices();
        if (findServices != null && findServices.length > 0) {
            return findServices[0];
        }
        throw new IllegalStateException("Unable to locate Service.");
    }

    @Override
    public Host getHost() {
        return findHost();
    }

    @Override
    public Engine getEngine() {
        return findEngine();
    }

    private Engine findEngine() {
        Server server = getServer();
        Service[] findServices = server.findServices();
        for (Service service : findServices) {
            Container container = service.getContainer();
            if (container instanceof Engine) {
                return (Engine) container;
            }
        }
        throw new IllegalStateException("Unable to locate Engine.");
    }

    private Host findHost() {
        if (this.host == null) {
            Engine engine = findEngine();
            String defaultHost = engine.getDefaultHost();
            Container child = engine.findChild(defaultHost);
            return (Host) child;
        } else {
            return this.host;
        }
    }

    public void init() throws LifecycleException {
        getServer();
        this.server.init();
    }

    /**
     * End of Tomcat configuration related methods. The following methods are public (exposed via OSGi service)
     * and can be considered as the true API.
     */

    /**
     * adding web-app with default-host and default listeners
     *
     * @param contextPath    unique web-app context
     * @param webappFilePath File location of the web-app
     * @return {@link Context} object of the added web-app
     */
    public Context addWebApp(String contextPath, String webappFilePath) throws CarbonTomcatException {
        Host defaultHost = (Host) this.getEngine().findChild(this.getEngine().getDefaultHost());
        return this.addWebApp(defaultHost, contextPath, webappFilePath, null);
    }

    /**
     * adding web-app with the default life-cycle listener
     *
     * @param host           virtual host for the web-app
     * @param contextPath    unique web-app context
     * @param webappFilePath File location of the web-app
     * @return {@link Context} object of the added web-app
     */
    public Context addWebApp(Host host, String contextPath, String webappFilePath) throws CarbonTomcatException {
        return this.addWebApp(host, contextPath, webappFilePath, null);
    }

    /**
     * adding web-app with default-host
     *
     * @param contextPath       unique web-app context
     * @param webappFilePath    File location of the web-app
     * @param lifecycleListener tomcat life-cycle listener
     * @return {@link Context} object of the added web-app
     */
    public Context addWebApp(String contextPath, String webappFilePath, LifecycleListener lifecycleListener)
            throws CarbonTomcatException {
        Host defaultHost = (Host) this.getEngine().findChild(this.getEngine().getDefaultHost());
        return this.addWebApp(defaultHost, contextPath, webappFilePath, lifecycleListener);
    }

    /**
     * web-app addition
     *
     * @param host              virtual host for the webapp
     * @param contextPath       unique web-app context
     * @param webappFilePath    File location of the web-app
     * @param lifecycleListener tomcat life-cycle listener
     * @return {@link Context} object of the added web-app
     */
    @Override
    public Context addWebApp(Host host, String contextPath, String webappFilePath,
            LifecycleListener lifecycleListener) throws CarbonTomcatException {
        JarFile webappJarFile = null;
        JarEntry contextXmlFileEntry;
        Context ctx = null;
        boolean removeContext = false;
        try {
            Container child = host.findChild(contextPath);
            if (child != null) {
                if (ctx != null && host != null) {
                    ctx.setRealm(null);
                    try {
                        ctx.stop();
                    } catch (LifecycleException x) {
                        log.error("Cannot stop context ", x);
                    }
                    host.removeChild(ctx);
                }
            }
            ctx = new StandardContext();
            ctx.setName(contextPath);
            ctx.setPath(contextPath);
            ctx.setDocBase(webappFilePath);
            ctx.setRealm(host.getRealm());
            //We dont need to init the DefaultWebXML since we maintain a web.xml file for a carbon server.
            // hence removing ctx.addLifecycleListener(new Tomcat.DefaultWebXmlListener()); code
            if (lifecycleListener != null) {
                ctx.addLifecycleListener(lifecycleListener);
            }
            SCIRegistrarContextConfig sciRegistrarContextConfig = new SCIRegistrarContextConfig();
            ctx.addLifecycleListener(sciRegistrarContextConfig);
            // Set global webXml to this context
            if (new File(globalWebXml).exists()) {
                sciRegistrarContextConfig.setDefaultWebXml(globalWebXml);
            } else {
                sciRegistrarContextConfig.setDefaultWebXml("org/apache/catalin/startup/NO_DEFAULT_XML");
            }

            if (new File(globalContextXml).exists()) {
                sciRegistrarContextConfig.setDefaultContextXml(globalContextXml);
            }

            File f = new File(webappFilePath);
            //During dir based webapp deployment
            if (f.isDirectory()) {
                File cf = new File(webappFilePath + File.separator + Constants.ApplicationContextXml);
                if (cf.exists()) {
                    ctx.setConfigFile(cf.toURI().toURL());
                }
            } else {
                // Check for embedded contextXml file in this webapp
                webappJarFile = new JarFile(webappFilePath);
                contextXmlFileEntry = webappJarFile.getJarEntry(Constants.ApplicationContextXml);
                if (contextXmlFileEntry != null) {
                    ctx.setConfigFile(new URL("jar:file:" + URLEncoder.encode(webappFilePath, "UTF-8") + "!/"
                            + Constants.ApplicationContextXml));
                }
            }
            if (ctx instanceof StandardContext) {
                ((StandardContext) ctx).setClearReferencesStopTimerThreads(true);
            }
            if (host == null) {
                host = this.getHost();
            }
            host.addChild(ctx);
            if (ctx.getState().equals(LifecycleState.STOPPED)) {
                ctx.setRealm(null);
                ctx.destroy();
                throw new Exception("Webapp failed to deploy, Lifecycle state is STOPPED");
            }
            if (log.isDebugEnabled()) {
                log.debug("Webapp context: " + ctx);
            }
        } catch (Exception e) {
            //since any exception can be thrown from Lifecycles, "Exception" is been caught.
            removeContext = true;
            throw new CarbonTomcatException("Webapp failed to deploy", e);
        } finally {
            if (removeContext && ctx != null && host != null) {
                ctx.setRealm(null);
                try {
                    if (!ctx.getState().equals(LifecycleState.STOPPED)) {
                        ctx.stop();
                    }
                } catch (LifecycleException e) {
                    log.error("Cannot stop context ", e);
                }
                host.removeChild(ctx);
                log.error("Webapp " + ctx + " failed to deploy");
            }
            if (webappJarFile != null) {
                try {
                    webappJarFile.close();
                } catch (Throwable t) {
                    ExceptionUtils.handleThrowable(t);
                }
            }
        }
        return ctx;
    }

    @Override
    public Tomcat getTomcat() {
        return this;
    }

    /**
     * getting port value by giving the connector scheme. we only support 'http' and 'https'
     * schemes
     *
     * @param scheme this value is http or https
     * @return port value of the scheme, -1 if the matching scheme not found
     */
    @Override
    public int getPort(String scheme) {
        for (Connector connector : this.getService().findConnectors()) {
            if (connector.getScheme().equals(scheme)) {
                return connector.getPort();
            }
        }
        return -1;
    }

    /**
     * getting proxy port value by giving the connector scheme. we only support 'http' and 'https'
     * schemes
     *
     * @param scheme this value is http or https
     * @return port value of the scheme, -1 if the matching scheme not found
     */
    @Override
    public int getProxyPort(String scheme) {
        for (Connector connector : this.getService().findConnectors()) {
            if (connector.getScheme().equals(scheme)) {
                return connector.getProxyPort();
            }
        }
        return -1;
    }

    /**
     * starting the connectors. We have overridden the CatalinaService. It doesn't start the connectors during
     * Engine startup
     *
     * @deprecated use {@link #startConnectors(int)} instead.
     *
     * @param portOffset that to be set while starting connectors
     */
    @Override
    @Deprecated
    public void startConnectors(int portOffset, String keyPass, String keyStorePass, String keyStoreFile) {
        //getting the list of connectors bound to this tomcat instance

        Connector[] connectors = this.getService().findConnectors();
        for (Connector connector : connectors) {
            try {
                int currentPort = connector.getPort();
                connector.setPort(currentPort + portOffset);
                if (connector.getProtocolHandler() instanceof Http11NioProtocol) {
                    ((Http11NioProtocol) connector.getProtocolHandler()).setKeyPass(keyPass);
                    ((Http11NioProtocol) connector.getProtocolHandler()).setKeystorePass(keyStorePass);
                    ((Http11NioProtocol) connector.getProtocolHandler()).setKeystoreFile(keyStoreFile);
                }
                connector.start();
                if (log.isDebugEnabled()) {
                    log.debug("staring the tomcat connector : " + connector.getProtocol());
                }
            } catch (LifecycleException e) {
                log.error("LifeCycleException while starting tomcat connector", e);
            }
        }
    }

    @Override
    public void startConnectors(int portOffset) {
        //getting the list of connectors bound to this tomcat instance
        Connector[] connectors = this.getService().findConnectors();
        for (Connector connector : connectors) {
            try {
                int currentPort = connector.getPort();
                connector.setPort(currentPort + portOffset);
                connector.start();
                if (log.isDebugEnabled()) {
                    log.debug("staring the tomcat connector : " + connector.getProtocol());
                }
            } catch (LifecycleException e) {
                log.error("LifeCycleException while starting tomcat connector", e);
            }
        }
    }

    /**
     * starting the connectors. We have overridden the CatalinaService. It doesn't start the connectors during
     * Engine startup
     *
     * @param scheme     e.g: http|https.
     * @param portOffset that to be set while starting connectors
     */
    @Override
    public void startConnector(String scheme, int portOffset) {
        //getting the list of connectors bound to this tomcat instance
        Connector[] connectors = this.getService().findConnectors();
        for (Connector connector : connectors) {
            if (connector.getScheme().equals(scheme)) {
                try {
                    int currentPort = connector.getPort();
                    connector.setPort(currentPort + portOffset);
                    connector.start();
                    if (log.isDebugEnabled()) {
                        log.debug("staring the tomcat connector : " + connector.getProtocol());
                    }
                } catch (LifecycleException e) {
                    log.error("LifeCycleException while starting tomcat connector", e);
                }
            }
        }
    }

    /**
     * stopping all the existing catalina connectors
     */
    @Override
    public void stopConnectors() {
        //getting the list of connectors bound to this tomcat instance
        Connector[] connectors = this.getService().findConnectors();
        for (Connector connector : connectors) {
            try {
                connector.stop();
                if (log.isDebugEnabled()) {
                    log.debug("stopping the tomcat connector : " + connector.getProtocol());
                }
            } catch (LifecycleException e) {
                log.error("LifeCycleException while starting tomcat connector", e);
            }
        }
    }

    /**
     * stopping all the existing catalina connectors
     *
     * @param scheme e.g: http|https
     */
    @Override
    public void stopConnector(String scheme) {
        //getting the list of connectors bound to this tomcat instance
        Connector[] connectors = this.getService().findConnectors();
        for (Connector connector : connectors) {
            if (connector.getScheme().equals(scheme)) {
                try {
                    connector.stop();
                    if (log.isDebugEnabled()) {
                        log.debug("stopping the tomcat connector : " + connector.getProtocol());
                    }
                } catch (LifecycleException e) {
                    log.error("LifeCycleException while stopping tomcat connector", e);
                }
            }
        }
    }

    /**
     * check if unpack wars enabled
     *
     * @return true if enabled.
     */
    public boolean isUnpackWARs() {
        StandardHost standardHost = (StandardHost) this.getHost();
        return standardHost.isUnpackWARs();
    }

    /**
     * we want get access {@link org.apache.catalina.startup.Catalina#createStartDigester()} method,
     * to configure our <pre>Tomcat</pre> instance using catalina-server.xml
     */
    private static class ExtendedCatalina extends Catalina {

        @Override
        public Digester createStartDigester() {
            return super.createStartDigester();
        }

    }
}