This demo is based on Burr Sutter presentation on Voxxed Days.
Before jumping into the demo, open:
-
https://docs.google.com/presentation/d/1eonvCh1J3lMcWYtz6vVmSoPjVPpBdTy0hjQ5Xez2S-k/edit
-
https://us-east-2.console.aws.amazon.com/ec2/home?region=us-east-2#Instances:sort=instanceId
-
https://console-openshift-console.apps.techtour.luszczynski.me
-
Open terminal on
-
cd ~/github/quarkus-demo/todo-frontend/
-
npm run dev
-
rm -r $HOME/Downloads/todo-backend
-
another tab for $HOME/Downloads/todo-backend
-
mvn compile quarkus:dev
-
another tab for postgresql
-
podman run -p 5432:5432 -e POSTGRES_USER=admin -e POSTGRES_PASSWORD=redhat -e POSTGRES_DB=todo postgres:10
-
VSCode for Instructions
-
VSCode for frontend
-
Pgadmin
-
rm -rf /home/gustavo/github/quarkus-demo/todo-frontend/.odo
-
Clear gpaste
Let’s created our first quarkus application. For this, open vscode, press Ctrl
+ Shift
+ p
or Command
+ Shift
+ p
on Mac and select Quarkus: Generate a Maven Project
.
We will be asked to fill some information about group-id, artifact-id, etc. Use the following values:
-
groupId:
com.redhat.quarkusdemo
-
artifactId:
todo-backend
-
version:
1.0.0-SNAPSHOT
-
package name:
com.redhat.quarkusdemo
-
resource name:
GreetingResource
Tip
|
You can create your quarkus application using the code generator as well. |
Now we need to select which extension we would like to use. For now, let’s skip this step by leaving 1 extensions selected
Choose your $HOME/Downloads
as this project will be temporary and click on button Generate Here
.
If you generated your code using the quarkus code generator, save your zip file and unzip it in the Downloads folder.
Before moving on with our demo, we need to make sure if java pack extension
is installed in our vscode environment. Click on Extensions
icon.
Then search for java
:
And make sure it is installed.
Let’s run our quarkus app. Press Ctrl
+ Shift
+ `
or Control
+ `` ` ` on MacOS to open the vscode integrated terminal and run:
./mvnw compile quarkus:dev
After running this command, we should see something like this in our integrated terminal.
Note
|
Notice the time that was necessary to start. Super fast and we are not using native mode yet. |
Now open your browser on http://localhost:8080/hello
Tip
|
You can also click on the URL above the hello method. |
Now change the hello method to return something else.
package com.redhat.demoquarkus;
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 from quarkus"; (1)
}
}
-
Change from
hello
tohello from quarkus
Save your file and refresh your browser.
Now you have an app that reload as fast as a nodejs application.
Let’s test our endpoint. For that, open GreetingResourceTest.java
.
Then, click on Run Test
.
When we run our test, it should not pass because our body content was changed in our rest endpoint but not in our test.
Note
|
If for some reason you find an error like Error: No delegateCommandHandler for vscode.java.checkProjectSettings , then restart your vscode and you are good to go.
|
Change the test body content according to our endpoint.
@Test
public void testHelloEndpoint() {
given()
.when().get("/hello")
.then()
.statusCode(200)
.body(is("hello from quarkus"));(1)
}
-
Change from
hello
tohello from quarkus
Now, run again our test and you should have passed on our endpoint test.
Press again Ctrl
+ Shift
+ p
or Command
+ Shift
+ p
on Mac and search for Quarkus: Add extensions to the current project
.
Now, select:
-
Hibernate ORM with Panache
-
RESTEasy JSON-B
-
JDBC Driver - PostgreSQL
-
REST resources for Hibernate ORM with Panache
And press Enter to finish.
Your integrated terminal, should look like this after hitting enter.
You can also search extensions using the CLI.
# For maven mvn quarkus:list-extensions # or for gradle gradle list-extensions
Add hibernate panache, jsonb, postgresql and rest datasource extensions using:
mvn quarkus:add-extension -Dextensions=quarkus-hibernate-orm-panache,quarkus-resteasy-jsonb,quarkus-jdbc-postgresql,quarkus-hibernate-orm-rest-data-panache
If you look at you pom.xml, you will see this last command added some new dependencies for us.
We should see now that new installed features are avaiable
Create a new entity Todo
inside our demoquarkus
folder.
Now, we need to add some JPA annotations and also extends our class from PanacheEntity.
For now, let’s just add a single field named title
to our entity.
Here is our entity
package com.redhat.demoquarkus;
import javax.persistence.Entity;
import io.quarkus.hibernate.orm.panache.PanacheEntity;
/**
* Todo
*/
@Entity
public class Todo extends PanacheEntity {
public String title;
}
Open a terminal and run:
# For Podman
podman run \
--rm \
--name postgres \
-p 5432:5432 \
-e POSTGRES_USER=admin \ (1)
-e POSTGRES_PASSWORD=redhat \ (2)
-e POSTGRES_DB=todo \ (3)
postgres:10
# Or Docker
docker run \
--rm \
--name postgres \
-p 5432:5432 \
-e POSTGRES_USER=admin \ (1)
-e POSTGRES_PASSWORD=redhat \ (2)
-e POSTGRES_DB=todo \ (3)
postgres:10
-
Database username
-
Database password
-
Database name
If everything worked as expected, you should see that your database is ready to accept connections.
Open application.properties
and generate a generic datasource configuration using the snippet qds
.
Change the generated values to look like the following content:
%dev.quarkus.datasource.db-kind=postgresql (1)
%dev.quarkus.datasource.username=admin
%dev.quarkus.datasource.password=redhat
%dev.quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/todo
%dev.quarkus.datasource.jdbc.min-size=5
%dev.quarkus.datasource.jdbc.max-size=15
%dev.quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.http.cors=true (2)
-
We are using the
%dev
prefix in every line. This is part of the quarkus profile and it tells quarkus to only apply this configuration when running in dev mode (mvn compile quarkus:dev) -
As we do not specify
%dev
this property will be avaiable in dev and prod mode
Note
|
Update these variables according to your environment. |
Now, let’s refresh our page on the browser.
If we look now at our database, quarkus should already created a new table. You can check this out using
docker exec -it postgres /usr/bin/psql -d todo -U admin -c "\dt"
docker exec -it postgres /usr/bin/psql -d todo -U admin -c "select * from public.todo"
Let’s describe the table
docker exec -it postgres /usr/bin/psql -d todo -U admin -c "\d+ todo"
We need to add some more fields to our entity. Let’s do that.
The final entity should look like this
package com.redhat.demoquarkus;
import javax.persistence.Column;
import javax.persistence.Entity;
import io.quarkus.hibernate.orm.panache.PanacheEntity;
/**
* Todo
*/
@Entity
public class Todo extends PanacheEntity {
public String title;
public boolean completed;
@Column(name = "ordering")
public int order;
}
Note
|
We have changed our column name for the field order because it may conflict with some reserved words used by the database.
|
Run again the describe table command
docker exec -it postgres /usr/bin/psql -d todo -U admin -c "\d+ todo"
Let’s say that we forgot to add the semi-colon in the end of our entitys last line.
...
public class Todo extends PanacheEntity {
public String title;
public boolean completed;
@Column(name = "ordering")
public int order (1)
}
-
Forgot to add the semi-colon
Now, open our browser and let’s see how quarkus handle this kind of situation. Refresh your page.
Correct your code, and hit refresh one more time.
Create a new interface TodoResource.java
.
Tip
|
You can use a code snippet qrc to create a new rest resource. You can see all snippets in the vscode extension repository.
|
Now, let’s make it a CRUD REST endpoint by extending it.
The final TodoResource that will be generated automatily by quarkus should be similar to this class
Warning
|
Do not copy the following class. It’s only an example. |
package com.redhat.quarkusdemo;
import java.util.List;
import javax.transaction.Transactional;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PATCH;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
/**
* TodoResource
*/
@Path("/todo")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class TodoResource {
@GET
public List<Todo> getAll() {
return Todo.listAll();
}
@POST
@Transactional
public Response create(Todo item){
item.persist();
return Response.ok(item).status(201).build();
}
@DELETE
@Transactional
@Path("/{id}")
public Response deleteOne(@PathParam("id") Long id) {
Todo entity = Todo.findById(id);
entity.delete();
return Response.status(204).build();
}
@PATCH
@Transactional
@Path("/{id}")
public Response update(Todo item, @PathParam("id") Long id) {
Todo entity = Todo.findById(id);
entity.id = item.id;
entity.title = item.title;
entity.completed = item.completed;
entity.order = item.order;
return Response.status(200).build();
}
}
Now, change your application.properties
to add the prod profile.
# Dev environment
%dev.quarkus.datasource.db-kind=postgresql
%dev.quarkus.datasource.username=admin
%dev.quarkus.datasource.password=redhat
%dev.quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/todo
%dev.quarkus.datasource.jdbc.min-size=5
%dev.quarkus.datasource.jdbc.max-size=15
%dev.quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.http.cors=true
# Production environment
%prod.quarkus.datasource.db-kind=postgresql
%prod.quarkus.datasource.username=admin
%prod.quarkus.datasource.password=redhat
%prod.quarkus.datasource.jdbc.url=jdbc:postgresql://postgresql:5432/todo
%prod.quarkus.datasource.jdbc.min-size=5
%prod.quarkus.datasource.jdbc.max-size=15
%prod.quarkus.hibernate-orm.database.generation=update
Test your todo app and make sure it is working correctly.
We can use swagger in our API. For that, we need to add a new extension:
Tip
|
Remember you can also add these two extensions below using Ctrl + Shift + p or Command + Shift + p on Mac
|
mvn quarkus:add-extension -Dextensions="quarkus-smallrye-openapi"
Restart quarkus and then open http://localhost:8080/swagger-ui/
Now you can test all the four CRUD operations using swagger-ui.
It’s important also to monitor the health check of our application. For that, add the smallrye-health-extension
mvn quarkus:add-extension -Dextensions="quarkus-smallrye-health"
Restart your quarkus and open http://localhost:8080/health
to see your health check.
Now let’s package our app.
# Package
mvn package -DskipTests
# List files
ls -lha target/
As you can see, our final jar is small because its libs is in other folders.
Now let’s add a new property to allow quarkus generate a uber-jar.
# Dev environment
%dev.quarkus.datasource.db-kind=postgresql
%dev.quarkus.datasource.username=admin
%dev.quarkus.datasource.password=redhat
%dev.quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/todo
%dev.quarkus.datasource.jdbc.min-size=5
%dev.quarkus.datasource.jdbc.max-size=15
%dev.quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.http.cors=true
# Production environment
%prod.quarkus.datasource.db-kind=postgresql
%prod.quarkus.datasource.username=admin
%prod.quarkus.datasource.password=redhat
%prod.quarkus.datasource.jdbc.url=jdbc:postgresql://postgresql:5432/todo
%prod.quarkus.datasource.jdbc.min-size=5
%prod.quarkus.datasource.jdbc.max-size=15
%prod.quarkus.hibernate-orm.database.generation=update
quarkus.package.uber-jar=true (1)
-
add this line
# Package
mvn package -DskipTests
# List files
ls -lha target/
Our jar is now bigger because it has its dependency in the same package
Test your todo app
# Run it
java -jar target/todo-backend-1.0.0-SNAPSHOT-runner.jar
We will face an error regarding UnknownHostException
. This is happening because when we package our application it runs as prod profile.
We can change the profile:
java -Dquarkus.profile=dev -jar target/todo-backend-1.0.0-SNAPSHOT-runner.jar
Get java PID:
JAVA_DEMO_PID=$(ps aux | grep java | grep todo-backend | awk '{ print $2}' | head -1)
Now check how much resource it’s been used by this java process:
ps -o pid,rss -p $JAVA_DEMO_PID | awk 'NR>1 {$2=int($2/1024)"M";}{ print;}'
# Without container
./mvnw package \
-Pnative \
-DskipTests
# Docker
./mvnw package \
-Pnative \
-Dquarkus.native.container-runtime=docker \
-DskipTests
# Podman
./mvnw package \
-Pnative \
-Dquarkus.native.container-runtime=podman \
-DskipTests
Run our app:
./target/todo-backend-1.0.0-SNAPSHOT-runner -Dquarkus.profile=dev
Now, let’s check how much memory our native instance is using.
JAVA_DEMO_PID=$(ps aux | grep todo-backend | grep runner | awk '{ print $2}' | head -1)
ps -o pid,rss -p $JAVA_DEMO_PID | awk 'NR>1 {$2=int($2/1024)"M";}{ print;}'
cd /tmp && git clone https://github.com/luszczynski/quarkus-demo.git && cd quarkus-demo/todo-frontend
npm install && npm run dev
Open http://localhost:8081
to see if it is working properly.
Let’s deploy our application to Openshift.
First, we need to deploy postgresql on Openshift. This is very simple.
oc new-project todo
Switch to the Developer Console
:
Now, choose the database
category.
Choose Postgresql (Ephemeral)
from the catalog.
Fill in the information as below
-
Memory Limit:
512Mi
-
Namespace:
openshift
-
Database Service Name:
postgresql
-
PostgreSQL Connection Username:
admin
-
PostgreSQL Connection Password:
redhat
-
PostgreSQL Database Name:
todo
-
Version of PostgreSQL Image:
10
We should see the following screen
If we click on Topology
on the left menu, we have the same blue circle we had on OCP v3.11.
# Using maven
./mvnw clean package -Dquarkus.container-image.build=true
./mvnw clean package -Dquarkus.kubernetes.deploy=true
# Using odo
odo project create todo
odo create java:11 todo-backend-native \
--binary target/todo-backend-1.0.0-SNAPSHOT-runner \
--app todo \
--env QUARKUS_DATASOURCE_JDBC_URL=jdbc:postgresql://postgresql:5432/todo
odo url create --port 8080
odo push
# Using oc
oc new-build quay.io/quarkus/ubi-quarkus-native-binary-s2i:19.2.1 --binary --name=todo-backend -l app=todo -n todo
oc start-build todo-backend --from-file target/*-runner --follow -n todo-app
oc new-app todo-backend && oc expose svc/todo-backend -n todo-app
oc rollout status -w dc/todo-backend
oc new-build --binary --name=quarkus-project -l app=quarkus-project
oc patch bc/quarkus-project -p '{"spec":{"strategy":{"dockerStrategy":{"dockerfilePath":"src/main/docker/Dockerfile.native"}}}}'
oc start-build quarkus-project --from-dir=. --follow
oc new-app --image-stream=quarkus-project:latest
oc expose service quarkus-project
oc new-project quarkus
# Using maven
./mvnw clean package \
-DskipTests \
-Dquarkus.container-image.build=true \
-Dquarkus.container-image.insecure=true
./mvnw clean package -DskipTests -Dquarkus.kubernetes.deploy=true
oc new-app registry.access.redhat.com/ubi8/openjdk-11:latest~https://github.com/quarkusio/quarkus-quickstarts.git --context-dir=todo-backend --name=todo-backend
oc logs -f bc/todo-backend
# To create the route
oc expose svc/todo-backend
# Using odo
odo project create todo
odo create java:11 todo-backend \
--binary target/todo-backend-1.0.0-SNAPSHOT-runner.jar \
--app todo \
--env QUARKUS_DATASOURCE_JDBC_URL=jdbc:postgresql://postgresql:5432/todo
odo url create --port 8080
odo push
odo log -f
# Using oc
#mkdir target/deployments && cp target/todo-backend-1.0.0-SNAPSHOT-runner.jar target/deployments
oc new-build --binary --image-stream=java:11 --name=todo-backend -l app=todo-backend -n todo
oc start-build todo-backend --from-file=target/todo-backend-1.0.0-SNAPSHOT-runner.jar --follow -n todo
#oc start-build todo-backend --from-dir=target/deployments --follow -n todo
oc new-app todo-backend -n todo
oc expose svc todo-backend -n todo
cd todo-frontend
odo project set todo
odo catalog list components
odo create nodejs todo-frontend --app todo
odo url create todo-frontend
odo push
odo watch
#odo link todo-backend --port 8080