Quarkus and the Java Developer Experience

GSSwain
The Startup
Published in
7 min readSep 20, 2020

This post covers

  • What is Quarkus?
  • Getting started with Quarkus
  • What is native executable?
  • Building native executable from Java using Quarkus
  • Startup time difference for Jar and native executables
  • Hot code replacement for Java (For me this is one of the biggest USP of the framework)

What is Quarkus?

Quarkus is a full-stack, Kubernetes-native Java framework made for Java virtual machines (JVMs) and native compilation, optimizing Java specifically for containers and enabling it to become an effective platform for serverless, cloud, and Kubernetes environments.

Getting started with Quarkus

You would need to have Java 8 or Java 11 installed along with Maven to get started with Quarkus.

Create a Boilerplate REST project

Let’s use the Maven plugin to create a boilerplate

mvn io.quarkus:quarkus-maven-plugin:1.8.1.Final:create

Now it would ask for the groupId, artifcatId, version to be entered. It would also ask if we want a REST resource (say yes) followed by the classname and the path of the rest resource. This is how it would look like

Now we have a Quarkus boilerplate project ready to be build and run.

Build the project

To create a Jar run the following command.

mvn package

The build took around 16 seconds.

We cover building native executable in the next section.

Run the project

java -jar target/quarkus-hello-world-1.0-SNAPSHOT-runner.jar

Test the project

Either use curl or open it on a browser and check

curl http://localhost:8080/hello

What is native executable?

In the world of Java, we compile the source code to Java byte code (Job of javac). When we run this compiled code, the Java byte code is converted to machine specific code (JRE does this). What if we could compile the Java source code to machine specific code and run the machine code directly without a JRE. This is exactly what a native executable is and GraalVM native-image can do this for us.

With native executables, we don’t need the JRE to run java code anymore . Those native executables are faster to bootstrap and need less resources to run. The only thing we need to give up is the Write Once Run Anywhere (WORA). That means the code would only run on similar systems on which it was compiled. But wait! Can’t we build for a specific machine and run the code along with that machine? Yes, we could do this with a container-image. So we still get WORA as long as the container run time can run on any machine.

Building native executable from Java using Quarkus

With Quarkus we can build the following native executables

  1. Native executable for our development machine (Won’t run on different OS or even with same OS)
  2. Container image with native executable (Runs everywhere where the container runtime is supported)

1. Build native executable for our development machine

Following are the steps to create a native executable for your development machine.

  • Install GraalVM
  • Set GRAALVM_HOME environment variable
  • Add GRAALVM_HOME/bin directory to PATH
  • Install GraalVM native-image utility (Run gu install native-image)
  • Set up C Langauge environment (Varies for different OS and covered below)
  • Finally run maven package with the native profile enabled (On Windows an extra step is required and covered below)

Setting up C

On a Mac run the following command

xcode-select --install

On a Linux based system run the following command

# dnf (rpm-based)
sudo dnf install gcc glibc-devel zlib-devel libstdc++-static
# Debian-based distributions:
sudo apt-get install build-essential libz-dev zlib1g-dev

On Windows 10, get the Visual Studio 2019 C++ build tools and install the checked optional packages (I have used the community edition)

Build native executable

For building the native image, we need to run the following command

mvn package -Pnative

For Windows 10, you must be using the command prompt (does not work on PowerShell yet). You need to run the following batch file before running the native maven build (The path to this file would vary depending upon the version of Visual Studio e.g. 2019, 2017 and if it Community or not)

"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat"

I have created a cmd file (native-build-windows.cmd) which runs both the commands for me.

call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat"mvn package -Pnative

Once you run this, you would get a native executable

On Mac, it creates an executable file named quarkus-hello-world-1.0-SNAPSHOT-runner under the target directory.

On Windows-10 an exe named quarkus-hello-world-1.0-SNAPSHOT-runner.exe is created under the target diectory.

Run this file and do a round of testing. Note this executable will not run on different OS and may not run on different machines with same OS as well. So we need to create a container image which would run irrespective of the OS.

2. Building container image with native executable

Let’s start Docker and get going. (Allow file sharing on Docker)

Run the following command

mvn package -Pnative -Dquarkus.native.container-build=true

It would again build an executable but this time if you run it on your own machine, it would fail as the container build happens on the ubi-quarkus-native-image:20.2.0-java11 container.

To run the executable, we can create a container image and run that with the following command

# Build the image
docker build -f src/main/docker/Dockerfile.native -t quarkus/quarkus-hello-world .
# Run the image
docker run -i --rm -p 8080:8080 quarkus/quarkus-hello-world

This brings ubi8/ubi-minimal:8.1 image as the runtime for the executable.

There is an easier way to create the container image. Just add the following Quarkus extension using the below command

mvn quarkus:add-extension -Dextensions=quarkus-container-image-jib

Run the following command to build the Docker image

mvn package -Pnative -Dquarkus.native.container-build=true -Dquarkus.container-image.build=true

The build took around 8 minutes.

By default the container image group is your user-name of the PC. You can change it with setting a different value for quarkus.container-image.group in the application.properties file

Now run the docker image (Replace the container-image-group value)

docker run -I --rm -p 8080:8080 <container-image-group>/quarkus-hello-world:1.0-SNAPSHOT

Startup time difference for Jar and native executables

Here is a quick summary of the startup time for the various kinds of builds and runtime on a Windows 10 with 20GB of RAM with intel i5 processor.

The jar version took around 1.773 seconds, the native machine executable started in 0.281 seconds and the native container image started in 0.017 seconds 😊.

Hot code replacement for Java

Hot code replacement is the idea, where we replace code which is already running. This was always there, SpringBoot Dev-tools does this when the classpath gets updated. The hot code replacement with Quarkus is cool and very similar in experience to the NodeJS based eco-system (with a watch option on source files). It detects the changes on the source files, compiles them and replaces them.

Let’s see this in action

Start the project in dev mode

mvnw quarkus:dev

Now let’s return “hi” instead of “hello” (any change would do) and save the file. You would notice the source file change would be detected and it would hot replace the code.

This certainly saves a few seconds to minutes (depending on your machine 😜). ❤️ this feature.

Conclusion:

Quarkus looks really good. I like the way it has given the much needed boost to the Java Developer experience and make us feel more productive. The supported eco-system is also promising (See it for yourself with mvn quarkus:list-extensions). I hope you would give it a try and discover for yourself.

References:

--

--