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

com.nhl.bootique.Bootique Maven / Gradle / Ivy

Go to download

Bootique is a simple DI-based framework for launching command-line Java applications of any kind. Be it webservices, webapps, jobs, etc.

There is a newer version: 0.18
Show newest version
package com.nhl.bootique;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
import java.util.ServiceLoader;

import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.ProvisionException;
import com.google.inject.multibindings.Multibinder;
import com.nhl.bootique.command.Command;
import com.nhl.bootique.command.CommandOutcome;
import com.nhl.bootique.command.Commands;
import com.nhl.bootique.env.DefaultEnvironment;
import com.nhl.bootique.log.BootLogger;
import com.nhl.bootique.log.DefaultBootLogger;

import joptsimple.OptionException;

/**
 * A main launcher class of Bootique. To start a Bootique app, you may write
 * your main method as follows:
 * 
 * 
 * public static void main(String[] args) {
 * 	Bootique.app(args)commands(_optional_commands_).modules(_optional_extensions_).run();
 * }
 * 
* * or * *
 * public static void main(String[] args) {
 * 	Bootique.app(args).commands(_optional_commands_).autoLoadModules().run();
 * }
 * 
*/ public class Bootique { protected static Module createModule(Class moduleType) { try { return moduleType.newInstance(); } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException("Error instantiating Module of type: " + moduleType.getName(), e); } } protected Collection providers; private Collection commands; private String[] args; private boolean autoLoadModules; private BootLogger bootLogger; public static Bootique app(String[] args) { return new Bootique(args); } private Bootique(String[] args) { this.args = args; this.providers = new ArrayList<>(); this.commands = new ArrayList<>(); this.autoLoadModules = false; this.bootLogger = createBootLogger(); } /** * Optionally overrides Bootique BootLogger. * * @since 0.12 * @return this instance of Bootique. */ public Bootique bootLogger(BootLogger bootLogger) { this.bootLogger = bootLogger; return this; } /** * Instructs Bootique to load any modules available on class-path that * expose {@link BQModuleProvider} provider. Auto-loaded modules will be * used in default configuration. Factories within modules will of course be * configured dynamically from YAML. *

* Use with caution, you may load more modules than you expected. Make * sure only needed Bootique dependencies are included on class-path. If in * doubt, switch to explicit Module loading via * {@link #modules(Class...)}. * * @return this Bootique instance * @see BQModuleProvider */ public Bootique autoLoadModules() { this.autoLoadModules = true; return this; } /** * @param moduleType * custom Module class to add to Bootique DI runtime. * @return this Bootique instance * @since 0.8 * @see #autoLoadModules() */ public Bootique module(Class moduleType) { Objects.requireNonNull(moduleType); providers.add(() -> createModule(moduleType)); return this; } /** * Adds an array of Module types to the Bootique DI runtime. Each type will * be instantiated by Bootique and added to the Guice DI container. * * @param moduleTypes * custom Module classes to add to Bootique DI runtime. * @return this Bootique instance * @since 0.8 * @see #autoLoadModules() */ @SafeVarargs public final Bootique modules(Class... moduleTypes) { Arrays.asList(moduleTypes).forEach(m -> module(m)); return this; } public Bootique module(Module m) { Objects.requireNonNull(m); providers.add(new BQModuleProvider() { @Override public Module module() { return m; } @Override public String name() { return "Bootique"; } }); return this; } /** * Adds an array of Modules to the Bootique DI runtime. * * @param modules * an array of modules to add to Bootiqie DI runtime. * @return this instance of {@link Bootique}. */ public Bootique modules(Module... modules) { Arrays.asList(modules).forEach(m -> module(m)); return this; } /** * Adds a Module generated by the provider. Provider may optionally specify * that the Module overrides services in some other Module. * * @since 0.12 * @param moduleProvider * a provider of Module and override spec. * @return this instance of {@link Bootique}. */ public Bootique module(BQModuleProvider moduleProvider) { Objects.requireNonNull(moduleProvider); providers.add(moduleProvider); return this; } /** * Starts an API call chain to override an array of Modules. * * @param overriddenTypes * an array of modules whose bindings should be overridden. * @return {@link BQModuleOverrideBuilder} object to specify a Module * overriding other modules. */ @SafeVarargs public final BQModuleOverrideBuilder override(Class... overriddenTypes) { return new BQModuleOverrideBuilder() { @Override public Bootique with(Class moduleType) { providers.add(new BQModuleProvider() { @Override public Module module() { return createModule(moduleType); } @Override public Collection> overrides() { return Arrays.asList(overriddenTypes); } }); return Bootique.this; } @Override public Bootique with(Module module) { providers.add(new BQModuleProvider() { @Override public Module module() { return module; } @Override public Collection> overrides() { return Arrays.asList(overriddenTypes); } }); return Bootique.this; } }; } /** * Registers a custom {@link Command} object. * * @param command * A custom {@link Command} instance to add to Bootique runtime. * @return this Bootique instance * @deprecated since 0.12 add to Bootique a {@link BQModuleProvider} * produced via {@link Commands} builder. */ public Bootique command(Command command) { this.commands.add(command); return this; } /** * Registers a custom {@link Command} object. * * @param commands * Custom {@link Command} instances to add to Bootique runtime. * @return this Bootique instance * @deprecated since 0.12 add to Bootique a {@link BQModuleProvider} * produced via {@link Commands} builder. */ public Bootique commands(Command... commands) { Arrays.asList(commands).forEach(c -> command(c)); return this; } /** * Creates and returns an instance of {@link BQRuntime} that contains all * Bootique services, commands, etc. This method is only needed if you need * to run your code manually, process {@link CommandOutcome} or don't want * Bootique to call {@link System#exit(int)}. Normally you should consider * using {@link #run()} instead. * * @since 0.13 * @return a new {@link BQRuntime} instance that contains all Bootique * services, commands, etc. * @see Bootique#run() */ public BQRuntime createRuntime() { Injector injector = createInjector(); return createRuntime(injector); } /** * @deprecated since 0.13 in favor of {@link #createRuntime()}. * @return a newly created runtime. */ @Deprecated public BQRuntime runtime() { return createRuntime(); } /** * Creates and runs {@link BQRuntime}, and processing its output. This * method is a rough alternative to "runtime().getRunner().run().exit()". In * most cases calling it would result in the current JVM process to * terminate. *

* If you don't want your app to shutdown after executing Bootique, you may * manually obtain {@link BQRuntime} by calling {@link #createRuntime()}, * and run it from your code without calling "exit()". */ public void run() { BQRuntime runtime = createRuntime(); runtime.addJVMShutdownHook(); CommandOutcome o = run(runtime); // report error if (!o.isSuccess()) { if (o.getMessage() != null) { runtime.getBootLogger().stderr( String.format("Error running command '%s': %s", runtime.getArgsAsString(), o.getMessage())); } else { runtime.getBootLogger().stderr(String.format("Error running command '%s'", runtime.getArgsAsString())); } if (o.getException() != null) { runtime.getBootLogger().stderr("Command exception", o.getException()); } } o.exit(); } protected CommandOutcome run(BQRuntime runtime) { try { return runtime.getRunner().run(); } // handle startup Guice exceptions catch (ProvisionException e) { // TODO: a dependency on JOPT OptionException shouldn't be here return (e.getCause() instanceof OptionException) ? CommandOutcome.failed(1, e.getCause().getMessage()) : CommandOutcome.failed(1, e); } } protected BQRuntime createRuntime(Injector injector) { return new BQRuntime(injector); } protected BootLogger createBootLogger() { return new DefaultBootLogger(System.getProperty(DefaultEnvironment.TRACE_PROPERTY) != null); } protected Injector createInjector() { Collection providers = new ArrayList<>(); providers.add(coreModuleProvider()); if (!commands.isEmpty()) { providers.add(commandsProvider()); } providers.addAll(builderProviders()); if (autoLoadModules) { providers.addAll(autoLoadedProviders()); } Collection modules = new ModuleMerger(bootLogger).getModules(providers); return Guice.createInjector(modules); } protected BQModuleProvider commandsProvider() { return () -> { bootLogger.trace(() -> "Adding module with custom commands..."); return (b) -> { Multibinder mb = BQCoreModule.contributeCommands(b); commands.forEach(c -> mb.addBinding().toInstance(c)); }; }; } protected Collection builderProviders() { return providers; } protected BQModuleProvider coreModuleProvider() { return new BQModuleProvider() { @Override public Module module() { return BQCoreModule.builder().args(args).bootLogger(bootLogger).build(); } @Override public String name() { return "Bootique"; } }; } protected Collection autoLoadedProviders() { Collection modules = new ArrayList<>(); ServiceLoader.load(BQModuleProvider.class).forEach(p -> modules.add(p)); return modules; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy