What's New in Bootique: Custom Shell Vars and Kotlin Integration
What's New in Bootique: Custom Shell Vars and Kotlin Integration
Bootique is stronger than ever, with improvements to their modularity engine, huge new customizations for apps' CLIs, and Kotlin support.
Join the DZone community and get the full member experience.Join For Free
Take 60 minutes to understand the Power of the Actor Model with "Designing Reactive Systems: The Role Of Actors In Distributed Architecture". Brought to you in partnership with Lightbend.
We just posted another consequential release of Bootique — version 0.22. The focus of the changes in the core was to make module customization more transparent and give more power to the users to shape the CLI (command-line interfaces) of their apps. There was a significant improvement in handling of the test data sets in bootique-jdbc-test. And, as has become a tradition lately, we’ve added 4 new top-level Bootique projects, providing integration facilities for JBoss Undertow, RabbitMQ, Apache Shiro, and also Kotlin!
Let’s start with the core changes…
The appeal of the Bootique modularity engine is in the ability to "contribute" objects to maps and collections defined in upstream modules. So a developer can define modules with certain reusable strategies, and then each app can simply provide objects that feed those strategies (think commands or servlets, or REST resources — Bootique standard modules have a lot of examples).
This is all great, except the "contribution" API was 80% Google Guice, and resulted in long and fairly confusing code. E.g.:
JettyModule.contributeServlets(binder) .addBinding() .to(MyServlet.class);
This code is too long for no good reason, other than it being a generic Guice binding API. By the time we get to the end of the call chain, we may forget what exactly we were contributing here. In 0.22 each Module that takes contributions implements a special "extender" class that is obtained via a static method:
In addition to being shorter, this API has other advantages. First, it is chainable, so you can add multiple extensions at once. Second, method names clearly indicate what this extension is about. And finally unlike generic binding API that may take either a type or an instance or a Guice
TypeLiteral or a Guice
Key, we restrict the types of arguments to those that make sense for a given extension.
The end result is more readable code and Modules that are easier to understand for new users. I encourage you to take a look at the API of the extenders of the standard Modules you already have in your apps, and start using extenders in your own Modules instead of "contribute*" methods.
Custom Shell Vars
This feature came from the realization that naming conventions for configuration shell variables gets ugly pretty fast. For example, I have an app called TaskExtractor that connects to various GitHub repositories to build a report on the tasks that I worked on within a month. To access GitHub it needs a password that happens to map to this configuration variable:
BQ_JERSEYCLIENT_AUTH_GITHUB_PASSWORD. Seriously? Who’s going to remember that? Wouldn’t it be better to define an app-specific shell variable, say
TE_GITHUB_PASSWORD. It makes sense in the context of the app and is more or less readable. Now you can do that easily, using the extender for BQCoreModule:
BQCoreModule.extend(binder).declareVar( "jerseyclient.auth.github.password", "TE_GITHUB_PASSWORD");
But there is a problem. If previously a savvy user could have figured out the name of this variable no matter however long it was by looking at the configuration structure (e.g. by running the app with
-H), the new variable name
TE_GITHUB_PASSWORD is completely arbitrary and there’s no way to find it. Except there is — “declared” variables are automatically added to the new
ENVIRONMENT section in the help (remember, help is printed in response to
$ java -jar task-extractor.jar -h NAME ... OPTIONS ... ENVIRONMENT TE_GITHUB_PASSWORD Password. A part of the application credentials to obtain oauth token.
Though we didn’t specify any description, Bootique was able to provide a description attached to the configuration property updated via the variable (see my previous blog post on how this works).
As somebody who has to run lots of Java apps with no documentation, I think this is pretty neat!
In the next release, we are also planning to tie configuration paths to option switches to give you even more control over your app’s public CLI.
Tests Data Sets
As one of the main authors of Apache Cayenne ORM, I am ashamed to admit that historically I haven’t done much unit testing of my code that involved persistence. That’s until the emergence of Bootique last year. Still, Bootique data test facilities (CayenneTestDataManager, Table, etc.) were pretty basic initially. They allowed to create a schema, do simple inserts/deletes, and check for single values. Real business scenarios (e.g. calculating team standings for a sports league, something I had to deal with just last week) was nearly impossible to emulate.
In 0.22, the Table class from bootique-jdbc-test allows you to load per-table data sets from
.csv files. Here is an example that uses both
bootique-cayenne-test (those 2 are designed to work well together) :
CayenneTestDataManager .createTableModel(TEST_RUNTIME, MyEntity1.class) .insertFromCsv("classpath:myentity1.csv");
The reverse operation of comparing a reference data set with the actual DB data is also available:
CayenneTestDataManager .createTableModel(TEST_RUNTIME, MyEntity2.class) .contentsMatchCsv("classpath:myentity2-expected.csv");
As a result, data set maintenance can now be done in a spreadsheet instead of Java code, which scales pretty well.
Finally, let’s take a look at the new integrations. Many are somewhat experimental in this release, so let’s experiment with them together.
First, there’s bootique-rabbitmq-client, which allows us to configure a client to connect to RabbitMQ and send/receive messages.
Second, there’s bootique-shiro, integrating with Apache Shiro security framework. It splits Shiro traditional
.ini configs into one part managed via dependency injection and another part managed by the Bootique configuration engine (meaning you place it in YAML). The end result is much easier to manage. Your security rules are no longer intertwined with Java classes for various strategies, Realms can be contributed via extenders, etc.
There’s also bootique-undertow, integrating Jboss Undertow webserver. While
bootique-jetty is great and is still the primary “web server” in Bootique, we are looking to explore the Undertow promise of a servlet-free lightweight web server.
Finally, there’s bootique-kotlin. Yes, it helps you to write Bootique modules in the Kotlin language. I am not a Kotlin user myself (yet), but what I was shown is promising. For example, config files can now be implemented in Kotlin Script (.kts) as an alternative to YAML/JSON, with full IDE support, such as autocomplete for properties.
In the following releases, we will be building Kotlin integrations for all the standard Bootique modules. Currently, Jetty and Logback are supported.
Published at DZone with permission of Andrus Adamchik , DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.