<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Yassine Benabbas </title>
    <description>The latest articles on DEV Community by Yassine Benabbas  (@yostane).</description>
    <link>https://dev.to/yostane</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F919313%2F9fb6b52f-da54-449a-8500-1b7c1053e581.jpg</url>
      <title>DEV Community: Yassine Benabbas </title>
      <link>https://dev.to/yostane</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yostane"/>
    <language>en</language>
    <item>
      <title>JBang, the missing scripting tool of the Java ecosystem</title>
      <dc:creator>Yassine Benabbas </dc:creator>
      <pubDate>Fri, 03 Jan 2025 19:19:10 +0000</pubDate>
      <link>https://dev.to/worldlinetech/jbang-the-missing-scripting-tool-of-the-java-ecosystem-3f7d</link>
      <guid>https://dev.to/worldlinetech/jbang-the-missing-scripting-tool-of-the-java-ecosystem-3f7d</guid>
      <description>&lt;p&gt;The Java ecosystem has already two powerful project management tools, namely Maven and Gradle, yet it lacked a simple and powerful scripting tool.&lt;br&gt;
This is where JBang comes in.&lt;br&gt;
It is a minimalist but powerful Java, Kotlin and Groovy file launcher.&lt;br&gt;
In fact, it allows you to run code as easily as you would run a script.&lt;br&gt;
It also provides many other features such as dependency management, templates, and an App Store.&lt;br&gt;
Let's explore JBang and its features in this post.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;jbang&lt;/code&gt; command-line can be installed on Windows, Linux, and macOS using different methods which are &lt;a href="https://www.jbang.dev/download/" rel="noopener noreferrer"&gt;well documented here&lt;/a&gt;.&lt;br&gt;
We can verify the installation by running &lt;code&gt;jbang --version&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In addition to that, it's preferable to install the accompanying IDE extension for our favorite IDE.&lt;br&gt;
The supported IDE extensions are &lt;a href="https://www.jbang.dev/documentation/guide/latest/editing.html#ide-support" rel="noopener noreferrer"&gt;listed here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;JBang does not depend on a JDK to JRE but a JDK is required to run scripts that use Java. &lt;br&gt;
You can install one with JBang by running &lt;code&gt;jbang jdk install 23&lt;/code&gt; which will install the JDK 23.&lt;/p&gt;

&lt;p&gt;We are now ready to write our first scripts.&lt;/p&gt;
&lt;h2&gt;
  
  
  First script
&lt;/h2&gt;

&lt;p&gt;Let's create a simple script that prints "Hello, World!" to the console.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; jbang init helloworld.java
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a file named &lt;code&gt;helloworld.java&lt;/code&gt; that can be run with &lt;code&gt;jbang helloworld.java&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; jbang helloworld.java
Hello world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you open the file, you will see that it is a plain Java file with a &lt;code&gt;main&lt;/code&gt; method and a particular first line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;///usr/bin/env jbang "$0" "$@" ; exit $?&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.*;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;helloworld&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello world"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we will see, JBang script are made up of three parts: the shebang, optional properties and the script itself.&lt;br&gt;
We'll use some properties the second part in the next sections but let's focus on the first part.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;chmod&lt;/span&gt; +x helloworld.java
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ./helloworld.java
Hello world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now develop our script as we would with any Java file.&lt;br&gt;
Once it is ready to be published, we can export it in different formats as follows: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;jar file&lt;/strong&gt;: &lt;code&gt;jbang export portable helloworld.java&lt;/code&gt;. If your script uses dependencies, the next commands are more recommended.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;fatjar&lt;/strong&gt;: which contains all the dependencies: &lt;code&gt;jbang export fatjar helloworld.java&lt;/code&gt;. This method still requires to install a JDK / JRE on the target machine. If you don't want to that, the next commands are more recommended.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;jlink&lt;/strong&gt; binary that encompases a JDK: &lt;code&gt;jbang export jlink helloworld.java&lt;/code&gt;. The binary to run is either &lt;code&gt;helloworld-jlink/bin/helloworld&lt;/code&gt; on Unix or &lt;code&gt;helloworld-jlink/bin/helloworld.bat&lt;/code&gt; on Windows.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;native imgae&lt;/strong&gt;: &lt;code&gt;jbang export native helloworld.java&lt;/code&gt;. This requires a GraalVM installation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The script can also be exported as a mavenrepo with: &lt;code&gt;jbang export mavenrepo helloworld.java&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  JDK management
&lt;/h2&gt;

&lt;p&gt;As seen in a previous chapter, JBang can install JDKs on your machine.&lt;br&gt;
You can list the installed JDKs with &lt;code&gt;jbang jdk list&lt;/code&gt;, list available ones to install with &lt;code&gt;jbang jdk list --available --show-details&lt;/code&gt;, install a new one with &lt;code&gt;jbang jdk install [version]&lt;/code&gt;. Jbang also supports the use of &lt;a href="https://www.jbang.dev/documentation/guide/latest/javaversions.html#using-managed-jdks-yourself" rel="noopener noreferrer"&gt;SDKMAN&lt;/a&gt; to manage JDKs on supported systems.&lt;/p&gt;

&lt;p&gt;Furthermore, it is possible to specify a JDK version in a scripts.&lt;br&gt;
This is done by adding the following line to the script properties: &lt;code&gt;//JAVA [version]&lt;/code&gt; if we want an exact version or &lt;code&gt;//JAVA [version]+&lt;/code&gt; if we want at least a specific version.&lt;br&gt;
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.&lt;/p&gt;

&lt;p&gt;For example, the following script uses Java 25 and some preview features.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// usr/bin/env jbang "$0" "$@" ; exit $?&lt;/span&gt;
&lt;span class="c1"&gt;//JAVA 25&lt;/span&gt;
&lt;span class="c1"&gt;//COMPILE_OPTIONS --enable-preview -source 25&lt;/span&gt;
&lt;span class="c1"&gt;//RUNTIME_OPTIONS --enable-preview&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.concurrent.Callable&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.concurrent.StructuredTaskScope&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.*;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello Java 25"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="nc"&gt;Callable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;task1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Task 1"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentThread&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Task 1"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;};&lt;/span&gt;

  &lt;span class="nc"&gt;Callable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;task2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Task 2"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentThread&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StructuredTaskScope&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;StructuredTaskScope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Subtask&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;subtask1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fork&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task1&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;StructuredTaskScope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Subtask&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;subtask2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fork&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task2&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printStackTrace&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Scripts without a "Main" class
&lt;/h2&gt;

&lt;p&gt;Since script tend to be lightweight, it would be preferable to write them without a class and a main method.&lt;br&gt;
Fortunately, Java has a feature called implicit &lt;a href="https://docs.oracle.com/en/java/javase/23/lnguage/implicitly-declared-classes-and-instance-main-methods.html#GUID-E49690F1-727E-45F6-A582-9821C9597112" rel="noopener noreferrer"&gt;declared classes and instance main methods&lt;/a&gt; (which is still in preview in Java 23).&lt;br&gt;
This feature allows to write java programs and JBang script without a class and a static main method.&lt;/p&gt;

&lt;p&gt;The following script will be compiled and executed without any problem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;///usr/bin/env jbang "$0" "$@" ; exit $?&lt;/span&gt;
&lt;span class="c1"&gt;//JAVA 23+&lt;/span&gt;
&lt;span class="c1"&gt;//COMPILE_OPTIONS --enable-preview -source 23&lt;/span&gt;
&lt;span class="c1"&gt;//RUNTIME_OPTIONS --enable-preview&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello World"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is made possible by adding the following properties to the script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;//JAVA 23+&lt;/span&gt;
&lt;span class="c1"&gt;//COMPILE_OPTIONS --enable-preview -source 23&lt;/span&gt;
&lt;span class="c1"&gt;//RUNTIME_OPTIONS --enable-preview&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first line, &lt;code&gt;//JAVA 23+&lt;/code&gt;, tells JBang to use Java 23 or later.&lt;br&gt;
The second and third lines, &lt;code&gt;//COMPILE_OPTIONS --enable-preview -source 23&lt;/code&gt; and &lt;code&gt;//RUNTIME_OPTIONS --enable-preview&lt;/code&gt;, enable preview features for compilation and runtime, respectively.&lt;/p&gt;

&lt;p&gt;Once the feature become stable, we can remove the 3 lines and the script will still work. Neat!&lt;/p&gt;
&lt;h2&gt;
  
  
  Dependencies
&lt;/h2&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;///usr/bin/env jbang "$0" "$@" ; exit $?&lt;/span&gt;
&lt;span class="c1"&gt;//DEPS com.github.lalyos:jfiglet:0.0.9&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.github.lalyos.jfiglet.FigletFont&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DependenciesDemo&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;FigletFont&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;convertOneLine&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"JBang is amazing"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Catalogs
&lt;/h2&gt;

&lt;p&gt;Catalogs in JBang allow to organize and share scripts and templates efficiently.&lt;br&gt;
This feature is particularly useful for teams or communities that want to share a collection of scripts for common tasks or workflows.&lt;br&gt;
It is also useful for teachers who want to distribute starter codes or show results of exercises without providing the source code.&lt;/p&gt;

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

&lt;p&gt;JBang looks for local catalogs in these directories in the following order (&lt;a href="https://www.jbang.dev/documentation/guide/latest/alias_catalogs.html#local-alias-catalogs" rel="noopener noreferrer"&gt;source JBang docs&lt;/a&gt;):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Current directory, &lt;code&gt;./jbang-catalog.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;In &lt;code&gt;./.jbang/jbang-catalog.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;In the parent directory, &lt;code&gt;../jbang-catalog.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;In the parent’s .jbang directory, &lt;code&gt;../.jbang/jbang-catalog.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;And repeating steps 3 and 4 recursively upwards to the root of the file system&lt;/li&gt;
&lt;li&gt;As the last step it will look in &lt;code&gt;$HOME/.jbang/jbang-catalog.json&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;JBang will look for remote the catalogs in many open source repositories like GitHub, GitLab, Bitbucket, and others.&lt;br&gt;
I'll use GitHub as an example in this post.&lt;br&gt;
To create a remote catalog, you need to add &lt;code&gt;jbang-catalog.json&lt;/code&gt; to the root folder of your repository.&lt;br&gt;
The catalog is then referred to by &lt;code&gt;account/repository_name&lt;/code&gt;.&lt;br&gt;
If your repository is named &lt;code&gt;jbang-catalog&lt;/code&gt;, then you can refer to it by &lt;code&gt;account&lt;/code&gt;. &lt;br&gt;
So, for example, if my GitHub account is named &lt;code&gt;yostane&lt;/code&gt; and I have a repository named &lt;code&gt;cours-java&lt;/code&gt; that contains a catalog which a file named &lt;code&gt;jbang-catalog.json&lt;/code&gt;, I can refer to that catalog by &lt;code&gt;yostane/cours-java&lt;/code&gt;. Furthermore, if I have a &lt;code&gt;jbang-catalog.json&lt;/code&gt; in a repository named &lt;code&gt;jbang-catalog&lt;/code&gt; then I can refer to it by &lt;code&gt;yostane/jbang-catalog&lt;/code&gt; or simply &lt;code&gt;yostane&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"catalogs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"aliases"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;aliases&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"templates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;templates&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following chapters will show how to use aliases and templates from a catalog.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aliases
&lt;/h3&gt;

&lt;p&gt;Aliases in JBang allow to run scripts from a catalog.&lt;br&gt;
The full syntax is &lt;code&gt;jbang alias@account/repository [args]&lt;/code&gt; and &lt;code&gt;jbang alias [args]&lt;/code&gt; for respectively a remote and local alias.&lt;/p&gt;

&lt;p&gt;Aliases can be defined in the &lt;code&gt;aliases&lt;/code&gt; section of the catalog file using the following format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"aliases"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alias-name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Some description"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"path-to-script.java or .kt or .groovy"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;More&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;aliases&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the &lt;a href="https://github.com/yostane/cours-java/blob/devoxxma2024/jbang-catalog.json" rel="noopener noreferrer"&gt;catalog I used&lt;/a&gt; during my session at DevoxxMA 2024.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"catalogs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"aliases"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"palcli"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"script-ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tutorials/devoxxma2024-jbang/paltools/palcli.java"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Palindrome tester CLI"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"palqrest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"script-ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tutorials/devoxxma2024-jbang/paltools/palqrest.java"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Palindrome tester Quarkus Rest API"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"hellojfx"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"script-ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tutorials/devoxxma2024-jbang/javafx/hellojfx.java"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Basic JavaFX window that shows Java and JavaFx versions"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can run these aliases with the following commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;jbang palcli@yostane/cours-java madam&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;jbang palqrest@yostane/cours-java&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;jbang hellojfx@yostane/cours-java&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The official &lt;a href="https://github.com/jbangdev/jbang-catalog/blob/main/jbang-catalog.json" rel="noopener noreferrer"&gt;JBang GitHub account provides a catalog&lt;/a&gt; with many aliases and templates.&lt;br&gt;
Let's run some of them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;jbang httpd@jbangdev&lt;/code&gt; run a local webserver.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;jbang gavsearch@jbangdev [arg]&lt;/code&gt; search for &lt;code&gt;[arg]&lt;/code&gt; on &lt;code&gt;search.maven.org&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Templates
&lt;/h3&gt;

