org.seedstack.seed.core.Seed.java Source code

Java tutorial

Introduction

Here is the source code for org.seedstack.seed.core.Seed.java

Source

/**
 * Copyright (c) 2013-2016, The SeedStack authors <http://seedstack.org>
 *
 * 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.core;

import com.google.common.base.Strings;
import io.nuun.kernel.api.Kernel;
import io.nuun.kernel.api.config.KernelConfiguration;
import org.fusesource.jansi.Ansi;
import org.fusesource.jansi.AnsiRenderer;
import org.seedstack.coffig.Coffig;
import org.seedstack.seed.ApplicationConfig;
import org.seedstack.seed.LoggingConfig;
import org.seedstack.seed.ProxyConfig;
import org.seedstack.seed.SeedException;
import org.seedstack.seed.core.internal.CoreErrorCode;
import org.seedstack.seed.core.internal.diagnostic.DiagnosticManagerImpl;
import org.seedstack.seed.core.internal.init.AutodetectLogManager;
import org.seedstack.seed.core.internal.init.BaseConfiguration;
import org.seedstack.seed.core.internal.init.ConsoleManager;
import org.seedstack.seed.core.internal.init.GlobalValidatorFactory;
import org.seedstack.seed.core.internal.init.KernelManager;
import org.seedstack.seed.core.internal.init.LogManager;
import org.seedstack.seed.core.internal.init.ProxyManager;
import org.seedstack.seed.diagnostic.DiagnosticManager;
import org.seedstack.seed.spi.SeedInitializer;
import org.seedstack.shed.ClassLoaders;
import org.seedstack.shed.reflect.Classes;
import org.seedstack.shed.text.TextTemplate;

import javax.annotation.Nullable;
import javax.validation.ValidatorFactory;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Scanner;
import java.util.ServiceLoader;
import java.util.Set;

/**
 * This class is the Seed framework entry point, which is used create and dispose kernels.
 * It handles global initialization and cleanup.
 */
public class Seed {
    private static final String WELCOME_MESSAGE = "\n" + " ____                _ ____  _             _    \n"
            + "/ ___|  ___  ___  __| / ___|| |_ __ _  ___| | __\n"
            + "\\___ \\ / _ \\/ _ \\/ _  \\___ \\| __/ _  |/ __| |/ /\n"
            + " ___) |  __/  __/ (_| |___) | || (_| | (__|   < \n"
            + "|____/ \\___|\\___|\\____|____/ \\__\\____|\\___|_|\\_\\";
    private static volatile boolean initialized = false;
    private static volatile boolean disposed = false;
    private static volatile boolean noLogs = false;
    private final DiagnosticManager diagnosticManager;
    private final String seedVersion;
    private final String businessVersion;
    private final ApplicationConfig applicationConfig;
    private final Coffig configuration;
    private final ConsoleManager consoleManager;
    private final LogManager logManager;
    private final ValidatorFactory validatorFactory;
    private final ProxyManager proxyManager;
    private final KernelManager kernelManager;
    private final Set<SeedInitializer> seedInitializers = new HashSet<>();

    private static class Holder {
        private static final Seed INSTANCE = new Seed();
    }

    /**
     * Disable logs globally if a supported SLF4J implementation is used. Currently only Logback is supported.
     */
    public static void disableLogs() {
        noLogs = true;
    }

    /**
     * Create and start a basic kernel without specifying a runtime context, nor a configuration. Seed JVM-global
     * state is automatically initialized before the first time a kernel is created.
     *
     * @return the {@link Kernel} instance.
     */
    public static Kernel createKernel() {
        return createKernel(null, null, true);
    }

    /**
     * Create, initialize and optionally start a kernel with the specified runtime context and configuration. Seed JVM-global
     * state is automatically initialized before the first time a kernel is created.
     *
     * @param runtimeContext      the runtime context object, which will be accessible from plugins.
     * @param kernelConfiguration the kernel configuration.
     * @param autoStart           if true, the kernel is started automatically.
     * @return the {@link Kernel} instance.
     */
    public static Kernel createKernel(@Nullable Object runtimeContext,
            @Nullable KernelConfiguration kernelConfiguration, boolean autoStart) {
        Seed instance = getInstance();
        return instance.kernelManager.createKernel(SeedRuntime.builder().context(runtimeContext)
                .diagnosticManager(instance.diagnosticManager).configuration(instance.configuration.fork())
                .validatorFactory(instance.validatorFactory).applicationConfig(instance.applicationConfig)
                .version(instance.seedVersion).businessVersion(instance.businessVersion).build(),
                kernelConfiguration, autoStart);
    }

    /**
     * Stops and dispose a running {@link Kernel} instance.
     *
     * @param kernel the kernel to dispose.
     */
    public static void disposeKernel(Kernel kernel) {
        KernelManager.get().disposeKernel(kernel);
    }

    /**
     * Provides the default {@link DiagnosticManager} instance to dump diagnostics outside a running kernel.
     *
     * @return the default diagnostic manager.
     */
    public static DiagnosticManager diagnostic() {
        return getInstance().diagnosticManager;
    }

    /**
     * Provides the application base configuration (i.e. not including configuration sources discovered after kernel
     * startup).
     *
     * @return the {@link Coffig} object for application base configuration.
     */
    public static Coffig baseConfiguration() {
        return getInstance().configuration;
    }

    /**
     * Cleanup Seed JVM-global state explicitly. Should be done before exiting the JVM. After calling this method
     * Seed is no longer usable in the current JVM.
     */
    public static void close() {
        if (initialized && !disposed) {
            getInstance().dispose();
        }
    }

    private static Seed getInstance() {
        if (disposed) {
            throw new IllegalStateException("Seed is no longer usable after having called close()");
        }
        try {
            return Holder.INSTANCE;
        } catch (Throwable t) {
            if (t instanceof ExceptionInInitializerError) {
                t = t.getCause();
            }
            throw SeedException.wrap(t, CoreErrorCode.UNABLE_TO_INITIALIZE_SEED);
        }
    }

    private Seed() {
        diagnosticManager = new DiagnosticManagerImpl();

        seedVersion = Optional.ofNullable(Seed.class.getPackage()).map(Package::getImplementationVersion)
                .orElse(null);
        businessVersion = Classes.optional("org.seedstack.business.internal.BusinessSpecifications")
                .map(Class::getPackage).map(Package::getImplementationVersion).orElse(null);

        Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
            if (throwable instanceof SeedException) {
                throwable.printStackTrace(System.err);
            } else {
                SeedException.wrap(throwable, CoreErrorCode.UNEXPECTED_EXCEPTION).printStackTrace(System.err);
            }
        });

        // Logging initialization (should silence logs until logging activation later in the initialization)
        logManager = AutodetectLogManager.get();

        // Validation
        validatorFactory = GlobalValidatorFactory.get();

        // Configuration
        configuration = BaseConfiguration.get();
        applicationConfig = configuration.get(ApplicationConfig.class);

        // Console
        consoleManager = ConsoleManager.get();
        consoleManager.install(applicationConfig.getColorOutput());

        // Banner
        if (!noLogs && applicationConfig.isPrintBanner()) {
            System.out.println(buildBannerMessage(applicationConfig).orElseGet(this::buildWelcomeMessage));
        }

        // Logging activation
        if (!noLogs) {
            logManager.configure(configuration.get(LoggingConfig.class));
        }

        // Proxy
        proxyManager = ProxyManager.get();
        proxyManager.install(configuration.get(ProxyConfig.class));

        // Nuun
        kernelManager = KernelManager.get();

        // Custom initializers
        for (SeedInitializer seedInitializer : ServiceLoader.load(SeedInitializer.class)) {
            try {
                seedInitializer.onInitialization(configuration);
                seedInitializers.add(seedInitializer);
            } catch (Exception e) {
                throw SeedException.wrap(e, CoreErrorCode.ERROR_IN_INITIALIZER).put("initializerClass",
                        seedInitializer.getClass().getName());
            }
        }

        markInitialized();
    }

    private static void markInitialized() {
        initialized = true;
    }

    private Optional<String> buildBannerMessage(ApplicationConfig applicationConfig) {
        String banner = getBanner();
        if (banner != null) {
            Map<String, Object> bannerReplacements = new HashMap<>();
            bannerReplacements.put("seed.version", seedVersion);
            bannerReplacements.put("business.version", businessVersion);
            bannerReplacements.put("app.id", applicationConfig.getId());
            bannerReplacements.put("app.name", applicationConfig.getName());
            bannerReplacements.put("app.version", applicationConfig.getVersion());
            return Optional.of(AnsiRenderer.render(new TextTemplate(banner).render(bannerReplacements)));
        } else {
            return Optional.empty();
        }
    }

    private String buildWelcomeMessage() {
        Ansi welcomeMessage = Ansi.ansi().reset().fgBrightGreen().a(WELCOME_MESSAGE).reset();
        if (seedVersion != null) {
            welcomeMessage.a("\n").a("Core v").a(Strings.padEnd(seedVersion, 16, ' '));
        }
        if (businessVersion != null) {
            welcomeMessage.a(seedVersion != null ? "" : "\n").a("Business v").a(businessVersion);
        }
        welcomeMessage.a("\n");
        return welcomeMessage.reset().toString();
    }

    private String getBanner() {
        InputStream bannerStream = ClassLoaders.findMostCompleteClassLoader(Seed.class)
                .getResourceAsStream("banner.txt");
        if (bannerStream != null) {
            try {
                return new Scanner(bannerStream, StandardCharsets.UTF_8.name()).useDelimiter("\\Z").next();
            } finally {
                try {
                    bannerStream.close();
                } catch (IOException e) {
                    // nothing to do
                }
            }
        }
        return null;
    }

    private void dispose() {
        seedInitializers.forEach(SeedInitializer::onClose);
        proxyManager.uninstall();
        validatorFactory.close();
        consoleManager.uninstall();
        logManager.close();
        Thread.setDefaultUncaughtExceptionHandler(null);
        markDisposed();
    }

    private static void markDisposed() {
        initialized = false;
        disposed = true;
    }
}