Quarkus 3.4 - Container-first Java Stack: Install with OpenJDK 21 and Create REST API

nabbisen - Oct 16 '23 - - Dev Community

Summary

Quarkus is one of Java frameworks for microservices development and cloud-native deployment. It is developed as container-first stack, is designed to get along with Kubernetes and can be integrated with GraalVM or HotSpot virtual machines (VM).

This post shows how to install Quarkus with OpenJDK 21 on Devuan 5 and create an example project to serve JSON REST APIs.

Why Quarkus ?

Java was not ready for the cloud-computing era once. It's because Java and its ecosystem were developed primarily for services which were expected to be always active or at least at most time. Once their servers and services were started, they kept memory consumed after some initiation time. Their services were expected not to be stopped or destroyed once started.

In contrast, services in the cloud remarkably depend on virtualization. VMs are frequently created and destroyed in cloud environment.
Thus cloud development and deployment, driven by container VM and kube pods etc., require fast start up to machines.
After many contributions by many, improvement and new encounters with GraalVM etc., Java lit the way to solve the problem at last.

Backed up by such progress, Quarkus has these good points:

  • Fast boot time
  • Low memory consumption

Environment

  • OS: Devuan 5 Daedalus
  • App Engine: OpenJDK 21
  • Project Build and Management: Apache Maven 3 (3.9.5), Gradle 8 (8.3)
  • Java Stack: Quarkus 3 (3.4.3)

Tutorial

Suppose you have OpenJDK 21 and Maven which are ready with environment variables PATH and JAVA_HOME set.

With Devuan used, my past posts to install Java 21 and Maven will help.

Starting point 💫

My environment was as below:

$ java --version
openjdk 21 2023-09-19
OpenJDK Runtime Environment (build 21+35-2513)
OpenJDK 64-Bit Server VM (build 21+35-2513, mixed mode, sharing)

$ mvn --version
Apache Maven 3.9.5 (57804ffe001d7215b5e7bcb531cf83df38f93546)
Maven home: /(...)/apache-maven-3.9.5
Java version: 21, vendor: Oracle Corporation, runtime: /(...)/jdk-21
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "6.1.0-13-amd64", arch: "amd64", family: "unix"
Enter fullscreen mode Exit fullscreen mode

Create a Quarkus project 🦯

Maven helps to create a project as below:

$ mvn io.quarkus.platform:quarkus-maven-plugin:3.4.3:create \
    -Dextensions='resteasy-reactive-jackson' \
    -DnoCode \
    -DprojectGroupId=com.quarkusrestjsonexample \
    -DprojectArtifactId=quarkus-rest-json-example \
    -DbuildTool=gradle
Enter fullscreen mode Exit fullscreen mode

The last -DbuildTool=gradle is optional. I prefer Gradle build.

The result was as below:

[INFO] Scanning for projects...
[INFO] 
[INFO] ------------------< org.apache.maven:standalone-pom >-------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] --------------------------------[ pom ]---------------------------------
[INFO] 
[INFO] --- quarkus:3.4.3:create (default-cli) @ standalone-pom ---
[INFO] -----------
[INFO] selected extensions: 
- io.quarkus:quarkus-resteasy-reactive-jackson

[INFO] 
applying codestarts...
[INFO] 📚 java
🔨 gradle
📦 quarkus
📝 config-properties
🔧 dockerfiles
🔧 gradle-wrapper
[INFO] 
-----------
[SUCCESS] ✅  quarkus project has been successfully generated in:
--> /(...)/quarkus-rest-json-example
-----------
[INFO] 
[INFO] ========================================================================================
[INFO] Your new application has been created in /(...)/quarkus-rest-json-example
[INFO] Navigate into this directory and launch your application with mvn quarkus:dev
[INFO] Your application will be accessible on http://localhost:8080
[INFO] ========================================================================================
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.209 s
[INFO] Finished at: 2023-10-15T20:38:05+09:00
[INFO] ------------------------------------------------------------------------
Enter fullscreen mode Exit fullscreen mode

Check build dependencies 🔍

Let's go inside the project:

$ cd rest-json-quickstart
Enter fullscreen mode Exit fullscreen mode

You can see implementation 'io.quarkus:quarkus-resteasy-reactive-jackson' in dependencies:

$ grep -a5 'dependencies {' build.gradle
repositories {
    mavenCentral()
    mavenLocal()
}

dependencies {
    implementation enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}")
    implementation 'io.quarkus:quarkus-resteasy-reactive-jackson'
    implementation 'io.quarkus:quarkus-arc'
    testImplementation 'io.quarkus:quarkus-junit5'
}
Enter fullscreen mode Exit fullscreen mode

OK.

Create .java files in src

There are no .java files in src by default:

$ ls src/main/java/
Enter fullscreen mode Exit fullscreen mode

Here we will create two files in src/main/java: Fruit.java and FruitResource.java as bean and resource.

$ nvim src/main/java/{Fruit.java,FruitResource.java}
Enter fullscreen mode Exit fullscreen mode

Write as below in Fruit.java:

package com.quarkusrestjsonexample;

public class Fruit {

    public String name;
    public String description;

    public Fruit() {
    }

    public Fruit(String name, String description) {
        this.name = name;
        this.description = description;
    }
}
Enter fullscreen mode Exit fullscreen mode

When you use NeoVim/Vim, you should type :next after :w (save) in order to switch files.

Then write as below in FruitResource.java:

package com.quarkusrestjsonexample;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Set;

import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;

@Path("/fruits")
public class FruitResource {

    private Set<Fruit> fruits = Collections.newSetFromMap(Collections.synchronizedMap(new LinkedHashMap<>()));

    public FruitResource() {
        fruits.add(new Fruit("Apple", "Winter fruit"));
        fruits.add(new Fruit("Pineapple", "Tropical fruit"));
    }

    @GET
    public Set<Fruit> list() {
        return fruits;
    }

    @POST
    public Set<Fruit> add(Fruit fruit) {
        fruits.add(fruit);
        return fruits;
    }

    @DELETE
    public Set<Fruit> delete(Fruit fruit) {
        fruits.removeIf(existingFruit -> existingFruit.name.contentEquals(fruit.name));
        return fruits;
    }
}
Enter fullscreen mode Exit fullscreen mode

Type :wq to close NeoVim/Vim.

Run service 🚀

Gradle helps to build it and start service as below:

$ ./gradlew --console=plain quarkusDev
Enter fullscreen mode Exit fullscreen mode

Then your server will start:

Starting a Gradle Daemon (subsequent builds will be faster)
(...)
__  ____  __  _____   ___  __ ____  ______ 
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \   
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/   
2023-10-15 20:49:36,188 INFO  [io.quarkus] (Quarkus Main Thread) quarkus-rest-json-example 1.0.0-SNAPSHOT on JVM (powered by Quarkus 3.4.3) started in 1.242s. Listening on: http://localhost:8080
2023-10-15 20:49:36,190 INFO  [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
2023-10-15 20:49:36,191 INFO  [io.quarkus] (Quarkus Main Thread) Installed features: [cdi, resteasy-reactive, resteasy-reactive-jackson, smallrye-context-propagation, vertx]
Enter fullscreen mode Exit fullscreen mode

Yay 🙌

Conclusion

According to the Gradle output above, access to http://localhost:8080 with your web browser.
You will see:

quarkus-01

Why 404 ? It's because there is no definition about @Path("/"), the root path, in FruitResource.java. It's OK now, for it can be extended easily.

Then go to /fruits and you will see 😉

quarkus-02

Our Quarkus Web API service is running in good health.

Acknowledgements

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .