com.xlson.standalonewar.Starter.java Source code

Java tutorial

Introduction

Here is the source code for com.xlson.standalonewar.Starter.java

Source

/*
 * 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 com.xlson.standalonewar;

import java.io.IOException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import org.eclipse.jetty.webapp.WebAppContext;

import java.io.File;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.ssl.SslContextFactory;

import java.net.URL;
import java.net.URLClassLoader;
import java.security.ProtectionDomain;
import java.util.List;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
//import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Starter for embedded Jetty. This class is intended to be packaged into a
 * war-file and set as the Main-Class in MANIFEST.MF. Running the war will
 * start a Jetty instance on port 8080 with the containing war loaded.
 * If you specify a port environment variable that will be used for Jetty
 * instead of 8080 (see example).
 * <p/>
 * The base of this class comes from this blogpost:
 * http://eclipsesource.com/blogs/2009/10/02/executable-wars-with-jetty/
 * <p/>
 * Example:
 * java -jar -Dwebserver.listenPort=80 webapp.war
 * <p/>
 * webapp.war is loaded on http://localhost/
 *
 * @author Leonard Axelsson
 * @author Joris Koster
 * @author Marcel Toele
 */
@SuppressWarnings({ "unchecked", "deprecation" }) // Tell the compiler to shut up: the old API is simpler and better!
public class Starter {

    private static Server server;
    private static PropertiesConfiguration defaultProperties;
    private static PropertiesConfiguration config;
    private static Logger logger = LoggerFactory.getLogger(Starter.class);
    private static URL warLocation;

    static {
        ProtectionDomain protectionDomain = Starter.class.getProtectionDomain();
        warLocation = protectionDomain.getCodeSource().getLocation();

        System.setProperty(Starter.class.getPackage().getName() + ".warLocation", warLocation.toString());
        logger.debug("War location: " + warLocation);

        System.getProperties().put(Starter.class.getName() + ".restart", new Runnable() {
            @Override
            public void run() {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            logger.warn("restarting...");
                            stop();
                            start();
                            logger.warn("restarted.");
                        } catch (Exception ex) {
                            logger.error("", ex);
                        }
                    }
                }).start();
            }
        });
    }

    private static String getString(String propertyName) {
        return getString(propertyName, null);
    }

    private static String getString(String propertyName, String defaultValue) {
        return System.getProperty(propertyName, config.getString(propertyName, defaultValue));
    }

    private static Integer getInteger(String propertyName, Integer defaultValue) {
        return Integer.getInteger(propertyName, config.getInteger(propertyName, defaultValue));
    }

    private static PropertiesConfiguration loadConfig() {
        final String PROP_NAME_WEBSERVER_CONFIG_FILE = defaultProperties
                .getString("PROP_NAME_WEBSERVER_CONFIG_FILE", "webserver.configFilename");
        String configFilename = System.getProperty(PROP_NAME_WEBSERVER_CONFIG_FILE,
                defaultProperties.getString("webserver.configFilename", "webserver.conf"));

        try {
            return new PropertiesConfiguration(configFilename);
        } catch (ConfigurationException e) {
            logger.warn("Could not load config file: {}", configFilename);
            return new PropertiesConfiguration();
        }
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        try {
            defaultProperties = new PropertiesConfiguration(
                    Starter.class.getClassLoader().getResource("webserver.properties").toURI().toURL());

            appendClasspath(defaultProperties.getList("webserver.extraClasspath"));

            config = loadConfig();

            final String PROP_NAME_WEBSERVER_TIMEOUT = defaultProperties.getString("PROP_NAME_WEBSERVER_TIMEOUT",
                    "webserver.timeout");
            int timeout = Integer.parseInt(getString(PROP_NAME_WEBSERVER_TIMEOUT, "0"));
            start();
            if (timeout != 0) {
                Thread.sleep(timeout);
                stop();
            }
        } catch (Exception ex) {
            logger.error("error", ex);
            System.err.println(ex.getMessage());
            System.exit(-1);
        }
    }

    public static void start() throws Exception {
        final String PROP_NAME_WEBSERVER_BIND_ADDRESS = defaultProperties
                .getString("PROP_NAME_WEBSERVER_BIND_ADDRESS", "webserver.bindAddress");
        final String PROP_NAME_WEBSERVER_LISTENPORT = defaultProperties.getString("PROP_NAME_WEBSERVER_LISTENPORT",
                "webserver.listenPort");
        final String PROP_NAME_WEBSERVER_TEMPDIR = defaultProperties.getString("PROP_NAME_WEBSERVER_TEMPDIR",
                "webserver.tempDir");
        final String PROP_NAME_WEBSERVER_ROOT = defaultProperties.getString("PROP_NAME_WEBSERVER_ROOT",
                "webserver.root");
        final String PROP_NAME_WEBSERVER_SSL = defaultProperties.getString("PROP_NAME_WEBSERVER_SSL",
                "webserver.ssl");
        final String PROP_NAME_WEBSERVER_SSL_LISTENPORT = defaultProperties
                .getString("PROP_NAME_WEBSERVER_SSL_LISTENPORT", "webserver.ssl.listenPort");
        final String PROP_NAME_WEBSERVER_SSL_KEYSTORE_TYPE = defaultProperties
                .getString("PROP_NAME_WEBSERVER_SSL_KEYSTORE_TYPE", "webserver.ssl.keyStoreType");
        final String PROP_NAME_WEBSERVER_SSL_KEYSTORE_FILE = defaultProperties
                .getString("PROP_NAME_WEBSERVER_SSL_KEYSTORE_FILE", "webserver.ssl.keyStoreFile");
        final String PROP_NAME_WEBSERVER_SSL_KEYSTORE_PASSWORD = defaultProperties
                .getString("PROP_NAME_WEBSERVER_SSL_KEYSTORE_PASSWORD", "webserver.ssl.keyStorePassword");

        config = loadConfig();

        String tempPath = getString(PROP_NAME_WEBSERVER_TEMPDIR, System.getProperty("java.io.tmpdir"));
        logger.info("using '" + tempPath + "' as temporary directory");

        String documentRoot = getString(PROP_NAME_WEBSERVER_ROOT, warLocation.toExternalForm());
        logger.info("using '" + documentRoot + "' as the document root");

        server = new Server();

        String bindAddress = getString(PROP_NAME_WEBSERVER_BIND_ADDRESS, "0.0.0.0");

        HttpConfiguration http_config = new HttpConfiguration();
        boolean hasSslConfigured = false;

        if ("true".equalsIgnoreCase(getString(PROP_NAME_WEBSERVER_SSL))) {
            /* Note: the default keystore file type for war-launcher is PKCS12, you create a PKCS12 key store file with:
             *
             *  1) OpenSSL: openssl pkcs12 -export -clcerts -in example.com.crt -inkey example.com.key -out example.com.p12
             *  2) keytool: @TODO
             */
            String keyStoreType = getString(PROP_NAME_WEBSERVER_SSL_KEYSTORE_TYPE, "PKCS12");
            logger.info("using '" + keyStoreType + "' as key store type");

            String keyStoreFile = getString(PROP_NAME_WEBSERVER_SSL_KEYSTORE_FILE, null);
            if (keyStoreFile == null) {
                throw new RuntimeException(
                        "ssl requires a property value for: " + PROP_NAME_WEBSERVER_SSL_KEYSTORE_FILE);
            } else {
                logger.info("using '" + keyStoreFile + "' as key store file");
            }

            String keyStorePassword = getString(PROP_NAME_WEBSERVER_SSL_KEYSTORE_PASSWORD);

            int defaultSslListenPort = Integer
                    .parseInt(defaultProperties.getString("webserver.ssl.defaultListenPort", "8443"));
            String sslListenPort = getString(PROP_NAME_WEBSERVER_SSL_LISTENPORT, null);
            int sslPort;
            if (sslListenPort == null) {
                logger.warn(
                        "'" + PROP_NAME_WEBSERVER_SSL_LISTENPORT + "' was not specified, using the default port.");
                sslPort = defaultSslListenPort;
            } else {
                sslPort = Integer.decode(sslListenPort);
                logger.info("using port '" + sslPort + "' for ssl connections");
            }

            http_config.setSecureScheme("https");
            http_config.setSecurePort(sslPort);
            http_config.setOutputBufferSize(1048576);
            http_config.setRequestHeaderSize(16384);
            http_config.setResponseHeaderSize(16384);
            hasSslConfigured = true;

            SslContextFactory sslContextFactory = new SslContextFactory();
            try {
                logger.info("Adding HTTPS connector for " + bindAddress + ":" + sslPort);
                // connector.setKeystoreType(keyStoreType); // @TODO do we need to set this?
                sslContextFactory.setKeyStorePath(keyStoreFile);
                sslContextFactory.setKeyStorePassword(keyStorePassword);
                sslContextFactory.setKeyManagerPassword(keyStorePassword);

            } catch (Exception ex) {
                logger.error("error", ex);
            }
        }

        int defaultListenPort = Integer
                .parseInt(defaultProperties.getString("webserver.defaultListenPort", "8080"));
        String listenPort = getString(PROP_NAME_WEBSERVER_LISTENPORT, null);
        System.out.println("The listenPort from getString: " + listenPort);
        if ((listenPort == null) || (!hasSslConfigured)) {
            ServerConnector connector = new ServerConnector(server, new HttpConnectionFactory(http_config));

            if (listenPort == null) {
                logger.warn("'" + PROP_NAME_WEBSERVER_LISTENPORT + "' was not specified, using the default port.");
            }

            int port = getInteger(PROP_NAME_WEBSERVER_LISTENPORT, defaultListenPort);
            connector.setHost(bindAddress);
            connector.setPort(port);
            connector.setIdleTimeout(1000 * 60 * 60);
            connector.setSoLingerTime(-1);

            logger.info("Adding HTTP connector for " + bindAddress + ":" + port);
            server.addConnector(connector);
        } else {
            logger.warn("'" + PROP_NAME_WEBSERVER_LISTENPORT
                    + "' was not specified, but other connectors already registered, so no default connector was added for port "
                    + defaultListenPort);
        }

        WebAppContext webapp = new WebAppContext();
        String extraClasspath = getString("webserver.extraClasspath", "");

        logger.info("Setting extra classpath for webapp: {}", extraClasspath);

        webapp.setExtraClasspath(extraClasspath);
        webapp.setContextPath("/");
        //      webapp.setTempDirectory(new File(tempPath));
        webapp.setServer(server);
        webapp.setWar(warLocation.toExternalForm());
        if (!"".equals(getString("javax.servlet.context.tempdir", ""))) {
            webapp.setAttribute("javax.servlet.context.tempdir", getString("javax.servlet.context.tempdir"));
        }
        webapp.setPersistTempDirectory(true);
        //      if(!warLocation.toExternalForm().equals(documentRoot)) {
        //         webapp.setResourceBase(documentRoot);
        //      }

        server.setHandler(webapp);
        server.start();
    }

    public static void stop() throws Exception {
        if (server != null) {
            server.stop();
            server.join();
        }
    }

    /**
     * Add paths to the system class loader.
     * (The paths are expected to be comma-separated.)
     */
    public static void appendClasspath(List<String> paths) throws IOException {
        logger.info("appendClassPath: {}", paths);
        for (String path : paths) {
            path = path.replaceAll("~", new File(System.getProperty("user.home")).toURI().toString()).replaceAll(
                    "\\$\\{com.xlson.standalonewar.warLocation\\}",
                    orElse(new File(System.getProperty("com.xlson.standalonewar.warLocation", "")).getParent(), ""))
                    + File.separator;
            URL url;
            try {
                url = new URL(path);
            } catch (MalformedURLException e) {
                url = new File(path).toURI().toURL();
            }
            logger.info("Appending the following path to the system class path: " + url);
            appendClasspath(url);
        }
    }

    private static final Class[] parameters = new Class[] { URL.class };

    /**
     * Add url to the system class loader.
     */
    public static void appendClasspath(URL url) throws IOException {

        URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader();
        Class sysclass = URLClassLoader.class;

        try {
            Method method = sysclass.getDeclaredMethod("addURL", parameters);
            method.setAccessible(true);
            method.invoke(sysloader, new Object[] { url });
        } catch (Throwable t) {
            t.printStackTrace();
            throw new IOException("Error, could not add URL to system classloader");
        }

    }

    public static <T> T orElse(T lhs, T rhs) {
        return lhs != null ? lhs : rhs;
    }
}