fathom.Server.java Source code

Java tutorial

Introduction

Here is the source code for fathom.Server.java

Source

/*
 * Copyright (C) 2015 the original author or authors.
 *
 * 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 fathom;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.inject.Injector;
import com.google.inject.servlet.GuiceFilter;
import fathom.conf.Settings;
import fathom.exception.FathomException;
import io.undertow.Handlers;
import io.undertow.Undertow;
import io.undertow.Undertow.Builder;
import io.undertow.UndertowOptions;
import io.undertow.server.HttpHandler;
import io.undertow.server.handlers.GracefulShutdownHandler;
import io.undertow.server.handlers.PathHandler;
import io.undertow.servlet.Servlets;
import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.DeploymentManager;
import io.undertow.servlet.api.FilterInfo;
import io.undertow.servlet.api.ListenerInfo;
import io.undertow.servlet.api.ServletInfo;
import io.undertow.servlet.handlers.DefaultServlet;
import io.undertow.servlet.util.ImmediateInstanceFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.servlet.DispatcherType;
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletException;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;

/**
 * Fathom's development and deployment engine is Undertow.
 *
 * @author James Moger
 * @see <a href="http://undertow.io">Undertow</a>
 */
public class Server {

    private static final Logger log = LoggerFactory.getLogger(Server.class);

    private Settings settings;

    private volatile boolean started;

    private Undertow server;

    private DeploymentManager fathomDeploymentManager;

    public final Settings getSettings() {
        return settings;
    }

    public final void setSettings(Settings settings) {
        Preconditions.checkState(started == false, "Can not reassign settings after Undertow has been started!");
        this.settings = settings;
    }

    public final void start() {
        startImpl();
        started = true;
    }

    public final void stop() {
        stopImpl();
        started = false;
    }

    public boolean isRunning() {
        return started;
    }

    protected void startImpl() {
        try {
            fathomDeploymentManager = createFathomDeploymentManager();
            HttpHandler fathomHandler = fathomDeploymentManager.start();

            String contextPath = settings.getContextPath();

            // create a handler than redirects non-context requests to the context
            PathHandler contextHandler = Handlers.path(Handlers.redirect(contextPath));

            // add the handler with the context prefix
            contextHandler.addPrefixPath(contextPath, fathomHandler);

            GracefulShutdownHandler rootHandler = new GracefulShutdownHandler(contextHandler);
            server = createServer(rootHandler);

            String version = server.getClass().getPackage().getImplementationVersion();
            log.info("Starting Undertow {}", version);

            server.start();
        } catch (Exception e) {
            throw new FathomException(e);
        }
    }

    public Injector getInjector() {
        for (ListenerInfo listener : fathomDeploymentManager.getDeployment().getDeploymentInfo().getListeners()) {
            if (ServletContextListener.class == listener.getListenerClass()) {
                try {
                    ServletContextListener servletContextListener = (ServletContextListener) listener
                            .getInstanceFactory().createInstance().getInstance();
                    Injector injector = servletContextListener.getInjector();
                    return injector;
                } catch (Exception e) {
                    log.error("Failed to get Guice injector!", e);
                    return null;
                }
            }
        }

        return null;
    }

    protected void stopImpl() {
        if (server != null) {
            try {
                String version = server.getClass().getPackage().getImplementationVersion();
                log.info("Stopping Undertow {}", version);
                server.stop();

                fathomDeploymentManager.undeploy();
            } catch (Exception e) {
                throw new FathomException(e);
            }
        }
    }

    protected io.undertow.Undertow createServer(HttpHandler contextHandler) {

        int ports = settings.getHttpPort() + settings.getHttpsPort() + settings.getAjpPort();
        Preconditions.checkState(ports > 0, "No port specified! Please review your server port settings!");

        log.debug("Configuring Undertow engine");

        Builder builder = io.undertow.Undertow.builder();

        if (settings.getHttpPort() > 0) {
            // HTTP
            builder.addHttpListener(settings.getHttpPort(), settings.getHttpListenAddress());
            logSetting(Settings.Setting.undertow_httpPort, settings.getHttpPort());
        }

        if (settings.getHttpsPort() > 0) {
            // HTTPS
            builder.setServerOption(UndertowOptions.ENABLE_HTTP2, true);
            try {
                KeyStore keyStore = loadKeyStore(settings.getKeystoreFile(), settings.getKeystorePassword());
                KeyStore trustStore = loadKeyStore(settings.getTruststoreFile(), settings.getTruststorePassword());
                SSLContext sslContext = createSSLContext(keyStore, trustStore);
                builder.addHttpsListener(settings.getHttpsPort(), settings.getHttpsListenAddress(), sslContext);
                logSetting(Settings.Setting.undertow_httpsPort, settings.getHttpsPort());
            } catch (Exception e) {
                throw new FathomException(e, "Failed to setup an Undertow SSL listener!");
            }
        }

        if (settings.getAjpPort() > 0) {
            // AJP
            builder.addAjpListener(settings.getAjpPort(), settings.getAjpListenAddress());
            logSetting(Settings.Setting.undertow_ajpPort, settings.getAjpPort());
        }

        int ioThreads = settings.getInteger(Settings.Setting.undertow_ioThreads, 0);
        if (ioThreads > 0) {
            builder.setIoThreads(ioThreads);
            logSetting(Settings.Setting.undertow_ioThreads, ioThreads);
        }

        int workerThreads = settings.getInteger(Settings.Setting.undertow_workerThreads, 0);
        if (workerThreads > 0) {
            builder.setWorkerThreads(workerThreads);
            logSetting(Settings.Setting.undertow_workerThreads, workerThreads);
        }

        long bufferSize = settings.getBytes(Settings.Setting.undertow_bufferSize, null);
        if (bufferSize > 0) {
            builder.setBufferSize((int) bufferSize);
            logSetting(Settings.Setting.undertow_bufferSize, bufferSize);
        }

        builder.setHandler(contextHandler);
        io.undertow.Undertow server = builder.build();
        return server;
    }

    protected void logSetting(Settings.Setting setting, Object value) {
        log.debug("{}{}", Strings.padEnd(setting.toString(), 32, '.'), value);
    }

    protected DeploymentManager createFathomDeploymentManager() throws ServletException {

        DeploymentInfo info = Servlets.deployment();
        info.setDeploymentName("Fathom");
        info.setClassLoader(this.getClass().getClassLoader());
        info.setContextPath(settings.getContextPath());
        info.setIgnoreFlush(true);
        info.setDefaultEncoding("UTF-8");

        FilterInfo guiceFilter = new FilterInfo("GuiceFilter", GuiceFilter.class);
        guiceFilter.setAsyncSupported(true);

        info.addFilterUrlMapping("GuiceFilter", "/*", DispatcherType.REQUEST);
        info.addFilters(guiceFilter);

        ServletInfo defaultServlet = new ServletInfo("DefaultServlet", DefaultServlet.class);
        defaultServlet.setAsyncSupported(true);
        defaultServlet.addMapping("/");

        ServletContextListener fathomListener = new ServletContextListener(settings);

        info.addListeners(
                new ListenerInfo(ServletContextListener.class, new ImmediateInstanceFactory<>(fathomListener)));

        MultipartConfigElement multipartConfig = new MultipartConfigElement(settings.getUploadFilesLocation(),
                settings.getUploadFilesMaxSize(), -1L, 0);
        defaultServlet.setMultipartConfig(multipartConfig);
        info.addServlets(defaultServlet);

        DeploymentManager deploymentManager = Servlets.defaultContainer().addDeployment(info);
        deploymentManager.deploy();

        return deploymentManager;
    }

    private SSLContext createSSLContext(final KeyStore keyStore, final KeyStore trustStore) throws Exception {
        KeyManagerFactory keyManagerFactory = KeyManagerFactory
                .getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(keyStore, settings.getKeystorePassword().toCharArray());
        KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();

        TrustManagerFactory trustManagerFactory = TrustManagerFactory
                .getInstance(KeyManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(trustStore);
        TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();

        SSLContext sslContext;
        sslContext = SSLContext.getInstance("TLS");
        sslContext.init(keyManagers, trustManagers, null);

        return sslContext;
    }

    private KeyStore loadKeyStore(String filename, String password) throws Exception {
        KeyStore loadedKeystore = KeyStore.getInstance("JKS");
        File file = new File(filename);
        if (file.exists()) {
            try (InputStream stream = new FileInputStream(file)) {
                loadedKeystore.load(stream, password.toCharArray());
            }
        } else {
            log.error("Failed to find keystore '{}'!", filename);
        }
        return loadedKeystore;
    }
}