Beyond PicoCLI

Pavel Vlasov
Nasdanika
Published in
4 min readMay 13, 2024

--

Table of Contents

· Overview of PicoCLI
· Nasdanika extensions
Wiring
Documentation
Serving HTTP
Packaging
· Roadmap

Overview of PicoCLI

PicoCLI is an absolutely fantastic piece of software which allows create powerful command line applications! With PicoCLI developer can focus on the “business” logic of the application and PicoCLI takes care of parsing the command line, injecting parameters and options, and calling the application. It also has very detailed documentation!

Recently I’ve revived a module which extends PicoCLI functionality and added a couple more CLI-related modules. This story briefly outlines the core PicoCLI functionality and explains how Nasdanika modules extend it.

Picocli logical model

The diagram above shows a high-level logical domain model of PicoCLI. There is no Contributor type in PicoCLI — it is a logical abstraction which both command and mix-in extend.

So, a command can have positional parameters, options, sub-commands, mix-ins and argument groups. All these things can be added to the command declaratively using annotation or programmatically by calling methods. A mix-in also can have all of the above, I’m not sure whether mix-ins can be cascaded, i.e. a mix-in can define another mix-in.

There an API for usage help.

With PicoCLI command hierarchies can be constructed declaratively in a top-down fashion — a parent command declares types of its subcommands.

There is a number of ways how PicoCLI applications can be packaged — executable jars, fat jars, GraalVM Native images, …

Nasdanika extensions

Although PicoCLI is great as it is, I needed more. I wanted:

  • To be able to construct command hierarchies and contribute mix-ins bottom-up with the parent command being unaware of the child commands and child commands residing possibly in different modules. In other words, I wanted to be able to construct command hierarchies by just adding dependencies to pom.xml. This requirement was inspired by OSGi declarative services. As a matter of fact, the first implementation many years ago was OSGi-based. OSGi turned out to be too much trouble to work with, so I switched to Java modules and created the capability framework, which is more generic than declarative services.
  • To have CLI-polymorphism — to be able to replace a “base” sub-command or mix-in with more advanced one.
  • Generate HTML documentation for command hierarchies with ability to add detailed documentation to generated usage help. I wanted to be able to generate stand-alone documentation and to be able to integrate documentation into.
  • Have a simple way to serve HTTP requests.

The below sections provide high-level descriptions of Nasdanika PicoCLI extensions, consult Nasdanika documentation and sources for addition information.

Wiring

By wiring I mean adding sub-command and mix-ins to commands.

In the core PicoCLI wiring is implemented with @Command.subcommands, @Mixins, and @ArgGroup annotations.

Nasdanika adds the following annotations for wiring:

  • @Parent — a child command or MixIn can define types of parent commands they want to be mounted to. Types are matched using instanceof — so the parent command may be of the declared type of sub-type.
  • @SubCommands — a parent command declares types of sub-command. Unlike @Command.subcommands, types listed in @SubCommands are not instantiated — they are used for type matching.
  • @MixIns is similar to @SubCommands — a parent command may declared types of mix-ins to match and add.

Overrider interface and @Overrides annotation allow to select one of sub-commands or mix-ins with several with the same name. By default a more specific sub-command or mix-in is selected. More specific means that the class of the more specific command is a sub-class of the more generic.

See the CLI Module Documentation for additional details

Documentation

@Description annotation allows to add markdown documentation, icon, and tooltip to generated usage. Markdown can contain PlantUML diagrams — no examples yet.

Documentation can be generated as one long plain text or HTML as explained in the help command documentation.

— action-model option outputs help to an action model which can be mounted to larger sites, which the above help command is an example of.

The help command also demonstrates bottom-up assembly of mix-ins ( — action-model) and sub-commands (site).

The site sub-command mentioned above generated a static HTML documentation site.

Serving HTTP

HTTP Module allows to declaratively contribute routes to a server and start an HTTP server using a command.

Packaging

Because Nasdanika CLI uses Java services it needs to be a modular application. At the same time some of dependencies such as Flexmark have to be in the classpath. Fat jars do not support modules, jpackage does not support classpath. So I had to build my own packager. It downloads dependencies and then builds a Java command putting some of the to the module path and some others to the class path. See CLI Module Documentation for details.

Roadmap

Going forward I’m planning to add more commands to Nasdanika CLI. For example, a command to generate architecture documentation from Drawio diagrams similar to Internet Banking System Architecture documentation.

One in my opinion interesting opportunity is to implement RAG on top of generated documentation. It would allow to have a large corpus of fine-grained commands and quickly find needed information.

--

--

Pavel Vlasov
Nasdanika

I'm a principal engineer at U.S. Bank at day and an Open Source enthusiast at my free time focusing on productivity capabilities for "Java Guerilla Innovators"