background image

Understanding Quarkus POM File Structure

by pp
Updated:

In the ever-evolving landscape of Java development, Quarkus has emerged as a game-changer, providing a framework tailored for GraalVM and container-native applications. The heart of any Quarkus project lies in its Project Object Model (POM) file, which orchestrates the build process and defines project configurations. We'll dissect the structure of a Quarkus POM file using a sample file as a guide.

Introduction to Quarkus POM

<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
    xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

This snippet sets the stage, declaring the XML version and the project namespace. The xsi:schemaLocation attribute specifies the location of the XML schema, ensuring compliance with Maven standards.

Project Information

<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>db-access-first</artifactId>
<version>1.0.0-SNAPSHOT</version>

These elements define fundamental project information. modelVersion indicates the version of the POM model. groupId and artifactId form a unique identifier for the project, and version denotes its current version.

Project Properties

Properties serve as placeholders for values used throughout the POM. They promote consistency and easy configuration.

  <properties>
    <compiler-plugin.version>3.12.1</compiler-plugin.version>
    <maven.compiler.release>21</maven.compiler.release>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
    <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
    <quarkus.platform.version>3.8.2</quarkus.platform.version>
    <skipITs>true</skipITs>
    <surefire-plugin.version>3.2.5</surefire-plugin.version>
  </properties>

Let's have a look at each of the configuration entry.

  1. compiler-plugin.version (3.12.1): This property specifies the version of the Maven Compiler Plugin that the project uses. The Maven Compiler Plugin is used to compile the sources of your project.

  2. maven.compiler.release (21): This property indicates the version of the Java platform to be used by the Maven Compiler Plugin. In this case, it’s set to 21, which means the project will be compiled with Java 21.

  3. project.build.sourceEncoding (UTF-8): This property defines the encoding of the source files. Here, it’s set to UTF-8, which is a common character encoding for Unicode.

  4. project.reporting.outputEncoding (UTF-8): This property defines the encoding that will be used when generating reports. Like the source encoding, it’s also set to UTF-8.

  5. quarkus.platform.artifact-id (quarkus-bom): This property specifies the artifactId of the Quarkus BOM file that the project uses. A BOM (Bill of Materials) is a special kind of POM that is used to control the versions of a project’s dependencies and provide a central place to define and update those versions.

  6. quarkus.platform.group-id (io.quarkus.platform): This property specifies the groupId of the Quarkus BOM file that the project uses. The groupId is a unique identifier for a project and is typically based on the fully qualified domain name of your organization.

  7. quarkus.platform.version (3.8.2): This property specifies the version of the Quarkus platform (BOM file) that the project uses.

  8. skipITs (true): This property is used to skip the integration tests when building the project. If it’s set to true, the integration tests won’t be run during the build.

  9. surefire-plugin.version (3.2.5): This property specifies the version of the Maven Surefire Plugin that the project uses. The Maven Surefire Plugin is used during the test phase of the build lifecycle to execute the unit tests of an application.