&lt;p&gt;Templates, which are pre-defined scripts that can be used as a starting point for new scripts.&lt;br&gt;
They are defined in the &lt;code&gt;templates&lt;/code&gt; section of the catalog file using the following format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"catalogs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"aliases"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"templates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"template-name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"file-refs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"filename.java"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"path-to-template-file.java, .kt, .groovy or .qute"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Some description"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;More&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;templates&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a template is used, JBang creates copies of all the files int the &lt;code&gt;file-refs&lt;/code&gt; property.&lt;br&gt;
When a &lt;code&gt;file-ref&lt;/code&gt; contains &lt;code&gt;{basename}&lt;/code&gt;, JBang replaces it with the name of the script being created.&lt;br&gt;
When a &lt;code&gt;file-ref&lt;/code&gt; uses the &lt;code&gt;.qute&lt;/code&gt; extension, JBang uses the &lt;a href="https://quarkus.io/guides/qute" rel="noopener noreferrer"&gt;Qute templating engine&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are some examples of some templates available out of the box:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A CLI script that uses &lt;code&gt;picocli&lt;/code&gt;: &lt;code&gt;jbang init -t cli hellocli.java&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;Quarkus&lt;/code&gt; single file REST API: &lt;code&gt;jbang init -t qrest helloqrest.java&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can also use templates from the internet shared by the community.&lt;br&gt;
For example, this command creates a &lt;code&gt;JUnit&lt;/code&gt; unit test file: &lt;code&gt;jbang init -t junit@jbangdev file_to_test.java&lt;/code&gt;.&lt;br&gt;
From the command we can locate the &lt;code&gt;jbang-catalog.json&lt;/code&gt; that defined the tempalte in the &lt;a href="https://github.com/jbangdev/jbang-catalog" rel="noopener noreferrer"&gt;jbangdev/jbang-catalog&lt;/a&gt; repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"catalogs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"aliases"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"templates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//...&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"junit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"file-refs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"{basename}Test.java"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"templates/junit.java.qute"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Basic template for JUnit tests"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  App Store
&lt;/h2&gt;

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

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn5nmf4qmoh6fjj4a8a35.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn5nmf4qmoh6fjj4a8a35.png" alt="Image description" width="800" height="569"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are some interesting and funny scripts that I found by browsing on the the App Store:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cowsay. Here are some examples of running the script:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;jbang cowsay@ricksbrown/cowsay MOO!&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;jbang cowsay@ricksbrown/cowsay -f dragon "I'm Veldora Tempest!"&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Find substring like grep: &lt;code&gt;jbang grep@a-services "hello" .&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create PDF from images: &lt;code&gt;images2pdf@a-services&lt;/code&gt;. The following commands will create a PDF file from two images:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  wget &lt;span class="nt"&gt;-O&lt;/span&gt; img1.png &lt;span class="s2"&gt;"https://blog.worldline.tech/images/post/modulith/modulith-thumb.png"&lt;/span&gt;
  wget &lt;span class="nt"&gt;-O&lt;/span&gt; img2.png &lt;span class="s2"&gt;"https://blog.worldline.tech/images/post/ryuk/testcontainers-396x120.png"&lt;/span&gt;
  &lt;span class="c"&gt;# Create a file that contains the paths to the images. This file is required by the script.&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"img1.png"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ./files.txt
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"img2.png"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ./files.txt
  jbang images2pdf@a-services files.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;When you publish a catalog, it will very probably appear after the next indexing of the JBang AppStore.&lt;br&gt;
It is a scheduled GitHub action &lt;a href="https://github.com/jbangdev/jbang.dev/blob/main/.github/workflows/appstore-update.yml" rel="noopener noreferrer"&gt;defined here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Some examples with notable frameowrks
&lt;/h2&gt;

&lt;p&gt;With JBang, we can create single file applications that use popular frameworks and libraries.&lt;br&gt;
Some examples include Quarkus, picolcli, and JavaFX.&lt;br&gt;
Let's explore some example in the following sections.&lt;/p&gt;
&lt;h2&gt;
  
  
  JavaFX (openjfx)
&lt;/h2&gt;

&lt;p&gt;JavaFX is a desktop and UI framework.&lt;br&gt;
Its official website is &lt;a href="https://openjfx.io/" rel="noopener noreferrer"&gt;openjfx.io&lt;/a&gt; and is also supported by &lt;a href="https://gluonhq.com/products/javafx/" rel="noopener noreferrer"&gt;Gluon&lt;/a&gt; which provides additional UI components and brings mobile app support to JavaFX.&lt;br&gt;
JBang supports this frameowrk and can be used to create signle file JavaFX applications.&lt;/p&gt;

&lt;p&gt;Here are some examples JavaFX apps created with JBang:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="//./javafx/hellojfx.java"&gt;Basic window&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://gist.github.com/FDelporte/c69a02c57acc892b4c996a9779d4f830" rel="noopener noreferrer"&gt;More beautiful example&lt;/a&gt; &lt;code&gt;jbang https://gist.github.com/FDelporte/c69a02c57acc892b4c996a9779d4f830&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A template &lt;code&gt;jbang init -t javafx@yostane hellojfx&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Quarkus
&lt;/h2&gt;

&lt;p&gt;Quarkus is a Java framework that is optimized for Kubernetes and serverless environments.&lt;br&gt;
It provides a fast boot time and low memory consumption, making it ideal for cloud-native applications.&lt;/p&gt;

&lt;p&gt;Thanks to JBang, we can create single-file Quarkus applications that leverage the power of this framework.&lt;br&gt;
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.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;///usr/bin/env jbang "$0" "$@" ; exit $?&lt;/span&gt;
&lt;span class="c1"&gt;//JAVA 17+&lt;/span&gt;
&lt;span class="c1"&gt;// Update the Quarkus version to what you want here or run jbang with&lt;/span&gt;
&lt;span class="c1"&gt;// `-Dquarkus.version=&amp;lt;version&amp;gt;` to override it.&lt;/span&gt;
&lt;span class="c1"&gt;//DEPS io.quarkus:quarkus-bom:${quarkus.version:3.15.1}@pom&lt;/span&gt;
&lt;span class="c1"&gt;//DEPS io.quarkus:quarkus-resteasy&lt;/span&gt;
&lt;span class="c1"&gt;//DEPS io.quarkus:quarkus-resteasy-jsonb&lt;/span&gt;
&lt;span class="c1"&gt;//DEPS io.quarkus:quarkus-smallrye-openapi&lt;/span&gt;
&lt;span class="c1"&gt;//DEPS io.quarkus:quarkus-swagger-ui&lt;/span&gt;

&lt;span class="c1"&gt;//JAVAC_OPTIONS -parameters&lt;/span&gt;
&lt;span class="c1"&gt;//JAVA_OPTIONS -Djava.util.logging.manager=org.jboss.logmanager.LogManager&lt;/span&gt;

&lt;span class="c1"&gt;//SOURCES PalindromeService.java&lt;/span&gt;

&lt;span class="c1"&gt;//Q:CONFIG quarkus.banner.enabled=false&lt;/span&gt;
&lt;span class="c1"&gt;//Q:CONFIG quarkus.swagger-ui.always-include=true&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.enterprise.context.ApplicationScoped&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.GET&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.Path&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.Produces&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.QueryParam&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.core.MediaType&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.Map&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/palindrome"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@ApplicationScoped&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;palqrest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@GET&lt;/span&gt;
    &lt;span class="nd"&gt;@Produces&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MediaType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;APPLICATION_JSON&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;isPalidrome&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@QueryParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"input"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"result"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="nc"&gt;PalindromeService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isPalindrome&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"Palindrome"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Not Palindrome"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We may notice a &lt;code&gt;//SOURCES PalindromeService.java&lt;/code&gt; line in the script.&lt;br&gt;
It tells JBang to look for a file named &lt;code&gt;PalindromeService.java&lt;/code&gt; in the same directory as the script.&lt;br&gt;
This means that JBang supports multi-file scripts.&lt;/p&gt;

&lt;p&gt;You can run the server with &lt;code&gt;jbang palqrest@yostane/cours-java&lt;/code&gt; and call the endpoint with &lt;code&gt;curl http://localhost:8080/palindrome?input=madam&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jbang palqrest@yostane/cours-java
&lt;span class="c"&gt;# In another terminal&lt;/span&gt;
curl http://localhost:8080/palindrome?input&lt;span class="o"&gt;=&lt;/span&gt;madam
&lt;span class="c"&gt;# {"result":"Palindrome"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Other languages
&lt;/h2&gt;

&lt;p&gt;JBang supports running Java, Kotlin, JShell and Groovy code.&lt;br&gt;
It can even run Java code from markdown files.&lt;br&gt;
Here are some examples of how to use JBang with different languages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Kotlin&lt;/strong&gt;: You can initialize a Kotlin script with &lt;code&gt;jbang init -t hello.kt filename.kt&lt;/code&gt;. Please note that this is different from the official &lt;code&gt;.main.kts&lt;/code&gt; &lt;a href="https://mbonnin.net/2024-11-21_state-of-kotlin-scripting/" rel="noopener noreferrer"&gt;Kotlin scripts&lt;/a&gt;. 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.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  ///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|@"))
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Interesting fact: the idea of &lt;a href="https://www.jbang.dev/documentation/guide/latest/faq.html" rel="noopener noreferrer"&gt;JBang&lt;/a&gt; came from kscript which is targeted to the Kotlin ecosystem.&lt;/li&gt;
&lt;li&gt;Kotlin already has native scripting support (with &lt;code&gt;.main.kts&lt;/code&gt; scripts) but seems to lack the catalog, template, and App Store features.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Groovy&lt;/strong&gt;: Initialize a Groovy script with &lt;code&gt;jbang init -t hello.groovy filename.groovy&lt;/code&gt;. Here is an example of a Groovy script created with JBang.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;  &lt;span class="c1"&gt;///usr/bin/env jbang "$0" "$@" ; exit $?&lt;/span&gt;
  &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hello Groovy World"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;JShell&lt;/strong&gt;: JBang supports JShell scripts with &lt;code&gt;.jsh&lt;/code&gt; or &lt;code&gt;.jshell&lt;/code&gt; extensions and inline scripts using &lt;code&gt;jbang -c 'System.out.println("Inline Java ☕ yay!")'&lt;/code&gt;. Here is an example of a JShell script created with JBang.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;  &lt;span class="c1"&gt;///usr/bin/env jbang "$0" "$@" ; exit $?&lt;/span&gt;
  &lt;span class="c1"&gt;//DEPS com.github.lalyos:jfiglet:0.0.9&lt;/span&gt;

  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.github.lalyos.jfiglet.FigletFont&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

  &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello from jshell"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;FigletFont&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;convertOneLine&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DevoxxMA"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Markdown with Java and JShell code blocks&lt;/strong&gt;: You can run Java and JShell code blocks directly from a markdown file using &lt;code&gt;jbang my_markdown.md&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;  # jbang runs markdown

  Jbang can execute code blocks.

  &lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;
&lt;/span&gt;
java
  class Demo {
      void test() {
          System.out.println("I am a print in a markdown!");
      }
  }


  &lt;span class="p"&gt;```&lt;/span&gt;

  &lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;
&lt;/span&gt;
jshelllanguage
  new Demo().test();


  &lt;span class="p"&gt;```&lt;/span&gt;

  &lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;
&lt;/span&gt;
jsh
  //DEPS com.github.lalyos:jfiglet:0.0.9
  import com.github.lalyos.jfiglet.FigletFont;

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


  &lt;span class="p"&gt;```&lt;/span&gt;

  &lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;
&lt;/span&gt;
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]);
  }


  &lt;span class="p"&gt;```&lt;/span&gt;

  &lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;
&lt;/span&gt;
kt
  fun main() {
      print("We love Kotlin")
  }


  &lt;span class="p"&gt;```&lt;/span&gt;

  &lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;
&lt;/span&gt;
kotlin
  println("We love Kotlin")


  &lt;span class="p"&gt;```&lt;/span&gt;

  &lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;
&lt;/span&gt;
groovy
  println("We love Kotlin")


  &lt;span class="p"&gt;```&lt;/span&gt;
  &lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;
&lt;/span&gt;
`

## Other and experimental 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 next.

### 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).

### 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).

### 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).

### 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:

```sh
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).

## 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)
- [jfiglet](https://github.com/lalyos/jfiglet)
- [fusesource/jansi](https://github.com/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)
- [baeldung.com/jbang](https://www.baeldung.com/jbang-guide)
- [JBang OpenAI example](https://www.infoq.com/news/2023/06/jbang-107/)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>java</category>
      <category>kotlin</category>
      <category>scriping</category>
      <category>tooling</category>
    </item>
    <item>
      <title>A tour of CLI tools for installing Java and creating projects</title>
      <dc:creator>Yassine Benabbas </dc:creator>
      <pubDate>Sat, 01 Jun 2024 17:02:27 +0000</pubDate>
      <link>https://dev.to/worldlinetech/a-tour-of-cli-tools-for-installing-java-and-creating-projects-1fj2</link>
      <guid>https://dev.to/worldlinetech/a-tour-of-cli-tools-for-installing-java-and-creating-projects-1fj2</guid>
      <description>&lt;p&gt;Java Developers have at their disposal many tools and libraries that make their DX (Developer eXperience) easier and more fun.&lt;/p&gt;

&lt;p&gt;Being a terminal lover, let me share with you some CLI (Command Line Interface) tools that will make installing the JDK and bootstrapping projects a breeze.&lt;br&gt;
You can even complement your existing tooling with the ones that we'll see here.&lt;/p&gt;

&lt;p&gt;💡 This post is also for you if you use Kotlin for the JVM.&lt;/p&gt;
&lt;h2&gt;
  
  
  JDK version management
&lt;/h2&gt;

&lt;p&gt;Let's start with the first thing that we need to do when we want to start a Java project: installing the JDK.&lt;/p&gt;

&lt;p&gt;With Java releasing a new version every 6 months and with all the available JDK distributions, having a proper JDK version management is a must.&lt;br&gt;
Thus, I strongly discourage to install a JDK using an installer.&lt;br&gt;
In addition to that, some JDKs have license costs in production environments and we need to be careful about that.&lt;br&gt;
So, I suggest you don't install a JDK from search engines without proper prior knowledge, such as the one offered by &lt;a href="https://whichjdk.com" rel="noopener noreferrer"&gt;whichjdk.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Instead, we can use an intermediary tool that will allow to install different JDKs and change the default one whenever we want.&lt;br&gt;
This kind of tool is called a Java version manager.&lt;br&gt;
I recommend these two tools depending on your OS.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On Windows: &lt;a href="https://scoop.sh/" rel="noopener noreferrer"&gt;scoop&lt;/a&gt; is a package manager which supports Java version management. It provides a &lt;a href="https://github.com/ScoopInstaller/Scoop/wiki/Java" rel="noopener noreferrer"&gt;Java wiki&lt;/a&gt; with detailed instructions.&lt;/li&gt;
&lt;li&gt;On Linux and macOS: &lt;a href="https://sdkman.io/" rel="noopener noreferrer"&gt;SDKMAN!&lt;/a&gt; is a SDK manager specialized in the Java ecosystem. Instructions on how to manage JDKs is &lt;a href="https://sdkman.io/usage" rel="noopener noreferrer"&gt;provided here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition to listing and installing JDKs with different versions and providers, these tools can change the current active JDK with a single command (by automatically updating JAVA_HOME and PATH environment variables).&lt;br&gt;
Furthermore, we can install with a single command other Java related tools such as Maven, Gradle, Kotlin, etc.&lt;/p&gt;

&lt;p&gt;For example, to list the available JDKs using scoop, we run &lt;code&gt;scoop search jdk&lt;/code&gt; to get an output similar the following one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;...
temurin17-nightly-jdk     17.0.10-6.0.202312241232  java
temurin18-jdk             18.0.2-101                java
temurin18-nightly-jdk     18.0.2-101.0.202210032342 java
temurin19-jdk             19.0.2-7                  java
temurin19-nightly-jdk     19.0.2-7.0.202302250348   java
temurin20-jdk             20.0.2-9                  java
temurin21-jdk             21.0.1-12.1               java
temurin8-jdk              8.0.392-8                 java
temurin8-nightly-jdk      8.0.402-5.0.202312251854  java
zulu-jdk                  21.30.15                  java
zulu10-jdk                10.3.5                    java
zulu11-jdk                11.68.17                  java
zulu12-jdk                12.3.11                   java
zulu13-jdk                13.54.17                  java
zulu14-jdk                14.29.23                  java
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, that there are plenty of choices.&lt;br&gt;
My general recommendation is to use the latest LTS release and a distribution which provides the best balance of features (license, community, performance, security updates, etc.).&lt;br&gt;
In this regard, I use either Zulu JDK or Temurin JDK.&lt;br&gt;
This seems to be in-line with &lt;a href="https://whichjdk.com/" rel="noopener noreferrer"&gt;whichjdk.com&lt;/a&gt; which recommends to use &lt;a href="https://whichjdk.com/#adoptium-eclipse-temurin" rel="noopener noreferrer"&gt;Adoptium Eclipse Temurin 21&lt;/a&gt; (which superseeds adoptopenjdk) (Please note that Java 21 is the current LTS at the time of writing).&lt;/p&gt;

&lt;p&gt;So let's install Temurin with &lt;code&gt;scoop install temurin21-jdk&lt;/code&gt; if you are on Windows or with &lt;code&gt;sdk install java 21.0.1-tem&lt;/code&gt; if you are on Linux, macOS or WSL.&lt;br&gt;
Once done, you can immediately check that that the setup succeeded with a &lt;code&gt;java --version&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Projects managers
&lt;/h2&gt;

&lt;p&gt;In this section, I'll show three tools for creating and managing Java projects from the command line.&lt;/p&gt;
&lt;h3&gt;
  
  
  JBang
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.jbang.dev/" rel="noopener noreferrer"&gt;JBang&lt;/a&gt; is one of the simplest and easiest ones to get started with Java.&lt;br&gt;
In fact, it allows to create self-contained source-only projects, where build configuration files are not needed.&lt;br&gt;
This means that a JBang project can fit in a single Java (or Kotlin) file.&lt;/p&gt;

&lt;p&gt;This tool also provides &lt;a href="https://www.jbang.dev/appstore/" rel="noopener noreferrer"&gt;an AppStore&lt;/a&gt; feature which allows to run Java projects shared by the community very easily.&lt;br&gt;
⚠ Of course, every script must be verified before running it on your machine.&lt;/p&gt;

&lt;p&gt;JBang can be installed on Windows using &lt;a href="https://scoop.sh/" rel="noopener noreferrer"&gt;scoop&lt;/a&gt; with this command: &lt;code&gt;scoop install jbang&lt;/code&gt;, or on macOS and Linux using &lt;a href="https://sdkman.io/" rel="noopener noreferrer"&gt;SDKMAN&lt;/a&gt; with this command: &lt;code&gt;sdk install jbang&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After that, we can create a basic project with &lt;code&gt;jbang init hello.java&lt;/code&gt;. We can run it with &lt;code&gt;jbang run hello.java&lt;/code&gt; (on Linux and macOS, we first need to run &lt;code&gt;chmod +x hello.java&lt;/code&gt; to make the java file executable).&lt;/p&gt;

&lt;p&gt;JBang provides many other templates that we can list with &lt;code&gt;jbang template list&lt;/code&gt;.&lt;br&gt;
Here is the output of this command at the time of writing this post.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;agent &lt;span class="o"&gt;=&lt;/span&gt; Agent template
cli &lt;span class="o"&gt;=&lt;/span&gt; CLI template
githubbot@quarkusio &lt;span class="o"&gt;=&lt;/span&gt; Example of making a github app
gpt &lt;span class="o"&gt;=&lt;/span&gt; Template using ChatGPT &lt;span class="o"&gt;(&lt;/span&gt;requires &lt;span class="nt"&gt;--preview&lt;/span&gt; and OPENAI_API_KEY&lt;span class="o"&gt;)&lt;/span&gt;
gpt.groovy &lt;span class="o"&gt;=&lt;/span&gt; Template using ChatGPT &lt;span class="k"&gt;for &lt;/span&gt;groovy &lt;span class="o"&gt;(&lt;/span&gt;requires &lt;span class="nt"&gt;--preview&lt;/span&gt; and OPENAI_API_KEY&lt;span class="o"&gt;)&lt;/span&gt;
gpt.kt &lt;span class="o"&gt;=&lt;/span&gt; Template using ChatGPT &lt;span class="k"&gt;for &lt;/span&gt;kotlin &lt;span class="o"&gt;(&lt;/span&gt;requires &lt;span class="nt"&gt;--preview&lt;/span&gt; and OPENAI_API_KEY&lt;span class="o"&gt;)&lt;/span&gt;
hello &lt;span class="o"&gt;=&lt;/span&gt; Basic Hello World template
hello.groovy &lt;span class="o"&gt;=&lt;/span&gt; Basic groovy Hello World template
hello.kt &lt;span class="o"&gt;=&lt;/span&gt; Basic kotlin Hello World template
qcli &lt;span class="o"&gt;=&lt;/span&gt; Quarkus CLI template
qmetrics &lt;span class="o"&gt;=&lt;/span&gt; Quarkus Metrics template
qrest &lt;span class="o"&gt;=&lt;/span&gt; Quarkus REST template
readme.md &lt;span class="o"&gt;=&lt;/span&gt; Basic markdown readme template
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;cli&lt;/code&gt; template create a starter project with &lt;code&gt;picocli&lt;/code&gt; which is a great library for creating console apps that consume command-line arguments.&lt;/p&gt;

&lt;p&gt;Another way to create projects is to pass a prompt that generates starter code thanks to OpenAI's (the company behind chatGPT).&lt;br&gt;
This feature is still experimental but it looks promising as shown in &lt;a href="https://www.infoq.com/news/2023/06/jbang-107/" rel="noopener noreferrer"&gt;this blog post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I personally used JBang to create Java projects for solving some advent of code challenges and it was really useful.&lt;br&gt;
The JBang community was also reactive to my feedback and fixed my issues very quickly.&lt;br&gt;
Big thanks to them!&lt;/p&gt;

&lt;p&gt;To summarize, JBang is particularly well suited for education, for small projects or to try tools or templates available in its AppStore (as long as we make sure they are safe beforehand).&lt;/p&gt;
&lt;h3&gt;
  
  
  Gradle
&lt;/h3&gt;

&lt;p&gt;Gradle is project management tool used by Android developers. It is also and also by Java developers as an alternative to Maven.&lt;br&gt;
Even though it seems to be mostly used by Kotlin or Java devs, Gradle is language agnostic and supports other languages which are at the time of writing: Groovy, Scala, C++ and Swift.&lt;/p&gt;

&lt;p&gt;In addition to project management, Gradle provides a &lt;code&gt;gradle init&lt;/code&gt; command which bootstraps a blank or a &lt;em&gt;hello world&lt;/em&gt; project.&lt;br&gt;
Let's try this out and create a Java project from scratch.&lt;br&gt;
The following snippet show a terminal interaction with the introduced command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ gradle init

Select &lt;span class="nb"&gt;type &lt;/span&gt;of project to generate:
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection &lt;span class="o"&gt;(&lt;/span&gt;default: basic&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;1..4] 2 &lt;span class="c"&gt;# Choose 2 for a 'Hello world' project&lt;/span&gt;

Select implementation language:
  1: C++
  2: Groovy
  3: Java
  4: Kotlin
  5: Scala
  6: Swift
Enter selection &lt;span class="o"&gt;(&lt;/span&gt;default: Java&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;1..6] 3 &lt;span class="c"&gt;# Choose 3 for Java&lt;/span&gt;

Generate multiple subprojects &lt;span class="k"&gt;for &lt;/span&gt;application? &lt;span class="o"&gt;(&lt;/span&gt;default: no&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;yes&lt;/span&gt;, no]

Select build script DSL:
  1: Kotlin
  2: Groovy
Enter selection &lt;span class="o"&gt;(&lt;/span&gt;default: Kotlin&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;1..2] &lt;span class="c"&gt;# Leave default to use Kotlin script for the build file&lt;/span&gt;

Select &lt;span class="nb"&gt;test &lt;/span&gt;framework:
  1: JUnit 4
  2: TestNG
  3: Spock
  4: JUnit Jupiter
Enter selection &lt;span class="o"&gt;(&lt;/span&gt;default: JUnit Jupiter&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;1..4] &lt;span class="c"&gt;# Leave default to use JUnit Jupiter&lt;/span&gt;

Project name &lt;span class="o"&gt;(&lt;/span&gt;default: gradle-java&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="c"&gt;# Leave default to use the folder name as the project name&lt;/span&gt;

Source package &lt;span class="o"&gt;(&lt;/span&gt;default: gradle.java&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="c"&gt;# Leave default to use the suggested package name&lt;/span&gt;

Enter target version of Java &lt;span class="o"&gt;(&lt;/span&gt;min. 7&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;default: 17&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="c"&gt;# Leave default to use your current Java version&lt;/span&gt;

Generate build using new APIs and behavior &lt;span class="o"&gt;(&lt;/span&gt;some features may change &lt;span class="k"&gt;in &lt;/span&gt;the next minor release&lt;span class="o"&gt;)&lt;/span&gt;? &lt;span class="o"&gt;(&lt;/span&gt;default: no&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;yes&lt;/span&gt;, no]


&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Task :init
To learn more about Gradle by exploring our Samples at https://docs.gradle.org/8.5/samples/sample_building_java_applications.html

BUILD SUCCESSFUL &lt;span class="k"&gt;in &lt;/span&gt;31s
2 actionable tasks: 2 executed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the project is generated, we can immediately open it with &lt;code&gt;gradle run&lt;/code&gt; or test it with &lt;code&gt;gradle test&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The project structure is as follows (output of PowerShell's &lt;code&gt;tree /F&lt;/code&gt; command):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;│   .gitattributes
│   .gitignore
│   gradlew
│   gradlew.bat
│   settings.gradle.kts
│
├───app
│   │   build.gradle.kts
│   │
│   └───src
│       ├───main
│       │   ├───java
│       │   │   └───gradle
│       │   │       └───java
│       │   │               App.java
│       │   │
│       │   └───resources
│       └───test
│           ├───java
│           │   └───gradle
│           │       └───java
│           │               AppTest.java
│           │
│           └───resources
└───gradle
    │   libs.versions.toml
    │
    └───wrapper
            gradle-wrapper.jar
            gradle-wrapper.properties
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can verify this follows the usual gradle project structure.&lt;br&gt;
Even a test case is provided out of the box!&lt;/p&gt;
&lt;h3&gt;
  
  
  Maven arcehtype:generate
&lt;/h3&gt;

&lt;p&gt;Maven is a Java project management which was predominant before Gradle and JBang appeared. Indeed, it appeared where the most used tool was &lt;a href="https://stackoverflow.com/questions/39645836/did-maven-killed-the-ant-or-it-is-still-alive" rel="noopener noreferrer"&gt;ant&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Among its features, the &lt;code&gt;mvn arcehtype:generate&lt;/code&gt; command allows to generate various types of projects from templates.&lt;br&gt;
The only requirement is to run the command with the correct template information: its &lt;code&gt;archetypeGroupId&lt;/code&gt;, &lt;code&gt;archetypeArtifactId&lt;/code&gt; and &lt;code&gt;archetypeVersion&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Locally installed templates can be listed by running &lt;code&gt;mvn archetype:generate&lt;/code&gt; and many more can be found by searching on the internet.&lt;br&gt;
Calling &lt;code&gt;mvn archetype:generate&lt;/code&gt; on my computer listed more than &lt;strong&gt;3000&lt;/strong&gt; projects, which can be a bit overwhelming for beginners.&lt;/p&gt;

&lt;p&gt;We must also be careful because the quantity does not necessarily mean quality. For example, to create a simple Java project, we run this command that we find in the official &lt;a href="https://maven.apache.org/archetypes/maven-archetype-quickstart/" rel="noopener noreferrer"&gt;Maven website&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mvn archetype:generate &lt;span class="nt"&gt;-DarchetypeGroupId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;org.apache.maven.archetypes &lt;span class="nt"&gt;-DarchetypeArtifactId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;maven-archetype-quickstart &lt;span class="nt"&gt;-DarchetypeVersion&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.4
&lt;span class="c"&gt;# in powershell, prefix the - with a `&lt;/span&gt;
mvn archetype:generate &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nt"&gt;-DarchetypeGroupId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;org.apache.maven.archetypes &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nt"&gt;-DarchetypeArtifactId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;maven-archetype-quickstart &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nt"&gt;-DarchetypeVersion&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I opened the generated project, I found out in the pom file (as shown in the below snippet) that it is using Java 7 while the current LTS at the time of writing is Java 21.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;project.build.sourceEncoding&amp;gt;&lt;/span&gt;UTF-8&lt;span class="nt"&gt;&amp;lt;/project.build.sourceEncoding&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;maven.compiler.source&amp;gt;&lt;/span&gt;1.7&lt;span class="nt"&gt;&amp;lt;/maven.compiler.source&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;maven.compiler.target&amp;gt;&lt;/span&gt;1.7&lt;span class="nt"&gt;&amp;lt;/maven.compiler.target&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have also encountered this issue with other archetypes but it is surprising that the one provided by the official website is so much outdated.&lt;/p&gt;

&lt;h3&gt;
  
  
  JBang vs Gradle vs Maven
&lt;/h3&gt;

&lt;p&gt;As you may have guessed by reading the previous section, my least favorite way of creating a Java project in the command line is Maven's &lt;code&gt;archetype:generate&lt;/code&gt; for these reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Long commands&lt;/li&gt;
&lt;li&gt;We need to look for them in the internet or in a long list of templates&lt;/li&gt;
&lt;li&gt;We may find outdated templates, even from the official website&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Maven being out of the way, let's continue by comparing &lt;code&gt;gradle init&lt;/code&gt; and JBang.&lt;br&gt;
They both support languages other than Java, namely Groovy and Kotlin, but JBang's support for those is still experimental and &lt;code&gt;gradle init&lt;/code&gt; supports more languages (such as C++ and Swift).&lt;/p&gt;

&lt;p&gt;JBang is adapted for small Java projects or for ones that have a template.&lt;br&gt;
For example, and as far as I know, only JBang provides a &lt;code&gt;picocli&lt;/code&gt; starter.&lt;br&gt;
&lt;code&gt;gradle init&lt;/code&gt; is a better choice for large projects that want to start from scratch and want to have a folder structure.&lt;br&gt;
However, if you want to create a project with a specific Java framework, you may need to use the tools provided by the framework:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spring boot: &lt;a href="https://start.spring.io/" rel="noopener noreferrer"&gt;spring initializr&lt;/a&gt; or &lt;a href="https://docs.spring.io/spring-boot/docs/current/reference/html/cli.html" rel="noopener noreferrer"&gt;Spring Boot CLI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Quarkus: &lt;a href="https://code.quarkus.io/" rel="noopener noreferrer"&gt;code.quarkus.io&lt;/a&gt; or &lt;a href="https://quarkus.io/guides/cli-tooling" rel="noopener noreferrer"&gt;Quarkus CLI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;JEE: &lt;a href="https://openliberty.io/start/" rel="noopener noreferrer"&gt;Open Liberty starter&lt;/a&gt;, &lt;a href="https://start.jakarta.ee/" rel="noopener noreferrer"&gt;Eclipse starter for Jakarta EE&lt;/a&gt;, &lt;a href="https://github.com/wildfly/quickstart" rel="noopener noreferrer"&gt;Wildlfy quickstart projects on GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To summarize, we have many tools at our disposal and all provide great features.&lt;/p&gt;

