
From Thorntail to Quarkus
At Qualogy, we stick to industry standards as much as we can in the projects we develop in-house. Some time ago we were using Jersey/Rest-Easy for projects, together with compatible dependencies for CDI, JWT etc. In doing so, even though we have the freedom to choose any technologies, we have to make sure these dependencies work together without conflicts. In this techblog, I will tell you more about Microprofile, how it relates to Thorntail, and how and why we finally changed to Quarkus.
What is Microprofile?
The microprofile.io community (http://microprofile.io/) is dedicated to optimizing the Enterprise Java mission for microservice-based architectures. The MicroProfile specification consists of a collection of Enterprise Java APIs and technologies that together form a core baseline for microservices. MicroProfile was created in 2016 and, shortly after version 1.0 was released, it became part of the Eclipse Foundation to ensure that it would remain vendor-neutral.
Microprofile's major implementations are Open Liberty, Wildfly, Thorntail, Helidon, Websphere Liberty and Payara. We started using Microprofile's Thorntail implementation for two of our projects: the GPS SaaS solution for managing vehicle parking products at municipalities, and one developed for KNSB, the Skating Association of The Netherlands.
What is Quarkus?
Quarkus was created to enable Java developers to create applications for a modern, cloud-native world. Quarkus is a Kubernetes-native Java framework tailored to GraalVM and HotSpot, crafted from best-of-breed Java libraries and standards. The goal is to make Java the leading platform in Kubernetes and serverless environments while offering developers a framework for addressing a wider range of distributed application architectures.
Specifications and technologies underlying Quarkus are:
- ● Eclipse MicroProfile
- ● Eclipse Vert.x
- ● Apache Camel
- ● Hibernate
- ● etc.
Interesting Features
Interesting Features include: Code changes are automatically reflected in your running application, Automatic provisioning and application wiring of supporting services, Easy configurations, Instant feedback on code changes, Best of Breed Libraries and Standards, Designed to seamlessly combine imperative and reactive style code.
End of Thorntail
After the final release 2.7.0.Final Thorntail stopped further development. We were monitoring the development of Quarkus to identify the right moment to switch from Thorntail. The good part here is that both are implementations of Microprofile, which made it easy to make the switch without major concerns. It proved again that keeping to standards always helps!

Migration Steps Explained
Migration Process
Get familiar with Quarkus / Proof of Concept
The first step was to get familiar with the Quarkus features and development process to properly make use of it. We went through the features and guides to get to know the available options. The main documents we referred to initially were: Configuration handling, Datasource configuration, CDI and start up events handling, Testing, REST application creation, Hibernate usage, Transaction Handling, JWT handling, Mail service.
The main technologies used in our application are: JWT - creating and using tokens for authentication/authorization, PostgreSQL / MS SQL Server, EclipseLink, Liquibase - Used to manage the database model and data, Maven, Azure - Application is deployed in Azure webapps, we are also using Azure storage from the application.
We were using EclipseLink, so it was necessary to migrate to Hibernate as that's the one supported by Quarkus. Depending on the features used, a considerable amount of time needed for this migration.
Dependency Changes
Using Quarkus BOM
<quarkus.platform.artifact-id>quarkus-universe-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id>
<quarkus.platform.version>SelectTheLatestQuarkusVersion</quarkus.platform.version>Quarkus-maven plugin usage
Next we changed the thorntail-maven plugin to the quarkus-maven plugin.
Thorntail to Quarkus dependencies
<!-- Remove Thorntail dependencies -->
<dependency>
<artifactId>microprofile</artifactId>
<groupId>org.eclipse.microprofile</groupId>
<scope>provided</scope>
<type>pom</type>
</dependency>
<dependency>
<artifactId>microprofile</artifactId>
<groupId>io.thorntail</groupId>
</dependency>
<dependency>
<artifactId>jaxrs-multipart</artifactId>
<groupId>io.thorntail</groupId>
</dependency>
<!-- Add Quarkus dependencies -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-openapi</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-oidc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-agroal</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client-jackson</artifactId>
</dependency>Uber Jar in Quarkus
Since Quarkus applications can be packaged as an Uber Jar, you will want to consider using that. In case the application is packaged as war, remove the web.xml file. Also notice that empty beans.xml is any longer needed in the Quarkus application.
Continue using Microprofile libraries
<dependency>
<groupId>org.eclipse.microprofile.fault-tolerance</groupId>
<artifactId>microprofile-fault-tolerance-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.microprofile.metrics</groupId>
<artifactId>microprofile-metrics-api</artifactId>
</dependency>Code Changes
Avoid private variables for Inject
Quarkus users are encouraged to not use private members in their beans. This involves injection fields, constructors and initializers, observer methods, producer methods and fields, disposers and interceptor methods. This is to avoid using reflection. You can use package-private modifiers.
// From:
@Inject private CategoryService service;
// To:
@Inject CategoryService service;
// From:
@Inject @Claim("upn") private ClaimValue<String> upn;
// To:
@Inject @Claim("upn") ClaimValue<String> upn;Explicitly add Scope and No Argument Constructor
Add @ApplicationScoped or proper scope to your classes if missing. Add No argument constructor explicitly in case you have a parameterized constructor.
Changes in imports
// From:
import javax.annotation.Nullable;
// To:
import io.smallrye.common.constraint.Nullable;Filter Classes to Configuration
We were using Filter classes for cors filter handling, which can be done in configuration.
// Instead of Filter class, add in application.properties:
quarkus.http.cors=true
quarkus.http.cors.methods=GET,PUT,POST,PATCH,DELETE,OPTIONS
quarkus.http.cors.exposed-headers=token,auth-2fa-enabledConfiguration using application.properties
META-INF/microprofil-config.properties to resources/application.properties
Transaction handling
All the boilerplate code to handle transaction and rolling back can be handled with just a @Transactional annotation on your service method.
// From:
try {
resourceService.startTransaction();
resourceService.persist(entity);
resourceService.commitTransaction();
} catch (Exception ex) {
log.error(ex.getMessage());
if (resourceService.getEntityManager().getTransaction().isActive())
resourceService.rollback();
throw ex;
}
// To:
@Transactional
public void persist(Entity entity) {
resourceService.persist(entity);
}EclipseLink to Hibernate migration
We were using EclipseLink, so it was necessary to migrate to Hibernate as that's the one supported by Quarkus. Depending on the features used, a considerable amount of time needed for this migration.
Dependency Change
// From:
<dependency>
<artifactId>eclipselink</artifactId>
<groupId>org.eclipse.persistence</groupId>
</dependency>
// To:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm</artifactId>
</dependency>NamedQuery - Alias of columns is stricter in Hibernate
We modified all queries with alias to avoid conflicts and explicitly with "as" keyword
+ " r.id as raceId, r.competitor.id as competitorId"
+ " from competition_races r"Eager vs Lazy loading of list
You might have to recheck any list loaded with fetchType.Eager and remove the Eager, otherwise you might get performance issues and in Hibernate exceptions.
Nullable Columns should use Object
// From:
@Column(name = "start_mode") private int startMode;
// To:
@Column(name = "start_mode") private Integer startMode;Conclusion
We can migrate from Thorntail to Quarkus easily as both are implementations of MicroProfile. Instead of just migration, we also checked better options available in Quarkus and started using it. Every day, we enjoy the benefit of automatic change detection and deployment on running applications. Integration with other technologies is possible with just a few configurations. Recently we started using multi-tenancy, which works very smoothly. Quarkus is developed actively with good documentation, which speeds up further developments.
Apart from in-house projects, we also see that our consultants are now working on Quarkus projects for their clients, which demonstrates the wide acceptance of Quarkus in the industry.