Dependency Management

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>${quarkus.platform.group-id}</groupId>
        <artifactId>${quarkus.platform.artifact-id}</artifactId>
        <version>${quarkus.platform.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

Quarkus leverages Maven's dependency management to handle versioning consistently across the project. The dependencyManagement section imports the Quarkus BOM (Bill of Materials), ensuring all Quarkus dependencies align with the specified version. That means, for instance, if you change the quarkus.platform.artifact-id, this change will automatically reflect on all managed dependencies described in the upcoming section.

Dependencies

  <dependencies>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-arc</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-resteasy-reactive</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-junit5</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>io.rest-assured</groupId>
      <artifactId>rest-assured</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

This section enumerates the project's dependencies. Quarkus embraces a modular approach, and each dependency contributes to specific functionalities. Our example of dependencies above is using libraries:

  1. quarkus-arc: This dependency is Quarkus’s own dependency injection (DI) solution. It’s based on the Jakarta Contexts and Dependency Injection 4.0 specification. It implements the CDI Lite specification, with selected improvements on top, and passes the CDI Lite TCK. It does not implement CDI Full. This means it provides a way to manage and inject dependencies in your Quarkus application.

  2. quarkus-resteasy-reactive: This dependency is a new Jakarta REST (formerly known as JAX-RS) implementation written from the ground up to work on Quarkus’s common Vert.x layer. This means it provides a way to create RESTful web services that are reactive and performant.

  3. quarkus-junit5: This dependency is required for testing. It provides the @QuarkusTest annotation that controls the JUnit 5 testing framework. This means it provides a way to write tests for your Quarkus application using JUnit 5.

  4. rest-assured: This is a Java-based library that is used to test RESTful Web Services. It behaves like a headless client to access REST web services. It’s not required but provides a convenient way to test HTTP endpoints. This means it provides a way to write tests for the HTTP endpoints of your Quarkus application.

  5. Listing Installed Extensions: You can obtain a list of currently active extensions with the following command:

quarkus extension list

or with Maven:

./mvnw quarkus:info

*Note: you need to be in project folder in your terminal

  1. Listing Available Extensions You can obtain a list of the available extensions with the following command:
quarkus extension --installable

or with Maven:

./mvnw quarkus:list-extensions 

For more information on Quarkus Extension follow Official Quarkus Extension Catalogue

  1. Adding an Extension: You can add an extension using the following command:
quarkus extension add hibernate-validator 

or with Maven:

./mvnw quarkus:add-extension -Dextensions='hibernate-validator' 

Build Configuration

The build section encapsulates configurations related to the build process. It includes plugins, compiler settings, and other build-related details.

  <build>
    <plugins>
      <plugin>
        <groupId>${quarkus.platform.group-id}</groupId>
        <artifactId>quarkus-maven-plugin</artifactId>
        <version>${quarkus.platform.version}</version>
        <extensions>true</extensions>
        <executions>
          <execution>
            <goals>
              <goal>build</goal>
              <goal>generate-code</goal>
              <goal>generate-code-tests</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>${compiler-plugin.version}</version>
        <configuration>
          <compilerArgs>
            <arg>-parameters</arg>
          </compilerArgs>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>${surefire-plugin.version}</version>
        <configuration>
          <systemPropertyVariables>
            <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
            <maven.home>${maven.home}</maven.home>
          </systemPropertyVariables>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-failsafe-plugin</artifactId>
        <version>${surefire-plugin.version}</version>
        <executions>
          <execution>
            <goals>
              <goal>integration-test</goal>
              <goal>verify</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <systemPropertyVariables>
            <native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
            <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
            <maven.home>${maven.home}</maven.home>
          </systemPropertyVariables>
        </configuration>
      </plugin>
    </plugins>
  </build>

let’s delve deeper into each of these build plugins:

  1. quarkus-maven-plugin: This is a core plugin for Quarkus that provides several goals to help with the project lifecycle.

    • build: This goal is responsible for compiling your project and producing an executable jar file. It does this by first compiling the source code of your project, then packaging it along with any dependencies into a jar file.
    • generate-code: This goal is used to generate boilerplate code for your project. This can include things like getters and setters for your classes, equals and hashCode methods, and more. This can help reduce the amount of manual coding you need to do.
    • generate-code-tests: Similar to generate-code, this goal generates boilerplate code, but specifically for tests. This can include things like basic unit tests for your classes, setup and teardown methods for your tests, and more.
  2. maven-compiler-plugin: This plugin is used to compile the sources of your project. The -parameters argument is used to instruct the Java compiler to generate metadata for method parameters. This metadata can then be accessed at runtime via reflection and can be useful in a variety of scenarios, such as when working with frameworks that use annotations to inject parameters.

  3. maven-surefire-plugin: This plugin is used during the test phase of the build lifecycle to execute the unit tests of an application. It is configured to use the JBoss LogManager for logging, which provides more advanced features than the standard Java logging manager. The maven.home system property is set to the home directory of Maven, which can be useful if your tests need to interact with Maven in some way.

  4. maven-failsafe-plugin: This plugin is designed to run integration tests for your application. It is similar to the Surefire plugin, but is designed to handle “failures” in a more graceful way, hence the name “failsafe”. The plugin is configured to run during the integration-test and verify phases of the build lifecycle. The native.image.path system property is set to the path of the native image, which is the compiled and packaged application that can be run without a JVM. The java.util.logging.manager and maven.home system properties are set in the same way as the Surefire plugin.

Profiles

<profiles>
  <profile>
    <id>native</id>
    <activation>
      <property>
        <name>native</name>
      </property>
    </activation>
    <properties>
      <skipITs>false</skipITs>
      <quarkus.package.type>native</quarkus.package.type>
    </properties>
  </profile>
</profiles>
  • <profiles>: This tag is used to define a set of different configurations that can be activated via various means and are used to adjust the build process in some way.
  • <profile>: Each profile defined in <profiles> provides a set of configuration values which override default values.
  • <id>: This is an identifier for the profile that can be used to activate it.
  • <activation>: This block indicates the conditions under which this profile will be activated.
  • <property>: Inside the <activation> block, this tag is used to specify the property name for activation of the profile.
  • <properties>: This block is used to define properties that can be referenced in the pom file itself.
  • <skipITs>: This property is used to control whether integration tests (ITs) are skipped. The value false means the ITs will not be skipped.
  • <quarkus.package.type>: This property is used to define the packaging type. The value native means the application will be packaged as a native executable.

Packaging with the Native Profile

To package your application using the native profile defined above, you can use the following command:

mvn package -Pnative

Here, -Pnative activates the native profile.

Setting Up GraalVM

If GraalVM is not set up correctly, you might encounter an error like

[ERROR] Failed to execute goal io.quarkus.platform:quarkus-maven-plugin:3.8.2:build (default) on project hello-world: Failed to build quarkus application: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
[ERROR] 	[error]: Build step io.quarkus.deployment.pkg.steps.NativeImageBuildStep#build threw an exception: java.lang.RuntimeException: Failed to get GraalVM version

Here’s a basic guide on how to set up GraalVM:

  1. Download GraalVM: You can download the appropriate GraalVM version from Official GraalVM download page.

  2. Install GraalVM: Extract the downloaded GraalVM tar file to a suitable location on your filesystem.

  3. Set GRAALVM_HOME Environment Variable: Point the GRAALVM_HOME environment variable to the GraalVM installation directory.

Conclusion

The Quarkus POM file is a nuanced orchestration of project details, dependencies, and build configurations. Understanding each section is pivotal for harnessing the full potential of Quarkus in building efficient and performant Java applications. As we continue our journey into the realm of Quarkus, a solid grasp of the POM file structure becomes the cornerstone for seamless development and deployment.