Getting started with QuarkusIO

This tutorial will introduce you to QuarkusIO, the latest framework for building supersonic Java! Quarkus features a Cloud Native, Container first, Microservice ready framework for writing Java applications based on the standards and frameworks you are 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

Here is a bird’s eye view of the Project:

As you can see, one of the most interesting aspect of quarkus is that you can build native executables using GraalVM. That makes Quarkus applications ideal for containers and serverless workloads. In this first tutorial we will learn how to build a Quarkus REST application using the Maven tooling available and add a minimal of complexity to it. There are two main major versions for Quarkus applications and each one has different requirements. Let’s start from the latest version.

Which is the latest version of Quarkus?

The latest stable version of Quarkus is 2.7.0.CR1 which requires the following BOM:

<dependencyManagement>
   <dependencies>
      <dependency>
         <groupId>io.quarkus</groupId>
         <artifactId>quarkus-bom</artifactId>
         <version>2.7.0.CR1</version>
         <type>pom</type>
         <scope>import</scope>
      </dependency>
   </dependencies>
</dependencyManagement>

Building your first Quarkus 2.x application

JDK 11 is the minimal version to use Quarkus 2.0. Quarkus 2.0 applications can be bootstrapped either using Quarkus CLI or its Maven plugin. Running Quarkus with Maven plugin is covered in the next paragraph.

You can install Quarkus CLI on Linux, macOS, and Windows (using a tool like like cygwin or mingw) as follows:

curl -Ls https://sh.jbang.dev | bash -s - app install --fresh --force quarkus@quarkusio

Once installed, the tool “quarkus” will be in your PATH.

Next, let’s use it to create one named “demo” with the following Maven coordinates:

$ quarkus create app --group-id com.sample --artifact-id demo --version 1.0
applying codestarts...
  java
  maven
  quarkus
  config-properties
  dockerfiles
  maven-wrapper
  resteasy-codestart[SUCCESS]   quarkus project has been successfully generated in:
--> /home/quarkus/demo

Fine, let’s see what we have got:

tree src
src
├── main
│   ├── docker
│   │   ├── Dockerfile.jvm
│   │   ├── Dockerfile.legacy-jar
│   │   ├── Dockerfile.native
│   │   └── Dockerfile.native-distroless
│   ├── java
│   │   └── com
│   │       └── sample
│   │           └── GreetingResource.java
│   └── resources
│       ├── application.properties
│       └── META-INF
│           └── resources
│               └── index.html
└── test
    └── java
        └── com
            └── sample
                ├── GreetingResourceTest.java
                └── NativeGreetingResourceIT.java

The most interesting items are:

  • The GreetingResource.java containing the Hello REST Endpoint
  • Several Dockerfile : In particular, the Dockerfile.jvm for building a Java Container Image of your application. And the Dockerfile.native to build a native image of the application.
  • An index.html file to remind you that you can add static files (HTML, images, Javascript) in that folder
  • Two Test classes added under the folder “test” to test the application with Java or as native executable.
  • A file named application.properties which is the main configuration file in Quarkus

Next, let’s have a look at the Controller app:

package com.sample;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/hello")
public class GreetingResource {

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

Next, build your application with the “build” command:

$ quarkus build

Quarkus CLI can also run applications using the “dev” command:

$ quarkus dev

[INFO] Scanning for projects...
[INFO] 
[INFO] --------------------------< com.sample:demo >---------------------------
[INFO] Building demo 1.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- quarkus-maven-plugin:2.0.0.Final:dev (default-cli) @ demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 2 resources
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /home/francesco/quarkus/demo/target/classes
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /home/francesco/quarkus/demo/src/test/resources
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 2 source files to /home/francesco/quarkus/demo/target/test-classes
Listening for transport dt_socket at address: 5005
__  ____  __  _____   ___  __ ____  ______ 
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \   
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/   
2021-07-04 12:35:09,464 INFO  [io.quarkus] (Quarkus Main Thread) demo 1.0 on JVM (powered by Quarkus 2.0.0.Final) started in 1.535s. Listening on: http://localhost:8080
2021-07-04 12:35:09,479 INFO  [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
2021-07-04 12:35:09,479 INFO  [io.quarkus] (Quarkus Main Thread) Installed features: [cdi, resteasy, smallrye-context-propagation]

The only REST Endpoint is available through the “/hello” Path:

Besides that, you can check at the bottom of the Quarkus log, the following log emitted by Continuous Testing:

--
Tests paused, press [r] to resume, [h] for more options>

Therefore, you can, at any time, execute a round of tests on your services. For example, press “r” to run tests:

All 1 tests are passing (0 skipped), 1 tests were run in 2191ms. Tests completed at 12:35:34.
Press [r] to re-run, [v] to view full results, [p] to pause, [h] for more options>

Building your first Quarkus 1.x application

Requirements to build a Quarkus Java Applications:

  • JDK 8 or 11+ installed with JAVA_HOME configured appropriately
  • Apache Maven 3.6.2+

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

mvn io.quarkus:quarkus-maven-plugin:1.4.1.Final:create -DprojectGroupId=com.sample -DprojectArtifactId=hello-quarkus -DclassName="com.sample.QuarkusEndpoint" -Dpath="/hello"

The following project structure will be created:

hello-quarkus/
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
    ├── main
    │   ├── docker
    │   │   ├── Dockerfile.jvm
    │   │   └── Dockerfile.native
    │   ├── java
    │   │   └── com
    │   │       └── sample
    │   │           └── QuarkusEndpoint.java
    │   └── resources
    │       ├── application.properties
    │       └── META-INF
    │           └── resources
    │               └── index.html
    └── test
        └── java
            └── com
                └── sample
                    ├── NativeQuarkusEndpointIT.java
                    └── QuarkusEndpointTest.java

The most interesting items are:

  • The QuarkusEndpoint.java containing the Hello REST Endpoint
  • Two Dockerfile : Dockerfile.jvm to build the application as a Container using a Java image. Dockerfile.native to build a native image of the application.
  • An index.html file to remind you that you can add static files (HTML, images, Javascript) in that folder
  • Two Test classes added under the folder “test” to test the application with Java or as native executable.
  • A file named application.properties which is the main configuration file in Quarkus

Here is the quintessential REST Endpoint, which returns a text string upon invocation of the “hello()” GET Resource.

package com.sample;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/hello")
public class QuarkusEndpoint {

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

The pom.xml file, reflects the libraries needed to bootstrap the project. Here is the dependencies section of it:

  <properties>
    <compiler-plugin.version>3.8.1</compiler-plugin.version>
    <maven.compiler.parameters>true</maven.compiler.parameters>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <quarkus-plugin.version>1.0.0.Final</quarkus-plugin.version>
    <quarkus.platform.artifact-id>quarkus-universe-bom</quarkus.platform.artifact-id>
    <quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id>
    <quarkus.platform.version>1.0.0.Final</quarkus.platform.version>
    <surefire-plugin.version>2.22.1</surefire-plugin.version>
  </properties>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>${quarkus.platform.group-id}</groupId>
        <artifactId>${quarkus.platform.artifact-id}</artifactId>
        <version>${quarkus.platform.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-resteasy</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-junit5</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>io.rest-assured</groupId>
      <artifactId>rest-assured</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

You can build and run your project from the root folder with:

mvn clean compile quarkus:dev

The application will bootstrap Quarkus in a snap:

[INFO] Running com.sample.QuarkusEndpointTest
2019-03-14 16:46:19,743 INFO  [io.qua.dep.QuarkusAugmentor] (main) Beginning quarkus augmentation
2019-03-14 16:46:20,292 INFO  [io.qua.dep.QuarkusAugmentor] (main) Quarkus augmentation completed in 549ms
2019-03-14 16:46:20,610 INFO  [io.quarkus] (main) Quarkus 1.0.0.Final started in 0.294s. Listening on: http://127.0.0.1:8081

You can test it with:

curl http://localhost:8080/hello

That will return the “Hello” String

Debugging and Hot Deploying with Quarkus

As you can see, we have launched Quarkus with quarkus:dev which runs Quarkus in development mode. This enables hot deployment with background compilation, which means that when you modify your Java files your resource files and refresh your browser these changes will automatically take effect. This works too for resource files like the configuration property file. Refreshing the browser triggers a scan of the workspace, and if any changes are detected the Java files are recompiled and the application is redeployed; your request is then serviced by the redeployed application. If there are any issues with compilation or deployment an error page will let you know.

Also, in that mode, Quarkus will listen for a debugger on port 5005. If your want to wait for the debugger to attach before running you can pass -Ddebug on the command line. If you don’t want the debugger at all you can use -Ddebug=false.

Building a more complex application

We will now learn how to build a slightly more complex Quarkus application which still uses a REST Endpoint but has a minimal UI to insert data in memory.

You can re-use the same project you have just built, or create a new one from scratch. Start by adding a Java Bean as Domain class:

package com.sample;

import java.util.Objects;

public class Person {

    String name;
    String surname;

    public Person( ) {  }

    public String getName() {
        return name;
    }
    public String getSurname() {  return surname;  }
    public void setName(String name) {
        this.name = name;
    }
    public void setSurname(String surname) {
        this.surname = surname;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", surname='" + surname + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return Objects.equals(name, person.name) &&
                Objects.equals(surname, person.surname);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, surname);
    }
}

Now a REST Endpoint to save this object in a Collection:

package com.sample;

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

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/persons")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class RESTEndpoint {

    private Set<Person> persons = Collections.newSetFromMap(Collections.synchronizedMap(new LinkedHashMap<>()));

    @GET
    public Set<Person> list() {
        return persons;
    }

    @POST
    public Set<Person> add(Person person) {
        System.out.println("Saving: " +person);
        persons.add(person);
        return persons;
    }

}

Basically, we have added a GET method to retrieve the list of Person, and a POST method to save a new Person in the Set.

As the above class can Produce and Consume JSON, we need to include also the quarkus-resteasy-jsonb artifact to our dependencies:

<dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-resteasy-jsonb</artifactId>
</dependency>

As it is, our application it is ready to be tested:

mvn clean compile quarkus:dev

You can add a new entry with cURL, passing as data the JSON to be consumed:

curl -d '{"name":"john", "surname":"black"}' -H "Content-Type: application/json" -X POST http://127.0.0.1:8080/persons

We can check that the data is available, with the corresponding GET request:

curl http://127.0.0.1:8080/persons
[{"name":"john","surname":"black"}]

Adding an UI to our Quarkus project

Finally, we can show how we can add Web application pages to our project. In order to do that, we need to add HTML pages under the following path:

│   └── resources
│       └── META-INF
│           └── resources

For example, we will add an HTML page using AngularJS to display the data in Tabular format, with a Form to add new items via POST:

<!doctype html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>Quarkus REST service</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/wingcss/0.1.8/wing.min.css"/>
    <!-- Load AngularJS -->
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
    <script type="text/javascript">
      var app = angular.module("PersonManagement", []);

      //Controller Part
      app.controller("PersonManagementController", function ($scope, $http) {

        //Initialize page with empty data
        $scope.persons = [];

        $scope.form = {
          name: "",
          surname: ""
        };

        //Now load the data from server
        _refreshPageData();

        //HTTP POST methods for add data
        $scope.add = function () {
          var data = { "name": $scope.form.name, "surname": $scope.form.surname };

          $http({
            method: "POST",
            url: '/persons',
            data: angular.toJson(data),
            headers: {
              'Content-Type': 'application/json'
            }
          }).then(_success, _error);
        };


        //HTTP GET- get all persons collection
        function _refreshPageData() {
          $http({
            method: 'GET',
            url: '/persons'
          }).then(function successCallback(response) {
            $scope.persons = response.data;
          }, function errorCallback(response) {
            console.log(response.statusText);
          });
        }

        function _success(response) {
          _refreshPageData();
          _clearForm();
        }

        function _error(response) {
          alert(response.data.message || response.statusText);
        }

        //Clear the form
        function _clearForm() {
          $scope.form.name = "";
          $scope.form.surname = "";
        }
      });
    </script>
</head>
<body ng-app="PersonManagement" ng-controller="PersonManagementController">

<div class="container">
    <h1>Quarkus REST Service</h1>

    <form ng-submit="add()">
        <div class="row">
            <div class="col-6"><input type="text" placeholder="Name" ng-model="form.name" size="60"/></div>
        </div>
        <div class="row">
            <div class="col-6"><input type="text" placeholder="Surname" ng-model="form.surname" size="60"/></div>
        </div>
        <input type="submit" value="Save"/>
    </form>

    <h3>Person List</h3>
    <div class="row">
        <div class="col-4">Name</div>
        <div class="col-8">Surname</div>
    </div>
    <div class="row" ng-repeat="person in persons">
        <div class="col-4">{{ person.name }}</div>
        <div class="col-8">{{ person.surname }}</div>
    </div>
</div>

</body>
</html>

Again, run your application with:

mvn clean compile quarkus:dev

And see it in action at: http://localhost:8080

Now experiment changing anything in the HTML page, for example the Heading contained in it. As the application is running in Development mode, you will see your changes without restarting your app!

Conclusion

In this first Quarkus tutorial we have covered the basic set up of an application and the tooling which you can use to bootstrap your projects. Continue learning QuarkusIO in this tutorial which shows how to create a native application: Building Container-ready native applications with Quarkus

Source code for this example: https://github.com/fmarchioni/mastertheboss/tree/master/quarkus/hello-quarkus

References: https://quarkus.io/

Securing Quarkus with Elytron Database Realm

Quarkus security includes several extensions from Elytron. In this tutorial we will learn how to use the Database Realm Authentication with a simple REST Service.

Our project will perform authentication against the H2 in-memory database. In order to bootstrap our project we will need the following extensions

mvn io.quarkus:quarkus-maven-plugin:1.0.0.CR1:create \
    -DprojectGroupId=com.mastertheboss \
    -DprojectArtifactId=quarkus-jdbc-realm \
    -DclassName="com.mastertheboss.SecuredResource" \
    -Dextensions="elytron-security-jdbc,  jdbc-h2, resteasy" \
    -Dpath="/hello"

Now let’s add at first some protected resources in our REST Service. We will add one resource secured against the “admin” Role and one secured against the “guest” Role:

package com.mastertheboss;

import javax.annotation.security.RolesAllowed;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/")
public class SecuredResource {

    @GET
    @Path("/securedAdmin")
    @RolesAllowed("admin")
    @Produces(MediaType.TEXT_PLAIN)
    public String helloAdmin() {
        return "Hello from secured resource!";

    }

    @GET
    @Path("/securedGuest")
    @RolesAllowed("guest")
    @Produces(MediaType.TEXT_PLAIN)
    public String helloGuest() {
        return "Hello from secured resource!";

    }
}
package com.mastertheboss;

import io.quarkus.test.junit.QuarkusTest;
import org.apache.http.HttpStatus;
import org.junit.jupiter.api.Test;

import static io.restassured.RestAssured.given;

@QuarkusTest
public class SecuredResourceTest {

    @Test
    public void testHelloEndpoint() {

        given()
                .auth().preemptive().basic("admin", "admin")
                .when()
                .get("/securedAdmin")
                .then()
                .statusCode(HttpStatus.SC_OK);

        given()
                .auth().preemptive().basic("guest", "guest")
                .when()
                .get("/securedGuest")
                .then()
                .statusCode(HttpStatus.SC_OK);

    }

}

To have Quarkus bootstrap the H2 Database, include the following class which triggers the H2DatabaseTestResource:

package com.mastertheboss;

import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.h2.H2DatabaseTestResource;

@QuarkusTestResource(H2DatabaseTestResource.class)
public class TestResources {
}

As the above class is included in an external JAR file, you have to add its dependency to the pom.xml file:

    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-test-h2</artifactId>
      <scope>test</scope>
    </dependency>

Configuring Elytron Database Realm with Quarkus

Done with the source code, let’s now add the configuration to bootstrap the Database Realm. First of all, an import.sql script to create the Tables at start is needed. It will also add two users for testing our project:

CREATE TABLE test_user (
  id INT,
  username VARCHAR(255),
  password VARCHAR(255),
  role VARCHAR(255)
);

INSERT INTO test_user (id, username, password, role) VALUES (1, 'admin', 'admin', 'admin');
INSERT INTO test_user (id, username, password, role) VALUES (2, 'user','user', 'user');

Then, into the application.properties file, add the configuration of the Elytron Realm, which points to the Table we have just created:

quarkus.datasource.url=jdbc:h2:tcp://localhost/mem:elytron_jdbc_test;DB_CLOSE_DELAY=-1;DB_CLOSE_DELAY=-1;INIT=RUNSCRIPT FROM 'classpath:import.sql'
quarkus.datasource.driver=org.h2.Driver
quarkus.datasource.username=username-default

quarkus.security.jdbc.enabled=true
quarkus.security.jdbc.principal-query.sql=SELECT u.password, u.role FROM test_user u WHERE u.username=?
quarkus.security.jdbc.principal-query.clear-password-mapper.enabled=true
quarkus.security.jdbc.principal-query.clear-password-mapper.password-index=1
quarkus.security.jdbc.principal-query.attribute-mappings.0.index=2
quarkus.security.jdbc.principal-query.attribute-mappings.0.to=groups

Here is the full project structure:

src
├── main
│   ├── docker
│   │   ├── Dockerfile.jvm
│   │   └── Dockerfile.native
│   ├── java
│   │   └── com
│   │       └── mastertheboss
│   │           └── SecuredResource.java
│   └── resources
│       ├── application.properties
│       └── META-INF
│           └── resources
│               └── index.html
└── test
    ├── java
    │   └── com
    │       └── mastertheboss
    │           ├── NativeSecuredResourceIT.java
    │           ├── SecuredResourceTest.java
    │           └── TestResources.java
    └── resources
        └── import.sql

Running the Test class

Done with the code and configuration, you can run the Test as follows:

mvn install

As you can see from the logs, the test completed successfully and, behind the hoods, the IT started and stopped the Database when done:

[INFO] H2 database started in TCP server mode; server status: TCP server running at tcp://10.5.126.52:9092 (only local connections)
2019-11-11 18:20:03,126 INFO  [io.qua.dep.QuarkusAugmentor] (main) Beginning quarkus augmentation
2019-11-11 18:20:04,122 INFO  [io.qua.dep.QuarkusAugmentor] (main) Quarkus augmentation completed in 996ms
2019-11-11 18:20:04,735 INFO  [io.quarkus] (main) Quarkus 1.0.0.CR1 started in 0.585s. Listening on: http://0.0.0.0:8081
2019-11-11 18:20:04,735 INFO  [io.quarkus] (main) Profile test activated. 
2019-11-11 18:20:04,736 INFO  [io.quarkus] (main) Installed features: [agroal, cdi, jdbc-h2, narayana-jta, resteasy, security, security-jdbc]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 7.258 s - in com.mastertheboss.SecuredResourceTest

That’s all! Source code for this tutorial: https://github.com/fmarchioni/mastertheboss/tree/master/quarkus/quarkus-jdbc-realm

Using Servlet Context in Quarkus

Quarkus includes the undertow extension, therefore it’s fully capable of delivering Servlet applications. In this example, we will show how to use the ServletContext in a REST Application. One challenge is that you cannot @Inject the ServletContext directly as you would do it in a Jave EE environment.

Nevertheless you can count on the JAX-RS javax.ws.rs.core.Context annotation to do that:

@Controller
@Path("/greeting/")
@RequestScoped
public class GreetingResource {

    @Context
    ServletContext servletContext;

}

 Or, as an alternative you can also do that:

@Controller
@Path("/rest/")
@RequestScoped
public class HelloResource {

    @GET
    @Path("/hello")
    @Produces({ MediaType.TEXT_PLAIN })
    public Response hello(@Context ServletContext servletContext) {
        // 
    }
}

 

Scheduling periodic Tasks in Quarkus

Quarkus does not provide support for EJB therefore you cannot rely on EJB Timers to schedule simple tasks. However, thanks to the extension quarkus-scheduler it is very simple to add this functionality.

Let’s create a simple project with Quarkus Maven plugin:

mvn io.quarkus:quarkus-maven-plugin:0.19.1:create \
    -DprojectGroupId=com.sample \
    -DprojectArtifactId=demo-scheduler \
    -DclassName="com.sample.CountEndpoint" \
    -Dpath="/count" \
    -Dextensions="scheduler"

By the way, when the Quarkus CLI is available, you can use a simpler script to bootstrap your projects:

$ quarkus create-project --groupid=com.sample --artifactId=demo-scheduler --version=1.0  demo-scheduler --extension=scheduler

And here is a minimalist example of a Task which uses the @io.quarkus.scheduler.Scheduled annotation to run a task every 10 seconds:

package com.sample;

import java.util.concurrent.atomic.AtomicInteger;
import javax.enterprise.context.ApplicationScoped;
import io.quarkus.scheduler.Scheduled;

@ApplicationScoped              
public class Counter {

    private AtomicInteger counter = new AtomicInteger();

    public int get() {
        return counter.get();
    }

    @Scheduled(every="10s")     
    void increment() {
        counter.incrementAndGet();
    }

}

You can monitor the status of your Counter through a simple REST Endpoint:

package com.sample;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/count")
public class CountEndpoint {

    @Inject
    Counter myCounter;            


    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String count() {
        return "count: " + myCounter.get();  
    }
}

Using Cron based scheduler

Another alternative format for the @Scheduled annotation is to include a cron expression within it:

Here is an example, which schedules a Task every day at 8 AM:

@Scheduled(cron="0 0 8 * * ?")
void morningTask() {}

Creating an Ajax front-end to a Quarkus REST application

In this article we will check out how query a REST Service running with Quarkus with a minimal Ajax and jQuery client.

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

mvn io.quarkus:quarkus-maven-plugin:0.16.1:create     -DprojectGroupId=com.sample     -DprojectArtifactId=hello-quarkus     -DclassName="com.sample.DemoEndpoint"     -Dpath="/persons"

Within our DemoEndpoint class, let’s add methods to perform CRUD Operations:

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.ws.rs.*;
import javax.ws.rs.core.Response;
import java.util.List;


@Path("persons")
@ApplicationScoped
@Produces("application/json")
@Consumes("application/json")
public class DemoEndpoint {

    @Inject DemoRepository demoRepository;

    @GET
    public List<Person> getAll() {
        return demoRepository.findAll();
    }

    @POST
    public Response create(Person p) {
        demoRepository.createPerson(p);
        return Response.status(201).build();

    }

    @PUT
    public Response update(Person p) {
        demoRepository.updatePerson(p);
        return Response.status(204).build();
    }
    @DELETE
    public Response delete(@QueryParam("id") Integer id) {
        demoRepository.deletePerson(id);
        return Response.status(204).build();
    }

}

The Repository class uses a plain memory structure to store your objects:

package com.packt.quarkus.chapter4;

import javax.enterprise.context.ApplicationScoped;
import java.util.ArrayList;
import java.util.List;


@ApplicationScoped
public class DemoRepository {

    List<Person> list = new ArrayList();
    int counter;

    public int getNextCustomerId() {
        return counter++;
    }

    public List<Person> findAll() {
        return list;
    }

    public Person findPersonById(Integer id) {
        for (Person c: list) {
            if (c.getId().equals(id))  {
                return c;
            }
        }
        throw new RuntimeException("Person not found!");
    }

    public void updatePerson(Person person) {
        Person personToUpdate = findPersonById(person.getId());
        personToUpdate.setName(person.getName());
        personToUpdate.setSurname(person.getSurname());
    }

    public void createPerson(Person person) {
        person.setId(getNextCustomerId());
        findAll().add(person);
    }

    public void deletePerson(Integer id) {
        Person c = findPersonById(id);
        findAll().remove(c);
    }
}

Then, here is the POJO Class named Person:

public class Person {
    private Integer id;
    private String name;
    private String surname;

    @Override
    public String toString() {
        return "Person{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", surname='" + surname + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSurname() {
        return surname;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }
}

Done with the server side. Now let’s add an index.hml page to display the List of Person in an HTML Table:

<html>
<head>
    <!-- little bit of css to beutify the table -->
    <link rel="stylesheet" type="text/css" href="stylesheet.css" media="screen" />
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>


    <script type="text/javascript">

$(document).ready(function () {
    $.getJSON("http://localhost:8080/persons",
    function (json) {
        var tr;
        for (var i = 0; i < json.length; i++) {
            tr = $('<tr/>');
            tr.append("<td>" + json[i].id + "</td>");
            tr.append("<td>" + json[i].name + "</td>");
            tr.append("<td>" + json[i].surname + "</td>");
            $('table').append(tr);
        }
    });
});
</script>
</head>

<body>
<div align="left" style="margin-top: 10%;">
    <fieldset style="border: none;">
        <legend><strong>Users List</strong></legend>

        <!-- table to show data -->
        <table class="greyGridTable">
            <thead>
            <tr>
                <th>ID</th>
                <th>Name</th>
                <th>Surname</th>
            </tr>
            </thead>
            <tbody>

            </tbody>
        </table>
    </fieldset>
</div>

</body>
</html>

Some CSS style has been included in the file stylesheet.css:

table.greyGridTable {
  border: 2px solid #FFFFFF;
  width: 100%;
  text-align: center;
  border-collapse: collapse;
}
table.greyGridTable td, table.greyGridTable th {
  border: 1px solid #FFFFFF;
  padding: 3px 4px;
}
table.greyGridTable tbody td {
  font-size: 13px;
}
table.greyGridTable td:nth-child(even) {
  background: #EBEBEB;
}
table.greyGridTable thead {
  background: #FFFFFF;
  border-bottom: 4px solid #333333;
}
table.greyGridTable thead th {
  font-size: 15px;
  font-weight: bold;
  color: #333333;
  text-align: center;
  border-left: 2px solid #333333;
}
table.greyGridTable thead th:first-child {
  border-left: none;
}

table.greyGridTable tfoot td {
  font-size: 14px;
}

That’s all. Make sure the following dependencies are included in your pom.xml file:

<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-resteasy-jsonb</artifactId>
</dependency>
<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-resteasy</artifactId>
</dependency>

Run the application with

$ mvn compile quarkus:dev

Now we can try adding some data with cURL:

$ curl -d '{"name":"john", "surname":"black"}' -H "Content-Type: application/json" -X POST http://127.0.0.1:8080/persons
$ curl -d '{"name":"victor", "surname":"frankenstein"}' -H "Content-Type: application/json" -X POST http://127.0.0.1:8080/persons

Let’s surf to localhost:8080 to check that Data is listed in our table:

Great. We could see how easily JSON data can be parsed with some help from jQuery and Ajax!

Building Container-ready native applications with Quarkus

Here is our second tutorial about QuarkusIO. Today we will learn how to create native executable applications with amazingly fast start up time. Next we will create a Container image of it and deploy it on a Cloud Platform (Openshift)


In our first tutorial – Getting started with Quarkus – we have learnt that Quarkus’s Maven plugin creates for you a native profile which you can use to build native applications:

<profile>
      <id>native</id>
      <activation>
        <property>
          <name>native</name>
        </property>
      </activation>
      <build>
        <plugins>
          <plugin>
            <artifactId>maven-failsafe-plugin</artifactId>
            <version>${surefire-plugin.version}</version>
            <executions>
              <execution>
                <goals>
                  <goal>integration-test</goal>
                  <goal>verify</goal>
                </goals>
                <configuration>
                  <systemPropertyVariables>
                    <native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
                    <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
                    <maven.home>${maven.home}</maven.home>
                  </systemPropertyVariables>
                </configuration>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
      <properties>
        <quarkus.package.type>native</quarkus.package.type>
      </properties>
 </profile>

On the other hand, you can use the default profile to build a standard Java application.

Let’s see how to wrap both kinds of applications (Java and Native) in a Container image to be run with Docker and eventually how to deploy this application on OpenShift.

Installing GRAALVM

In order to build native images we need to install GraalVM, 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 latest version of Quarkus 2.7.0.CR1 it is recommend installing GraalVM version 21.3.0 with JDK 11 support.

Firstly, download GraalVM from here  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

Then, export the GRAALVM_HOME with the path where you have installed GraalVM:

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

Building a Quarkus native application

We will now build the HelloWorld Quarkus application discussed in the first tutorial Getting started with Quarkus. Move to the root folder and build the native executable use the ‘mvn verify -Pnative‘ command:

$ mvn verify -Pnative

You should see at the end of the process the following output:

__  ____  __  _____   ___  __ ____  ______ 
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \   
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/   
2021-07-05 10:07:14,688 INFO  [io.quarkus] (main) demo 1.0 native (powered by Quarkus 2.0.0.Final) started in 0.018s. Listening on: http://0.0.0.0:8081
2021-07-05 10:07:14,688 INFO  [io.quarkus] (main) Profile prod activated. 
2021-07-05 10:07:14,688 INFO  [io.quarkus] (main) Installed features: [cdi, kubernetes, resteasy, smallrye-context-propagation]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 4.423 s - in com.sample.NativeGreetingResourceIT

Note: if you are getting this error “Basic header file missing ()” then it means that headers are not available on your system. You can install them with the following command:

sudo apt-get install libz-dev

If you are using a Fedora/RHEL system, use instead:

sudo dnf install zlib-devel

Once that the native generation has been completed, check the target folder:

$ ls -al target
drwxrwxr-x.  4 francesco francesco     4096 Jul  4 17:38 classes
-rw-rw-r--.  1 francesco francesco     4722 Jul  5 10:05 demo-1.0.jar
drwxrwxr-x.  3 francesco francesco     4096 Jul  5 10:07 demo-1.0-native-image-source-jar
-rwxrwxr-x.  1 francesco francesco 42900768 Jul  5 10:07 demo-1.0-runner
drwxrwxr-x.  2 francesco francesco     4096 Jul  5 10:07 failsafe-reports
drwxrwxr-x.  3 francesco francesco     4096 Jul  4 17:38 generated-sources
drwxrwxr-x.  3 francesco francesco     4096 Jul  4 17:38 generated-test-sources
drwxrwxr-x.  2 francesco francesco     4096 Jul  5 10:07 kubernetes
drwxrwxr-x.  2 francesco francesco     4096 Jul  4 17:38 maven-archiver
drwxrwxr-x.  3 francesco francesco     4096 Jul  4 17:38 maven-status
drwxrwxr-x.  3 francesco francesco     4096 Jul  4 17:38 quarkus
drwxrwxr-x.  5 francesco francesco     4096 Jul  5 10:05 quarkus-app
-rw-rw-r--.  1 francesco francesco      272 Jul  5 10:07 quarkus-artifact.properties
-rw-rw-r--.  1 francesco francesco      624 Jul  5 10:07 quarkus.log
drwxrwxr-x.  2 francesco francesco     4096 Jul  4 17:38 surefire-reports
drwxrwxr-x.  3 francesco francesco     4096 Jul  5 10:07 test-classes

The native executable demo-1.0-runner . Let’s run it:

$ target/demo-1.0-runner 
__  ____  __  _____   ___  __ ____  ______ 
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \   
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/   
2021-07-05 10:10:09,541 INFO  [io.quarkus] (main) demo 1.0 native (powered by Quarkus 2.0.0.Final) started in 0.035s. Listening on: http://0.0.0.0:8080
2021-07-05 10:10:09,542 INFO  [io.quarkus] (main) Profile prod activated. 
2021-07-05 10:10:09,542 INFO  [io.quarkus] (main) Installed features: [cdi, kubernetes, resteasy, smallrye-context-propagation]

As you can see, in a few milliseconds the application will boot. Test it with:

$ curl http://localhost:8080/hello

Building Docker images for Quarkus applications

It is amazing how simple is to build a Docker image out of your native executable application. Quarkus’s Maven plugin already arranged for you two Dockerfile in the project folder src/main/docker:

src
├── main
│   ├── docker
│   │   ├── Dockerfile.jvm
│   │   ├── Dockerfile.legacy-jar
│   │   ├── Dockerfile.native
│   │   └── Dockerfile.native-distroless
  • The file Dockerfile.jvm builds a Container image of Quarkus Java application based on the FastJar option which bundles all required libraries under the quarkus-app directory.
  • The file Dockerfile.native builds a native Container image of Quarkus application.
  • Then, the file Dockerfile.legacy-jar builds a Container image of Quarkus Java application using the legacy approach (application libraries under the target folder).
  • Finally, the file Dockerfile.native-distroless creates a distroless container that runs the Quarkus application in native (no JVM) mode

Aside from the Dockerfile.native-distroless, all Dockerfiles are built from the following base Image:

FROM registry.access.redhat.com/ubi8/ubi-minimal

On the top of that, the Dockerfile.jvm will install the JAR file containing the Java application, while the Dockerfile.native will add the native executable.

What is an UBI Image? A Red Hat Universal Base Image is a certified containers and operators Red Hat Enterprise Linux which is targeted toward specific development languages, such as Java, .NET, Golang, Node.js, Perl, etc.

Building the JVM Docker image for a Quarkus application

In order to build a container image of a Quarkus application, first build the application as follows:

mvn package

Then build the Container image from the Dockerfile.jvm:

docker build -f src/main/docker/Dockerfile.jvm -t quarkus/hello-quarkus .

Finally, run the application as follows:

docker run -i --rm -p 8080:8080 quarkus/hello-quarkus

Building the native Docker image for a Quarkus application

In order to build the Quarkus native container image, you will need to use the native profile, when packaging your application:

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

Then, build the Quarkus Docker image “quarkus/hello-quarkus” as follows:

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

Now check that the image is available:

$ docker images
REPOSITORY                                  TAG                 IMAGE ID            CREATED             SIZE
quarkus/hello-quarkus                       latest              761947bb9d9d        25 seconds ago      125 MB

It’s time to run it, with:

docker run -i --rm -p 8080:8080 quarkus/hello-quarkus
__  ____  __  _____   ___  __ ____  ______ 
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \   
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/   
2021-07-05 08:52:54,640 INFO  [io.quarkus] (main) demo 1.0 native (powered by Quarkus 2.0.0.Final) started in 0.015s. Listening on: http://0.0.0.0:8080
2021-07-05 08:52:54,640 INFO  [io.quarkus] (main) Profile prod activated. 
2021-07-05 08:52:54,640 INFO  [io.quarkus] (main) Installed features: [cdi, kubernetes, resteasy, smallrye-context-propagation]

You can test the Quarkus Docker image with:

$ curl http://localhost:8080/hello

Deploying Quarkus applications on Openshift

Once that you have the Container image of your application, it’s very simple to deploy it on Openshift or Kubernetes. To keep it as simple as possible, we will use Code Ready Container as Openshift cluster.

Check the following tutorial to learn how to install Code Ready Containers on your laptop: Getting started with Code Ready Containers

Start crc with:

$ crc start

Create a sample project named “quarkus”:

$ oc new-project quarkus
Now using project "quarkus" on server "https://192.168.42.204:8443".

In order to deploy your Quarkus application on OpenShift, the easiest way to go is to leverage the openshift extension which needs to be added to your project:

$ mvnw quarkus:add-extension -Dextensions="openshift"

To create automatically an OpenShift route, add the following line to the application.properties file:

quarkus.openshift.expose=true

Now it’s time to deploy your application. To deploy a Java based application, run the following command:

$ ./mvnw clean package -Dquarkus.kubernetes.deploy=true

On the other hand, to deploy a native container image, add the “native” profile in it:

$ ./mvnw clean package -Pnative -Dquarkus.kubernetes.deploy=true

Note, make sure you have set up correctly GRAALVM_HOME and JAVA_HOME in order to complete the above step.

In a few minutes your application will be deployed :

oc get pods
NAME            READY   STATUS      RESTARTS   AGE
demo-1-5p4rb    1/1     Running     0          4m57s
demo-1-build    0/1     Completed   0          5m42s
demo-1-deploy   0/1     Completed   0          5m

If you haven’t set to expose the Route in the application.properties file, it’s time to do it now:

$ oc expose svc/demo
route.route.openshift.io/demo exposed

The Route is now available, and you can use it to test your application:

If you want to learn how to use Mandrel distribution to build native executables, then check this tutorial: Building Quarkus native applications with Mandrel

Conclusion

At the end of this brief journey through Quarkus applications, we have discovered how to build a simple application which can be compiled easily into native code. One major feature of Quarkus is the ability to create instantly Container images of your applications which can be deployed on Openshift or Kubernates.