org.wso2.developerstudio.eclipse.embedded.tomcat.server.EmbeddedTomcatServer.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.developerstudio.eclipse.embedded.tomcat.server.EmbeddedTomcatServer.java

Source

/*
 * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 * 
 * 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.
 */
package org.wso2.developerstudio.eclipse.embedded.tomcat.server;

import static org.apache.commons.lang.StringUtils.isNotEmpty;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.startup.Tomcat;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.Bundle;
import org.wso2.developerstudio.eclipse.embedded.tomcat.EmbeddedTomcatPlugin;
import org.wso2.developerstudio.eclipse.embedded.tomcat.Messages;
import org.wso2.developerstudio.eclipse.embedded.tomcat.exception.AppNotFoundException;
import org.wso2.developerstudio.eclipse.embedded.tomcat.exception.DuplicateAppIDException;
import org.wso2.developerstudio.eclipse.embedded.tomcat.exception.DuplicateContextException;
import org.wso2.developerstudio.eclipse.embedded.tomcat.exception.EmbeddedTomcatException;
import org.wso2.developerstudio.eclipse.logging.core.IDeveloperStudioLog;
import org.wso2.developerstudio.eclipse.logging.core.Logger;

public class EmbeddedTomcatServer {

    private static final String TOMCAT_TMP = "DevSTomcatTmp"; //$NON-NLS-1$
    private static final String JAVA_IO_TMPDIR = "java.io.tmpdir"; //$NON-NLS-1$
    private static final String FOLDER_PREFIX = "tmp"; //$NON-NLS-1$
    private static final String WAR_EXT = "war"; //$NON-NLS-1$
    private static final String RELATIVE_PATH = "relativePath"; //$NON-NLS-1$
    private static final String CONTEXT = "context"; //$NON-NLS-1$
    private static final String APP_ID = "appID"; //$NON-NLS-1$
    private static final String HTTP_PROTOCOL_PREFIX = "http://"; //$NON-NLS-1$
    private static final String WEB_APP_EXT_POINT_ID = "org.wso2.developerstudio.embedded.webapp"; //$NON-NLS-1$
    private static final String WEB_XML_REL_PATH = "/WEB-INF/web.xml"; //$NON-NLS-1$

    private static IDeveloperStudioLog log = Logger.getLog(EmbeddedTomcatPlugin.PLUGIN_ID);

    protected Tomcat tomcat;
    protected Integer port;
    protected String rootURL;
    protected Map<String, String> appIDToURLMap;
    protected Map<String, String> contextToAppIDMap;
    private boolean isStartupRequired;

    public EmbeddedTomcatServer() {
        appIDToURLMap = new HashMap<>();
        contextToAppIDMap = new HashMap<>();
    }

    /**
     * Starts Embedded Tomcat server.
     * 
     * @throws EmbeddedTomcatException
     */
    public void start() throws EmbeddedTomcatException {
        try {
            tomcat.getHost();
            tomcat.start();
        } catch (LifecycleException e) {
            throw new EmbeddedTomcatException(Messages.ERROR_TOMCAT_STARTUP_ERROR, e);
        }
    }

    /**
     * Stops Embedded Tomcat server.
     * 
     * @throws EmbeddedTomcatException
     */
    public void stop() throws EmbeddedTomcatException {
        try {
            tomcat.stop();
        } catch (LifecycleException e) {
            throw new EmbeddedTomcatException(Messages.ERROR_TOMCAT_SHUTDOWN_ERROR, e);
        }
    }

