Quarkus DevServices with JNoSQL
In this post, we will get an introduction to JNoSQL and Quarkus DevServices, two great tools that facilitate our lives as developers.
We will implement a simple DevService feature for quarkus-jnosql
project. Basically, if you change to a CouchDB database the extension will provide a CouchDB container for you, if you change to a ArangoDB database the extension will provide a ArangoDB container for you!
With this post, you will be able to understand how Quarkus DevService works and get started with Quarkus extension contribution. Why not?
JNoSQL
Recently, in the Java world, the JakartaOne event took place in Portuguese. In this event, Max Arruda talked about JNoSQL. In a simple way, in my humble opinion, I can summarize: JNoSQL is the Strategy pattern but for switching database implementations. What does it mean? For example, if you are using a key-value database like Redis and you want to change to another key-value database like ArangoDB, you just need to add the implementation (ArangoDB), make a simple configuration change, and voilà, you are now using ArangoDB to persist all the necessary data.
Quarkus DevServices
If you are using Quarkus, you already know about the developer experience that Quarkus provides. DevServices is just the same thing: it increases the developer experience when you are coding a Quarkus application. Basically, if your application needs to access a database or send a message to Kafka, Quarkus provides it through application.properties
configurations. It is an amazing experience because you do not have to worry about accessing the Kafka Docker website, obtaining the necessary configuration to start the Kafka container.
It utilizes the TestContainers project behind the scenes to set up all the required infrastructure for testing and development mode.
Knowing the quarkus-jnosql project
Let's think that you want to use the Quarkus JNoSQL extension with ArangoDB implementation, to do it, you need to create a Quarkus application and add the following dependency into your pom.xml
, it is very simple, looks:
Creating a Quarkus application
If you have Quarkus CLI executes: quarkus create app
, if not access https://code.quarkus.io and create your Quarkus application.
<dependency>
<groupId>io.quarkiverse.jnosql</groupId>
<artifactId>quarkus-jnosql-document-arangodb</artifactId>
<version>3.2.2.2</version>
</dependency>
<dependency>
<groupId>org.eclipse.jnosql.databases</groupId>
<artifactId>jnosql-arangodb</artifactId>
<version>1.0.4</version>
</dependency>
And, to configure the application.properties
file:
jnosql.document.database=arangodb
jnosql.arangodb.host=localhost:8529
jnosql.arangodb.password=openSesame
And, to execute the container image containing ArangoDB database:
Ok, it is relatevily simple, no? But, we can dot it more simple with DevServices.
Code implementation
Our API will have a simple Developer
entity and a simple resource. Below, you can see the Developer
entity:
The Resource:
If you access the API endoint through the following cURL
, you will create a Developer
.
curl --request POST \
--url http://localhost:8080/devs \
--header 'Content-Type: application/json' \
--header 'User-Agent: insomnia/8.5.1' \
--data '{
"name": "Matheus Cruz",
"github": "https://github.com/mcruzdev"
}'
To get all users, execute the following cURL
command:
You can see something like this:
Done, we are using Quarkus JNoSQL with ArangoDB, and for now, the idea here is not to change the implementation (classes, etc.). We want to keep the codebase intact. If we decide to change the database, it is necessary only to modify the pom.xml
(dependencies) and the application.properties
file (configurations).
Getting Started with DevServices
If you saw the last section, it was necessary to execute a Docker container with some configurations, right? A Quarkus extension is not just to provide build-time augmentation; it is meant to offer a better developer experience for extension users.
Let's offer for our extension users a way to provide the container when they change the jnosql.document.database
property.
Info
The goal here is to demonstrate how Quarkus DevServices works in practice. It does not reflect a real-world implementation but can serve as a starting point for you. We will implement DevServices for both ArangoDB Document and CouchDB Document.
We will divide this in 3 steps:
- Look at the
jnosql.document.database
to determine which database we are using. - Read the configuration property (only the necessary to run the container) for each database type. This is necessary because a database configuration differs from another one.
- Start a container for ArangoDB or CouchDB if necessary.
1. Reading the jnosql.document.database property
Developing a Quarkus extension
If you are not familiar with Quarkus extensions, I recommend checking out my first post on this subject.
Having the quarkus-jnosql
fork, let's create the class responsible for DevService:
There are some ways to read the application.properties
, to do it we will use the ConfigProvider
class:
- We are configuring all allowed databases;
- We are using
ConfigProvider.getConfig()
method to get thejnosql.document.database
value;
2. Reading specific database configuration
The goal here is to obtain only the necessary configuration to run the container with the application.
For the ArangoDB container, the minimal configuration to execute the container is docker run -p 8529:8529 -e ARANGO_ROOT_PASSWORD=openSesame arangodb/arangodb:3.11.6
, mapping to jnosql configuration, we need:
jnosql.document.database=arangodb
jnosql.arangodb.host=localhost:8529
jnosql.arangodb.password=openSesame
For the CouchDB container, the minimal configuration to execute the container is docker run -e COUCHDB_PASSWORD=password -e COUCHDB_USER=admin -p 5984:5984 couchdb
, mapping to jnosql configuration, we need:
jnosql.document.database=couchdb
jnosql.couchdb.port=5984
jnosql.couchdb.host=localhost
jnosql.couchdb.password=password
jnosql.couchdb.username=admin
3. Start a container ...
To use TestContainers we need to add the dependency in pom.xml
:
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
</dependency>
The TestContainer library provides a class that allows us to run and control a container. This class is called GenericContainer<T>
. We need to create two classes that extend GenericContainer<T>
, where we will include the necessary code to configure the container.
CouchDBContainer
- We are defining the image name
ArangoDBContainer
// ommited
static class ArangoDBContainer extends GenericContainer<ArangoDBContainer> {
public static final Integer ARANGO_DB_DEFAULT_PORT = 8529;
public ArangoDBContainer() {
super(DockerImageName.parse("arangodb:latest")); // (1)
}
}
- We are defining the image name
Now, let's use those class into our @BuildStep
method.
Disclaimer
All the code provided here is crafted to streamline the utilization of TestContainer. In a real-world scenario, it is essential to consider and adhere to best practices in coding.
-
In line 1 we are defining that this
@BuildStep
method will be called if theBooleanSupplier
IsNormal.class
resolves to false (onlyIfNot = { IsNormal.class }
) and if thequarkus.devservices.enabled
(onlyIf = { GlobalDevServicesConfig.Enabled.class }
) value is true. This is necessary because DevServices only run inTest
andDev
modes and in our scenario if Quarkus DevServices is enabled. -
At line 5, we are retrieving the value of the
jnosql.document.database
property to obtain the database name to be used later. - In lines 17-19 we are reading the specific configuration for CouchDB.
- At line 30 we are starting the container.
- In lines 34-38, we are using the
BuildProducer<DevServicesResultBuildItem>
instance to produce aDevServicesResultBuildItem
build item, which will be utilized later by Quarkus. Note that we are using theCouchDBContainer::close
Closeable
reference in theBuildStep
. This is very useful for Quarkus, as it allows us to "close" the container when theTest
orDev
mode is finished.
The same thing is made for ArangoDB database.
Testing DevServices
After changing the quarkus-jnosql
project, we will install it locally. Go to the quarkus-jnosql
directory and execute:
You need now, to install it in your Quarkus project using a SNAPSHOT
version. Today, in my case the latest version is 3.2.2.2-SNAPSHOT
, add the following dependency into your Quarkus project. We will test the CouchDB first.
<dependency>
<groupId>io.quarkiverse.jnosql</groupId>
<artifactId>quarkus-jnosql-document-couchdb</artifactId>
<version>3.2.2.2-SNAPSHOT</version>
</dependency>
Now, if you change the jnosql.document.database
property value to couchdb
you will get a container running in your local development or test.
Testing CouchDB
Necessay dependencies:
<dependency>
<groupId>io.quarkiverse.jnosql</groupId>
<artifactId>quarkus-jnosql-document-couchdb</artifactId>
<version>3.2.2.2-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jnosql.databases</groupId>
<artifactId>jnosql-couchdb</artifactId>
<version>1.0.4</version>
</dependency>
Necessary configurations:
jnosql.document.database=couchdb
jnosql.couchdb.port=5984
jnosql.couchdb.host=localhost
jnosql.couchdb.password=password
jnosql.couchdb.username=admin
quarkus.devservices.enabled=true
Running quarkus dev
and using the API, you can see that the application works well!
Request to create dev
Request to get all devs
Result after creating a dev and getting all:
[
{
"id": "3ff80981-925b-43f6-bcce-65a86469400a",
"name": "Matheus Cruz",
"github": "https://github.com/mcruzdev"
}
]
Chaning to ArangoDB
Necessary dependencies:
Note
Remove the CouchDB dependencies first.
<dependency>
<groupId>io.quarkiverse.jnosql</groupId>
<artifactId>quarkus-jnosql-document-arangodb</artifactId>
<version>3.2.2.2-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jnosql.databases</groupId>
<artifactId>jnosql-arangodb</artifactId>
<version>1.0.4</version>
</dependency>
Necessary properties:
jnosql.document.database=arangodb
jnosql.arangodb.host=localhost:8529
jnosql.arangodb.password=openSesame
Result after creating a dev and getting all:
References
Source code
The source code from this post can be reached here (JNoSQL fork) and here(Quarkus Application that uses Quarkus JNoSQL fork)!
Thank you
That's all; thank you for reading! See you in the next post. Goodbye!