&lt;p&gt;Let's explore further the tools we just introduced.&lt;/p&gt;
&lt;h3&gt;
  
  
  Spring Boot CLI and Quarkus CLI
&lt;/h3&gt;

&lt;p&gt;Two of the most famous Java server frameworks, namely &lt;a href="https://spring.io/" rel="noopener noreferrer"&gt;Spring&lt;/a&gt; and &lt;a href="https://quarkus.io/" rel="noopener noreferrer"&gt;Quarkus&lt;/a&gt;, provide CLIs for improving DX.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.spring.io/spring-boot/docs/current/reference/html/cli.html" rel="noopener noreferrer"&gt;Spring Boot CLI&lt;/a&gt; generates new Spring boot projects and encodes passwords (for use with Spring Security).&lt;br&gt;
The project generation feature is the CLI counterpart of the web UI &lt;a href="https://start.spring.io/" rel="noopener noreferrer"&gt;start.spring.io&lt;/a&gt;.&lt;br&gt;
Below are some examples of using the Spring Boot CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generate a zip file that contains a Kotlin project which uses Gradle Kotlin build file and inclids the web-services and postgresql dependencies&lt;/span&gt;
spring init &lt;span class="nt"&gt;--build&lt;/span&gt; gradle &lt;span class="nt"&gt;-l&lt;/span&gt; kotlin &lt;span class="nt"&gt;-t&lt;/span&gt; gradle-project-kotlin &lt;span class="nt"&gt;-d&lt;/span&gt; web-services,postgresql
&lt;span class="c"&gt;# Generate a Java 21 project that uses maven and includes the web-services and postgresql dependencies&lt;/span&gt;
spring init &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="nt"&gt;--build&lt;/span&gt; maven &lt;span class="nt"&gt;-j&lt;/span&gt; 21 &lt;span class="nt"&gt;-a&lt;/span&gt; sb-cli-demo &lt;span class="nt"&gt;-g&lt;/span&gt; org.sb.test &lt;span class="nt"&gt;-d&lt;/span&gt; web-services,postgresql &lt;span class="nt"&gt;--description&lt;/span&gt; &lt;span class="s2"&gt;"project created with Spring Boot CLI"&lt;/span&gt;
&lt;span class="c"&gt;# List all possible options to initialize a project&lt;/span&gt;
spring &lt;span class="nb"&gt;help &lt;/span&gt;init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spring Boot CLI features are very basic.&lt;br&gt;
Some features that I miss are upgrading spring version and adding new dependencies.&lt;br&gt;
Maybe they'll be implemented in the future.&lt;br&gt;
But as it is right now, I don't need to keep it installed on my computer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://quarkus.io/guides/cli-tooling" rel="noopener noreferrer"&gt;Quarkus CLI&lt;/a&gt; provides much more features than Spring Boot CLI.&lt;br&gt;
Not only it allows to create new Quarkus projects, but it's also able to manage other life cycle tasks: running dev mode, building for production, upgrading versions, etc.&lt;br&gt;
Thus, it can be used instead of Gradle or Maven for most tasks.&lt;br&gt;
This makes the DX with Quarkus much more universal and agnostic of the underlying build tool (Gradle or Maven).&lt;/p&gt;

&lt;p&gt;Here are some sample uses of quarkus cli:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List available quarkus extension&lt;/span&gt;
quarkus ext &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;span class="c"&gt;# Create a Quarkus rest API wirh Gradle build system&lt;/span&gt;
quarkus create app &lt;span class="nt"&gt;--extension&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"rest"&lt;/span&gt; &lt;span class="nt"&gt;--gradle-kotlin-dsl&lt;/span&gt; &lt;span class="nt"&gt;--wrapper&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; .&lt;span class="se"&gt;\c&lt;/span&gt;ode-with-quarkus&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="c"&gt;# Run the app in dev mode&lt;/span&gt;
./gradlew quarkusDev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a Quarkus CLI app (with Picocli) that uses Kotlin and Gradle with Kotlin DSL&lt;/span&gt;
quarkus create cli &lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"quarkus-cli-demo"&lt;/span&gt; &lt;span class="nt"&gt;--kotlin&lt;/span&gt; &lt;span class="nt"&gt;--gradle-kotlin-dsl&lt;/span&gt; &lt;span class="nt"&gt;--wrapper&lt;/span&gt;
&lt;span class="c"&gt;# Open the folder&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;code-with-quarkus
&lt;span class="c"&gt;# run the app with gradle&lt;/span&gt;
./gradlew quarkusRun &lt;span class="nt"&gt;-Dquarkus&lt;/span&gt;.args&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'-c -w --val 1'&lt;/span&gt; &lt;span class="nt"&gt;--console&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;plain
&lt;span class="c"&gt;# Run the app with quarkus cli (this fails at the time of writing)&lt;/span&gt;
quarkus run &lt;span class="nt"&gt;-Dquarkus&lt;/span&gt;.args&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'-c -w --val 1'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quarkus CLI is a very interesting and useful tool, a must-have for Quarkus devs.&lt;br&gt;
I personally used it to migrate a &lt;a href="https://blog.worldline.tech/2023/12/26/feedback_upgrade_quarkus_2_3.html" rel="noopener noreferrer"&gt;Quarkus project&lt;/a&gt; and this tool helped me a lot!&lt;br&gt;
I was also surprised to discover that we can create a picocli app with Quarkus.&lt;br&gt;
So, please give it a try.&lt;/p&gt;
&lt;h2&gt;
  
  
  Project scaffolders
&lt;/h2&gt;

&lt;p&gt;We have seen earlier that JBang, Gradle and Maven are able to generate projects from scratch.&lt;br&gt;
Scaffolding goes a bit further by also generating other layers of the app (database, front-end, etc.).&lt;/p&gt;
&lt;h3&gt;
  
  
  Yeoman
&lt;/h3&gt;

&lt;p&gt;Yeoman is a general purpose project scaffolder which is framework and language agnostic.&lt;br&gt;
Even though the tool itself relies on &lt;code&gt;npm&lt;/code&gt; (which is installed alongside nodeJS), it can generate any type of project as long as the corresponding project generator is available.&lt;br&gt;
A project generator defines how to scaffold a set of projects.&lt;br&gt;
Fortunately for us, we can explore generators in the &lt;a href="https://yeoman.io/generators/" rel="noopener noreferrer"&gt;discover page&lt;/a&gt; and search for the one that we need in a webUI.&lt;br&gt;
There we can find for example, starter projects for VSCode extensions, Office extensions, webaaps, or even servers.&lt;/p&gt;

&lt;p&gt;Anyone can create a &lt;a href="https://yeoman.io/authoring/" rel="noopener noreferrer"&gt;project generator&lt;/a&gt; and publish it to npm so that it is available in the &lt;a href="https://yeoman.io/generators/" rel="noopener noreferrer"&gt;yeoman search engine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In order to generate a yeoman project locally, we first need to install yeoman with &lt;code&gt;npm i -g yo&lt;/code&gt;.&lt;br&gt;
Next, we install the generator with &lt;code&gt;npm install -g [generator]&lt;/code&gt;.&lt;br&gt;
For example, the &lt;a href="https://github.com/daggerok/generator-jvm" rel="noopener noreferrer"&gt;generator-jvm&lt;/a&gt; can be installed &lt;code&gt;npm install -g generator-jvm&lt;/code&gt; and provides some JVM project generators.&lt;br&gt;
Finally, we need to run the generator with &lt;code&gt;yo generator&lt;/code&gt;.&lt;br&gt;
For example, to generate a JVM project, we can run &lt;code&gt;yo jvm&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For Java developers, there is a more tailored scaffolder based on Yeoman which is called JHispter.&lt;/p&gt;
&lt;h3&gt;
  
  
  JHipster and JHipsterLite
&lt;/h3&gt;

&lt;p&gt;JHipster is a project scaffolder specialized in Java projects.&lt;br&gt;
It generates ready-to-use full stack projects with a database, a Java backend, a web frontend and different common services (such as authentication and caching).&lt;br&gt;
The backend is based on Spring Boot with Java and the frontend is based on Angular, React or Vue.&lt;/p&gt;

