How to list Maven local artifacts using JBang

Listing the available artifacts in your Maven local repository generally requires a Maven project. In this article we will show how to create a simple JBang script to list all dependencies for an artifact in your local Maven repository.

Firstly, install JBang in your environment:

curl -Ls https://sh.jbang.dev | bash -s - app setup

Then, if you are new to JBang, I’d recommend checking this article to have a basic exposure of it: JBang: Create Java scripts like a pro

In the next section, we will show to develop a simple Java script that uses the Pico CLI to connect the parameters you need for your script.

Coding the Script

Firstly, it is good to know that you can init a JBang script using one of the available templates. for example, the “cli” template will help you to kick start a script which uses the picocli commandline:

jbang init --template=cli mvnversion.java

Then, let’s add the following script with the name “mvnversion.java”:

///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS info.picocli:picocli:4.6.3


import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Parameters;

import java.util.concurrent.Callable;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Command(name = "mvnversion", mixinStandardHelpOptions = true, version = "mvnversion 0.1",
    description = "mvnversion made with jbang")
class mvnversion implements Callable < Integer > {


    @CommandLine.Option(
        names = {
            "-g",
            "--groupId"
        },
        description = "The groupId",
        required = true)
    private String groupId;

    @CommandLine.Option(
        names = {
            "-a",
            "--artifactId"
        },
        description = "The artifactId",
        required = true)
    private String artifactId;

    public static void main(String...args) throws Exception {
        int exitCode = new CommandLine(new mvnversion()).execute(args);
        System.exit(exitCode);
    }


    @Override
    public Integer call() throws Exception {

        File localRepo = new File(System.getProperty("user.home"), ".m2/repository");
        if (!localRepo.exists()) {
            System.err.println("Local repository not found at " + localRepo);
            return 1;
        }

        Pattern pattern = Pattern.compile(artifactId + "-(\\d+\\.\\d+\\.\\d+.*?)(\\.jar|\\.pom)");
        List < String > versions = new ArrayList < > ();
        scan(localRepo, pattern, versions);

        Collections.sort(versions, new VersionComparator());

        System.out.println("The following versions of " + groupId + ":" + artifactId + " files were found in your local Maven repository:");
        for (String version: versions) {
            System.out.println("  - " + version);
        }
        return 0;
    }

    private static void scan(File file, Pattern pattern, List < String > versions) throws IOException {
        if (file.isDirectory()) {
            for (File child: file.listFiles()) {
                scan(child, pattern, versions);
            }
        } else {
            Matcher matcher = pattern.matcher(file.getName());
            if (matcher.matches()) {
                versions.add(matcher.group(1));
            }
        }
    }

    static class VersionComparator implements java.util.Comparator < String > {
        public int compare(String v1, String v2) {
            String[] v1parts = v1.split("\\.");
            String[] v2parts = v2.split("\\.");

            int majorCompare = Integer.compare(Integer.parseInt(v1parts[0]), Integer.parseInt(v2parts[0]));
            if (majorCompare != 0) {
                return majorCompare;
            }

            int minorCompare = Integer.compare(Integer.parseInt(v1parts[1]), Integer.parseInt(v2parts[1]));
            if (minorCompare != 0) {
                return minorCompare;
            }

            int patchCompare = Integer.compare(Integer.parseInt(v1parts[2]), Integer.parseInt(v2parts[2]));
            return patchCompare;
        }
    }
}

Some words about the annotations:

  • The @Command annotation define a class as a command, which can be executed from the command line.
  • The @CommandLine.Option annotation defines the command line options. Options are command line arguments that start with a – or — and are used to configure the behavior of the command.
  • When you use the @CommandLine.Option annotation on a field, picocli will automatically parse the command line arguments and set the value of that field based on the arguments passed to the program.

Finally, please note that this script also includes a VersionComparator Class in it. This adds a bit of verbosity to the script, however it allows to sort the artifacts by major.minor.patch version. By using the default Comparator mechanism the output was quite messy.

You can run the script as follows:

jbang mvnversion.java -a quarkus-bom -g io.quarkus

Here is the list of versions for the Quarkus Maven BOM in my local Maven repository:

The following versions of io.quarkus:quarkus-bom files were found in your local Maven repository:

  - 2.11.2.Final
  - 2.12.0.Final
  - 2.12.0.Final
  - 2.12.2.Final
  - 2.15.0.Final

Adding an alias for your Script

To make simpler the execution of the script, you can add a Linux alias for it. Otherwise, you can also add a jbang alias. For example, I will store in my local jbang repository a reference to the script available on GitHub:

jbang alias add --name mvnversion https://github.com/fmarchioni/mastertheboss/blob/master/jbang/mvnversion.java

With the alias in place, you can now run the script from anywhere in your filesystem with:

jbang mvnversion -a quarkus-bom -g io.quarkus

Conclusion

This article showed how to create a JBang CLI script with picocli libraries to find the maven versions of an artifact in your local Maven repository.