# Scripting with Quarkus

Quarkus provides integration with jbang which allows you to write Java scripts/applications requiring no Maven nor Gradle to get running.

In this guide, we will see how you can write a REST application using just a single Java file.

| This technology is considered preview. In preview, backward compatibility and presence in the ecosystem is not guaranteed. Specific improvements might require changing configuration or APIs, and plans to become stable are under way. Feedback is welcome on our mailing list or as issues in our GitHub issue tracker. For a full list of possible statuses, check our FAQ entry.

## Prerequisites

To complete this guide, you need:

- Roughly 5 minutes

- An IDE

- JDK 17+ installed with `JAVA_HOME` configured appropriately

- Optionally Mandrel or GraalVM installed and configured appropriately if you want to build a native executable (or Docker if you use a native container build)

- JBang

## Solution

Normally we would link to a Git repository to clone but in this case there is no additional files than the following:

    //usr/bin/env jbang "$0" "$@" ; exit $?
    //DEPS io.quarkus.platform:quarkus-bom:3.26.0.CR1@pom
    //DEPS io.quarkus:quarkus-rest
    //JAVAC_OPTIONS -parameters
    //JAVA_OPTIONS -Djava.util.logging.manager=org.jboss.logmanager.LogManager

    import io.quarkus.runtime.Quarkus;
    import jakarta.enterprise.context.ApplicationScoped;
    import jakarta.inject.Inject;
    import jakarta.ws.rs.GET;
    import jakarta.ws.rs.Path;
    import jakarta.ws.rs.Produces;
    import jakarta.ws.rs.core.MediaType;

    @Path("/hello")
    @ApplicationScoped
    public class quarkusapp {

        @GET
        public String sayHello() {
            return "hello";
        }

        public static void main(String[] args) {
            Quarkus.run(args);
        }

        @Inject
        GreetingService service;

        @GET
        @Produces(MediaType.TEXT_PLAIN)
        @Path("/greeting/{name}")
        public String greeting(String name) {
            return service.greeting(name);
        }

        @ApplicationScoped
        static public class GreetingService {

            public String greeting(String name) {
                return "hello " + name;
            }
        }
    }

## Architecture

In this guide, we create a straightforward application serving a `hello` endpoint with a single source file, no additional build files like `pom.xml` or `build.gradle` needed. To demonstrate dependency injection, this endpoint uses a `greeting` bean.

## Creating the initial file

First, we need a Java file. JBang lets you create an initial version using:

    jbang init scripting/quarkusapp.java
    cd scripting

This command generates a .java file that you can directly run on Linux and macOS, i.e. `./quarkusapp.java` - on Windows you need to use `jbang quarkusapp.java`.

This initial version will print `Hello World` when run.

Once generated, look at the `quarkusapp.java` file.

You will find at the top a line looking like this:

    //usr/bin/env jbang "$0" "$@" ; exit $?

This line is what on Linux and macOS allows you to run it as a script. On Windows this line is ignored.

The next lines

    //DEPS <dependency1> <dependency2>

illustrate how you add dependencies to this script. This is a feature of JBang.

Go ahead and update this line to include the `quarkus-bom` and the `quarkus-rest` dependency like so:

    //DEPS io.quarkus.platform:quarkus-bom:3.26.0.CR1@pom
    //DEPS io.quarkus:quarkus-rest

Now, run `jbang quarkusapp.java` and you will see JBang resolving this dependency and building the jar with help from Quarkus' JBang integration.

    $ jbang quarkusapp.java

    [jbang] Resolving dependencies...
    [jbang]     Resolving io.quarkus:quarkus-resteasy:3.26.0.CR1...Done
    [jbang] Dependencies resolved
    [jbang] Building jar...
    [jbang] Post build with io.quarkus.launcher.JBangIntegration
    Mar 22, 2023 9:47:51 A.M. org.jboss.threads.Version <clinit>
    INFO: JBoss Threads version 3.5.0.Final
    Mar 22, 2023 9:47:51 A.M. io.quarkus.deployment.QuarkusAugmentor run
    INFO: Quarkus augmentation completed in 722ms
    Hello World

For now the application does nothing new.

| How do I edit this file and get content assist? To edit the JBang script in an IDE/editor with content assist you can run `jbang edit quarkusapp.java` or `jbang edit quarkusapp.java`. For more information please refer to the the JBang documentation.

### The Jakarta REST resources

Now let us replace the class with one that uses Quarkus features:

    import io.quarkus.runtime.Quarkus;
    import jakarta.enterprise.context.ApplicationScoped;
    import jakarta.ws.rs.GET;
    import jakarta.ws.rs.Path;

    @Path("/hello")
    @ApplicationScoped
    public class quarkusapp {

        @GET
        public String sayHello() {
            return "hello";
        }

        public static void main(String[] args) {
            Quarkus.run(args);
        }
    }

It’s a very simple class with a main method that starts Quarkus with a REST endpoint, returning "hello" to requests on "/hello".

| Why is the `main` method there? A `main` method is currently needed for the JBang integration to work - we might remove this requirement in the future.

## Running the application

Now when you run the application you will see Quarkus start up.

Use: `jbang quarkusapp.java`:

    $ jbang quarkusapp.java

    [jbang] Building jar...
    [jbang] Post build with io.quarkus.launcher.JBangIntegration
    Mar 22, 2023 9:48:39 A.M. org.jboss.threads.Version <clinit>
    INFO: JBoss Threads version 3.5.0.Final
    Mar 22, 2023 9:48:39 A.M. io.quarkus.deployment.QuarkusAugmentor run
    INFO: Quarkus augmentation completed in 521ms
    __  ____  __  _____   ___  __ ____  ______
     --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
     -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
    --\___\_\____/_/ |_/_/|_/_/|_|\____/___/
    2023-03-22 09:48:39,891 INFO  [io.quarkus] (main) quarkus 999-SNAPSHOT on JVM (powered by Quarkus 3.26.0.CR1) started in 0.283s. Listening on: http://0.0.0.0:8080
    2023-03-22 09:48:39,904 INFO  [io.quarkus] (main) Profile prod activated.
    2023-03-22 09:48:39,904 INFO  [io.quarkus] (main) Installed features: [cdi, rest, smallrye-context-propagation, vertx]

Once started, you can request the provided endpoint:

    $ curl -w "\n" http://localhost:8080/hello
    hello

After that, hit `CTRL+C` to stop the application.

| Automatically add newline with `curl -w "\n"` We are using `curl -w "\n"` in this example to avoid your terminal printing a '%' or put both result and next command prompt on the same line.

| Why is `quarkus-rest` not resolved? In this second run you should not see a line saying it is resolving `quarkus-rest` as JBang caches the dependency resolution between runs. If you want to clear the caches to force resolution use `jbang cache clear`.

## Using injection

Dependency injection in Quarkus is based on ArC which is a CDI-based dependency injection solution tailored for Quarkus' architecture. You can learn more about it in the Contexts and Dependency Injection guide.

ArC comes as a dependency of `quarkus-rest` so you already have it handy.

Let’s modify the application and add a companion bean.

Normally you would add a separate class, but as we are aiming to have it all in one file you will add a nested class.

Add the following inside the `quarkusapp` class body.

    @ApplicationScoped
    static public class GreetingService {

        public String greeting(String name) {
            return "hello " + name;
        }

    }

| Use of nested static public classes We are using a nested static public class instead of a top level class for two reasons: JBang currently does not support multiple source files. All Java frameworks relying on introspection have challenges using top level classes as they are not as visible as public classes; and in Java there can only be one top level public class in a file.

Edit the `quarksapp` class to inject the `GreetingService` and create a new endpoint using it, you should end up with something like:

    //usr/bin/env jbang "$0" "$@" ; exit $?
    //DEPS io.quarkus.platform:quarkus-bom:3.26.0.CR1@pom
    //DEPS io.quarkus:quarkus-rest

    import io.quarkus.runtime.Quarkus;
    import jakarta.enterprise.context.ApplicationScoped;
    import jakarta.inject.Inject;
    import jakarta.ws.rs.GET;
    import jakarta.ws.rs.Path;
    import jakarta.ws.rs.Produces;
    import jakarta.ws.rs.core.MediaType;

    @Path("/hello")
    @ApplicationScoped
    public class quarkusapp {

        @GET
        public String sayHello() {
            return "hello";
        }

        public static void main(String[] args) {
            Quarkus.run(args);
        }

        @Inject
        GreetingService service;

        @GET
        @Produces(MediaType.TEXT_PLAIN)
        @Path("/greeting/{name}")
        public String greeting(String name) {
            return service.greeting(name);
        }

        @ApplicationScoped
        static public class GreetingService {

            public String greeting(String name) {
                return "hello " + name;
            }
        }
    }

Now when you run `jbang quarkusapp.java` you can check what the new end point returns:

    $ curl -w "\n" http://localhost:8080/hello/greeting/quarkus
    hello null

Now that is unexpected, why is it returning `hello null` and not `hello quarkus`?

The reason is that Quarkus REST (formerly RESTEasy Reactive) relies on the `-parameters` compiler flag to be set to be able to map `{name}` to the `name` parameter.

We fix that by adding the following comment instruction to the file:

    //JAVAC_OPTIONS -parameters

Now when you run with `jbang quarkusapp.java` the end point should return what you expect:

    $ curl -w "\n" http://localhost:8080/hello/greeting/quarkus
    hello quarkus

## Debugging

To debug the application you use `jbang --debug quarkusapp.java` and you can use your IDE to connect on port 4004; if you want to use the more traditional Quarkus debug port you can use `jbang --debug=5005 quarkusapp.java`.

Note: JBang debugging always suspends thus you need to connect the debugger to have the application run.

## Logging

To use logging in Quarkus scripting with JBang you do as usual, with configuring a logger, i.e.

    public static final Logger LOG = Logger.getLogger(quarkusapp.class);

To get it to work you need to add a Java option to ensure the logging is initialized properly, i.e.

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

With that in place running `jbang quarkusapp.java` will log and render as expected.

## Configuring Application

To configure the application you can use the `application.properties` file as usual, but you need to add it to the script:

    //FILES application.properties

    // ...
    @ConfigProperty(name = "prefix", defaultValue = "WG -")
    String prefix;

This will make the `application.properties` file available to the script, and process the configuration as usual.

You can also use the `application.yaml` file. For this, you need to add it to the `application.yaml` file to the script and include the `quarkus-config-yaml` dependency:

    //DEPS io.quarkus:quarkus-config-yaml
    //FILES application.yaml

| The path to the `application.properties` and `application.yaml` files are relative to the script file.

## Running as a native application

If you have the `native-image` binary installed and `GRAALVM_HOME` set, or a container runtime (e.g., podman or docker) installed on Linux, you can get the native executable built and run using `jbang --native quarkusapp.java`:

    $ jbang --native quarkusapp.java
    [jbang] Building jar...
    [jbang] Post build with io.quarkus.launcher.JBangIntegration
    Mar 22, 2023 9:58:47 A.M. org.jboss.threads.Version <clinit>
    INFO: JBoss Threads version 3.5.0.Final
    Mar 22, 2023 9:58:47 A.M. io.quarkus.deployment.pkg.steps.JarResultBuildStep buildNativeImageThinJar
    INFO: Building native image source jar: /tmp/quarkus-jbang8082065952748314720/quarkus-application-native-image-source-jar/quarkus-application-runner.jar
    Mar 22, 2023 9:58:47 A.M. io.quarkus.deployment.pkg.steps.NativeImageBuildStep build
    INFO: Building native image from /tmp/quarkus-jbang8082065952748314720/quarkus-application-native-image-source-jar/quarkus-application-runner.jar
    Mar 22, 2023 9:58:47 A.M. io.quarkus.deployment.pkg.steps.NativeImageBuildStep getNativeImageBuildRunner
    WARN: Cannot find the `native-image` in the GRAALVM_HOME, JAVA_HOME and System PATH. Attempting to fall back to container build.
    Mar 22, 2023 9:58:47 A.M. io.quarkus.deployment.pkg.steps.NativeImageBuildContainerRunner <init>
    INFO: Using docker to run the native image builder
    Mar 22, 2023 9:58:47 A.M. io.quarkus.deployment.pkg.steps.NativeImageBuildContainerRunner setup
    INFO: Checking image status quay.io/quarkus/ubi-quarkus-mandrel-builder-image:22.3-java17
    Mar 22, 2023 9:58:51 A.M. io.quarkus.deployment.pkg.steps.NativeImageBuildStep checkGraalVMVersion
    INFO: Running Quarkus native-image plugin on native-image 22.3.1.0-Final Mandrel Distribution (Java Version 17.0.6+10)
    Mar 22, 2023 9:58:51 A.M. io.quarkus.deployment.pkg.steps.NativeImageBuildRunner build
    INFO: docker run --env LANG=C --rm --user 1000:1000 -v /tmp/quarkus-jbang8082065952748314720/quarkus-application-native-image-source-jar:/project:z --name build-native-XaZUc quay.io/quarkus/ubi-quarkus-mandrel-builder-image:22.3-java17 -J-Dsun.nio.ch.maxUpdateArraySize=100 -J-Djava.util.logging.manager=org.jboss.logmanager.LogManager -J-Dlogging.initial-configurator.min-level=500 -J-Dvertx.logger-delegate-factory-class-name=io.quarkus.vertx.core.runtime.VertxLogDelegateFactory -J-Dvertx.disableDnsResolver=true -J-Dio.netty.noUnsafe=true -J-Dio.netty.leakDetection.level=DISABLED -J-Dio.netty.allocator.maxOrder=3 -J-Duser.language=en -J-Duser.country=IE -J-Dfile.encoding=UTF-8 --features=io.quarkus.runner.Feature,io.quarkus.runtime.graal.DisableLoggingFeature -J--add-exports=java.security.jgss/sun.security.krb5=ALL-UNNAMED -J--add-opens=java.base/java.text=ALL-UNNAMED -J--add-opens=java.base/java.io=ALL-UNNAMED -J--add-opens=java.base/java.lang.invoke=ALL-UNNAMED -J--add-opens=java.base/java.util=ALL-UNNAMED -H:+CollectImageBuildStatistics -H:ImageBuildStatisticsFile=quarkus-application-runner-timing-stats.json -H:BuildOutputJSONFile=quarkus-application-runner-build-output-stats.json -H:+AllowFoldMethods -J-Djava.awt.headless=true --no-fallback --link-at-build-time -H:+ReportExceptionStackTraces -H:-AddAllCharsets --enable-url-protocols=http -H:NativeLinkerOption=-no-pie -H:-UseServiceLoaderFeature -H:+StackTrace -J--add-exports=org.graalvm.sdk/org.graalvm.nativeimage.impl=ALL-UNNAMED --exclude-config io\.netty\.netty-codec /META-INF/native-image/io\.netty/netty-codec/generated/handlers/reflect-config\.json --exclude-config io\.netty\.netty-handler /META-INF/native-image/io\.netty/netty-handler/generated/handlers/reflect-config\.json quarkus-application-runner -jar quarkus-application-runner.jar
    Mar 22, 2023 9:37:56 A.M. io.quarkus.deployment.pkg.steps.NativeImageBuildRunner runCommand
    INFO: docker run --env LANG=C --rm --user 1000:1000 -v /tmp/quarkus-jbang9315448339582904220/quarkus-application-native-image-source-jar:/project:z --entrypoint /bin/bash quay.io/quarkus/ubi-quarkus-mandrel-builder-image:22.3-java17 -c objcopy --strip-debug quarkus-application-runner
    Mar 22, 2023 9:37:57 A.M. io.quarkus.deployment.QuarkusAugmentor run
    INFO: Quarkus augmentation completed in 31729ms
    __  ____  __  _____   ___  __ ____  ______
     --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
     -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
    --\___\_\____/_/ |_/_/|_/_/|_|\____/___/
    2023-03-22 09:37:57,471 INFO  [io.quarkus] (main) quarkus 999-SNAPSHOT native (powered by 3.26.0.CR1) started in 0.009s. Listening on: http://0.0.0.0:8080
    2023-03-22 09:37:57,472 INFO  [io.quarkus] (main) Profile prod activated.
    2023-03-22 09:37:57,472 INFO  [io.quarkus] (main) Installed features: [cdi, rest, smallrye-context-propagation, vertx]

This native build will take some time on first run but any subsequent runs (without changing `quarkusapp.java`) will be close to instant thanks to JBang cache:

    $ jbang --native quarkusapp.java
    __  ____  __  _____   ___  __ ____  ______
     --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
     -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
    --\___\_\____/_/ |_/_/|_/_/|_|\____/___/
    2023-03-22 09:38:45,450 INFO  [io.quarkus] (main) quarkus 999-SNAPSHOT native (powered by 3.26.0.CR1) started in 0.009s. Listening on: http://0.0.0.0:8080
    2023-03-22 09:38:45,450 INFO  [io.quarkus] (main) Profile prod activated.
    2023-03-22 09:38:45,450 INFO  [io.quarkus] (main) Installed features: [cdi, rest, smallrye-context-propagation, vertx]

### Using Qute

You can use the Qute templating engine in your JBang script by adding the `quarkus-qute` dependency. You also need to include the `templates` directory in the script:

    //DEPS io.quarkus:quarkus-qute
    //FILES templates/=templates/*

    // ...

     @Inject
     Template template; // Locate and load the `templates/template.html` file

If your `templates` directory includes sub-directories, use `templates/=templates/*/` instead.

### Conclusion

If you want to get started with Quarkus or write something quickly, Quarkus Scripting with jbang lets you do that. No Maven, no Gradle - just a Java file. In this guide we outlined the very basics on using Quarkus with JBang; if you want to learn more about what JBang can do, go see https://jbang.dev.