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.
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.
<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:
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.
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.
-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);
}
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;
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
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:
%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
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 .
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:
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