# JBang, the awesome scripting tool of the Java ecosystem
Yassine Benabbas | 15 Min To Read | 21 Jan, 2025 | #Development and programming
=> https://blog.worldline.tech/2025/01/21/jbang.html Article on Worldline Tech Blog

The Java ecosystem has already two powerful project management tools, namely Maven and Gradle, yet it lacked a simple and powerful scripting tool. This is where JBang comes in. It is a simple yet powerful Java, Kotlin and Groovy file runner. In fact, it allows you to run code as easily as you would run a script. It also provides many other features such as dependency management, templates, and an App Store. Let’s explore JBang and its features in this post.


## Setup
The `jbang` command-line can be installed on Windows, Linux, and macOS using different methods which are well documented here . We can verify the installation by running `jbang --version`.
=> https://www.jbang.dev/download/ well documented here


In addition to that, it’s preferable to install the accompanying IDE extension for our favorite IDE. The supported IDE extensions are listed here .
=> https://www.jbang.dev/documentation/guide/latest/editing.html#ide-support listed here


JBang itslef can be used as to install JDKs from adoptium.net which distributes the **Eclipse Temurin** JDKs. We can install a specific version with `jbang jdk install 23` which will install the **Eclipse Temurin JDK** 23.
=> https://adoptium.net/ adoptium.net


We are now ready to write our first scripts.


## First script
Let’s create a simple script that prints “Hello, World!” to the console.

```
> jbang init helloworld.java
```
This will create a file named `helloworld.java` that can be run with `jbang helloworld.java`.

```
> jbang helloworld.java
Hello world
```
When you open the file, you will see that it is a plain Java file with a `main` method and a particular first line.

```
///usr/bin/env jbang "$0" "$@" ; exit $?
import static java.lang.System.*;

public class helloworld {
   public static void main(String... args) {
       out.println("Hello world");
   }
}
```
As we will see, a JBang script is made up of three parts: the shebang, optional properties and the script itself. We’ll use some properties in the next sections but let’s focus first on the shebang.

This part `///usr/bin/env jbang "$0" "$@" ; exit $?` tells the system to use JBang to run the script. It is called a shebang in the Unix ecosystem and is used to specify the interpreter for the script. We can illustrate this on a Unix System (macOS, Linux) by running `chmod +x helloworld.java` to make the script executable and then running `./helloworld.java`.
=> https://en.wikipedia.org/wiki/Shebang_%28Unix%29 shebang


```
> chmod +x helloworld.java
> ./helloworld.java
Hello world
```

## Exporting scripts
In addition to running scripts, JBang can also build and export them into the usual JVM formats. This has the benefit of allowing the script to be run on machines that do not have JBang installed. The different available export formats are as follows:

* A jar file: jbang export portable helloworld.java. If your script uses dependencies, the next commands are more recommended.
* A fatjar: which contains all the dependencies: jbang export fatjar helloworld.java. This method still requires to install a JDK / JRE on the target machine.
The next commands are more recommended if you want the dependencies to be included.
* A jlink binary that compasses a JDK: jbang export jlink helloworld.java.
The binary to run is either helloworld-jlink/bin/helloworld on Unix or helloworld-jlink/bin/helloworld.bat on Windows.
* A native imgae: jbang export native helloworld.java. This requires a GraalVM installation.

The script can also be exported as a mavenrepo with: `jbang export mavenrepo helloworld.java`


## JDK management
As seen in a previous chapter, JBang can install JDKs on your machine. You can list the installed JDKs with `jbang jdk list`, list available ones to install with `jbang jdk list --available --show-details`, install a new one with `jbang jdk install [version]`. Jbang also supports the use of SDKMAN to manage JDKs on supported systems.
=> https://www.jbang.dev/documentation/guide/latest/javaversions.html#using-managed-jdks-yourself SDKMAN


Furthermore, it is possible to specify a JDK version in a script. This is done by adding the following line to the script properties: `//JAVA [version]` if we want an exact version or `//JAVA [version]+` if we want a minimum version. In that case JBang will automatically install the required JDK version and use it only for that script without changing the default JDK of the system.

For example, the following script uses Java 25 and some preview features.

```
///usr/bin/env jbang "$0" "$@" ; exit $?
//JAVA 25
//COMPILE_OPTIONS --enable-preview -source 25
//RUNTIME_OPTIONS --enable-preview

import java.util.concurrent.Callable;
import java.util.concurrent.StructuredTaskScope;

import static java.lang.System.*;

void main(String... args) {
 out.println("Hello Java 25");
 Callable<String> task1 = () -> {
   out.println("Task 1" + Thread.currentThread());
   return "Task 1";
 };

 Callable<Integer> task2 = () -> {
   out.println("Task 2" + Thread.currentThread());
   return 2;
 };

 try (
   var scope = new StructuredTaskScope<Object>()) {
   StructuredTaskScope.Subtask<String> subtask1 = scope.fork(task1);
   StructuredTaskScope.Subtask<Integer> subtask2 = scope.fork(task2);
   scope.join();
 } catch (Exception e) {
   e.printStackTrace();
 }
}
```