    /**
     * Method to add a web application to server.
     * 
     * @param appID
     *            A unique ID for the app.
     * @param context
     *            Deploying context.
     * @param docBase
     *            Root folder of the application.
     * @throws Exception
     */
    public void addWebApp(String appID, String context, String docBase) throws EmbeddedTomcatException {
        if (appIDToURLMap.containsKey(appID)) {
            throw new DuplicateAppIDException(appID, appIDToURLMap.get(appID));
        } else if (contextToAppIDMap.containsKey(context)) {
            throw new DuplicateContextException(context, contextToAppIDMap.get(context));
        }
        try {
            Context appContext = tomcat.addWebapp(context, docBase);
            File webXML = new File(docBase + WEB_XML_REL_PATH);
            if (webXML.exists()) {
                appContext.setConfigFile(webXML.toURI().toURL());
            }
            appContext.setParentClassLoader(Thread.currentThread().getContextClassLoader());
            appIDToURLMap.put(appID, getURLForContext(context));
            contextToAppIDMap.put(context, appID);
            log.info(NLS.bind(Messages.INFO_APP_ADDED_SUCCESSFULLY, appID, appIDToURLMap.get(appID)));
        } catch (Exception e) {
            String errorMessage = NLS.bind(Messages.ERROR_ADDING_WEBAPP_FAILED, appID, docBase);
            throw new EmbeddedTomcatException(errorMessage, e);
        }
    }

    /**
     * Method to get full URL to access a particular web application.
     * 
     * @param appID
     *            Unique ID of the web application.
     * 
     * @return complete URL to access given web application.
     */
    public String getAppURL(String appID) throws EmbeddedTomcatException {
        if (!appIDToURLMap.containsKey(appID)) {
            throw new AppNotFoundException(appID);
        }
        return appIDToURLMap.get(appID);
    }

    /**
     * Getter for configured port.
     * 
     * @return port used by Tomcat.
     */
    public Integer getServerPort() {
        return port;
    }

    /**
     * Constructs the complete URL for a particular context.
     * 
     * @param context relative context path.
     * @return Full URL of the app.
     */
    private String getURLForContext(String context) {
        return rootURL + context;
    }

    /**
     * Read extensions to webapp extension point and deploy them.
     */
    private void initWebAppExtensions() {

        // Fetch extensions to extension point from registry.
        IExtensionRegistry extentionRegistry = Platform.getExtensionRegistry();
        IExtensionPoint webAppExtensionPoint = extentionRegistry.getExtensionPoint(WEB_APP_EXT_POINT_ID);
        IExtension[] webAppExtensions = webAppExtensionPoint.getExtensions();

        // if there are any apps only, server should be started.
        if (webAppExtensions.length > 0) {
            isStartupRequired = true;
        }

        // Iterate over all web extensions and deploy web apps.
        // IMPORTANT: Continue deploying other apps upon any failures.
        for (IExtension extensionConfig : webAppExtensions) {

            // Read extension point configuration.
            String contributingBundleID = extensionConfig.getContributor().getName();
            Bundle contributingBundle = Platform.getBundle(contributingBundleID);
            IConfigurationElement webAppExtension = extensionConfig.getConfigurationElements()[0];
            // Derive attributes of WebApp Extension
            String applicationID = webAppExtension.getAttribute(APP_ID);
            String relativePath = webAppExtension.getAttribute(RELATIVE_PATH);
            String context = webAppExtension.getAttribute(CONTEXT);

            // A common error message to display upon deployment error.
            String appDeploymentErrorMsg = NLS.bind(Messages.ERROR_APP_DEPLOYMENT_FAILED, applicationID,
                    contributingBundleID);
            //TODO: is this necessary
            if (isNotEmpty(applicationID) && isNotEmpty(relativePath) && isNotEmpty(context)) {
                URL docBaseResource = FileLocator.find(contributingBundle, new Path(relativePath), null);
                // FIXME: add a boolean to indicate success 
                File readableDocBase = null;
                String deployableDocBase = null;
                try {
                    // resolve resource from bundle jar
                    URL fileURL = FileLocator.toFileURL(docBaseResource);
                    readableDocBase = new File(fileURL.toURI());
                    // Fixme: seperate methods
                } catch (IOException | URISyntaxException e) {
                    String errorMessage = NLS.bind(Messages.ERROR_RESOLVING_RESOURCES_FAILED, docBaseResource);
                    log.error(errorMessage, e);
                }
                if (readableDocBase != null) {
                    if (readableDocBase.isDirectory()) {
                        // case 1 : this is an exploded web application
                        deployableDocBase = readableDocBase.getAbsolutePath();
                    } else if (WAR_EXT.equals(FilenameUtils.getExtension(readableDocBase.getAbsolutePath()))) {
                        // case 2: this is a war file
                        File extractDir = new File(readableDocBase.getParentFile(), FOLDER_PREFIX + applicationID);
                        if (!extractDir.exists()) {
                            if (!extractDir.mkdirs()) {
                                String errorMessage = NLS.bind(Messages.ERROR_TMP_FILE_CREATION_FAILED, extractDir);
                                log.error(errorMessage);
                            }
                        }
                        try {
                            extractWar(readableDocBase, extractDir);
                            deployableDocBase = extractDir.getAbsolutePath();
                        } catch (IOException e) {
                            String errorMessage = NLS.bind(Messages.ERROR_WAR_EXTRACTION_FAILED,
                                    readableDocBase.getPath(), extractDir.getPath());
                            log.error(errorMessage, e);
                        }
                    }
                }
                if (deployableDocBase != null) {
                    try {
                        addWebApp(applicationID, context, deployableDocBase);
                    } catch (Exception e) {
                        log.error(appDeploymentErrorMsg, e);
                    }
                } else {
                    log.error(appDeploymentErrorMsg);
                }
            } else {
                String errorMessage = NLS.bind(Messages.ERROR_MISSING_REQUIRED_CONFIG,
                        new Object[] { applicationID, relativePath, context });
                log.error(errorMessage);
                log.error(appDeploymentErrorMsg);
            }
        }
    }

