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

bt.runtime.BtRuntimeBuilder Maven / Gradle / Ivy

There is a newer version: 1.10
Show newest version
package bt.runtime;

import bt.BtException;
import bt.module.BtModuleProvider;
import bt.module.ProtocolModule;
import bt.module.ServiceModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.util.Modules;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.stream.Collectors;

/**
 * Runtime builder.
 *
 * @since 1.0
 */
public class BtRuntimeBuilder {

    private static final Logger LOGGER = LoggerFactory.getLogger(BtRuntimeBuilder.class);

    private Config config;
    private Collection customProviders;

    private boolean shouldDisableAutomaticShutdown;
    private boolean shouldAutoLoadModules;

    /**
     * Create runtime builder with default config.
     *
     * @since 1.4
     */
    public BtRuntimeBuilder() {
        this.config = new Config();
    }

    /**
     * Create runtime builder with provided config.
     *
     * @param config Runtime config
     * @since 1.0
     */
    public BtRuntimeBuilder(Config config) {
        this.config = Objects.requireNonNull(config, "Missing runtime config");
    }

    /**
     * Set runtime config.
     *
     * @since 1.4
     */
    public BtRuntimeBuilder config(Config config) {
        this.config = Objects.requireNonNull(config, "Missing runtime config");
        return this;
    }

    /**
     * Get this builder's config.
     *
     * @return Runtime config
     * @since 1.0
     */
    public Config getConfig() {
        return config;
    }

    /**
     * Contribute an extra module into the runtime.
     *
     * @since 1.0
     */
    public BtRuntimeBuilder module(Module adapter) {
        Objects.requireNonNull(adapter);
        if (customProviders == null) {
            customProviders = new ArrayList<>();
        }
        customProviders.add(() -> adapter);
        return this;
    }

    /**
     * Contribute an extra module into the runtime.
     *
     * @since 1.1
     */
    public BtRuntimeBuilder module(Class adapterType) {
        Objects.requireNonNull(adapterType);
        if (customProviders == null) {
            customProviders = new ArrayList<>();
        }
        customProviders.add(() -> {
            try {
                return adapterType.newInstance();
            } catch (Exception e) {
                throw new BtException("Failed to instantiate custom module: " + adapterType.getName(), e);
            }
        });
        return this;
    }

    /**
     * Disable automatic runtime shutdown, when all clients have been stopped.
     *
     * @since 1.0
     */
    public BtRuntimeBuilder disableAutomaticShutdown() {
        this.shouldDisableAutomaticShutdown = true;
        return this;
    }

    /**
     * If this option is set, Bt will use the service loading mechanism
     * to load any modules that are available on application's classpath.
     *
     * To support auto-loading a module should expose {@link BtModuleProvider} provider.
     * Auto-loaded modules will be used in default configuration.
     *
     * @since 1.1
     */
    public BtRuntimeBuilder autoLoadModules() {
        this.shouldAutoLoadModules = true;
        return this;
    }

    /**
     * @since 1.0
     */
    public BtRuntime build() {
        BtRuntime runtime = new BtRuntime(createInjector(), config);
        if (shouldDisableAutomaticShutdown) {
            runtime.disableAutomaticShutdown();
        }
        return runtime;
    }

    @SuppressWarnings("unchecked")
    private Injector createInjector() {
        Collection standardModules = collectModules(standardProviders());

        Collection customModules;
        if (shouldAutoLoadModules) {
            Map, Module> autoLoadedProviders = mapByClass(collectModules(autoLoadedProviders()));
            Map, Module> customProviders = mapByClass(collectModules(customProviders()));

            autoLoadedProviders.forEach((k, v) -> {
                if (!customProviders.containsKey(k)) {
                    LOGGER.info("Auto-loading module {} with default configuration", k.getName());
                }
            });

            customProviders.forEach((k, v) -> {
                if (autoLoadedProviders.containsKey(k)) {
                    LOGGER.info("Overriding auto-loaded module {}", k.getName());
                } else {
                    LOGGER.info("Loading module {}", k.getName());
                }
            });
            autoLoadedProviders.putAll(customProviders);
            customModules = autoLoadedProviders.values();
        } else {
            customModules = collectModules(customProviders());
        }

        Injector injector;
        if (customModules.size() > 0) {
            Module customModule = Modules.override(standardModules).with(customModules);
            injector = Guice.createInjector(customModule);
        } else {
            injector = Guice.createInjector(standardModules);
        }
        return injector;
    }

    @SuppressWarnings("unchecked")
    private Collection collectModules(Collection... providers) {
        return Arrays.asList(providers).stream()
                .flatMap(Collection::stream)
                .map(BtModuleProvider::module)
                .collect(Collectors.toList());
    }

    @SuppressWarnings("unchecked")
    private  Map, T> mapByClass(Collection collection) {
        return collection.stream().collect(HashMap::new, (m, o) -> m.put((Class)o.getClass(), o), Map::putAll);
    }

    private Collection standardProviders() {
        Collection standardProviders = new ArrayList<>();
        standardProviders.add(() -> new ServiceModule(config));
        standardProviders.add(ProtocolModule::new);
        return standardProviders;
    }

    private Collection customProviders() {
        if (customProviders == null) {
            return Collections.emptyList();
        } else {
            return customProviders.stream()
                    .map(BtRuntimeBuilder::nullCheckingProvider)
                    .collect(Collectors.toList());
        }
    }

    private Collection autoLoadedProviders() {
        Collection autoLoadedProviders = new ArrayList<>();
        ServiceLoader.load(BtModuleProvider.class).forEach(p -> {
            autoLoadedProviders.add(nullCheckingProvider(p));
        });
        return autoLoadedProviders;
    }

    private static BtModuleProvider nullCheckingProvider(BtModuleProvider provider) {
        return () -> Objects.requireNonNull(provider.module(), "Missing module in provider:" + provider.getClass().getName());
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy