All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.seedstack.seed.undertow.internal.UndertowLauncher Maven / Gradle / Ivy

There is a newer version: 3.15.0
Show newest version
/*
 * Copyright © 2013-2021, The SeedStack authors 
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
package org.seedstack.seed.undertow.internal;

import io.nuun.kernel.api.Kernel;
import io.undertow.Undertow;
import io.undertow.server.HttpHandler;
import io.undertow.servlet.api.Deployment;
import io.undertow.servlet.api.DeploymentManager;
import org.seedstack.coffig.Coffig;
import org.seedstack.seed.SeedException;
import org.seedstack.seed.core.Seed;
import org.seedstack.seed.spi.SeedLauncher;
import org.seedstack.seed.undertow.UndertowConfig;
import org.seedstack.seed.web.WebConfig;
import org.seedstack.seed.web.internal.ServletContextUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xnio.OptionMap;
import org.xnio.Options;
import org.xnio.Xnio;
import org.xnio.XnioWorker;

import javax.servlet.ServletException;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

public class UndertowLauncher implements SeedLauncher {
    private static final Logger LOGGER = LoggerFactory.getLogger(UndertowLauncher.class);
    private static final AtomicBoolean firstRun = new AtomicBoolean(true);
    private final AtomicBoolean launched = new AtomicBoolean(false);
    private XnioWorker xnioWorker;
    private DeploymentManager deploymentManager;
    private Undertow undertow;
    private HttpHandler httpHandler;
    private Map kernelParameters;

    @Override
    public void launch(String[] args, Map kernelParameters) throws Exception {
        if (launched.compareAndSet(false, true)) {
            this.kernelParameters = Collections.unmodifiableMap(kernelParameters);
            startAll();
        } else {
            throw SeedException.createNew(UndertowErrorCode.UNDERTOW_ALREADY_LAUNCHED);
        }
    }

    @Override
    public void shutdown() throws Exception {
        if (launched.compareAndSet(true, false)) {
            try {
                stopAll();
            } finally {
                this.kernelParameters = null;
            }
        } else {
            throw SeedException.createNew(UndertowErrorCode.UNDERTOW_NOT_LAUNCHED);
        }
    }

    @Override
    public void refresh() throws Exception {
        if (launched.get()) {
            LOGGER.info("Refreshing Web application");
            stopAll();
            startAll();
        } else {
            throw SeedException.createNew(UndertowErrorCode.UNDERTOW_NOT_LAUNCHED);
        }
    }

    public Optional getKernel() {
        return Optional.ofNullable(deploymentManager)
                .map(DeploymentManager::getDeployment)
                .map(Deployment::getServletContext)
                .map(ServletContextUtils::getKernel);
    }

    private void startAll() throws Exception {
        if (!firstRun.compareAndSet(true, false)) {
            Seed.refresh();
        }
        Coffig baseConfiguration = Seed.baseConfiguration();
        createWorker(baseConfiguration);
        deploy(baseConfiguration);
        start();
    }

    private void stopAll() throws Exception {
        stop();
        undeploy();
        shutdownWorker();
    }

    private void createWorker(Coffig config) throws Exception {
        UndertowConfig undertowConfig = config.get(UndertowConfig.class);
        try {
            xnioWorker = Xnio.getInstance().createWorker(OptionMap.builder()
                    .set(Options.WORKER_IO_THREADS, undertowConfig.getIoThreads())
                    .set(Options.WORKER_TASK_CORE_THREADS, undertowConfig.getWorkerThreads())
                    .set(Options.WORKER_TASK_MAX_THREADS, undertowConfig.getWorkerThreads())
                    .set(Options.TCP_NODELAY, undertowConfig.isTcpNoDelay())
                    .set(Options.READ_TIMEOUT, undertowConfig.getReadTimeout())
                    .set(Options.WRITE_TIMEOUT, undertowConfig.getWriteTimeout())
                    .getMap());
        } catch (RuntimeException e) {
            throw unwrapUndertowException(e);
        }
    }

    private void shutdownWorker() throws Exception {
        try {
            if (xnioWorker != null) {
                xnioWorker.shutdownNow();
                xnioWorker.awaitTermination(2, TimeUnit.SECONDS);
            }
        } catch (RuntimeException e) {
            throw unwrapUndertowException(e);
        } finally {
            xnioWorker = null;
        }
    }

    private void deploy(Coffig configuration) throws Exception {
        try {
            DeploymentManagerFactory factory = new DeploymentManagerFactory(
                    xnioWorker,
                    configuration,
                    kernelParameters
            );
            deploymentManager = factory.createDeploymentManager();
            deploymentManager.deploy();
            httpHandler = deploymentManager.start();
        } catch (RuntimeException e) {
            throw unwrapUndertowException(e);
        }
    }

    private void undeploy() throws Exception {
        if (deploymentManager != null) {
            try {
                if (deploymentManager.getState() == DeploymentManager.State.STARTED) {
                    deploymentManager.stop();
                }
                if (deploymentManager.getState() == DeploymentManager.State.DEPLOYED) {
                    deploymentManager.undeploy();
                }
            } catch (ServletException | RuntimeException e) {
                throw unwrapUndertowException(e);
            } finally {
                httpHandler = null;
                deploymentManager = null;
            }
        }
    }

    private void start() throws Exception {
        try {
            UndertowPlugin undertowPlugin = getUndertowPlugin();
            WebConfig.ServerConfig serverConfig = undertowPlugin.getServerConfig();
            UndertowConfig undertowConfig = undertowPlugin.getUndertowConfig();
            undertow = new ServerFactory(xnioWorker, serverConfig, undertowConfig).createServer(
                    httpHandler,
                    undertowPlugin.getSslProvider()
            );
            undertow.start();
            LOGGER.info("Undertow Web server started");
        } catch (RuntimeException e) {
            throw unwrapUndertowException(e);
        }
    }

    private void stop() throws Exception {
        if (undertow != null) {
            try {
                undertow.stop();
                LOGGER.info("Undertow Web server stopped");
            } catch (RuntimeException e) {
                throw unwrapUndertowException(e);
            } finally {
                undertow = null;
            }
        }
    }

    private UndertowPlugin getUndertowPlugin() {
        return getKernel()
                .map(kernel -> kernel.plugins().get(UndertowPlugin.NAME))
                .filter(plugin -> plugin instanceof UndertowPlugin)
                .map(plugin -> (UndertowPlugin) plugin)
                .orElseThrow(() -> SeedException.createNew(UndertowErrorCode.MISSING_UNDERTOW_PLUGIN));
    }

    private Exception unwrapUndertowException(Exception e) {
        if (e instanceof RuntimeException) {
            Throwable cause = e.getCause();
            if (cause instanceof Exception) {
                return Seed.translateException((Exception) cause);
            }
        }
        return e;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy