Bootique Core Documentation


Table of Contents

I. Overview
1. What is Bootique
2. Java Version
3. Build System
4. Programming Skills
II. Programming
5. Modules
6. Modules Auto-Loading
7. Configuration and Configurable Factories
Configuration via YAML Files
Configuration via Properties
Configuration via Environment Variables
Polymorphic Configuration Objects
8. Using Modules
Injecting Other Module's Services
Contributing to Other Modules
9. Application Class
Application as a Module
Common Main Class
10. Commands
Writing Commands
Injection in Commands
11. Options
12. Logging
Loggers in the Code
Configurable Logging with Logback
BootLogger
III. Testing
13. Bootique and Testing
14. Creating Test Stacks
15. Common Test Scenarios
Testing Services that are Part of Bootique Runtime
Testing Network Services
Testing Commands
Testing Module Validity
IV. Assembly and Deployment
16. Runnable Jar
17. Tracing Bootique Startup

Part I. Overview

Chapter 1. What is Bootique

Bootique is a minimally opinionated technology for building container-less runnable Java applications. No JavaEE container required to run your app! It is an ideal platform for microservices, as it allows you to create a fully functional app with minimal-to-no setup. Though it is not limited to a specific kind of app (or the "micro" size) and can be used for REST services, webapps, runnable jobs, DB migrations, JavaFX GUI apps to mention a few examples.

Unlike traditional container-based apps, Bootique allows you to control your main() method and create Java apps that behave like simple executable commands that can be run with Java:

java -jar my.jar [arguments]

Each Bootique app can be started with a YAML configuration loaded from a file or from a remote URL. Among other benefits, such configuration approach ideally suits cloud deployment environments.

Bootique was inspired by two similar products - Dropwizard and SpringBoot, however its focus is different. Bootique favors modularity and clean pluggable architecture. Bootique is built on top of Google Guice dependency injection (DI) container, which provides the core of its modularity mechanism. This means that pretty much anything in Bootique can be customized/overridden to your liking.

Chapter 2. Java Version

Java 8 or newer is required.

Chapter 3. Build System

Bootique apps can be built using any Java build system (Ant, Maven, Gradle, etc). Examples in the documentation are based on Maven and maven-shade-plugin. While this is not strictly a requirement, Bootique apps are usually packaged into "fat" runnable jars and don't have any external dependencies beyond the JRE.

Chapter 4. Programming Skills

Everyting you know about Java programming will be applicable when working with Bootique. You may need to "unlearn" some of the practices related to JavaEE configuration and container deployment though.

Integration between various parts of a Bootique app is done via Google Guice. In most cases Bootique API would steer you towards idiomatic approach to integration, so deep knowledge of Guice is not required. Though it wouldn't hurt to understand a few main concepts: modules, bindings, multibindings, overrides.

Java ServiceLoader facility is another important part of Bootique, and probably yet another thing that you shouldn't worry too much about initially.

Part II. Programming

Chapter 5. Modules

Bootique apps are made of "modules". The framework simply locates all available modules, loads them in the DI environment, parses the command line, and then transfers control to a Command (that can originate from any of the modules) that matched the user choice. There's a growing list of modules created by Bootique development team. And you can easily write your own. In fact, programming in Bootique is primarily about writing Modules.

A module is a Java library that contains some code. What makes it a module is a special Java class that implements Guice Module interface. This class defines what "services" or other types of objects the module provides (in other words what will be injectable by the module users). This is done in a form of "bindings", i.e. associations between publicly visible injectable service interfaces and specific implementations:

public class MyModule implements Module {
	@Override
	public void configure(Binder binder) {
		binder.bind(MyService.class).to(MyServiceImpl.class);
	}
}

There are other flavors of bindings in Guice. Please refer to Guice documentation for details. One important form extensively used in Bootique is Multibinding.

Chapter 6. Modules Auto-Loading

Modules can be automatically loaded via Bootique.autoLoadModules() as long as they are included in your aplication dependencies. Auto-loading depends on the Java ServiceLoader mechanism. To ensure your modules can be auto-loaded do two things. First implement io.bootique.BQModuleProvider interface specific to your module:

public class MyModuleProvider implements BQModuleProvider {
	@Override
	public Module module() {
		return new MyModule();
	}
}

After then create a file META-INF/services/io.bootique.BQModuleProvider with the only line being the name of your BQModuleProvider implementor. E.g.:

com.foo.MyModuleProvider

BQModuleProvider has two more methods that you can optionally implement to help Bootique to make sense of the module being loaded:

public class MyModuleProvider implements BQModuleProvider {
	...
	// provides human-readable name of the module
	@Override
	public String name() {
		return "CustomName";
	}
	
	// a collection of modules whose services are overridden by this module
	@Override
	public Collection<Class<? extends Module>> overrides() {
		return Collections.singleton(BQCoreModule.class);
	}
}

If in your Module you are planning to redefine any services from the upstream modules, specify those upstream modules in the overrides() collection.

Chapter 7. Configuration and Configurable Factories

Bootique Modules obtain their configuration in a form of "factory objects". We'll show some examples shortly. For now let's focus on the big picture, namely the fact that Bootique app configuration is multi-layered and roughly follows the sequence of "code - config files - overrides". "Code" is the default values that are provided in constructors of factory objects. Config files overlay those defaults with external configuration values. Config files is where the bulk of configuration usually stored. Finally config values may be further overridden via Java properties and/or environment variables.

Now let's discuss how each step works.

Configuration via YAML Files

As mentioned above, a Bootique app can be started with one or more YAML configuration files. To specify more than one file, use --config option multiple times. Configurations will be loaded and merged together in the order of their appearance on the command line. Here is a simple example of a config file:

log:
  level: warn
  appenders:
    - type: file
      logFormat: '%c{20}: %m%n'
      file: target/logback/debug.log

jetty:
  context: /myapp
  connector:
    port: 12009

While this is not strictly required, as a rule the top-level keys in the file belong to configuration objects of individual modules. In the example above "log" subtree configures bootique-logback module, while "jetty" subtree configures bootique-jetty-module. For standard modules, refer to module-specific documentation on the structure of the supported configuration. Here we'll discuss how to build your own configuration-aware module.

Bootique allows the Module to read its specific configuration subree as an object of the type defined in the Module. Very often such an object is written as a factory that contains a bunch of setters for the configuration properties, and a factory method to produce some service that a Module is interested in based on this configuration. Here is an example factory:

public class MyFactory {

	private int intProperty;
	private String stringProperty;

	public void setIntProperty(int i) {
		this.intProperty = i;
	}

	public void setStringProperty(String s) {
		this.stringProperty = s;
	}

	// factory method
	public MyService createMyService(SomeOtherService soService) {
		return new MyServiceImpl(soService, intProperty, stringProperty);
	}
}

The factory contains configuration property declarations, as well as public setters for these properties (you can create the getters if you want as well. While this is not strictly required, it may be useful. E.g. for unit tests). Now let's take a look at the Module class:

public class MyModule extends ConfigModule {
	@Provides
	public MyService createMyService(
             ConfigurationFactory configFactory, 
             SomeOtherService soService) {

		return configFactory
                 .config(MyFactory.class, configPrefix)
                 .createMySerice(soService);
	}
}

And now a sample configuration that will work with our module:

my:
  intProperty: 55
  stringProperty: 'Hello, world!'

A few points to note here:

  • Calling our module "MyModule" and extending from ConfigModule gives it access to the protected "configPrefix" instance variable that is initialized to the value of "my" (the naming convention here is to use the Module simple class name without the "Module" suffix and converted to lowercase).

  • @Provides annotation is a Guice way of marking a Module method as a "provider" for a certain type of injectable service. All its parameters are themselves injectable objects.

  • ConfigurationFactory is the class used to bind a subtree of the app YAML configuration to a given Java object (in our case - MyFactory). The structure of MyFactory is very simple here, but it can be as complex as needed, containing nested objects, arrays, maps, etc. Internally Bootique uses Jackson framework to bind YAML to a Java class, so all the features of Jackson can be used to craft configuration.

Configuration via Properties

YAML file can be thought of as a set of nested properties. E.g. the following config

my:
  prop1: val1
  prop2: val2

can be represented as two properties ("my.prop1", "my.prop2") being assigned some values. Bootique takes advantage of this structural equivalence and allows to define configuration via properties as an alternative (or more frequently - an addition) to YAML. If the same "key" is defined in both YAML file and a property, ConfigurationFactory would use the value of the property (in other words properties override YAML values).

To turn a given property into a configuration property, you need to prefix it with "bq.". This "namespace" makes configuration explicit and helps to avoid random naming conflicts with properties otherwise present in the system.

Properties can be provided to Bootique in one of two ways: either via JVM -D flag or via DI contribution. Examples below demonstrate the two approaches, defining the same properties:

java -Dbq.my.prop1=valX -Dbq.my.prop2=valY -jar myapp.jar
class MyModule implements Module {
	public void configure(Binder binder) {

		BQCoreModule.contributeProperties(binder)
		  .addBinding("bq.my.prop1")
		  .toInstance("valX");

		BQCoreModule.contributeProperties(binder)
		  .addBinding("bq.my.prop2")
		  .toInstance("valY");
	}
}

Often the main configuration is specified in YAML, and properties are used to override just a few values. Next we'll discuss another way to override values - via environment variables.

Configuration via Environment Variables

In addition to JVM properties, Bootique allows to use environment variables to specify/override configuration values. Variable names start with BQ_ prefix, the rest of the variable name being case insensitive, with property components separated with underscore. E.g.:

export BQ_MY_PROP1=valX
export BQ_MY_PROP1=valY

While variables work similar to JVM properties, using them has advantages in certain situations:

  • They may be used to configure credentials, as unlike YAML they won't end up in version control, and unlike Java properties, they won't be visible in the process list.

  • They provide customized application environment without changing the launch script.

Just watch out for possible naming conflicts due to case-insensitive nature of the properties.

Polymorphic Configuration Objects

A powerful feature of Jackson is the ability to dynamically create subclasses of the configuration objects. Bootique takes full advantage of this. E.g. imagine a logging module that needs "appenders" to output its log messages (file appender, console appender, syslog appender, etc.). The framework might not be aware of all possible appenders its users might come up with in the future. Yet it still wants to have the ability to instantiate any of them, based solely on the data coming from YAML. Moreover each appender will have its own set of incompatible configuration properties. In fact this is exactly the situation with bootique-logback module.

Here is how you ensure that such a polimorphic configuration is possible. Let's start with a simple class hierarchy and a factory that contains a variable of the supertype that we'd like to init to a concrete subclass in runtime:

public abstract class SuperType {
...
}

public class ConcreteType1 extends SuperType {
...
}

public class ConcreteType2 extends SuperType {
...	
}

public class MyFactory {

	// can be a class or an interface
	private SuperType subconfig;
	

	public void setSubconfig(SuperType s) {
		this.subconfig = s;
	}	
...
}

To make polimorphism work, we need to provide some instructions to Jackson. First we need to annotate the supertype and subtypes:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, 
     property = "type", 
     defaultImpl = ConcreteType1.class)
public abstract class SuperType {

}

@JsonTypeName("type1")
public class ConcreteType1 extends SuperType {

}

@JsonTypeName("type2")
public class ConcreteType2 extends SuperType {
	
}

After that we need to create a service provider file called META-INF/service/io.bootique.config.PolymorphicConfiguration where all the types participating in the hierarchy are listed (including the supertype):

com.foo.SuperType
com.foo.ConcreteType1
com.foo.ConcreteType2

This should be enough to work with configuration like this:

my:
  subconfig:
    type: type2
    someVar: someVal

Or this (here type is implicitly assumed to be "type1", see the "defaultImpl" in the @JsonTypeInfo annotation above:

my:
  subconfig:
    someOtherVar: someOtherVal

If another module decides to create yet another subclass of SuperType, it will need to create its own META-INF/service/io.bootique.config.PolymorphicConfiguration file that mentions the new subclass.

Chapter 8. Using Modules

Modules can use other "upstream" modules in a few ways:

  • "Import": a downstream module uses another module as a library, ignoring its injectable services.

  • "Use" : downstream module's classes inject classes from an upstream module.

  • "Contribute": downstream module injects objects to collections and maps defined in upstream modules.

Import case is trivial, so we'll concentrate on the two remaining scenarios. We will use BQCoreModule as an example of an upstream module, as it is available in all apps.

Injecting Other Module's Services

You can inject any services declared in other modules. E.g. BQCoreModule provides a number of objects and services that can be accessed via injection:

class MyService {

	@Args
	@Inject
	private String[] args;

	public String getArgsString() {
		return Arrays.asList(getArgs()).stream().collect(joining(" "));
	}
}

In this example we injected command line arguments that were used to start the app. Note that since there can potentially be more than one String[] in a DI container, Bootique @Args annotation is used to uniquely identify the array that we want here.

Contributing to Other Modules

Guice supports multibindings, intended to contribute objects defined in a downstream module to collections/maps used by services in upstream modules. E.g. the following code makes MyCommand available in the app set of commands:

public class MyModule implements Module {

	@Override
	public void configure(Binder binder) {
		BQCoreModule.contributeCommands(binder)
			.addBinding()
			.to(MyCommand.class);
	}
}

Here we obtained a Multibinder instance via a static method on BQCoreModule. Most standard modules define such helper methods for every collection/map that should be populated by module users. This is a pattern you might want to follow in your own modules.

Chapter 9. Application Class

A class that contains the "main()" method is informally called "application". Bootique does not impose any additional requirements on this class. You decide what to put in it. It can be limited to just "main()", or turned into a REST API resource, etc.

Application as a Module

Most often then not it makes sense to turn the application class into a Module though. After all a Bootique app is just a collection of Modules, and this way the application class would represent that one final Module to rule them all:

public class Application implements Module {

   public static void main(String[] args) {
      Bootique.app(args).module(Application.class).autoLoadModules().run();
   }

   public void configure(Binder binder) {
      // load app-specific services; redefine standard ones
   }
}

You may also implement a separate BQModuleProvider for the Application module. Then autoLoadModules() will discover it just like any other Module, and there won't be a need to add Application module explicitly.

Common Main Class

If all your code is packaged in auto-loadable modules (which is always a good idea), you may not even need a custom main class. io.bootique.Bootique class itself declares a main() method and can be used as an app launcher. This creates some interesting possibilities. E.g. you can create Java projects that have no code of their own and are simply collections of modules declared as compile dependencies. More details on packaging are given in the "Runnable Jar" chapter.

Chapter 10. Commands

Bootique runtime contains a set of commands coming from Bootique core and from all the modules currently in effect in the app. On startup Bootique attempts to map command-line arguments to a single command type. If no match is found, a default command is executed (which is normally a "help" command). To list all available commands, the app can be run with --help option (in most cases running without any options will have the same effect). E.g.:

$ java -jar myapp-1.0.jar --help

Option                    Description                          
------                    -----------                          
--config <yaml_location>  Specifies YAML config location, which
                            can be a file path or a URL.       
--help                    Prints this message.                 
--server                  Starts Jetty server.

Writing Commands

Most common commands are already available in various standard modules, still often you'd need to write your own. To do that, first create a command class. It should implement io.bootique.command.Command interface, though usually it more practical to extend io.bootique.command.CommandWithMetadata and provide some metadata used in help and elsewhere:

public class MyCommand extends CommandWithMetadata {

	private static CommandMetadata createMetadata() {
		return CommandMetadata.builder(MyCommand.class)
				.description("My command does something important.")
				.build();
	}

	public MyCommand() {
		super(createMetadata());
	}

	@Override
	public CommandOutcome run(Cli cli) {

		// ... run the command here....

		return CommandOutcome.succeeded();
	}
}

The command initializes metadata in constructor and implements the "run" method to run its code. The return CommandOutcome object instructs Bootique what to do when command finishes. The object contains system exit code, and exceptions that occurred during execution. To make the new command available to Bootique, "contribute" it to BQCoreModule, as was already shown above:

public class MyModule implements Module {

	@Override
	public void configure(Binder binder) {
		BQCoreModule.contributeCommands(binder)
			.addBinding()
			.to(MyCommand.class);
	}
}

To implement a "daemon" command running forever until it receives an OS signal (e.g. a web server waiting for user requests) , do something like this:

@Override
public CommandOutcome run(Cli cli) {

	// ... start some process in a different thread ....

	// now wait till the app is stopped from another thread 
	// or the JVM is terminated
	try {
		Thread.currentThread().join();
	} catch (InterruptedException e) {
		// ignore exception or log if needed
	}

	return CommandOutcome.succeeded();
}

Injection in Commands

Commands can inject services, just like most other classes in Bootique. There are some specifics though. Since commands are sometimes instantiated, but not executed (e.g. when --help is run that lists all commands), it is often desirable to avoid immediate instantiation of all dependencies of a given command. So a common pattern with commands is to inject Guice Provider instead of direct dependency:

@Inject
private Provider<SomeService> provider;

@Override
public CommandOutcome run(Cli cli) {
	provider.get().someMethod();
}

Chapter 11. Options

In addition to commands, the app can define "options". Options are not associated with any runnable java code, and simply pass command-line values to commands and services. E.g. the standard "--config" option is used by CliConfigurationSource service to locate configuration file. Unrecognized options cause application startup errors. To be recognized, an option needs to be "contributed" to Bootique similar to commands:

CliOption option = CliOption
	.builder("email", "An admin email address")
	.valueRequired("email_address").build();

BQCoreModule.contributeOptions(binder).addBinding().toInstance(option);

To read a value of the option, a service should inject io.bootique.cli.Cli object (commands also get this object as a parameter to "run") :

@Inject
private Cli cli;

public void doSomething() {
	Collection<String> emails = cli.optionStrings("email");
	// do something with option values....
}

Chapter 12. Logging

Loggers in the Code

Standard Bootique modules use SLF4J internally, as it is the most convenient least common denominator framework, and can be easily bridged to other logging implementations. Your apps or modules are not required to use SLF4J, though if they do, it will likely reduce the amount of bridging needed to route all logs to a single destination.

Configurable Logging with Logback

For better control over logging a standard module called bootique-logback is available, that integrates Logback framework in the app. It seamlessly bridges SLF4J (so you keep using SLF4J in the code), and allows to configure logging via YAML config file, including appenders (file, console, etc.) and per class/package log levels. Just like any other module, bootique-logback can be enabled by simply adding it to the pom.xml dependencies, assuming autoLoadModules() is in effect:

<dependency>
	<groupId>io.bootique.logback</groupId>
	<artifactId>bootique-logback</artifactId>
</dependency>

See bootique-logback module documentation for further details.

BootLogger

To perform logging during startup, before DI environment is available and YAML configuration is processed, Bootique uses a special service called BootLogger, that is not dependent on SLF4J and is not automatically bridged to Logback. It provides an abstraction for writing to stdout / stderr, as well as conditional "trace" logs sent to stderr. To enable Bootique trace logs, start the app with -Dbq.trace as described in the deployment section.

BootLogger is injectable, in case your own code needs to use it. If the default BootLogger behavior is not satisfactory, it can be overridden right in the main(..) method, as unlike other services, you may need to change it before DI is available:

public static void main(String[] args) {
   Bootique.app(args).bootLogger(new MyBootLogger()).run();
}

Part III. Testing

Chapter 13. Bootique and Testing

Bootique is uniquely suitable to be used as a test framework. Within a single test it allows you to start and stop multiple embedded stacks with distinct set of modules and distinct YAML configurations, making it a powerful tool for integration testing. Bootique core module and some other modules provide companion test extensions that contain reusable test stacks.

Chapter 14. Creating Test Stacks

To use basic Bootique test framework, import the following module in the "test" scope:

<dependency>
	<groupId>io.bootique</groupId>
	<artifactId>bootique-test</artifactId>
	<scope>test</scope>
</dependency>

For module-specific "companion" test frameworks (e.g. bootique-jetty-test), check documentation of those modules or GitHub.

While there are a number of built-in and custom stacks that you can create, they usually fall into two broad categories - "foreground" - those that are running in the main test thread, and "background" - those that are running in an isolated thread pool (usually network services like Jetty). To create a foreground stack, use BQTestFactory, annotated with @Rule or @ClassRule:

public class ForegroundTest {
	
	@Rule
	public BQTestFactory testFactory = new BQTestFactory();

	@Test
	public void testXyz() {
		BQTestRuntime runtime = testFactory.app("--help").createRuntime();
		...
	}
}

As you see, the test class declares a factory, and test methods can create BQTestRuntime instances with different command-line arguments, including commands (--help in this example), configuration file ("--config=some.yml"), etc. So your test runtime will behave just like a real Java app and will allow to verify various scenarios.

If a stack requires a thread pool to run (e.g. we are starting a webserver), it needs to be started with BQDaemonTestFactory instead of BQTestFactory:

public class BackgroundTest {
	
	@Rule
	public BQDaemonTestFactory testFactory = new BQDaemonTestFactory();
	
	@Test
	public void testBackground() {
		BQDaemonTestRuntime runtime = testFactory.app("--server").start();
		... 
	}
}

You don't need to stop it explicitly. BQDaemonTestFactory will take care of it via JUnit lifecycle.

The next thing you may want to do is to add various modules to the basic stack (either foreground or background). testFactory.app() returns a builder object that allows loading extra modules to add or override runtime services. This API is designed to mimic Bootique class, so that your tests look similar to actual applications:

@Test
public void testAbc() {
	
	testFactory.app("--help")
		// ensure all classpath modules are included
		.autoLoadModules()
		// add an adhoc module specific to the test
		.module((binder) -> {
		   binder.bind(MyService.class).to(MyServiceImpl.class);
		})
		.createRuntime();
	... 
}

In real test suites creating configurators for every test becomes unwieldy rather quickly. So consider subclassing BQDaemonTestFactory or BQTestFactory to build reusable configurators for your entire app.

Chapter 15. Common Test Scenarios

Now that we can start stacks on foreground or background, we can finally write some tests. Some things that can be tested include runtime services with real dependencies, standard output of full Bootique applications (i.e. the stuff that would be printed to console if this were a real app), network services using real network connections (e.g. your REST API's), and so on. Some examples are given below, outlining common techniques.

Testing Services that are Part of Bootique Runtime

Services can be obtained from test runtime, their methods called, and assertions made about the results of the call:

@Test
public void testService() {

	BQTestRuntime runtime = testFactory.app("--config=src/test/resources/my.yml").createRuntime();

	MyService service = runtime.getRuntime().getInstance(MyService.class);
	assertEquals("xyz", service.someMethod());
}

Testing Network Services

If a test stack is started on the background, and if runs a web server (like bootique-jetty-test) or some other network service, it can be accessed via a URL. E.g.:

@Test
public void testServer() {

	BQDaemonTestRuntime runtime = testFactory.app("--server").start();

	// using JAX-RS client API
	WebTarget base = ClientBuilder.newClient().target("http://localhost:8080/");
	Response r1 = base.path("/somepath").request().get();
	assertEquals(Status.OK.getStatusCode(), r1.getStatus());
	assertEquals("{}", r1.readEntity(String.class));
}

Testing Commands

You can emulate a real app execution in a unit test, by running a command and then checking the values of the exist code and stdin and stderr contents:

@Test
public void testCommand() {
	BQTestRuntime runtime = testFactory.app("--help").createRuntime();
	CommandOutcome outcome = runtime.run();

	assertEquals(0, outcome.getExitCode());

	String help = runtime.getStdout();
	assertTrue(help.contains("--help"));
	assertTrue(help.contains("--config"));
}

Testing Module Validity

When you are writing your own modules, you may want to check that they are configured properly for autoloading (i.e. META-INF/services/io.bootique.BQModuleProvider is present in the expected place and contains the right provider. There's a helper class to check for it:

@Test
public void testPresentInJar() {
	BQModuleProviderChecker.testPresentInJar(MyModuleProvider.class);
}

Part IV. Assembly and Deployment

Chapter 16. Runnable Jar

To build a runnable jar, Bootique relies on maven-shade-plugin. To simplfy its configuration, your app pom.xml may inherit from bootique-parent pom. In this case configuration would look like this:

<parent>
    <groupId>io.bootique.parent</groupId>
    <artifactId>bootique-parent</artifactId>
    <version>0.12</version>
</parent>

...
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
        </plugin>
    </plugins>
</build>

This configuration will build an app with the framework-provided main class, namely io.bootique.Bootique. If you want to use a custom main class (and in most cases you do), you will need to redefine Maven main.class property:

<properties>
    <main.class>com.foo.Application</main.class>
</properties>

If you want to avoid inheriting from the framework parent pom, you will need to explicitly provide the following unwieldy configuration similar to the one found in bootique-parent:

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-shade-plugin</artifactId>
	<version>2.4.2</version>

	<configuration>
		<createDependencyReducedPom>true</createDependencyReducedPom>
		<filters>
			<filter>
				<artifact>*:*</artifact>
				<excludes>
					<exclude>META-INF/*.SF</exclude>
					<exclude>META-INF/*.DSA</exclude>
					<exclude>META-INF/*.RSA</exclude>
				</excludes>
			</filter>
		</filters>
	</configuration>
	<executions>
		<execution>
			<phase>package</phase>
			<goals>
				<goal>shade</goal>
			</goals>
			<configuration>
				<transformers>
					<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
					<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
						<mainClass>${main.class}</mainClass>
					</transformer>
				</transformers>
			</configuration>
		</execution>
	</executions>
</plugin>

Either way, once your pom is configured, you can assemble and run your jar. E.g.:

mvn clean package
java -jar target/myapp-1.0.jar

Chapter 17. Tracing Bootique Startup

To see what modules are loaded and to trace other events that happen on startup, run your jar with -Dbq.trace option. E.g.:

java -Dbq.trace -jar target/myapp-1.0.jar --server

You may see an output like this:

Skipping module 'JerseyModule' provided by 'JerseyModuleProvider' (already provided by 'Bootique')...
Adding module 'BQCoreModule' provided by 'Bootique'...
Adding module 'JerseyModule' provided by 'Bootique'...
Adding module 'JettyModule' provided by 'JettyModuleProvider'...
Adding module 'LogbackModule' provided by 'LogbackModuleProvider'...