## Scripts without a “Main” class
Since scripts tend to be lightweight, it would be preferable to write them without a class and a main method. Fortunately, Java has a feature called implicit declared classes and instance main methods (which is still in preview in Java 23). This feature allows to write java programs and JBang scripts without a class and a static main method.
=> https://docs.oracle.com/en/java/javase/23/lnguage/implicitly-declared-classes-and-instance-main-methods.html#GUID-E49690F1-727E-45F6-A582-9821C9597112 declared classes and instance main methods


The following script will be compiled and executed without any problem.

```
///usr/bin/env jbang "$0" "$@" ; exit $?
//JAVA 23+
//COMPILE_OPTIONS --enable-preview -source 23
//RUNTIME_OPTIONS --enable-preview

void main(String... args) {
 System.out.println("Hello World");
}
```
This is made possible by adding the following properties to the script.

```
//JAVA 23+
//COMPILE_OPTIONS --enable-preview -source 23
//RUNTIME_OPTIONS --enable-preview
```
The first line, `//JAVA 23+`, tells JBang to use Java 23 or later. The second and third lines, `//COMPILE_OPTIONS --enable-preview -source 23` and `//RUNTIME_OPTIONS --enable-preview`, enable preview features for compilation and runtime, respectively.

Once the feature become stable, we can remove these 3 lines and the script will still work. Neat!


## Dependencies
JBang supports adding dependencies to scripts in the form of Gradle style dependencies by adding a `//DEPS atrefact-id:atrefact-name:version` line for each dependency. For example, to use the `jfiglet` library, we can add the following line to the script: `//DEPS com.github.lalyos:jfiglet:0.0.8`.

```
///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS com.github.lalyos:jfiglet:0.0.9

import com.github.lalyos.jfiglet.FigletFont;

public class DependenciesDemo {
   public static void main(String... args) throws Exception {
       System.out.println(FigletFont.convertOneLine("JBang is amazing"));
   }
}
```

## Catalogs
Catalogs in JBang allow to organize and share scripts and templates efficiently. This feature is particularly useful for teams or communities that want to share a collection of scripts for common tasks or workflows. It is also useful for teachers who want to distribute templates or show results of exercises.

A catalog is a JSON file named `jbang-catalog.json` that contains two groups of items: aliases and templates. Aliases allow to run scripts from a catalog using a simple command, while templates provide a starting point for new scripts. Catalogs can be remote or local and it is possible to add and use as many local or remote repositories as needed. It is interesting to point out that JBang creates, during its setup, a local catalog with some aliases and templates out of the box.

JBang looks for local catalogs in specific directories (as shown in the official docs ). The most obvious one are the `.jbang` directory in the user’s home directory and the current working folder.
=> https://www.jbang.dev/documentation/guide/latest/alias_catalogs.html#local-alias-catalogs as shown in the official docs


JBang looks for remote the catalogs in many open source repositories like GitHub, GitLab, BitBucket, and others. I’ll use GitHub as an example in this post. To create a remote catalog, you need to add `jbang-catalog.json` to the root folder of your repository. The catalog is then referred to by `account/repository_name`. If your repository is named `jbang-catalog`, then you can refer to it by `account`. So, for example, let’s say my GitHub account is named `yostane` and I have a repository named `cours-java` that contains a catalog with a file named `jbang-catalog.json`. I can refer to that catalog by `yostane/cours-java`. Furthermore, if I have a `jbang-catalog.json` in a repository named `jbang-catalog` then I can refer to it by `yostane/jbang-catalog` or simply `yostane`.

```
 {
   "catalogs": {},
   "aliases": {
     // aliases
   },
   "templates": {
     // templates
   }
 }
```
The following chapters will show how to use aliases and templates from a catalog.


### Aliases
Aliases in JBang allow to run scripts from a catalog. The full syntax is `jbang alias@account/repository [args]` and `jbang alias [args]` for respectively a remote and local alias.

Aliases can be defined in the `aliases` section of the catalog file using the following format:

```
{
 "aliases": [
   {
     "name": "alias-name",
     "description": "Some description",
     "location": "path-to-script.java or .kt or .groovy"
   },
   // More aliases ...
 ]
}
```
Here is the catalog I used during my session at DevoxxMA 2024.
=> https://github.com/yostane/cours-java/blob/devoxxma2024/jbang-catalog.json catalog I used


```
{
 "catalogs": {},
 "aliases": {
  "palcli": {
 "script-ref": "tutorials/devoxxma2024-jbang/paltools/palcli.java",
 "description": "Palindrome tester CLI"
  },
   "palqrest": {
 "script-ref": "tutorials/devoxxma2024-jbang/paltools/palqrest.java",
 "description": "Palindrome tester Quarkus Rest API"
  },
   "hellojfx": {
 "script-ref": "tutorials/devoxxma2024-jbang/javafx/hellojfx.java",
 "description": "Basic JavaFX window that shows Java and JavaFx versions"
  }
}
}
```
You can run these aliases with the following commands:

* jbang palcli@yostane/cours-java madam
* jbang palqrest@yostane/cours-java
* jbang hellojfx@yostane/cours-java

The official JBang GitHub account provides a catalog with many aliases and templates. Let’s run some of them:
=> https://github.com/jbangdev/jbang-catalog/blob/main/jbang-catalog.json JBang GitHub account provides a catalog


* jbang httpd@jbangdev run a local webserver.
* jbang gavsearch@jbangdev [arg] search for [arg] on search.maven.org.


### Templates
Templates are pre-defined scripts that can be used as a starting point for new scripts. They are defined in the `templates` section of the catalog file using the following format:

```
{
 "catalogs": {},
 "aliases": {
   //...
 },
 "templates": {
   "template-name": {
     "file-refs": {
       "filename.java": "path-to-template-file.java, .kt, .groovy or .qute"
     },
     "description": "Some description"
   },
   // More templates ...
 }
}
```
When a template is used, JBang creates copies of all the files referred in the `file-refs` property. When a `file-ref` contains `{basename}`, JBang replaces it with the name of the script being created. When a `file-ref` uses the `.qute` extension, JBang uses the Qute templating engine
=> https://quarkus.io/guides/qute Qute templating engine


Here are some examples of templates available out of the box:

* A CLI script that uses picocli: jbang init -t cli hellocli.java
* A Quarkus single file REST API: jbang init -t qrest helloqrest.java

We can also use templates from the internet shared by the community. For example, this command creates a `JUnit` unit test file: `jbang init -t junit@jbangdev file_to_test.java`. From the command we can locate the `jbang-catalog.json` that defined the template in the jbangdev/jbang-catalog repository.
=> https://github.com/jbangdev/jbang-catalog jbangdev/jbang-catalog


```
{
 "catalogs": {},
 "aliases": {
   //...
 },
 "templates": {
   //...
   "junit": {
     "file-refs": {
       "{basename}Test.java": "templates/junit.java.qute"
     },
     "description": "Basic template for JUnit tests"
   }
 }
}
```

## App Store
The JBang App Store is a web app that allows to browse aliases of indexed catalogs. It provides a convenient way to discover and use various tools and utilities without the need for complex setup or installation processes. For example, when we search for `yostane`, we should be able to find the different aliases that I have defined in my catalogs. The following image shows the result of the search.
=> https://www.jbang.dev/appstore/ App Store


=> https://blog.worldline.tech/images/post/jbang/jbang-appstore.webp JBang catalog search [image]


Here are some interesting and funny scripts that I found while browsing the App Store. `cowsay@ricksbrown/cowsay` displays a message in a speech bubble of a cow or other animals.

```
jbang cowsay@ricksbrown/cowsay MOO!`
jbang cowsay@ricksbrown/cowsay -f dragon "I'm Veldora Tempest!"`
```
Next, `grep@a-services` provides a grep implementation `jbang grep@a-services "hello" .`. Finally, `images2pdf@a-services` creates a PDF from images and can be used as follows.

```
wget -O img1.png "https://blog.worldline.tech/images/post/modulith/modulith-thumb.png"
wget -O img2.png "https://blog.worldline.tech/images/post/ryuk/testcontainers-396x120.png"
# Create a file that contains the paths to the images. This file is required by the script.
echo "img1.png" > ./files.txt
echo "img2.png" >> ./files.txt
jbang images2pdf@a-services files.txt
```
When you publish a catalog, it will likely appear after the next indexing of the JBang AppStore. It is a scheduled GitHub action defined here .
=> https://github.com/jbangdev/jbang.dev/blob/main/.github/workflows/appstore-update.yml defined here



## Some examples with notable frameworks
With JBang, we can create single file applications that use popular frameworks and libraries. Some examples include Quarkus, picolcli, and JavaFX. Let’s explore some examples in the following sections.


### JavaFX (openjfx)
JavaFX is a desktop and UI framework. Its official website is openjfx.io and is also supported by Gluon which provides additional UI components and brings mobile app support to JavaFX. JBang supports this framework and can be used to create single file JavaFX applications.
=> https://openjfx.io/ openjfx.io

=> https://gluonhq.com/products/javafx/ Gluon


Here are some examples JavaFX apps created with JBang:

* Basic window

=> ./javafx/hellojfx.java Basic window
* Balls moving randomly
jbang https://gist.github.com/FDelporte/c69a02c57acc892b4c996a9779d4f830

=> https://gist.github.com/FDelporte/c69a02c57acc892b4c996a9779d4f830 Balls moving randomly
* A template jbang init -t javafx@yostane hellojfx


### Quarkus
Quarkus is a Java framework that is optimized for Kubernetes and serverless environments. It provides a fast boot time and low memory consumption, making it ideal for cloud-native applications.

Thanks to JBang, we can create single-file Quarkus applications that leverage the power of this framework. The following example shows a rest API that tests if a string is a palindrome. It has JSON parsing, logging and provides an OpenAPI and Swagger documentation.

```
///usr/bin/env jbang "$0" "$@" ; exit $?
//JAVA 17+
// Update the Quarkus version to what you want here or run jbang with
// `-Dquarkus.version=<version>` to override it.
//DEPS io.quarkus:quarkus-bom:${quarkus.version:3.15.1}@pom
//DEPS io.quarkus:quarkus-resteasy
//DEPS io.quarkus:quarkus-resteasy-jsonb
//DEPS io.quarkus:quarkus-smallrye-openapi
//DEPS io.quarkus:quarkus-swagger-ui

//JAVAC_OPTIONS -parameters
//JAVA_OPTIONS -Djava.util.logging.manager=org.jboss.logmanager.LogManager

//SOURCES PalindromeService.java

//Q:CONFIG quarkus.banner.enabled=false
//Q:CONFIG quarkus.swagger-ui.always-include=true

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;

import java.util.Map;

@Path("/palindrome")
@ApplicationScoped
public class palqrest {
   @GET
   @Produces(MediaType.APPLICATION_JSON)
   public Map<String, String> isPalidrome(@QueryParam("input") String input) {
       return Map.of("result",
               PalindromeService.isPalindrome(input) ? "Palindrome" : "Not Palindrome");
   }
}
```
We may notice a `//SOURCES PalindromeService.java` line in the script. It tells JBang to look for a file named `PalindromeService.java` in the same directory as the script. This means that JBang supports multi-file scripts.

You can run the server with `jbang palqrest@yostane/cours-java` and call the endpoint with `curl http://localhost:8080/palindrome?input=madam`.

```
jbang palqrest@yostane/cours-java
# In another terminal
curl http://localhost:8080/palindrome?input=madam
# {"result":"Palindrome"}
```

## Other languages
JBang supports running Java, Kotlin, JShell and Groovy code. It can even run Java code from markdown files. Here are some examples of how to use JBang with different languages:


### Kotlin
A Kotlin script can be initialized with `jbang init -t hello.kt filename.kt`. Please note that this is different from the official `.main.kts` Kotlin scripts . In fact, Kotlin scripts created by JBang can benefit from the catalog and App Store features. Here is an example of a Kotlin script created with JBang.
=> https://mbonnin.net/2024-11-21_state-of-kotlin-scripting/ Kotlin scripts


```
///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS org.fusesource.jansi:jansi:2.4.1

import org.fusesource.jansi.Ansi.*
import org.fusesource.jansi.Ansi.Color.*
import org.fusesource.jansi.AnsiConsole

public fun main() {
   AnsiConsole.systemInstall()
   println(
       ansi().eraseScreen().fg(RED).a("Hello").fg(GREEN).a(" DevoxxMA ").fg(RED).a("2024").reset()
   )
   println(ansi().eraseScreen().render("@|red JBang|@ @|green 💖 Kotlin|@"))
}
```
Please note that JBang is not the only tool that supports Kotlin scripting but seems to be the one with the most features. Indeed, while Kotlin already has native scripting support (with `.main.kts` scripts), seems to lack the catalog, template, and App Store features. There is also kscrit which was a source of inspiration to JBang but now does not seems to be maintained and lacks in features compared to JBang.
=> https://github.com/kscripting/kscript kscrit

=> https://www.jbang.dev/documentation/guide/latest/faq.html JBang



### Groovy
We can initialize a Groovy script with `jbang init -t hello.groovy filename.groovy`. Here is an example of a Groovy script created with JBang.

```
///usr/bin/env jbang "$0" "$@" ; exit $?
println("Hello Groovy World");
```

### JShell
JBang supports JShell scripts with `.jsh` or `.jshell` extensions and inline scripts using `jbang -c 'System.out.println("Inline Java ☕ yay!")'`. Here is an example of a JShell script created with JBang.

```
///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS com.github.lalyos:jfiglet:0.0.9

import com.github.lalyos.jfiglet.FigletFont;

System.out.println("hello from jshell");
System.out.println(FigletFont.convertOneLine("DevoxxMA"));
```

### Markdown with Java and JShell code blocks
You can run Java and JShell code blocks directly from a markdown file using `jbang my_markdown.md`.

```
# jbang runs markdown

Jbang can execute code blocks.

```java
class Demo {
 void test() {
     System.out.println("I am a print in a markdown!");
 }
}
```

```jshelllanguage
new Demo().test();
```

```jsh
//DEPS com.github.lalyos:jfiglet:0.0.9
import com.github.lalyos.jfiglet.FigletFont;

System.out.println(FigletFont.convertOneLine("Hello " + ((args.length>0)?args[0]:"jbang")));
```

```java
if(args.length==0){
 System.out.

println("You have no arguments!");
}else{
 System.out.

printf("You have %s arguments! First is %s",args.length, args[0]);
}
```

```kt
fun main() {
 print("We love Kotlin")
}
```

```kotlin
println("We love Kotlin")
```

```groovy
println("We love Kotlin")
```
```

## Other features
In this section, we will delve into some additional and experimental features of JBang that can further enhance your scripting experience. These features include file configuration, caching, the JBang wrapper, and even generating scripts using OpenAI. Let’s briefly view some of them.


### File Configuration
JBang allows you to configure various settings through a configuration file. This can be particularly useful for setting default options, managing dependencies, and customizing the behavior of your scripts. You can learn more about file configuration here .
=> https://www.jbang.dev/documentation/guide/latest/config.html here



### Caching
To improve performance and reduce redundant downloads, JBang supports caching of dependencies and other resources. For certain situations, we may want to clear the cache with `jbang cache clear` and ignore the cache when fetching a catalog with `--fresh` argument. For example, `jbang --fresh palqrest@yostane/cours-java` will ignore the cache and get the latest version of the alias. Documentation on caching can be found here .
=> https://www.jbang.dev/documentation/guide/latest/caching.html here



### JBang Wrapper
The JBang wrapper is a convenient tool that allows you to bundle JBang with your project. This ensures that anyone who clones your repository can run your scripts without needing to install JBang separately. More details on the JBang wrapper are available here .
=> https://www.jbang.dev/documentation/guide/latest/cli/jbang-wrapper.html here



### OpenAI Script Generation (Experimental)
JBang has the ability to generate scripts using OpenAI. By providing a description of what you want your script to do, JBang can leverage OpenAI’s capabilities to create a script for you. This feature is still in preview and requires an OpenAI API key (I personally could not get an OpenAPI key so I couldn’t test by myself).

For example, you can generate a script to count the number of people in an image using OpenCV with the following command:

```
jbang --preview --verbose init ImageProcessor.java "Write a Java program that counts the number of people in an image with OpenCV"
```
As a side note, if we want to prompt from the terminal, there are already many stable tools such as mods .
=> https://github.com/charmbracelet/mods mods



## Conclusion
JBang is a highly versatile tool that brings powerful scripting capabilities to the Java ecosystem. It is particularly useful for single-file projects, allowing developers to quickly and easily run Java, Kotlin, and Groovy scripts. It can be applied to a wide range of use cases: Rest APIs, GUI apps, general scripting, etc. The tool’s features, such as templating, the App Store, and catalogs, enhance its utility by facilitating sharing and collaboration among developers.

Whether it is for teaching or for scripting, JBang is a valuable addition to the Java developer’s toolkit.


## Credits and resources
* jbang

=> https://jbang.dev jbang
* jfiglet

=> https://github.com/lalyos/jfiglet jfiglet
* fusesource/jansi

=> https://github.com/fusesource/jansi fusesource/jansi
* Java 23 Implicitly Declared Classes and Instance Main Methods

=> https://docs.oracle.com/en/java/javase/23/language/implicitly-declared-classes-and-instance-main-methods.html Java 23 Implicitly Declared Classes and Instance Main Methods
* baeldung.com/jbang

=> https://www.baeldung.com/jbang-guide baeldung.com/jbang
* JBang OpenAI example

=> https://www.infoq.com/news/2023/06/jbang-107/ JBang OpenAI example