Bootstrapping a Quarkus App

Praveen G. Nair
Javarevisited
Published in
7 min readMar 14, 2021
Quarkus : Supersonic Subatomic Java

Overview

Microservices is now a common term with every software development workspace. Let me remind you, in the simplest definition of what microservice is:

Microservices are a way of breaking large software projects into loosely coupled modules, which communicate with each other through simple Application Programming Interfaces (APIs).

The Java ecosystem is always an amazing example of how technology can reinvent itself in new ways and frameworks.
For example, it is interesting to reflect on how Spring Boot brought the Java community features that made the language use among the most widely used in the world for most of the new projects.

But now, we have so many innovative projects that are designed from the ground up to run on the cloud and with microservice architectures.

Java is proven to be a fantastic technology with an active community but has lost ground in many projects where Python or NodeJS made cloud costs cheaper due to their lower CPU, startup time, and memory consumption.

Thinking of making Java relevant and stronger in Cloud Native application, the Quarkus project was launched.

Quarkus

Quarkus is a Red Hat Initiative that features Cloud Native, Container first, Microservice ready framework for writing Java applications based on the standards and frameworks you are already using today (Hibernate, RESTEasy, Camel, Vert.X etc).

What does it mean?

  • Container First: Minimal footprint Java applications optimal for running in containers.
  • Cloud Native: Embraces 12 factor architecture in environments like Kubernetes.
  • Microservice First: Brings lightning-fast startup time and code turn around to Java apps.

I think now we have a general understanding of why we are experimenting with Quarkus. In this blog, we would create a simple quarkus app and run it using the maven tool.

1. Our First Application

Requirements to build a Quarkus Java Applications:

  • JDK 8 or 11+ installed with JAVA_HOME configured appropriately
  • Apache Maven 3.6.2+
  • GraalVM (if native apps needed)

Quarkus applications can be quickly bootstrapped using Maven or Gradle plugin. The plugin will generate a minimal project structure with a sample REST Endpoint and the Quarkus’s Maven dependencies included in the configuration file.

If you prefer an interactive graphical user interface, you can navigate to code.quarkus.io to download a bootstrapped project with your specific requirements.

Lets assume you have maven installed, run the below command on the terminal :

mvn io.quarkus:quarkus-maven-plugin:1.12.2.Final:create \
-DprojectGroupId=com.praveen.samples.quarkus \
-DprojectArtifactId=sample-quarkus-app \
-DclassName="com.praveen.samples.quarkus.HelloResource" \
-Dpath="helloworld"

This will generate the project skeleton, a HelloResource with a /helloworld endpoint exposed, configuration, Maven project, and Dockerfiles.

Once imported into an IDE, we’ll have a structure similar to that shown in the image below:

Project tree Structure

Once imported successfully examine the contents of our resource file HelloResource :

@Path("/helloworld")
public class HelloResource {

@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "Hello RESTEasy";
}
}

Looks good so far. Now we have a simple qurakus app with a single RESTEasy JAX-RS endpoint. You can find more details on the generated README file in the root. Let’s go ahead and test it by opening a terminal and running the command:

mvn compile quarkus:dev
Startup console logs.

Our REST endpoint should be exposed at http://localhost:8080/helloworld. you can hit the url with the browser or curl command, & you will see:

Hello RESTEasy

You would see Quarkus now ships with a Dev UI, which is available in dev mode only at http://localhost:8080/q/dev/. It allows you to quickly visualize all the extensions currently loaded, see their status and go directly to their documentation. For more details visit Dev UI.

2. Dependency Injection

Quarkus provides a hot-reload capability when running in development mode (mvn compile quarkus:dev). We don’t need to restart or do save changes explicitly. Let us create a service and inject into our sample application and see how hot reload and DI works.

First, we’ll create a HelloService class:

@ApplicationScoped 
public class HelloService{

public String sayHello(String name){
return "Hello " + name;
}
}

Now, we’ll modify the HelloResource class, injecting the HelloService and adding a new method:

@Inject 
HelloService helloService;
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/hello/{name}")
public String greet(@PathParam("name") String name) {
return helloService.sayHello(name);
}

Next, let’s test our new endpoint: http://localhost:8080/helloworld/hello/jasmine

Hello jasmine

We’ll make one more change to demonstrate that the same can be applied to property files. Let’s edit the application.properties file and add one more key:

greet-message=Good day

After that, we’ll modify the HelloService to use our new property:

@ConfigProperty(name = "greet-message") 
private String greetMessage;
public String sayHello(String name) {
return greetMessage + " " + name;
}

If we hit the same url again, we should now see:

Good day jasmine

3. Package Application

Now that we have tried some features, it is time to package the application using JVM. You could run the below command to package the app.

mvn package

This will generate 2 jar files inside the target directory:

  • /quarkus-app/quarkus-run.jar — an executable jar with the dependencies copied to target/lib.
  • /sample-quarkus-app-1.0-SNAPSHOT.jar — contains classes and resource files.

Run the packaged application:

java -jar target/quarkus-app/quarkus-run.jar

Wohoo !! we just started a Quarkus app in JVM mode.

4. GraalVM and Native App

Next, we’ll produce a native image of our application. A native image will improve start-up time and time to first response. In other words, it contains everything it needs to run, including the minimal JVM necessary to run the application.

Installing GraalVM

To start with, we need to have GraalVM installed which is a high-performance, embeddable, polyglot virtual machine for running applications written in JavaScript, Python, Ruby, R, JVM-based languages like Java, Scala, Kotlin, and LLVM-based languages such as C and C++.

GraalVM releases are available at: https://github.com/graalvm/graalvm-ce-builds/releases

With the Quarkus version 1.12.2.Final it is recommend installing GraalVM 21 with JDK 11 support (graalvm-ce-java11–21.0.0).

Download GraalVM and unzip it in a folder of your likes. Within the GraalVM package you will find:

  • A JVM
  • A JavaScript engine & node.js runtime
  • A LLVM engine

Now export the GRAALVM_HOME with the path where GraalVM was installed:

export GRAALVM_HOME=/path/to/graal

Next, we need the native-image tool which allows you to ahead-of-time compile Java code to a standalone executable. You can install the native-image tool as follows:

${GRAALVM_HOME}/bin/gu install native-image

For more details on GraalVM installation visit install-graalvm.

Build Native Executable

Now that we have configured graalvm, we can do native package of our app. We have native profile already defined in pom.xml. We’ll now stop the application (Ctrl + C), if not stopped already, and run the command:

mvn package -Pnative

This can take a few seconds to complete. Because native images try to create all code AOT(Ahead of Time) to boot faster, as a result, we’ll have a bit longer build times.

Run the below command to verify that our native artifact is properly constructed.

mvn verify -Pnative

If the result is success then your artifact is all good. After the package and verify command you would see a native executable runner file in your target folder. To run a native executable directly just execute below in terminal.

./sample-quarkus-app-1.0.0-SNAPSHOT-runner
Native App started.

Bingo !! Our Quarkus Native App just started. Now see the difference between the previous JVM app startup time and native app startup time.

JVM mode Start up : 1.219s

Native mode Start up : 0.015s

~98% reduction in startup time. Isn’t that impressive !!!

5. Docker Build

Now that we have our native artifact ready, we’ll create an image using our native artifact. For that, we must have a container runtime (i.e. Docker) running in our machine.

In order to build a native container image of a Quarkus application, first build the application using native profile and container-build as follows:

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

The above command will take some time to complete...

Now If you see when we created the project, it created a docker file for us Dockerfile.native, which has minimal requirements to run the app. if you want to change the base OS you can modify the docker file as required.

The provided Dockerfiles use UBI (Universal Base Image) as parent image. This base image has been tailored to work perfectly in containers. The Dockerfiles use the minimal version of the base image to reduce the size of the produced image. For more details visit here.

FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3
WORKDIR /work/
RUN chown 1001 /work \
&& chmod "g+rwX" /work \
&& chown 1001:root /work
COPY --chown=1001:root target/*-runner /work/application

EXPOSE 8080
USER 1001

CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]

Now run the below cmd to build a docker image:

docker build -f src/main/docker/Dockerfile.native -t quarkus/sample-quarkus-app .

This would have created the image. Run docker images to confirm. Now run the container using below:

docker run -i --rm -p 8080:8080 quarkus/sample-quarkus-app
docker run

The container started at an incredibly low time of 0.041s.

Now we know that's one of the strengths of Quarkus. Now our image can be shipped anywhere Openshift, Kubernetes etc.

Finally, we can test our REST endpoint to validate our application. Hit http://localhost:8080/helloworld/hello/jasmine

Good day jasmine

Summary

In this blog, we have seen that Quarkus is a great addition that can bring Java more effectively to the cloud, and most importantly we don’t need to learn anything new, after all it's all Java !! There are a lot of new features getting added daily in Quarkus so you can try and keep watch on them at the Quarkus GitHub repository.

I have shared the code for the above sample app we tried here. Happy learning !!

References:

https://quarkus.io/guides/

https://www.graalvm.org/docs/getting-started/

--

--

Praveen G. Nair
Javarevisited

I am a Software Developer and a Technologist. Interested in all cool stuffs of software development, Machine Learning and Cloud. https://praveeng-nair.web.app/