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. The Getting Started article in the 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 --hello Hello, 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.yml I 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 build 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's 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 the Bootique platform — its combination of an extensible core with ready-to-use modules. It allows you to get started quickly and with a 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 scalable, modular design and supports full customization of the stack. Even the 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 the liberal Apache license and we encourage anyone interested in container-less Java to show up on GitHub and take part in Bootique's trial and improvement.