Bootique: a Minimally Opinionated Platform for Modern Java Apps

Andrus Adamchik
4 min readFeb 17, 2016

--

Bootique improves on the prior art of container-less app launchers. It allows you to write Java apps that look like commands and are made of modules.

We created Bootique for developers who want to reclaim their main() method and liberate their apps from the Java container. Bootique is a great fit for microservices as well as traditional larger multi-functional apps. Both kinds will look like simple UNIX commands at the end.

Bootique core provides just a few simple services: processing command line, reading application config YAML and dispatching control to a Command. There are a few built-in commands, so even an app that doesn’t have much code and does not import any modules can be executed. E.g. try running this one-liner:

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

It will print the following:

NAME
my.jar

OPTIONS
-c yaml_location, --config=yaml_location
Specifies YAML config location, which can be a file path
or a URL.

--help
Prints this message.

Note that I omitted dependencies and directions for building a runnable .jar. “Getting Started” article in Bootique docs provides step-by-step instructions for a typical app.

Hello, World!

Now let’s write our own command pretending it does something useful:

public class HelloCommand implements Command {   @Override
public CommandOutcome run(Cli cli) {
System.out.println("Hello, world!");
return CommandOutcome.succeeded();
}
}

How do you add this command to Bootique? A Bootique is composed of Guice Modules, so let’s turn our app into a Module and add it to Bootique:

public class Application implements Module {   public static void main(String[] args) {
Bootique.app(args).module(Application.class).run();
}
@Override
public void configure(Binder binder) {
// BQCoreModule provides access to
// Guice Multibinder for commands
BQCoreModule.contributeCommands(binder)
.addBinding()
.to(HelloCommand.class);
}
}

Now rerun the app and check that a new option appeared on the list:

NAME
my.jar

OPTIONS
-c yaml_location, --config=yaml_location
Specifies YAML config location, which can be a file path
or a URL.

--hello

--help
Prints this message.

As you may have guessed, now you can run the app with “dash dash hello”, and will see our command executed:

java -jar my.jar --helloHello, world!

There’s actually another way to add commands to Bootique via Commands class, but let’s not overload our basic example with too many APIs.

Hello, YAML!

How do you write more complex commands? Two words: injection and configuration. I’ll show the examples of both. By default configuration is read from YAML (you’ve seen “config” option printed when you ran the app above). Pieces of configuration can be turned into custom Java beans. Let’s create such a bean:

public class MyConfig {
private String property1;
private String property2;
// setters/getters follow
// ...
}

And now create “my.yml” with compatible config (using “my” prefix to distinguish our “subconfig” from other configurations that may be present in this file):

my:
property1: p1
property2: p2

Now let’s read the config. For this we need to inject ConfigurationFactory into the command class:

public class HelloCommand implements Command {    // injecting configFactory as Guice Provider
// ensures lazy initialization
@Inject
private Provider<ConfigurationFactory> configFactory;
@Override
public CommandOutcome run(Cli cli) {
MyConfig config = configFactory.get()
.config(MyConfig.class, "my");
String message = String.format("I was started with (%s, %s)",
config.getProperty1(),
config.getProperty2());

System.out.println(message);
return CommandOutcome.succeeded();
}
}

Rerunning the app with config file:

java -jar my.jar --hello --config=my.ymlI was started with (p1, p2)

This way we can put all our configuration in one place in an expressive format, and the file can be easily substituted for each application run.

Modules

Very likely you won’t need to write most of the commands yourself. You can use any of the existing standard Modules (listed on the site), and use their commands. The easiest way to do it is to declare needed modules as your project dependencies, and turn on auto-loading:

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

This way you can quickly and declaratively builds REST apps, servlet apps, job runners, migration scripts, database apps, and what not.

Do I Have an Opinion?

I have many (often strong) opinions on how things should be programmed. But Bootique philosophy is to allow you to have your own. The only requirement for it is Guice. This part is not possible to swap out. The rest of Bootique is completely open to extension and customization. This includes command-line style, dispatch strategy, configuration format, etc.

Each module on the other hand is “opinionated”. E.g. bootique-jetty works with Jetty and will not work with Tomcat. But it doesn’t have to. If you want to embed Tomcat, you create a Tomcat integration module instead and do not use Jetty.

This is the main advantage of Bootique platform - its combination of extensible core with ready-to-use modules. It allows you to get started quickly and with minimal amount of code by using standard modules, yet doesn’t limit what kind of app you can build or what technology you can use. It encourages scaleable modular design and supports full customization of the stack. Even logging framework is your choice!

Come Together

Bootique is still a young technology, though already used in production for complex enterprise services. We are currently working on a new project site at bootique.io and writing documentation. The framework and extensions are open source under liberal Apache license and we encourage anyone interested in container-less Java to show up on GitHub and take part in Bootique trial and improvement.

--

--

Andrus Adamchik

ObjectStyle ; Open Source: ApacheCayenne, Agrest, Bootique, and more…