&lt;p&gt;The tool works by asking questions to the user and generating the project based on the answers.&lt;br&gt;
Here is an example of the questions asked and the anwsers that I gave when creating a new project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;? What is the base name of your application? jhispterDemo
? Which &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt; of application would you like to create? Monolithic application &lt;span class="o"&gt;(&lt;/span&gt;recommended &lt;span class="k"&gt;for &lt;/span&gt;simple projects&lt;span class="o"&gt;)&lt;/span&gt;
? What is your default Java package name? com.mycompany.myapp
? Would you like to use Maven or Gradle &lt;span class="k"&gt;for &lt;/span&gt;building the backend? Gradle
? Do you want to make it reactive with Spring WebFlux? Yes
? Which &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt; of authentication would you like to use? JWT authentication &lt;span class="o"&gt;(&lt;/span&gt;stateless, with a token&lt;span class="o"&gt;)&lt;/span&gt;
? Besides JUnit, which testing frameworks would you like to use?
? Which &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt; of database would you like to use? SQL &lt;span class="o"&gt;(&lt;/span&gt;H2, PostgreSQL, MySQL, MariaDB, Oracle, MSSQL&lt;span class="o"&gt;)&lt;/span&gt;
? Which &lt;span class="k"&gt;*&lt;/span&gt;production&lt;span class="k"&gt;*&lt;/span&gt; database would you like to use? PostgreSQL
? Which &lt;span class="k"&gt;*&lt;/span&gt;development&lt;span class="k"&gt;*&lt;/span&gt; database would you like to use? PostgreSQL
? Which cache &lt;span class="k"&gt;do &lt;/span&gt;you want to use? &lt;span class="o"&gt;(&lt;/span&gt;Spring cache abstraction&lt;span class="o"&gt;)&lt;/span&gt; Ehcache &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;local &lt;/span&gt;cache, &lt;span class="k"&gt;for &lt;/span&gt;a single node&lt;span class="o"&gt;)&lt;/span&gt;
? Do you want to use Hibernate 2nd level cache? Yes
? Which other technologies would you like to use? Elasticsearch as search engine, Apache Kafka as asynchronous messages broker
? Do you want to &lt;span class="nb"&gt;enable &lt;/span&gt;Gradle Enterprise integration? No
? Which &lt;span class="k"&gt;*&lt;/span&gt;framework&lt;span class="k"&gt;*&lt;/span&gt; would you like to use &lt;span class="k"&gt;for &lt;/span&gt;the client? React
? Besides Jest/Vitest, which testing frameworks would you like to use? Cypress
? Do you want to generate the admin UI? Yes
? Would you like to use a Bootswatch theme &lt;span class="o"&gt;(&lt;/span&gt;https://bootswatch.com/&lt;span class="o"&gt;)&lt;/span&gt;? Default JHipster
? Would you like to &lt;span class="nb"&gt;enable &lt;/span&gt;internationalization support? Yes
? Please choose the native language of the application French
? Please choose additional languages to &lt;span class="nb"&gt;install &lt;/span&gt;English
? Would you like to audit Cypress tests? Yes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the project is created, the database can be designed with &lt;a href="https://start.jhipster.tech/jdl-studio/" rel="noopener noreferrer"&gt;JDL Studio&lt;/a&gt; and imported into the project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://lite.jhipster.tech/" rel="noopener noreferrer"&gt;Jhipster Lite&lt;/a&gt; (or JHLite) is the web counterpart of JHipster.&lt;br&gt;
It is not feature-equivalent to JHipster but it allows to create a project from a web UI.&lt;br&gt;
&lt;a href="https://www.jhipster.tech/jhipster-lite/" rel="noopener noreferrer"&gt;This page&lt;/a&gt; notes that JHLite it is better suited for designing around business and XDD approaches (eXtreme Design Driven).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkgysahlwbknslg1962n1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkgysahlwbknslg1962n1.png" alt="Jhipster lite" width="800" height="589"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Both JHipster and JHLite are very useful for quickly prototyping or for projects that use the same technologies provided by them.&lt;br&gt;
However, the opinionated nature of the generated code and the selected frameworks may not suit everyone.&lt;br&gt;
For example, Quarkus is not supported by JHipster and we need to use the &lt;a href="https://quarkus.io/guides/cli-tooling" rel="noopener noreferrer"&gt;Quarkus CLI&lt;/a&gt; to generate a Quarkus project.&lt;/p&gt;
&lt;h3&gt;
  
  
  Advantages and drawbacks
&lt;/h3&gt;

&lt;p&gt;Project scaffolders allow to get a project running really fast where a lot of boilerplate code is already written for us.&lt;br&gt;
Thus the gain in terms of effort and time is considerable.&lt;br&gt;
However, the generated code may not coincide with the developer's way of coding.&lt;br&gt;
Also, some choices are very opinionated, such as the exclusive use of Spring on JHipster.&lt;br&gt;
There's also the issue of vendor-locking where we must update our project using tools provided by the scaffolder if we don't want to take the risk of breaking the dependencies.&lt;br&gt;
In addition to that, since we are dealing with a community project, we face the usual issues of trust and updates.&lt;br&gt;
For example, when I installed Yeoman (on January 2024), npm detected 7 high vulnerabilities.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; yo

added 801 packages, and audited 802 packages &lt;span class="k"&gt;in &lt;/span&gt;1m

122 packages are looking &lt;span class="k"&gt;for &lt;/span&gt;funding
  run &lt;span class="sb"&gt;`&lt;/span&gt;npm fund&lt;span class="sb"&gt;`&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;details

13 vulnerabilities &lt;span class="o"&gt;(&lt;/span&gt;6 moderate, 7 high&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, in my opinion, scaffolders are quite relevant for prototypes, PoCs or when we have tight deadlines.&lt;br&gt;
For long term projects, I would use official tools to generate them and avoid scaffolders so that I keep more control over my code.&lt;br&gt;
For example, to develop a Quarkus + Vue project, I'll use &lt;a href="https://quarkus.io/guides/getting-started" rel="noopener noreferrer"&gt;Quarkus CLI&lt;/a&gt; or &lt;a href="https://code.quarkus.io/" rel="noopener noreferrer"&gt;code.quarkus.io&lt;/a&gt; to create the Quarkus project and &lt;a href="https://vitejs.dev/guide/" rel="noopener noreferrer"&gt;vite&lt;/a&gt; to create a Vue project.&lt;br&gt;
Of course, this is my current personal opinion which may change in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This post has shown how to get advantage of the command line tools to install a JDK and to create new projects.&lt;br&gt;
We first have seen how to install and manage Java JDKs with &lt;code&gt;scoop&lt;/code&gt; and &lt;code&gt;SDKMAN!&lt;/code&gt;.&lt;br&gt;
Next, we studied some tools that create Java projects which are &lt;strong&gt;JBang&lt;/strong&gt;, &lt;strong&gt;Gradle&lt;/strong&gt; and &lt;strong&gt;Maven&lt;/strong&gt;.&lt;br&gt;
After that, we have compared &lt;code&gt;Spring Boot CLI&lt;/code&gt; and &lt;code&gt;Quarkus CLI&lt;/code&gt; which are specialized for their respective frameworks.&lt;br&gt;
Finally, we have seen how to scaffold projects with &lt;code&gt;Yeoman&lt;/code&gt;, &lt;code&gt;JHipster&lt;/code&gt; and &lt;code&gt;JHipsterLite&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I have also shared my opinion on the use of these tools.&lt;br&gt;
To summarize, I recommend to use CLI tools as much as possible depending on the use case to avoid GUIs and to keep the DX as simple as possible.&lt;/p&gt;

&lt;p&gt;I hope that this post has been useful to you and that you have discovered new tools that will make your Java development experience more enjoyable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://incusdata.com/blog/which-java-jdk-should-you-use" rel="noopener noreferrer"&gt;Which Java JDK Should You Use?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://marcus-biel.com/cropped-java-craftsman-duke-png/" rel="noopener noreferrer"&gt;cropped-java-craftsman-duke image from Marcus Biel&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>tooling</category>
      <category>jbang</category>
      <category>sdkman</category>
    </item>
    <item>
      <title>My feedback after upgrading a Quarkus project from v2 to v3</title>
      <dc:creator>Yassine Benabbas </dc:creator>
      <pubDate>Mon, 04 Dec 2023 11:12:51 +0000</pubDate>
      <link>https://dev.to/worldlinetech/my-feedback-after-upgrading-a-quarkus-project-from-v2-to-v3-17da</link>
      <guid>https://dev.to/worldlinetech/my-feedback-after-upgrading-a-quarkus-project-from-v2-to-v3-17da</guid>
      <description>&lt;p&gt;In our team (Worldline TechSquad), we are develop and maintain a CRUD REST API for managing our internal events. The development started with Kotlin and Quarkus 1 which was updated to Quarkus 2.&lt;br&gt;
Since July 2023 Quarkus 3.2 is the current &lt;a href="https://quarkus.io/blog/lts-releases/" rel="noopener noreferrer"&gt;LTS release&lt;/a&gt;. Thus, we wanted to update our app as soon as possible to remain on a proper long term release.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1fpd17p5aahvg3ojps2m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1fpd17p5aahvg3ojps2m.png" alt="Easy upgrade"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This post shares my experience upgrading our REST API from Quarkus 2 to Quarkus 3 hoping that it'll help you avoid our issues if you do a similar upgrade 😊.&lt;/p&gt;
&lt;h2&gt;
  
  
  (Almost) Easy upgrade with Quarkus-cli
&lt;/h2&gt;

&lt;p&gt;The official documentation recommends to upgrade using the quarkus-cli by running &lt;code&gt;quarkus update&lt;/code&gt;. However, this did not work on our project on the first try.&lt;br&gt;
This may be caused by many reasons, maybe because our project was not created using a recent Quarkus starter or because we modified the POM file a bit too much.&lt;br&gt;
For example, we were not using the Quarkus BOM and we were specifying the version on each Quarkus library.&lt;/p&gt;

&lt;p&gt;Since my assumptions were based on a POM file which is not quarkus-cli friendly, I decided to make a new one.&lt;br&gt;
In order to do that, I first created a new project through &lt;a href="https://code.quarkus.io/" rel="noopener noreferrer"&gt;code.quarkus.io&lt;/a&gt; and selected all the extensions that we use in our project: Kotlin, Postgres and Panache, RESTEasy Classic Jackson, etc. After that, I replaced our old POM with the one the generated one. Finally, I added the libraries that we use and that were not available as extensions, such as &lt;strong&gt;arrow&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After updating the POM and checking that the project still compiles, I tried to run &lt;code&gt;quarkus update&lt;/code&gt; again but the update failed. Since I'm on Windows I tried on PowerShell Core and on CMD but both failed. My last hope was using a proper Linux environment: &lt;strong&gt;WSL&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So I installed the quakus-cli, again, on a WSL Ubuntu and ran another &lt;code&gt;quarkus update&lt;/code&gt;.&lt;br&gt;
This time, the upgrade successfully completed. One of the biggest changes that this tool did was to rename all &lt;code&gt;javax&lt;/code&gt; imports to &lt;code&gt;jakarta&lt;/code&gt;.&lt;br&gt;
I didn't count how many lines were affected but I think it was near to 100 lines.&lt;br&gt;
So the upgrade tool proved to be really helpful.&lt;/p&gt;

&lt;p&gt;After the upgrade, our project was also building without issues and the REST API was running again. Yay!&lt;br&gt;
The next step was to check if there were no regressions on runtime.&lt;/p&gt;
&lt;h2&gt;
  
  
  Issues encountered after the upgrade
&lt;/h2&gt;

&lt;p&gt;Can you guess if we encountered any issues ? Of course yes!&lt;/p&gt;

&lt;p&gt;Actually, most of the bugs that we had originated from migrating Hibernate from version 5 (used by Quarkus 2) to version 6 (used by Quarkus 3). Quarkus provided two migration guides (&lt;a href="https://github.com/quarkusio/quarkus/wiki/Migration-Guide-3.0#jpa--hibernate-orm" rel="noopener noreferrer"&gt;this one&lt;/a&gt;, and &lt;a href="https://github.com/quarkusio/quarkus/wiki/Migration-Guide-3.0:-Hibernate-ORM-5-to-6-migration" rel="noopener noreferrer"&gt;this other one&lt;/a&gt;) just for Hibernate. Since I didn't read the docs beforehand 🤦‍♂️, the issues that we encountered were to be expected.&lt;/p&gt;

&lt;p&gt;Here is a listing of the errors that we got and how we fixed them:&lt;/p&gt;
&lt;h3&gt;
  
  
  Composite keys new requirement
&lt;/h3&gt;

&lt;p&gt;Composite keys require to implement &lt;code&gt;Comparable&lt;/code&gt; or they fail to instantiate at runtime.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// before: fails on Quarkus 3 (with Hibernate 6)&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;CompositeKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;OtherCompositeKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OtherCompositeKey&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Serializable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To fix this, we made our composite keys implement &lt;code&gt;Comparable&lt;/code&gt; as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// after&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;CompositeKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;otherCompositeKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;OtherCompositeKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OtherCompositeKey&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Serializable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Comparable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CompositeKey&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Comparing reference works in our case becase we use a UUID &lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;compareTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CompositeKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compareTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our implementation of &lt;code&gt;compareTo(other: CompositeKey)&lt;/code&gt; compares only &lt;code&gt;reference&lt;/code&gt; field instead of using both fields of the composite key. This works fine for us (at least for now) because we use UUIDs for &lt;code&gt;reference&lt;/code&gt;s, and it's very rare to get a &lt;a href="https://stackoverflow.com/questions/2513573/how-good-is-javas-uuid-randomuuid" rel="noopener noreferrer"&gt;duplicate UUID&lt;/a&gt;. Maybe in the future we'll implement a more correct &lt;code&gt;compareTo&lt;/code&gt; like this one :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;compareTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CompositeKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reference&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;otherCompositeKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compareTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reference&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;otherCompositeKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What do you think ?&lt;/p&gt;

&lt;h3&gt;
  
  
  Implicit foreign column names not working anymore
&lt;/h3&gt;

&lt;p&gt;We had JPQL queries that reference foreign columns and we used the names generated by hibernate. After migrating to Quarkus 3 (thus Hibernate 6), those queries failed because the foreign columns were not found anymore. For example, these foreign column names were not detected anymore in JPQL: &lt;code&gt;edition_event_reference&lt;/code&gt; and &lt;code&gt;edition_reference&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It looks like we could have fixed this using the &lt;code&gt;foreignKey&lt;/code&gt; attribute of the &lt;code&gt;@JoinColumn&lt;/code&gt; annotation as explained &lt;a href="https://stackoverflow.com/a/30121636" rel="noopener noreferrer"&gt;in this post&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@ManyToOne&lt;/span&gt;
&lt;span class="nd"&gt;@JoinColumn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"edition_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;foreignKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@ForeignKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"edition_reference"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Edition&lt;/span&gt; &lt;span class="n"&gt;editionReference&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But in our case, we simply replaced those columns with &lt;code&gt;join&lt;/code&gt; statements. For example, we replaced uses of &lt;code&gt;edition_reference&lt;/code&gt; by a join; &lt;code&gt;select s from Session s join s.edition e&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  It's good to be lazy
&lt;/h3&gt;

&lt;p&gt;Non-lazy (or eager) OneToMany fields started to generate cryptic errors. A &lt;a href="https://fluca1978.github.io/2019/10/22/Hibernate.html" rel="noopener noreferrer"&gt;fortunately found&lt;/a&gt; forum post gave me the hint to set the &lt;code&gt;fetch&lt;/code&gt; attribute to &lt;code&gt;FetchType.LAZY&lt;/code&gt; and the error was magically fixed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Angry sequence generator
&lt;/h3&gt;

&lt;p&gt;We were using a &lt;code&gt;@GeneratedValue&lt;/code&gt; which was referencing an automatically generated sequence by hibernate which is called &lt;code&gt;hibernate_sequence&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Id&lt;/span&gt;
&lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;strategy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GenerationType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SEQUENCE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;generator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"hibernate_sequence"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After migrating to Hibernate 6, this caused issues with the generated ids. After some investigation, my colleague shared &lt;a href="https://ntsim.uk/posts/how-to-use-hibernate-identifier-sequence-generators-properly" rel="noopener noreferrer"&gt;an article that helped us fix the issue&lt;/a&gt;. Kudos to the author &lt;a href="https://ntsim.uk/" rel="noopener noreferrer"&gt;Nicholas Tsim&lt;/a&gt; !&lt;/p&gt;

&lt;p&gt;After reading the article, we understood that issue was that the allocation size (the range of ids that managed by the Hibernate) which was not in sync between hibernate and the database sequence linked to it.&lt;br&gt;
To put it simply, Hibernate optimizes requests for the next id the sequence by managing a set of ids internally which is called &lt;code&gt;allocationSize&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, if the next number of the database sequence is 5 and &lt;code&gt;allocationSize&lt;/code&gt;, then Hibernate will internally manage the autoincrement from 5 to 14 (or 15 I'm not sure here :)). &lt;br&gt;
When it reaches the next id, it will request the next database sequence&lt;br&gt;
We fixed this by explicitly specifying the allocation size in code and &lt;/p&gt;

&lt;p&gt;Si our code we added a &lt;code&gt;@SequenceGenerator&lt;/code&gt; annotation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Id&lt;/span&gt;
&lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;strategy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GenerationType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SEQUENCE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;generator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"registration_id_seq"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@SequenceGenerator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"registration_id_seq"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sequenceName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"hibernate_sequence"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;allocationSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in the database we set the increment of the sequence with the same amount of the allocation size. &lt;br&gt;
We achieved this by running this SQL script &lt;code&gt;ALTER SEQUENCE hibernate_sequence INCREMENT BY 10;&lt;/code&gt;. &lt;br&gt;
After these changes, our id generation issue was finally fixed !&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Upgrading our Quarkus project from version 2 to version 3 went globally very smoothly given the legacy code that we had. &lt;br&gt;
Our pain points were the POM which was not friendly with upgrade tool, and the migration from Hibernate 5 to 6.&lt;br&gt;
But the biggest pain point was the latter.&lt;br&gt;
Thus, the lesson that I learnt is to not underestimate the migration impacts of a dependency which can be bigger than the framework that uses it.&lt;/p&gt;

&lt;p&gt;After overcoming all the challenges, our API is now rocking on Quarkus 3 LTS and we are waiting for the next LTS to arrive. &lt;/p&gt;

&lt;p&gt;Happy coding !&lt;/p&gt;

</description>
      <category>quarkus</category>
      <category>upgrade</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>Creating an iCalendar event with HTML content in Kotlin / JVM</title>
      <dc:creator>Yassine Benabbas </dc:creator>
      <pubDate>Wed, 25 Jan 2023 19:33:37 +0000</pubDate>
      <link>https://dev.to/worldlinetech/creating-an-icalendar-event-with-html-content-pbe</link>
      <guid>https://dev.to/worldlinetech/creating-an-icalendar-event-with-html-content-pbe</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://en.wikipedia.org/wiki/ICalendar" rel="noopener noreferrer"&gt;iCalendar&lt;/a&gt; format allows to define calendar events in a simple text file. The most common file extension of an iCalendar file is .ics and is supported by many apps, such as Outlook.&lt;/p&gt;

&lt;p&gt;An iCalendar can define many types of information, such as events and to-dos, using many standard properties. Among these properties, an important one is missing for standard events, which is to define the event description in HTML. Indeed, the default description property &lt;code&gt;DESCRIPTION&lt;/code&gt; takes only plain text values. Fortunately, there is an experimental property &lt;code&gt;X-ALT-DESC&lt;/code&gt; that can supports HTML markup.&lt;/p&gt;

&lt;p&gt;In this post, we'll use a library called &lt;a href="https://github.com/mangstadt/biweekly" rel="noopener noreferrer"&gt;biweekly&lt;/a&gt; in order to programmatically create an iCalendar event with an HTML description. But first let's start with some manually created files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Manually created iCalendar with an X-ALT-DESC property
&lt;/h2&gt;

&lt;p&gt;The following file illustrates a basic iCalendar event that contains both &lt;code&gt;DESCRIPTION&lt;/code&gt; and &lt;code&gt;X-ALT-DESC&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//xyz Corp//NONSGML PDA Calendar Version 1.0//EN
BEGIN:VEVENT
UID:uid1@awesome.com
ORGANIZER:mailto:organizer@awesome.com
DTSTAMP:20230101T100000Z
DTSTART:20230101T100000Z
DTEND:20230102T113000Z
SUMMARY:Awesome Event
DESCRIPTION:Please join this awesome event
X-ALT-DESC;FMTTYPE=text/html:&amp;lt;!doctype html&amp;gt;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&amp;lt;h1 style="color:blue"&amp;gt;Awesome event&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;Please join&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;
END:VEVENT
END:VCALENDAR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setting both &lt;code&gt;DESCRIPTION&lt;/code&gt; and &lt;code&gt;X-ALT-DESC&lt;/code&gt; properties in the same event is valid and works on any tool that is compatible with iCalendar. In fact, if the tool does not support &lt;code&gt;X-ALT-DESC&lt;/code&gt;, then &lt;code&gt;DESCRIPTION&lt;/code&gt; will be used instead. Tools that do support &lt;code&gt;X-ALT-DESC&lt;/code&gt; can ignore the &lt;code&gt;DESCRIPTION&lt;/code&gt; field and use &lt;code&gt;X-ALT-DESC&lt;/code&gt; instead. That's what Outlook does as we'll see later.&lt;/p&gt;

&lt;p&gt;You can test iCalendar by first putting this content in a text file and changing its extension to &lt;code&gt;.ics&lt;/code&gt;. Next you can just open it if you have a calendar app installed (such as Outlook) or import it into a Calendar app (such as Google Calendar). You can also &lt;a href="///assets/files/posts/html-icalendar/awesome-event.ics"&gt;download this ics file&lt;/a&gt; which already contains the above text.&lt;/p&gt;

&lt;p&gt;This is how Outlook renders the ics file:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn4zebnesjvcl4rc5ccui.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn4zebnesjvcl4rc5ccui.png" alt="manual ics outlook" width="422" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can note that Outlook ignores &lt;code&gt;DESCRIPTION&lt;/code&gt; when &lt;code&gt;X-ALT-DESC&lt;/code&gt; is available.&lt;/p&gt;

&lt;p&gt;And here is the rendering of Google Calendar. We can note that it just ignores &lt;code&gt;X-ALT-DESC&lt;/code&gt; and uses &lt;code&gt;DESCRIPTION&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkr1faa2srcmx18zjb73t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkr1faa2srcmx18zjb73t.png" alt="manual ics calendar" width="548" height="594"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating an iCalendar with an X-ALT-DESC property with biweekly
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/mangstadt/biweekly" rel="noopener noreferrer"&gt;biweekly&lt;/a&gt; is an iCalendar library which supports the JVM and Android. This means that it can target a wide range of applications. In this chapter, we'll use this library to create and ics file in a Kotlin project. We'll use IntelliJ IDEA community edition because it's free and provide powerful Kotlin support.&lt;/p&gt;

&lt;p&gt;So, let's create a new project by choosing &lt;strong&gt;Kotlin&lt;/strong&gt; as language:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy5ullxao166rda9uy1fc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy5ullxao166rda9uy1fc.png" alt="new project" width="566" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After that, we need to add biweekly as a &lt;em&gt;dependency&lt;/em&gt; in &lt;code&gt;build.gradle.kts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies {
    ...
    implementation("net.sf.biweekly:biweekly:0.6.6") // add this line
    ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next comes the fun part where we write some code. The following snippet creates an iCalendar event and prints it to the console and writes it into a file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;val event = VEvent()
// set start and end dates
val startDateTime = LocalDateTime.of(2022, 1, 1, 10, 0)
event.setDateStart(Date.from(startDateTime.toInstant(ZoneOffset.UTC)), true)
val endDateTime = startDateTime.plusDays(2).plusHours(1).plusMinutes(30)
event.setDateEnd(Date.from(endDateTime.toInstant(ZoneOffset.UTC)), true)
// set summary, description and organizer
event.setSummary("Awesome event")
event.setDescription("Please join this awesome event")
event.setOrganizer("organizer@awesome.com")
// set the HTML content to X-ALT-DESC. We also need to set FMTTYPE parameter to text/html
event.setExperimentalProperty(
    "X-ALT-DESC",
    "&amp;lt;!doctype html&amp;gt;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;" +
            "&amp;lt;h1 style='color:blue'&amp;gt;Awesome event&amp;lt;/h1&amp;gt;" +
            "&amp;lt;p&amp;gt;Please join&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;"
).setParameter("FMTTYPE", "text/html")
// create the iCalendar
val ical = ICalendar()
ical.addEvent(event)
// Print it on the console
print(Biweekly.write(ical).go())
// Write to file
val file = File("awesome-event.ics")
Biweekly.write(ical).go(file)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generated ics is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Michael Angstadt//biweekly 0.6.6//EN
BEGIN:VEVENT
UID:4a945aec-3b58-437d-90fe-7ded8e352ec9
DTSTAMP:20230125T184903Z
DTSTART:20220101T100000Z
DTEND:20220103T113000Z
SUMMARY:Awesome event
DESCRIPTION:Please join this awesome event
ORGANIZER:mailto:organizer@awesome.com
X-ALT-DESC;FMTTYPE=text/html:&amp;lt;!doctype html&amp;gt;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&amp;lt;h1 style='color:bl
 ue'&amp;gt;Awesome event&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;Please join&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;
END:VEVENT
END:VCALENDAR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can note that biweekly automatically adds the VERSION, PRODID and UID fields, which is always nice.&lt;/p&gt;

&lt;p&gt;Opening the ics with outlook show this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F50wwn1qhm57y9i5bm1mk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F50wwn1qhm57y9i5bm1mk.png" alt="biweekly ics outlook" width="402" height="304"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can go even further by embedding an image using a base64 src. Here is the improved code that embeds an image in the ics.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;val event = VEvent()
val startDateTime = LocalDateTime.of(2023, 1, 1, 10, 0)
event.setDateStart(Date.from(startDateTime.toInstant(ZoneOffset.UTC)), true)
val endDateTime = startDateTime.plusDays(2).plusHours(1).plusMinutes(30)
event.setDateEnd(Date.from(endDateTime.toInstant(ZoneOffset.UTC)), true)
event.setSummary("Awesome event")
event.setDescription("Please join this awesome event")
event.setOrganizer("organizer@awesome.com")
event.setExperimentalProperty(
    "X-ALT-DESC",
    "&amp;lt;!doctype html&amp;gt;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;" +
            "&amp;lt;h1 style='color:blue'&amp;gt;Awesome event&amp;lt;/h1&amp;gt;" +
            "&amp;lt;p&amp;gt;Please join&amp;lt;/p&amp;gt;" +
            "&amp;lt;img src=\"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcHBw8LCwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7/wAARCABkAGQDASIAAhEBAxEB/8QAHQAAAgMAAwEBAAAAAAAAAAAAAAgFBgcBAwQJAv/EAFAQAAECBQIDBAUFCgcRAAAAAAECAwAEBQYRByEIEjETQVFxFCJhgZEVF0Kh4TJSVVZicoKTsdIWIzNjkpSiGCQnNjdFRkdUV3ODhLLBwtH/xAAbAQACAwEBAQAAAAAAAAAAAAAABQMEBgcBAv/EADQRAAEEAQEGAggFBQAAAAAAAAEAAgMEEQUGEiExQVETcRQVFiKBkaGxMkJSYcEjU9Hh8f/aAAwDAQACEQMRAD8AcuCCIa8bloto25N3DcE83JU6TRzuuLPXwSB3qJ2AHWBClH3mpdhbz7iGm0JKluLVhKQOpJPQQuer3FrZNquvU21GDdNSbJSXGl8kohQ/nMHn/RGPbGI6gak6i8R12O2rZ7ExTLZbVlTIVypLfc7MrHkSEDI22BO8bBpPoDZlkstzVRlkV6tAAqmZpsFttX822cgAZ6nJyOohTqWsV6Aw85d2CsQ13Sceiyh3UXid1VV2lBZnKRTHDsZFkSjQT/xV+sr3KMeT+5x1YuBanrlvKULqjuJieemFb9e4j64b5KeVAR3JG2wH7I5O8Y+fbC084iaGj5lMWafEOZylBPCddssQ5KXjSw4OhCXUY94BjtZsHiZsEl23Ljn55psjDcnUi8kgfzTuxHswYbrvzHJBwOm0QxbWXWHLsEeX+F6dPiKWOzuLa+rWqKaTqjahmeU8q3W2TKzSR4lChyq+CfOGp0w1OsvUil+nWpWWppSAC9Kr9SYZ/PQdx5jI8DFauy2KBddMVTripctUZc5OHkZKDjqlXVJ6bgiFi1M0KubTmpC99KKjUlplFdp2TS/75lwNzgj+UT4pIzgbhW8ajTdpK9s7kg3HfQ/FUpqToxkHKfWCF44W+IuR1IQ1bN0FmQuptHqEeq1PAdSgdy+8p948Aw8aRUkQQQQIXVMOty7Dj7ziW2m0la1qOAlIGSSe4Qgusd43DxG6utWfajy27ZkHVdgog9mQNlzTgHj0SD3EDqoxt3HnqM5amm7Np0x/s6lcRU26pJwpEqnHaf0iQnyKowy0rbr1u2/bemlrqVJ3ffATNVWaxhcnJb8qNt04SFqVjB6jvBiramcwBkf4ncB/J+CkiAJy7kFuVs1bSPSais2nL3LSJJxreZ5pgLddc25lOFIOFdNjjAwAABGgUKuUevSgnKJVJOoyxOA5LPJWnPgSDsfYd4rrmiWk2nemtTn5y0ZOvOU2QdmpiYn09o9MFCCo4JyEZI6JAxnvhGrQ1Cq1o325c9tsM01px9SlU5pSjLlon+SIUSSnGwJJI7sRmLuyvitdI2QmQ9+RKusvYIG7gL6MQRB2Fc1NvK0afclLUfR51rn5VdW1AkKQSO8EEe7PfE7y7ZzGAkjMTyx3Ajmm8ZDhkLiPBX67RqDLCarNVkacyfuVzT6WwojGw5iMncbDJipa33+mwLSE1LMemVeec9GpkqASXHjgZIG5CcgnHUkDbIiv6bcN7VwNN3ZrRPzters4O1VTzMKQzKpO4QeUgkjvSMJB236xoNF2edfb4r3brfqfJVLNxsRwBkq9Ua/LLrUyJWj3TR52YOAGmptBUc52AByTt3RYwdttoVvjZtzTiwZKiUS1rJkqdVZ0KmDPsKWlTLaFBPKBzYKlHvIJGNuuR5eFTWiorrcrYl1zbk5LTI7Onzbx5nG3BkpbUo7qSroCckHA6dGGp7LeBGZYHZxxI6qCK/vnDxhe7ih0fdp7qtS7GQuTmpRYmZ9mWykoIIPbtgdCDuoD84dDnd+FDWJvVSxy3UnG0XJSgluoIG3bJOyXkjwVjfwIPcRFmWlLiFIcSlSFApUlQyCDsQQeohOqmZrh24l5KsyJcFuzywpTY+lKOEBxs+JQdx+ak98MtmdXdO30aU8Ry/cdvgortbd99o4dV9CII6pV9qalmpmXdS4y6gLbWncKSRkEeYgjXJckO1bf+dLjUZoLuXqbTJpEny5yOzYSVu/FfOPhGvaSyyKjxh3rUH8KVSaKwxL5TjkCwjOPDbPxjFuEFw17XW6Lkfyt70aYmEqO553Xx+0EiNJrz2olga/1u8rPsh+5afWaayy6gLLaUqSE/SGdwUeHRUIH22t1bceQAGdT1JVtsbjBkDPFNbUpSWqVOmKfONB2WmWlNPNq6LQoEKHvBIhVzwX0A3WJpN4zqaGXOcSfo6e3CfvO1zjGNs8vT2xM/PlrYf8AUc8P+tV+7HJ1x1rx/kPd/rqv3Ybem1v7jfmFD4L+xUPwsINFqF/WUy445JUSvOIlStWSEKUpIGfJsE+3PjG4hUY9w0W7cVMZu24rppTtLqVfq6pkyzhyUp3UMHvGXFAZPdk9Y18b5jlmvPjfqEjozkZ/gJ3Uy2EArIHZJq6OMagU6ey5KW5RlVJDR3SXiohKvDIKkHzSIZ0YhWdRmb7tLXWn6iWVaa7iS9R1SM3LJXyhOFHAKh0P3JBxvgiPf8+Wt3+4d7+tq/djoGj26zaMY3gOHcc0rsRvMrjhaFxC6LUfV2jSbU3Pu06pSClGVnEICwEqxzIWkkcwOAdiMEdeoK56t8PVK0i0oXeLVdm6nXZOpyq0v9n2LbaS5jCUhR3JKfWJ7ugjShrhrcVD/AO7/W1fuxTNYrn1m1WtdqzZvSaYokrMTzLr0yl8r2SroQQBgEg5z9GGTr1Yg/1B8woRE/PIph5F70qSYmynl7ZtLmc5zzJBz9ePdGI8adrorOlaa220FTVGmUucwHrdishCx5A8h90bdKtBiWaYRgJabS2kAYHqgAfsiu6tSCappfc0gtOUu0uYAH5QbKk/WkGOU6fYMF2N47p5MN6Fw/Zfvg6u8XNoFQnJt8GapwXTnSpW57I4R/YKIISvRvU+fsu15ilSzy0ocnFv4B7yhCf/AFgjsCzqvfAgfR7+uaTcBDgkU5/QdGfrMNLdNzW9a0o1N3FV5WmMvL7Npb6uVKlYzgHxwCfKFd0taFg8ZddtyYBQzNzU1KtDplKz2zXuICfiIaS67Xt+6pJqTuKky1Sl2nO0Qh9JPKrBGRgjBwSMxzzaONjdRDps7hA5c+3VOaTz4OGqvfPDpcn/AE4o5/532Rx88el348Uf9d9kJPKUOUlbjr1Jm5Rta5GecaBI3AC1ADy2iQ+Q6T/sDPwhxHsfUe0OD3YTKrVsWohI3HHzTkfPBpd+PNH/AFv2QfPBpd+PNH/W/ZCb/ItJP+b2fhB8i0j8Hs/CPr2Mq/rcp/VVvuPqnI+eDS78eaP+t+yD54NLvx5o/wCt+yE3+RaR+D2fhB8i0j8Hs/CD2Nq/rd80eqrfcfVOQdYNLsf480f9b9ke2hak2FXam1S6PdlLnZ57PZsNu5UvAJIAxuQAT7oSr5FpH4PZ+EfiUdZte7LdrtOZSw5KVJpaikY5hzDO/hjIx5xHNshXbG4tcc4UFinYrRmR2OHmvoGIiL3cS1ZVdcWfVbpsws+QbVn9sS4Vt0jPOJStpoWidyTHaci5iW9Dax1KnSE7e4qPkIw1SIyWWMHcfdQvO6wlIDS6TOT0up6XQSkLKSfbgf8A2CG/4NdKJC4dHjW6o3601U3yySOraUoR/wByVQR2ZZpRHHlbc5aup9u6o0lCkJmC228sbBMwyeZBP5yBj9AwwNlXFI3XatOuCnqBYnWUuAA/ckgBST4EKCgR7Is+s9iSGpGnVUtSe5UKmG+eVeIz2L6d0LHkdj7CRCd8Mt8zunN6z+lV7JVIhU6W2VOHAl5jOCkEjHKvYg7DofpZjN7SaYbcAkZ+Jn2V2nMI3YPVQHENR3bS11m5sNlEjXWxMoVjYlWErHmFg584gxDQcSOnJ1BsbEg2DW6aVPyW27mRhbee7mABHtA9sKTb9T9IQZOaSpmdYJQ6hYwrI2Ox6HYgjuIi7s7fbZqhv5m8CtNpNjwpDC88CchS8Ecd3lGraNaWJuaWTX69zt0vm/iWUkpU/gkEk9QkYx4nffAydCnssrYxvOWVQQ6dJt6hUllLFNpMnKtpGAG2kg+84yfeY8VyWXbVflSxUaTLqKtw4hAQtJ8QoAHPnkeyPneVH1g0cwk5iJqcuqq3BQqFL5VMTk80lKcffKAB+sxoWr1lr0+m0rdeU7S3gTLvqGST94fyse4jcRJcKFkTVy3krUSrSyk06nKUinhaf5R7pkHvCQScjYqPnC/U7jKtZ8ju3DzVfVbcboREziXfZNelIQhKM55RjPjiFX40bndrdw0HTWihb7/bIffabOSp9z1WkeeFE/pjw33zV6+6Xp5Z0zXKg4lT5SW5OW+lMPEeqkewHBJ6AZ9kYrwWaf1O+NQJ7WO7UKebYmFqk1ODZ+aVsVgfetg4Htxj7mMPstpr5JTaePdHLz/0s3fn3R4bU2elVqMWTp1QrVZwfk6TQ04ofScxlaveoqPvgizwR0BKUQvHFzoKjUinG6LZZQ3dUk1go+5E80n6BPcsfRJ8j3YYeCBCR7h+13XT5lFjakrfkpuVX2DE9MpKVIIOOzezgpIxjnPdscYzFz1v0Lp17PG5rWmmaZcKk8xUDhmc22J5R6qj98M5787Ro3EBw+WrqoyupN8tHuRKcN1BpAKXcdEvJ+mO7Oyh4npCv+n65cOs58n1inrqVuIWQgrBek1A97bg3aJHdt7QYzlrRpIpvSaDt13UdCr0VoY3JPn1CptWYuG0aimm3vRJunDmCVPhnKFDOOZJBwoYydic46Q2dp6m6YP0yXlKVd1IaZabS22288GlBKQAAUrAPQRTLX4lNNrlk0SdzyTtKWoYW3OMiZlyT4KAJx03UkYiR/g5w6XR/HoTabji+pYmxLkeaUqSB8IG6/Yre7aruB7gZCaC5K9oaXg47q6TupNgybBdmLyoSUjpyziFk+QSTFCuXiJtNtz5PtCQqN01NRw01LS6ktk92VEFRHsCT06xIMaZaA08l4ylvJ/KeqnOke5ThEel3VDRKxJUs0+pURhSerVJlwtSvMtggnfvV4x47aYv4V4XOPl/1fD5ZOrgAqZTdML91Vr8vXdV3/kmjsK52KJKkggEHPMMnlzgAk5URtgDEapfl6WbpRaTJnFS8q001ySVPlQA47gbJQjuT0yo7DPxw+9OJ+sVt8UbTS25kTL6i21MPt9s+onH3DKQRnwJJ69IkdLeGS8L5rSbs1jqE3KsuqCzIl3mmn/Ys9Gk/kjfG2B1it6ru6nIJLx3Wj8oVJ1lsfFvE91ULJte+OJ7Uf5ZrSnqfa0ksJdcST2bLY37FrOApxQ6qxt1PcC+ltUWmW7QZKh0aTbk6fJNBlhlAwEpH7SepPeTmC3KJSbdo0tRaHT2KfT5ZAQywynlSgf+T4k7nviSjUxRMiYGMGAEuc4uOSiCCCJF4iCCCBCI6ZtlmYlXGZhlt5paSFtuJCkqHgQeogggQsgv7hr0iubtpty2xSpogqLtMcMvk/mDKP7MJVrVppQbMqjsvSpqpOIScD0h1Cj9SBBBHqFmtBkGZ+eSy8txKSd+QgH6xDf6F8NGm9xUxFTraq1Oq2JZVNpQ2f6CAfrgggQmesfT+y7IluxtW26dS8jCnGWh2i/zlnKj7zFnggjxCIIIIEIggggQv//Z\" alt=\"\" /&amp;gt;" +
            "&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;"
).setParameter("FMTTYPE", "text/html")
val ical = ICalendar()
ical.addEvent(event)
print(Biweekly.write(ical).go())
Biweekly.write(ical).go(File("awesome-event-with-image.ics"))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ics should render as follows in Outlook.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4xkho77fqjk0ybtdqdyj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4xkho77fqjk0ybtdqdyj.png" alt="biweekly ics image outlook" width="418" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this post, we have created some iCalendar files by hand and by code using biweekly. We also took advantage of the experimental property &lt;code&gt;X-ALT-DESC&lt;/code&gt; to display a more fun and colorful description thanks to the power of HTML.&lt;/p&gt;

&lt;p&gt;You can find the source code of the &lt;a href="https://github.com/wlybe/biweekly-kotlin-demo" rel="noopener noreferrer"&gt;project here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy coding.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>openai</category>
      <category>productivity</category>
      <category>netlify</category>
    </item>
    <item>
      <title>How to build a native Linux app of a Quarkus project from another OS using a container</title>
      <dc:creator>Yassine Benabbas </dc:creator>
      <pubDate>Wed, 07 Dec 2022 20:37:43 +0000</pubDate>
      <link>https://dev.to/worldlinetech/generating-a-native-quarkus-linux-app-from-another-os-using-a-container-1c0m</link>
      <guid>https://dev.to/worldlinetech/generating-a-native-quarkus-linux-app-from-another-os-using-a-container-1c0m</guid>
      <description>&lt;p&gt;Quarkus qualifies itself as &lt;strong&gt;SUPERSONIC/SUBATOMIC/JAVA&lt;/strong&gt; partly thanks to its support for GraalVM. This post shows how to build a GraalVM &lt;strong&gt;native image&lt;/strong&gt; for Linux from using a container, allowing to perform this task from Windows or macOS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Motivation
&lt;/h2&gt;

&lt;p&gt;GraalVM is a high performance JDK distribution which brings many interesting features such as supporting non JVM languages (such as Python). Among the features of this JDK, the one that we're interested in here is called &lt;strong&gt;native image&lt;/strong&gt;, which compiles Java code into a native binary. This means that the resulting binary will contain machine code instead of the traditional bytecode. Theoretically, we would gain in performance and startup speed.&lt;/p&gt;

&lt;p&gt;Quarkus allows to generate a &lt;strong&gt;native image&lt;/strong&gt; for the current platform quite easily. However, since most servers run on Linux, we would like to find the simplest way to generate a native Linux binary. Unfortunately, GraalVM does not seem to support cross-compilation yet. Thus, we need to find other alternatives. Hopefully, Quarkus provide a very accessible technique, &lt;a href="https://quarkus.io/guides/building-native-image#container-runtime" rel="noopener noreferrer"&gt;described here&lt;/a&gt;, which requires to have a container engine installed and does not require to install GraalVM.&lt;/p&gt;

&lt;p&gt;In this post, we'll try out to use a container to build the native image and we'll use &lt;a href="https://podman.io/" rel="noopener noreferrer"&gt;podman&lt;/a&gt; as the container engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Please read &lt;a href="https://dev.to/yostane/using-podman-to-run-testcontainers-in-a-quarkus-project-11me"&gt;my previous post&lt;/a&gt; which describes how to install the prerequisites which are summarized as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Java JDK 17&lt;/li&gt;
&lt;li&gt;podman&lt;/li&gt;
&lt;li&gt;On Windows, WSL needs to be installed and enabled&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can verity that you're good to go by running these commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;java &lt;span class="nt"&gt;--version&lt;/span&gt;
podman run hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We don't need to install GraalVM nor any native compilation toolchain as they will be handled by the container which will build the native image.&lt;br&gt;
That's what makes this technique so easy !.&lt;/p&gt;
&lt;h3&gt;
  
  
  Sample project
&lt;/h3&gt;

&lt;p&gt;We're going to use the &lt;a href="https://github.com/yostane/quarkus-rest-api-demo" rel="noopener noreferrer"&gt;sample project developed&lt;/a&gt; during a previous post. It consists of a Quarkus Rest API that uses a PostgreSQL database for development and a H2 in-memory database for production. For the sake of simplification, H2 is chosen in because it makes the production app autonomous and easier to test. The &lt;strong&gt;application.yaml&lt;/strong&gt; file is shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;greeting&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hello"&lt;/span&gt;

&lt;span class="na"&gt;quarkus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;datasource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;db-kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PostgreSQL&lt;/span&gt;

&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;%prod"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;quarkus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;datasource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;db-kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;H2&lt;/span&gt;
      &lt;span class="na"&gt;jdbc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jdbc:h2:mem:default&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After cloning the project, please run the following commands to verify that we're ready to go:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run tests: &lt;code&gt;.\gradlew test&lt;/code&gt; or in Windows &lt;code&gt;.\gradlew.bat test&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run the server in production mode: &lt;code&gt;.\gradlew quarkusDev -Dquarkus.profile=prod&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;strong&gt;backtick&lt;/strong&gt; character is required on PowerShell for arguments that have a dot &lt;strong&gt;.&lt;/strong&gt;. So, &lt;code&gt;-Dquarkus.profile=prod&lt;/code&gt; becomes &lt;code&gt;(backtick)-Dquarkus.profile=prod&lt;/code&gt; in PowerShell, and so on.&lt;/p&gt;

&lt;p&gt;Checking the production profile locally is recommended because the native image will use that profile.&lt;br&gt;
Once you're ready, let's create the native image.&lt;/p&gt;
&lt;h2&gt;
  
  
  Creating the native image using a container
&lt;/h2&gt;

&lt;p&gt;Open a terminal on the project folder and run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;.&lt;span class="se"&gt;\g&lt;/span&gt;radlew build &lt;span class="nt"&gt;-Dquarkus&lt;/span&gt;.package.type&lt;span class="o"&gt;=&lt;/span&gt;native &lt;span class="nt"&gt;-Dquarkus&lt;/span&gt;.native.container-build&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;-Dquarkus&lt;/span&gt;.native.container-runtime&lt;span class="o"&gt;=&lt;/span&gt;podman
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This argument &lt;code&gt;-Dquarkus.native.container-runtime=podman&lt;/code&gt; allows to use podman as a container tool.&lt;/p&gt;

&lt;p&gt;The command should finish with an output similar to this one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;------------------------------------------------------------------------------------------------------------------------&lt;/span&gt;
Produced artifacts:
 /project/testcontainers-demo-1.0.0-SNAPSHOT-runner &lt;span class="o"&gt;(&lt;/span&gt;executable&lt;span class="o"&gt;)&lt;/span&gt;
 /project/testcontainers-demo-1.0.0-SNAPSHOT-runner-build-output-stats.json &lt;span class="o"&gt;(&lt;/span&gt;json&lt;span class="o"&gt;)&lt;/span&gt;
 /project/testcontainers-demo-1.0.0-SNAPSHOT-runner-timing-stats.json &lt;span class="o"&gt;(&lt;/span&gt;raw&lt;span class="o"&gt;)&lt;/span&gt;
 /project/testcontainers-demo-1.0.0-SNAPSHOT-runner.build_artifacts.txt &lt;span class="o"&gt;(&lt;/span&gt;txt&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;========================================================================================================================&lt;/span&gt;
Finished generating &lt;span class="s1"&gt;'testcontainers-demo-1.0.0-SNAPSHOT-runner'&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;3m 34s.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The native app (also called native image) is &lt;strong&gt;/project/testcontainers-demo-1.0.0-SNAPSHOT-runner&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the Linux app from other OSes
&lt;/h2&gt;

&lt;p&gt;We'll be exploring two techniques even if there may be more, such using a virtual machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using WSL on Windows
&lt;/h3&gt;

&lt;p&gt;On windows, it's quite easy to test thanks to WSL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wsl &lt;span class="c"&gt;# open a WSL session&lt;/span&gt;
./build/testcontainers-demo-1.0.0-SNAPSHOT-runner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output should display this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;__  ____  __  _____   ___  __ ____  ______
 &lt;span class="nt"&gt;--&lt;/span&gt;/ __ &lt;span class="se"&gt;\/&lt;/span&gt; / / / _ | / _ &lt;span class="se"&gt;\/&lt;/span&gt; //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,&amp;lt; / /_/ /&lt;span class="se"&gt;\ \&lt;/span&gt;
&lt;span class="nt"&gt;--&lt;/span&gt;&lt;span class="se"&gt;\_&lt;/span&gt;__&lt;span class="se"&gt;\_\_&lt;/span&gt;___/_/ |_/_/|_/_/|_|&lt;span class="se"&gt;\_&lt;/span&gt;___/___/
2022-12-06 22:42:05,809 INFO  &lt;span class="o"&gt;[&lt;/span&gt;io.quarkus] &lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; testcontainers-demo 1.0.0-SNAPSHOT native &lt;span class="o"&gt;(&lt;/span&gt;powered by Quarkus 2.14.2.Final&lt;span class="o"&gt;)&lt;/span&gt; started &lt;span class="k"&gt;in &lt;/span&gt;0.942s. Listening on: http://0.0.0.0:8
080
2022-12-06 22:42:05,828 INFO  &lt;span class="o"&gt;[&lt;/span&gt;io.quarkus] &lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; Profile prod activated.
2022-12-06 22:42:05,828 INFO  &lt;span class="o"&gt;[&lt;/span&gt;io.quarkus] &lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; Installed features: &lt;span class="o"&gt;[&lt;/span&gt;agroal, cdi, config-yaml, hibernate-orm, hibernate-orm-panache, hibernate-orm-panache-kotlin, jdbc-h2, j
dbc-postgresql, kotlin, narayana-jta, resteasy-reactive, resteasy-reactive-jackson, resteasy-reactive-kotlin-serialization, smallrye-context-propagation, vertx]
^C2022-12-06 22:42:12,136 INFO  &lt;span class="o"&gt;[&lt;/span&gt;io.quarkus] &lt;span class="o"&gt;(&lt;/span&gt;Shutdown thread&lt;span class="o"&gt;)&lt;/span&gt; testcontainers-demo stopped &lt;span class="k"&gt;in &lt;/span&gt;0.012s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means that the server is up and running.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using a container
&lt;/h3&gt;

&lt;p&gt;We can also test the native Linux app by running it from a container. This allows to run the app from any OS as long as there is a tool that runs containers. But, we need to first create the image.&lt;/p&gt;

&lt;p&gt;Hopefully, projects generated using Quarkus starter already provides the configuration file, located in &lt;strong&gt;src/main/docker/Dockerfile.native-micro&lt;/strong&gt; that is used by the container tool in order to generate the image that hosts and runs the native Linux app. It just requires that the native Linux app is already generated, which we already did. So, let's run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;podman build &lt;span class="nt"&gt;-f&lt;/span&gt; src/main/docker/Dockerfile.native-micro &lt;span class="nt"&gt;-t&lt;/span&gt; IMAGE_NAME &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please replace IMAGE_NAME with the desired name of the image, such as: &lt;code&gt;myapp/native-server&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can test the image by running &lt;code&gt;podman run IMAGE_NAME_OR_ID&lt;/code&gt; and upload it to a container registry by running &lt;code&gt;podman push IMAGE_NAME_OR_ID REGISTRY_NAME&lt;/code&gt;. &lt;strong&gt;IMAGE_NAME_OR_ID REGISTRY_NAME&lt;/strong&gt; corresponds to either the name of the image that you used earlier, or its ID that you can get by running &lt;code&gt;podman images&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If running the image fails, it may be due to a missing execution permission for the native app. Please add a &lt;code&gt;RUN chmod +x /work/application&lt;/code&gt; in the dockerfile &lt;strong&gt;src/main/docker/Dockerfile.native-micro&lt;/strong&gt; to fix this. The file that I used is shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; quay.io/quarkus/quarkus-micro-image:1.0&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /work/&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chown &lt;/span&gt;1001 /work &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;chmod&lt;/span&gt; &lt;span class="s2"&gt;"g+rwX"&lt;/span&gt; /work &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;chown &lt;/span&gt;1001:root /work
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --chown=1001:root build/*-runner /work/application&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /work/application

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8080&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; 1001&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["./application", "-Dquarkus.http.host=0.0.0.0"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can run my generated image hosted on &lt;a href="https://quay.io" rel="noopener noreferrer"&gt;quay.io&lt;/a&gt; as an example: &lt;code&gt;podman run quay.io/yostane/quarkus-jvm-demo/quarkus-jvm-demo-micro&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;podman run quay.io/yostane/quarkus-jvm-demo/quarkus-jvm-demo-micro
__  ____  __  _____   ___  __ ____  ______
 &lt;span class="nt"&gt;--&lt;/span&gt;/ __ &lt;span class="se"&gt;\/&lt;/span&gt; / / / _ | / _ &lt;span class="se"&gt;\/&lt;/span&gt; //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,&amp;lt; / /_/ /&lt;span class="se"&gt;\ \&lt;/span&gt;
&lt;span class="nt"&gt;--&lt;/span&gt;&lt;span class="se"&gt;\_&lt;/span&gt;__&lt;span class="se"&gt;\_\_&lt;/span&gt;___/_/ |_/_/|_/_/|_|&lt;span class="se"&gt;\_&lt;/span&gt;___/___/
2022-12-07 20:19:51,660 INFO  &lt;span class="o"&gt;[&lt;/span&gt;io.quarkus] &lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; testcontainers-demo 1.0.0-SNAPSHOT native &lt;span class="o"&gt;(&lt;/span&gt;powered by Quarkus 2.14.2.Final&lt;span class="o"&gt;)&lt;/span&gt; started &lt;span class="k"&gt;in &lt;/span&gt;0.357s. Listening on: http://0.0.0.0:8080
2022-12-07 20:19:51,669 INFO  &lt;span class="o"&gt;[&lt;/span&gt;io.quarkus] &lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; Profile prod activated.
2022-12-07 20:19:51,669 INFO  &lt;span class="o"&gt;[&lt;/span&gt;io.quarkus] &lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; Installed features: &lt;span class="o"&gt;[&lt;/span&gt;agroal, cdi, config-yaml, hibernate-orm, hibernate-orm-panache, hibernate-orm-panache-kotlin, jdbc-h2, jdbc-postgresql, kotlin, narayana-jta, resteasy-reactive, resteasy-reactive-jackson, resteasy-reactive-kotlin-serialization, smallrye-context-propagation, vertx]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This post showed how to generate a native Linux image of a Quarkus application in a very simple and straightforward way. This native app does not a require a JRE and is &lt;strong&gt;"theoretically"&lt;/strong&gt; faster and more optimized than a JVM counterpart.&lt;/p&gt;

&lt;p&gt;We used a container to generated the image which handled all the native complication toolchain. It also allowed to cross-compile to Linux. I was really surprised to see how it's easy to generate a native image using this technique. So, why not try it out on your side ?&lt;/p&gt;

</description>
      <category>github</category>
      <category>webdev</category>
      <category>developer</category>
      <category>portfolio</category>
    </item>
    <item>
      <title>Using Podman to run Testcontainers in a Quarkus project</title>
      <dc:creator>Yassine Benabbas </dc:creator>
      <pubDate>Tue, 29 Nov 2022 20:12:20 +0000</pubDate>
      <link>https://dev.to/worldlinetech/using-podman-to-run-testcontainers-in-a-quarkus-project-11me</link>
      <guid>https://dev.to/worldlinetech/using-podman-to-run-testcontainers-in-a-quarkus-project-11me</guid>
      <description>&lt;p&gt;In the area of container engines, podman seems to be a relevant alternative to docker, at least during the development phase on a Windows or macOS machine. Indeed, both podman and podman-desktop, it's desktop UI, do not require a paid licence. Which is not the case anymore for Docker-Desktop under certain conditions (it's still free for individual developers, education, open source communities, and small businesses). In addition to that, podman is daemonless and can run in rootless mode.&lt;/p&gt;

&lt;p&gt;This article shows how to use podman as a container engine instead of Docker and Docker-Desktop on Windows and macOS. We'll see how to install this tool and how to configure it to run tests that rely on Testcontainers. So let's get started with the prerequisites.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Even though you can use the official installers, I suggest to use package managers moving forward.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On Windows, you can use &lt;a href="https://scoop.sh" rel="noopener noreferrer"&gt;scoop&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On macOS, you can use &lt;a href="https://sdkman.io/" rel="noopener noreferrer"&gt;sdkman&lt;/a&gt; for java-related tools ad &lt;a href="https://brew.sh/" rel="noopener noreferrer"&gt;homebrew&lt;/a&gt; for the others.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Java JDK 17&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;podman&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Optionnal: podman-desktop&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On Windows, &lt;a href="https://learn.microsoft.com/windows/wsl/install" rel="noopener noreferrer"&gt;WSL needs to be enabled&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Start and configure podman
&lt;/h2&gt;

&lt;p&gt;Open a terminal and run these commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;podman machine init&lt;/code&gt;, which initializes a new VM. You just need to run it once.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;podman machine start&lt;/code&gt;, which starts the VM. You need to re-run it after each reboot.&lt;/li&gt;
&lt;li&gt;Podman-desktop allows to configure the podman machine to auto-start. You can start if from the apps menu.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyn4idyr9f73mliq6hcwl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyn4idyr9f73mliq6hcwl.png" alt="Podman desktop app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw2nn79nrubiww9hbow5s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw2nn79nrubiww9hbow5s.png" alt="Podman desktop auto-start settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Now we are ready to launch containers. Run &lt;code&gt;podman run hello&lt;/code&gt; which launches a basic container.&lt;/li&gt;
&lt;li&gt;The last thing we need to configure is to set this environment variable to &lt;code&gt;TESTCONTAINERS_RYUK_DISABLED&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The next section demonstrates that we can use Testcontainers through a sample project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample Quarkus project that uses Testcontainers
&lt;/h2&gt;

&lt;p&gt;Let's create with an app with Quarkus which consists of a REST API that backs its data on a postgreSQL database.&lt;/p&gt;

&lt;p&gt;Quarkus provides &lt;a href="https://quarkus.io/guides/databases-dev-services" rel="noopener noreferrer"&gt;zero-confguration databases&lt;/a&gt; for development and test modes (called &lt;strong&gt;DEV SERVICES FOR DATABASES&lt;/strong&gt;). It relies on a containerization tool, such as docker or podman, for some databases. Since we have podman installed and configured, we are ready to go.&lt;/p&gt;

&lt;p&gt;In order to create a starter project, open &lt;a href="https://code.quarkus.io/" rel="noopener noreferrer"&gt;code.quarkus.io&lt;/a&gt; and choose &lt;strong&gt;JDBC Driver - PostgreSQL&lt;/strong&gt; extension and any other extensions that you'd like to use for you project. &lt;/p&gt;

&lt;p&gt;In the following, I have selected the following extensions just for fun 😁.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;YAML Configuration&lt;/li&gt;
&lt;li&gt;JDBC Driver - PostgreSQL&lt;/li&gt;
&lt;li&gt;Hibernate ORM with Panache&lt;/li&gt;
&lt;li&gt;Hibernate ORM with Panache and Kotlin&lt;/li&gt;
&lt;li&gt;Kotlin&lt;/li&gt;
&lt;li&gt;RESTEasy Reactive&lt;/li&gt;
&lt;li&gt;RESTEasy Reactive Kotlin Serialization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also selected &lt;strong&gt;Gradle with Kotlin SDL&lt;/strong&gt; build tool, but you can use any available one.&lt;/p&gt;

&lt;p&gt;Once the applications information and extensions are selected, generate and download the zip archive. After that, unzip that archive and open a terminal on the root folder of the project (the one contains &lt;strong&gt;pom.xml&lt;/strong&gt;, &lt;strong&gt;build.gradle&lt;/strong&gt; or &lt;strong&gt;build.gradle.kts&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;Next, run the tests using &lt;code&gt;.\gradlew.bat test&lt;/code&gt; (if you are using powershell). You should see the following output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

********************************************************************************
Ryuk has been disabled. This can cause unexpected behavior in your environment.
********************************************************************************
2022-11-27 00:16:55,210 INFO  [org.tes.DockerClientFactory] (build-57) Checking the system...
2022-11-27 00:16:55,212 INFO  [org.tes.DockerClientFactory] (build-57) ?? Docker server version should be at least 1.6.0
2022-11-27 00:16:55,241 INFO  [org.tes.uti.ImageNameSubstitutor] (build-57) Image name substitution will be performed by: DefaultImageNameSubstitutor (composite of 'ConfigurationFileImageNameSubstitutor' and 'PrefixingImageNameSubstitutor')
2022-11-27 00:16:55,284 INFO  [doc.io/postgres:14]] (build-57) Pulling docker image: docker.io/postgres:14. Please be patient; this may take some time but only needs to be done once.
...
BUILD SUCCESSFUL in 21s
9 actionable tasks: 1 executed, 8 up-to-date


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The tests were executed successfully. The output proves that a container image has been downloaded and started. We can deduce that Podman was successfully able to interact with Testcontaiers, which is the library that is responsible for testing with containers.&lt;/p&gt;

&lt;p&gt;Let's analyse this output. The following line means that the environment variable that we set earlier was detected.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Ryuk has been disabled. This can cause unexpected behavior in your environment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This line means that postgreSQL container image &lt;strong&gt;docker.io/postgres:14&lt;/strong&gt; is being downloaded.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;2022-11-27 00:16:55,284 INFO  [doc.io/postgres:14]] (build-57) Pulling docker image: docker.io/postgres:14. Please be patient; this may take some time but only needs to be done once.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Finally, we can check with podman the presence of a postgreSQL image. Open a terminal and run &lt;code&gt;podman images&lt;/code&gt;, you should see an output similar to this one.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

REPOSITORY                  TAG         IMAGE ID      CREATED      SIZE
docker.io/library/postgres  14          c920f82d2df2  11 days ago  384 MB


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Indeed, the postgres image is present. We have used containers with Podman. Horaaay !&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing with an entity
&lt;/h3&gt;

&lt;p&gt;Even though we validated that podman works with testcontainers, we did not really use the database yet. Thus, for the sake of completion, let's create a Panache entity as well as a repository, and update our REST resource and tests to use this entity.&lt;/p&gt;

&lt;p&gt;Here is the code on the app side:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;

&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@Serializable&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Greeting&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PanacheEntity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;lateinit&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@ApplicationScoped&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GreetingRepository&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PanacheRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;findByPrefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"message like ?1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"$prefix%"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GreetingResource&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Inject&lt;/span&gt;
    &lt;span class="k"&gt;lateinit&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;greetingRepository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;GreetingRepository&lt;/span&gt;

    &lt;span class="nd"&gt;@GET&lt;/span&gt;
    &lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/greetings/{prefix}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@Produces&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MediaType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;APPLICATION_JSON&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;greetings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@PathParam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"prefix"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;greetingRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByPrefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;orEmpty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The library used for entity mangement is called &lt;a href="https://quarkus.io/guides/hibernate-orm-panache" rel="noopener noreferrer"&gt;Panache&lt;/a&gt; and allows to use the repository pattern with Hibernate.&lt;/p&gt;

&lt;p&gt;⚠ We added &lt;code&gt;@Serializable&lt;/code&gt; on the entity so that it can be parsed into and from JSON. However, the Quarkus project generator did not add an important plugin that makes serialization work with Kotlin. Please add this plugin to &lt;strong&gt;build.gradle.kts&lt;/strong&gt; in order to enable this feature: &lt;code&gt;id("org.jetbrains.kotlin.plugin.serialization") version "1.7.21"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The plugin part should look like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;

&lt;span class="nf"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;kotlin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"jvm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s"&gt;"1.7.21"&lt;/span&gt;
    &lt;span class="nf"&gt;kotlin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"plugin.allopen"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s"&gt;"1.7.21"&lt;/span&gt;
&lt;span class="c1"&gt;// add this line&lt;/span&gt;
    &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"org.jetbrains.kotlin.plugin.serialization"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s"&gt;"1.7.21"&lt;/span&gt;
    &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.quarkus"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is the test code. The method &lt;code&gt;prepareData&lt;/code&gt; adds initial data to the database and &lt;code&gt;testHelloEndpoint&lt;/code&gt; tests the above endpoint. The library that is used for testing the REST API is called &lt;a href="https://rest-assured.io/" rel="noopener noreferrer"&gt;rest-assured&lt;/a&gt; and provides a nice DSL (Domain Specific Language) with functions such as &lt;code&gt;given&lt;/code&gt; and &lt;code&gt;when&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;

&lt;span class="nd"&gt;@QuarkusTest&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GreetingResourceTest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Inject&lt;/span&gt;
    &lt;span class="k"&gt;lateinit&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;greetingRepository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;GreetingRepository&lt;/span&gt;

    &lt;span class="nd"&gt;@Transactional&lt;/span&gt;
    &lt;span class="nd"&gt;@BeforeEach&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;prepareData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;greetingRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deleteAll&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;greeting&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;greeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"hello world"&lt;/span&gt;
        &lt;span class="n"&gt;greetingRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;persist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;greeting&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;greetingRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;testHelloEndpoint&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;given&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;`when`&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/greetings/hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SC_OK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"$.size()"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;equalTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;hasItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello world"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As stated earlier, no database configuration is needed, thanks to Quarkus' Dev Services. We just need to run &lt;code&gt;.\gradlew.bat test&lt;/code&gt; to run the tests and &lt;code&gt;./gradlew quarkusDev&lt;/code&gt; to run the development server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This article showed how to use podman as a container tool on a Quarkus + testcontainers project. The most important setting that make Podman and Testcontainers interact with each other is setting the environment variable &lt;code&gt;TESTCONTAINERS_RYUK_DISABLED&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Happy coding&lt;/p&gt;

</description>
      <category>quarkus</category>
      <category>podman</category>
      <category>testing</category>
      <category>testcontainers</category>
    </item>
  </channel>
</rss>
