Quarkus Command mode

In one of DevNation meetups, Max Andersen made a presentation of new Quarkus feature called Command mode, thanks to him I made this article.

Quarkus 1.4.0.CR1 brought to us a really interesting feature. In this article, you’ll see how you can manipulate your Quarkus app via the command line and start quarkus from main method. If you’ve ever used Laravel, for example, you know that it has really great support for the command line.

Quarkus Command mode gives you the ability to write an application(or part of it) that starts, does some work, and then exits.

For better copy-paste experience please open this article on Quarkify

Setting up project

Command mode already included in Quarkus 1.4.0.Final. You can either create a new app or use our empty git project. At the time of writing Quarkus 1.4.0 is yet in the process of publication, but you can use http://che.openshift.io/ to use it without the need to build quarkus project itself.

Let’s clone quarkus-command-mode project:

git clone --branch clean https://github.com/quarkifynet/quarkus-command-mode.git

This is our famous Greeting example, already on 1.4.0.Final . Beware that it won't work any version below 1.4.0. Look around the project, you can see GreetingResource and GreetingService . Let's add a new class at src/main/java/org.../started/GreetingApplication.java and make it so it implements QuarkusApplication, like this:

@QuarkusMain

public class GreetingApplication implements QuarkusApplication {

}

Now you’ll be prompted to implement method from QuarkusApplication called run , let's do it.

@Override

public int run(String... args) throws Exception {

return 0;

}

Nice, now it’s ready to be used, but we don’t have any interesting use-cases, let’s add some useful commands to it, here’s the final example of what it looks like

Once it’s there, you can execute it via command line:

./mvnw compile quarkus:dev -Dquarkus.args="--help"

Adding compile to our usual maven command will enable command mode, and for the output, you'll see usual console output, but with one additional line.

2020-04-23 23:24:34,949 INFO [io.quarkus] (Quarkus Main Thread) getting-started 1.0-SNAPSHOT (powered by Quarkus 1.4.0.Final) started in 0.902s. Listening on: http://0.0.0.0:8080

2020-04-23 23:24:34,966 INFO [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.

2020-04-23 23:24:34,966 INFO [io.quarkus] (Quarkus Main Thread) Installed features: [cdi, resteasy]

2020-04-23 23:24:34,967 INFO [org.acm.get.sta.GreetingApplication] (Quarkus Main Thread)

This tool helps greet users by their name

Available commands:

--help Show helpful message

--greet={name} Greet person by their name

2020-04-23 23:24:34,994 INFO [io.quarkus] (Quarkus Main Thread) getting-started stopped in 0.026s

Quarkus application exited with code 0

Press Enter to restart or Ctrl + C to quit

This is our help output. If you’ll specify -Dquarkus.args=”-greet=Quarkify”, you’ll get greeting for us.

./mvnw compile quarkus:dev -Dquarkus.args="--greet=Quarkify"

# ... ommited lines

# 2020-04-23 23:01:36,092 INFO [org.acm.get.sta.GreetingApplication] (Quarkus Main Thread) hello Quarkify

Notice, that app terminated at the end with a phrase Press Enter to restart or Ctrl + C to quit . This is the main magic of command mode. You can hit enter and your app will repeat the same cycle, it's useful when you change something in the code and then hit enter to see new result.

Injecting beans

But wait, have you noticed that we haven’t used our GreetingService . This is his main purpose to be decoupled from any view logic so that we can use it either from our resource or command line, let's fix it. Firstly, let's inject our GreetingService into GreetingApplication . Notice that Our @QuarkusMain automatically makes class scoped, so we don't need to add @ApplicationScoped .

@QuarkusMain

public class GreetingApplication implements QuarkusApplication {

@Inject

GreetingService greetingService;

...

}

Now, in a place where we manually wrote LOGGER.info("hello" + name); let's replace it with injected service:

if (args[0].startsWith("--greet")) {

String name = args[0].substring(8);

LOGGER.info(greetingService.greeting(name));

return 0;

}

Here’s full class, you can call git checkout master to get all the steps above.

Now everything works correctly, you can change greeting in one single place called GrettingService , and this will affect both web resources and the command line! There's only a single bonus left.

Start app from IDE

If you’re Intellij IDEA user, you might be annoyed that you need to use the command line or maven command to start Quarkus. With Command mode this frustration also goes away. You can again use git checkout feature/ide to get code for this section.

Firstly, let’s say to our run function if there are no arguments, we don't want to terminate Quarkus, this can be done with Quarkus.waitForExit(); .

public int run(String... args) throws Exception {

if(args.length == 0) {

Quarkus.waitForExit();

return 0;

}

...

Now, with this we can create pleasant main function and call Quarkus.run()

public static void main(String[] args) {

Quarkus.run(GreetingApplication.class);

}

As always, here’s complete example for our class:

Done, you can use your favorite IDE to start Quarkus without the command line.

Conclusion

Command mode can give you more flexibility and freedom. You can not only call simple GreetingService, but query users from the database, do migrations, run some jobs, do some complex logic with few attributes that could take more time otherwise, and even automate some things.

One great direction that you can take is to look at Piccoli. This can help you build a beautiful command-line interface or parsing and structuring commands(like in Laravel, for example). Moreover, you can use command mode in the native images as well.