Quarkus and "meta" extension
Quarkus is a great Java framework with a large ecosystem thanks to the extensions.
Basically, if using Maven, your
This extension doesn't contain actual code, it "wraps" other extensions in an unique one.
Our "meta" extension is actually a "real" extension, meaning we have the "classic" extension structure with both
Let's start with our extension
The
Let's create the
For the
Let's start with the
Now, we have to create the "meta" extension descriptor
This "meta" extension has several benefits:
I'm using this approach in a framework PoC I'm doing in my company, and it's pretty convenient to "hide" actual extensions to the teams just by using the "meta" one.
Basically, if using Maven, your
pom.xml
contains the list of the extensions you want to use, meaning something like this:
...
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-grpc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-opentelemetry</artifactId>
</dependency>
...
It can quickly become large, and sometime you can have "conflict" between extensions (for example if you mix in the same dependencies set reactive and non reactive extensions).
Quarkus "meta" extension
To simplify the dependencies for the extensions you want to use (especially if it's basically the same extensions in all your services), you can create your own "meta" extension.This extension doesn't contain actual code, it "wraps" other extensions in an unique one.
Our "meta" extension is actually a "real" extension, meaning we have the "classic" extension structure with both
deployment
and runtime
modules. Let's start our extension with a pom.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.nanthrax.quarkus</groupId>
<artifactId>my-meta-extension-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<quarkus.version>3.0.2.Final</quarkus.version>
</properties>
<modules>
<module>deployment</module>
<module>runtime</module>
</modules>
</project>
We see here the two main parts of the Quarkus extension framework:
deployment
is the buildtime augmentation. It's responsible for all metadata processing (reading annotations, XML descriptors, ...). The output of this augmentation phase is recorded bytecode which is responsible for directly instantiating the relevant runtime services. This means that metadata is only processed once at build time, which both saves on startup time, and also on memory usage as the classes etc that are used for processing are not loaded (or even present) in the rumtime JVM. In our "meta" extension, we do kind of "assembly" of all deployment parts of wrapped extensions.runtime
is the actual runtime part of the extension, mostly "injecting" the runtime configuration. Remember that most of the job should be/is done at build time (deployment phase).
Let's start with our extension
deployment
(the buildtime part). The pom.xml
is pretty simple:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.nanthrax.quarkus</groupId>
<artifactId>my-meta-extension-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>my-meta-extension-deployment</artifactId>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-core-deployment</artifactId>
<version>${quarkus.version}</version>
</dependency>
<!-- Quarkus extensions -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-grpc-deployment</artifactId>
<version>${quarkus.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-kubernetes-client-deployment</artifactId>
<version>${quarkus.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-kubernetes-config-deployment</artifactId>
<version>${quarkus.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-logging-json-deployment</artifactId>
<version>${quarkus.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-opentelemetry-deployment</artifactId>
<version>${quarkus.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-extension-processor</artifactId>
<version>${quarkus.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>
Our meta-extension deployment lists all extensions it will bring, in our case:
quarkus-grpc-deployment
quarkus-kubernetes-client-deployment
quarkus-kubernetes-config-deployment
quarkus-logging-json-deployment
quarkus-opentelemetry-deployment
The
pom.xml
is the only thing we need for our "meta" extension deployment module.
Let's create the
runtime
module now for our "meta" extension.
For the
runtime
module, we need two parts:
- the
pom.xml
lists the runtime artifacts of the extensions provided by our "meta" extension (and also use thequarkus-extension-maven-plugin
) - a
quarkus-extension.yaml
resource file describing our "meta" extension
Let's start with the
pom.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.nanthrax.quarkus</groupId>
<artifactId>my-meta-extension-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>my-meta-extension</artifactId>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-grpc</artifactId>
<version>${quarkus.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-kubernetes-client</artifactId>
<version>${quarkus.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-kubernetes-config</artifactId>
<version>${quarkus.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-logging-json</artifactId>
<version>${quarkus.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-opentelemetry</artifactId>
<version>${quarkus.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-extension-maven-plugin</artifactId>
<version>${quarkus.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-extension-processor</artifactId>
<version>${quarkus.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>
We can see here the runtime artifacts of the extensions provided by our "meta" extension:
quarkus-grpc
quarkus-kubernetes-client
quarkus-kubernetes-config
quarkus-logging-json
quarkus-opentelemetry
quarkus-extension-maven-plugin
to generate all required resources for our "meta" extension.
Now, we have to create the "meta" extension descriptor
src/main/resources/META-INF/quarkus-extension.yaml
:
---
artifact: ${project.groupId}:${project.artifactId}:${project.version}
name: "My Meta Extension"
metadata:
keywords:
- "my-meta-extension"
guide: "https://jbonofre.github.com/my-meta-extension"
categories:
- "cloud"
status: "testing"
config:
- "net.nanthrax.quarkus."
Our "meta" extension is now ready, we just have to build it with mvn clean install
.
Usage and advantages of your "meta" extension
To use our "meta-extension", we just need to define our extension as dependency of our application:
...
<dependencies>
...
<dependency>
<groupId>net.nanthrax.quarkus</groupId>
<artifactId>my-meta-extension</artifactId>
<version>1.0..0-SNAPSHOT</version>
</dependency>
...
</dependencies>
...
That's it ! We now have all extensions coming from our meta one. It means, in our application, we have all beans and annotations from all extensions defined in our "meta" extension.
This "meta" extension has several benefits:
- you have an unique dependency bringing all extensions transitively
- you have all annotations and beans available in your IDE with an unique extension
- the "meta" extension can be managed and released by a team (a kind of "framework" team), validating the extensions other teams can use and with guarantee of good working all together
I'm using this approach in a framework PoC I'm doing in my company, and it's pretty convenient to "hide" actual extensions to the teams just by using the "meta" one.
Comments
Post a Comment