    /**
     * Initialize Tomcat server.
     * 
     * @throws EmbeddedTomcatException
     */
    public void init() throws EmbeddedTomcatException {
        tomcat = new Tomcat();
        try {
            port = getAvailablePort();
            tomcat.setPort(port);
            tomcat.setBaseDir(System.getProperty(JAVA_IO_TMPDIR) + File.separator + TOMCAT_TMP);
            String hostName = InetAddress.getLocalHost().getHostName();
            tomcat.getHost().setName(hostName);
            rootURL = HTTP_PROTOCOL_PREFIX + hostName + ":" + port; //$NON-NLS-1$
            initWebAppExtensions();
        } catch (IOException e) {
            throw new EmbeddedTomcatException(Messages.ERROR_TOMCAT_INIT_ERROR, e);
        }
    }

    /**
     * Finds a port which is currently not in use.
     * 
     * @return Available Port.
     * 
     * @throws IOException
     */
    private Integer getAvailablePort() throws IOException {
        ServerSocket socket = new ServerSocket(0);
        Integer port = socket.getLocalPort();
        socket.close();
        return port;
    }

    /**
     * Extracts a war file to a given directory.
     * 
     * @param warFile Source war file.
     * @param extractpath Destination to extract.
     * 
     * @throws IOException
     */
    private void extractWar(File warFile, File extractpath) throws IOException {
        ZipFile zipFile = new ZipFile(warFile);
        try {
            Enumeration<? extends ZipEntry> entries = zipFile.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = entries.nextElement();
                File entryDestination = new File(extractpath, entry.getName());
                if (entry.isDirectory())
                    entryDestination.mkdirs();
                else {
                    entryDestination.getParentFile().mkdirs();
                    InputStream in = zipFile.getInputStream(entry);
                    OutputStream out = new FileOutputStream(entryDestination);
                    IOUtils.copy(in, out);
                    IOUtils.closeQuietly(in);
                    out.close();
                }
            }
        } finally {
            zipFile.close();
        }
    }

    public boolean isStartupRequired() {
        return isStartupRequired;
    }

}