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. For a full list of possible statuses, check our FAQ entry. |
准备
要完成本指南,您需要:
-
Roughly 5 minutes
-
An IDE
-
JDK 11+ 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)
完整源码
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.6.3@pom
//DEPS io.quarkus:quarkus-resteasy-reactive
//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;
}
}
}
架构
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-resteasy-reactive
dependency like so:
//DEPS io.quarkus.platform:quarkus-bom:3.6.3@pom
//DEPS io.quarkus:quarkus-resteasy-reactive
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.6.3...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
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 |
运行应用
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.6.3) 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, resteasy-reactive, smallrye-context-propagation, vertx]
启动后,可以请求提供的接口:
$ curl -w "\n" http://localhost:8080/hello
hello
After that, hit CTRL+C
to stop the application.
用
curl -w "\n" 自动添加换行在本例中,我们使用 |
Why is
quarkus-resteasy-reactive not resolved?In this second run you should not see a line saying it is resolving
|
使用注入
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-resteasy-reactive
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:
|
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.6.3@pom
//DEPS io.quarkus:quarkus-resteasy-reactive
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 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
You can use //Q:CONFIG <property>=<value>
to set up static configuration
for your application.
I.e. if you wanted to add the smallrye-openapi
and swagger-ui
extensions
and have the Swagger UI always show up you would add the following:
//DEPS io.quarkus:quarkus-smallrye-openapi:3.6.3
//DEPS io.quarkus:quarkus-swagger-ui:3.6.3
//Q:CONFIG quarkus.swagger-ui.always-include=true
Now during build the quarkus.swagger-ui.always-include
will be generated
into the resulting jar and http://0.0.0.0:8080/q/swagger-ui
will be
available when run.
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. Install it using `gu install native-image` 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.6.3) 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, resteasy-reactive, 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.6.3) 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, resteasy-reactive, smallrye-context-propagation, vertx]
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.