Skip to main content

Quarkus goes Reactive☢

In this tutorial I will explain how to create a quarkus based java application that will use reactive datasource to connect to a postgres database and be able to perform crud operations via rest endpoint. To make it more real this application will also be published into kubernetes cluster and will use configmap to store datasource configuration. To lern more about benefits of reactive data sources vs. jdbc regular data sources you can go to following link https://cutt.ly/1l260tK. 

Step 1: Configure Project 


In order to start we use and artifact to generate our base project by using the following command:

mvn io.quarkus:quarkus-maven-plugin:1.6.1.Final:create \ -DprojectGroupId=com.pierosteffano \ -DprojectArtifactId=quarkus-reactive-demo \
-DclassName="com.pierosteffano.EmployeeResource" \ -Dpath="/employees" \ -Dextensions="quarkus-resteasy, quarkus-junit5, rest-assured, quarkus-resteasy-jsonb, quarkus-jdbc-postgresql, quarkus-hibernate-orm-rest-data-panache, quarkus-hibernate-orm-panache, quarkus-smallrye-openapi, quarkus-agroal, quarkus-kubernetes, quarkus-kubernetes-config, quarkus-container-image-docker, quarkus-reactive-pg-client, quarkus-resteasy-mutiny"


This will create a maven base project containing all the dependencies that will be used in this tutorial.

Step 2: Add pojo class

In package com.pierosteffano create a class file named Employee. import io.smallrye.mutiny.Multi;
import io.vertx.mutiny.pgclient.PgPool;
import io.vertx.mutiny.sqlclient.Row;

public class Employee {

private Long id;
private String name;
private String lastname;


On above I'm showing the imports you will need to add and the properties of the Pojo class that will be mapped to table columns. Don't forget to generate mutators and accessors...
After that we need to add the following methods that will be used to retrieve data from employee table.

private static Employee from(Row row) {
    return new Employee(row.getLong("id"), row.getString("name"),row.getString("lastname"));
}
public static Multi<Employee> findAll(PgPool client) {
    return client.query("SELECT id, name,lastname FROM employees ORDER BY name ASC").execute()
            .onItem().transformToMulti(set -> Multi.createFrom().iterable(set))
            .onItem().transform(Employee::from);
}

Step 3: Resource class


Having done that you will need to create another class that will act as entrypoint of the application.

import io.smallrye.mutiny.Multi;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("employees")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class EmployeeResource {


In this case I named the class as EmployeeResource but you can choose another name.
The important things you need to follow is to include all the imports mentioned on above example, later on you will need to add following fields:

@Inject
io.vertx.mutiny.pgclient.PgPool client;
@Inject
@ConfigProperty(name = "myapp.schema.create", defaultValue = "true")
boolean schemaCreate;

Then you add the following methods in order to create table and populate it:

@PostConstruct
void config() {
    if (schemaCreate) {
        initdb();
    }
}

private void initdb() {
    client.query("DROP TABLE IF EXISTS employees").execute()
             .flatMap(r -> client.query("CREATE TABLE employees (id SERIAL PRIMARY KEY, name TEXT NOT NULL,lastname TEXT NOT NULL)").execute())
            .flatMap(r -> client.query("INSERT INTO employees (name,lastname) VALUES ('John','Doe')").execute())
            .flatMap(r -> client.query("INSERT INTO employees (name,lastname) VALUES ('Jane','Doe')").execute())
            .flatMap(r -> client.query("INSERT INTO employees (name,lastname) VALUES ('Hamilton','Lincoln')").execute())
            .await().indefinitely();
}


Finnally as part of demostration we will add a GET operation in order to fetch employee table information:

@GET
public Multi<Employee> get() {
    return Employee.findAll(client);
}

In order to test you can use following docker sentence to create a postgress db instance:

docker run --ulimit memlock=-1:-1 -it --rm=true --memory-swappiness=0 --name quarkus_test -e POSTGRES_USER=quarkus -e POSTGRES_PASSWORD=quarkus -e POSTGRES_DB=quarkusdb -p 5432:5432 postgres:10.5

After container is up and running you can test the application by using following command:

Windows: mvn compile quarkus:dev
Linux: ./mvn compile quarkus:dev


When application is Up you can hit the following url in your browser: http://localhost:8080/employees in order to list Employee table contents.

Furter steps:

To be more real life example here we will set hostname of database connection as config map entry. In order to do that first we need to create a yml file with the sintax and the use kubectl apply command like the following:

apiVersion: v1 data:
database: "172.17.0.2"
kind: ConfigMap
metadata:
name: quarkus-reactive-demo-config

To use this configmap on our application we add the following on application.properties:

%prod.kubernetes.env-vars[0].name=DB_HOST
%prod.kubernetes.env-vars[0].value=database
%prod.kubernetes.env-vars[0].configmap=quarkus-reactive-demo-config

Notice %prod prefix. This means that property will be used only on prod profile defined on your pom.xml
You can create a profile by adding following on your pom.xml file inside <profiles> tag:

<profile>
    <id>prod</id>
    <activation>
        <property>
            <name>environment</name>
            <value>prod</value>
        </property>
    </activation>
</profile>


After that you will be able to use configMap variable DB_HOST inside you application.properties but name will have some differences: Name will be lowercase and dash character need to be replaced as dot. You can see the following as an example:

quarkus.datasource.reactive.url=postgresql://${db.host}:5432/quarkusdb

In order to package docker container in quarkus you have 3 options: fast-jar, jvm or native each of them has pro and cons(for more information on quarkus packae types you can go to https://quarkus.io/guides/maven-tooling).
You can get how to package on the three different ways by having a look on src/main/docker directory.
In this tutorial I've chosen native profile and also added my own prod profile so app can make use of configmap values created before.
To package the jar I used following sentence:

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

After jar is generated we can use following command to create docker image:

docker build -f src/main/docker/Dockerfile.native -t pierosteffano/quarkus-reactive-demo:1.0-SNAPSHOT .

I updated tag of the image in order to make it work with kubertnetes extension that is used after.
After we do maven package ther will be a kubernetes yml and json files that can be used to create pods based on following properies defined on application.properties.

quarkus.container-image.group=pierosteffano
%prod.quarkus.kubernetes-config.enabled=true
%prod.quarkus.kubernetes.replicas=1
%prod.quarkus.kubernetes.readiness-probe.initial-delay=20s
%prod.quarkus.kubernetes.readiness-probe.period=45s
%prod.quarkus.kubernetes.deployment-target=kubernetes
%prod.quarkus.container-image.tag=1.0-SNAPSHOT


In order to create kubernetes pods / services / deployments we use the following:

kubectl apply -f target/kubernetes/kubernetes.json

After that if we need to test in localhost we can create a port forward using the following command:

kubectl port-forward service/quarkus-reactive-demo 8080:8080

Finally we can test by using the following url:

http://localhost:8080/employees






Comments

Popular posts from this blog

Configuring web application with gwt+spring+hibernate

Building a web application using hibernate, spring and gwt. This time we will show how to create and cofigure an application that will use hibernate , spring and gwt. I will use HR sample database that comes with OracleXE. First , you have to create a GWT project (you can follow the steps for that on  https://developers.google.com/web-toolkit/ ) After you have the project created you need to create a tree of packages like that: Open HumanResources.gwt.xml and add the following line : By doing that you tell gwt to include model package and all dependent packages into gwt compilation Now we can start configuring spring and hibernate. to configure spring first you have to add the following lines on web.xml This will allow spring context to start doing its business. The you will have to create a field called "app-config.xml" in which you will define the settings for spring and hibernate. Over this app-config file first configure the properties as follo...

Configuring web application with gwt+spring+hibernate (continued)

Last Time we learned how to configure a hibernate+ spring+ GWT application. Now we will continue explaining how I built this  application. First, we'll start with the Beans. For this example we will define one bean called Regions and an interface called Bean . Region will be the pojo class that contains the mapping for HR schema regions table The  @Entity  annotation is used to mark this class as an Entity bean. To use this annotation the class must have at least a package scope no-argument constructor. The  @Table  annotation is used to specify the table to persist the data. The  name  attribute refers to the table name. If  @Table  annotation is not specified then Hibernate will by default use the class name as the table name. The  @Id  annotation is used to specify the identifier property of the entity bean. The placement of the @Id  annotation determines the default access strategy that Hibernate will use f...