DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
11 Monitoring and Observability Tools for 2023
Learn more
  1. DZone
  2. Coding
  3. Java
  4. Auto-Updatable, Self-Contained CLI With Java 11

Auto-Updatable, Self-Contained CLI With Java 11

Want to learn how to implement an auto-updatable, self-contained CLI in Java 11? Check out this tutorial to learn how to use CLI with the upcoming Java release.

Uday Tatiraju user avatar by
Uday Tatiraju
·
Aug. 15, 18 · Tutorial
Like (3)
Save
Tweet
Share
6.20K Views

Join the DZone community and get the full member experience.

Join For Free

Introduction

Over the course of the last 11 months, we have seen two major releases of Java — Java 9 and Java 10. Come September, we will get yet another release — Java 11. This is all thanks to the new 6-month release train. Each new release introduces exciting features to assist the modern Java developer. Let’s take some of these features for a spin and build an auto-updatable, self-contained command line interface.

The minimum viable feature-set for our CLI is defined as follows:

  • Display the current bitcoin price index by calling the free coin desk API
  • Check for new updates and, if available, auto update the CLI
  • Ship the CLI with a custom Java runtime image to make it self-contained

Prerequisites

To follow along, you will need a copy of the JDK 11 early-access build. You will also need the latest version (4.9 at time of writing) of Gradle. Of course, you can use your preferred way of building Java applications. Though not required, familiarity with JPMS and JLink can be helpful, since we are going to use the module system to build a custom runtime image.

Let's Get Started

We begin by creating a class that provides the latest bitcoin price index. Internally, it reads a configuration file to get the URL of the coin desk REST API and builds an HTTP client to retrieve the latest price. This class makes use of the new fluent HTTP client classes that are part of “java.net.http” module.

var bpiRequest = HttpRequest.newBuilder()
        .uri(new URI(config.getProperty("bpiURL")))
        .GET()
        .build();

var bpiApiClient = HttpClient.newHttpClient();

bpiApiClient
    .sendAsync(bpiRequest,
        HttpResponse.BodyHandlers.ofString())
    .thenApply(response -> toJson(response))
    .thenApply(bpiJson -> 
        bpiJson.getJsonObject("usd").getString("rate"));


Per Java standards, this code is actually very concise. We used the new fluent builders to create a GET request, call the API, convert the response into JSON, and pull the current bitcoin price in USD currency.

In order to build a modular jar and set us up to use “jlink,” we need to add a “module-info.java” file to specify the CLI’s dependencies on other modules.

module ud.bpi.cli {
    requires java.net.http;
    requires org.glassfish.java.json;
}

From the code snippet, we observe that our CLI module requires the http module shipped in Java 11 and an external JSON library.


Now, let’s turn our attention to implement an auto-updater class. This class should provide a couple of methods. One method to talk to a central repository and check for the availability of newer versions of the CLI and another method to download the latest version. The following snippet shows how easy it is to use the new HTTP client interfaces to download remote files.

CompletableFuture<Boolean> update(String downloadToFile) {
    try {
        HttpRequest request = HttpRequest.newBuilder()
                .uri(new URI("http://localhost:8080/2.zip"))
                .GET()
                .build();

        return HttpClient.newHttpClient()
                .sendAsync(request, HttpResponse.BodyHandlers
                        .ofFile(Paths.get(downloadToFile)))
                .thenApply(response -> {
                    unzip(response.body());
                    return true;
                });

    } catch (URISyntaxException ex) {
        return CompletableFuture.failedFuture(ex);
    }
}

The new predefined HTTP body handlers in Java 11 can convert a response body into common high-level Java objects. We used the HttpResponse.BodyHandlers.ofFile() method to download a zip file that contains the latest version of our CLI.

Let’s put these classes together by using a launcher class. It provides an entry point to our CLI and implements the application flow. Right when the application starts, this class calls its launch() method that will check for new updates.


void launch() {
    var autoUpdater = new AutoUpdater();

    try {

        if (autoUpdater.check().get()) {
            System.exit(autoUpdater.update().get() ? 100 : -1);
        }

    } catch (InterruptedException | ExecutionException ex) {
        throw new RuntimeException(ex);
    }
}

As you can see, if a new version of the CLI is available, we download the new version and exit the JVM by passing in a custom exit code 100. A simple wrapper script will check for this exit code and rerun the CLI.


#!/bin/sh
...
start
EXIT_STATUS=$?

if [ ${EXIT_STATUS} -eq 100 ]; then
    start
fi


And finally, we will use “jlink” to create a runtime image that includes all the necessary pieces to execute our CLI. jlink is a new command line tool provided by Java that will look at the options passed to it to assemble and optimize a set of modules and their dependencies into a custom runtime image. In the process, it builds a custom JRE — thereby making our CLI self-contained.


jlink --module-path build/libs/:${JAVA_HOME}/jmods \
      --add-modules ud.bpi.cli,org.glassfish.java.json \
      --launcher bpi=ud.bpi.cli/ud.bpi.cli.Launcher \
      --output images

Let’s look at the options that we passed to jlink:

  • “ module-path” tells jlink to look into the specified folders that contain java modules
  • “ add-modules” tells jlink which user-defined modules are to be included in the custom image
  • “launcher” is used to specify the name of the script that will be used to start our CLI and the full path to the class that contains the main method of the application
  • “output” is used to specify the folder name that holds the newly created self-contained custom image

When we run our first version of the CLI and there are no updates available, the CLI prints something like this:








Say we release a new version (2) of the CLI and push it to the central repo. Now, when you rerun the CLI, you will see something like this:


Voila! The application sees that a new version is available and auto-updates itself. It then restarts the CLI. As you can see, the new version adds an up/down arrow indicator to let the user know how well the bitcoin price index is doing.

Head over to GitHub to grab the source code and experiment with it.

Command-line interface Java (programming language)

Published at DZone with permission of Uday Tatiraju, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • The 5 Books You Absolutely Must Read as an Engineering Manager
  • The Power of Zero-Knowledge Proofs: Exploring the New ConsenSys zkEVM
  • Key Elements of Site Reliability Engineering (SRE)
  • Top 10 Best Practices for Web Application Testing

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: