- Background
- Additional Resources
- Prerequisites
- Run the application
- Run the tests
- Examine the internals
- Analyze the application for migration
- Correct Issues
- Re-analyze the application
- Migrate Data source properties
- Fix the tests
- Bonus: No hassle native image
Hands-on tutorial based on a demo application that builds and runs as either Spring Boot or Quarkus.
This tutorial takes a Spring Boot application using Spring MVC, Spring Data JPA, and a PostgreSQL database and converts it to Quarkus with little-to-no source code changes. It uses the Red Hat Migration Toolkit for Applications to analyze the Spring Boot application and offer suggestions for how to migrate it to Quarkus.
The completed solution to this exercise can be found in this repo's solution
branch.
- Quarkus for Spring Developers eBook
- Quarkus Insights: Quarkus for Spring Developers
- Why should I choose Quarkus over Spring for my microservices?
- Spring Boot on Quarkus - Magic or Madness?
- Evolution of the Quarkus Developer Experience
- Red Hat Migration Toolkit for Applications
- A Java 17 runtime
- A container runtime (i.e. Docker or Podman)
docker
commands are used throughout this example
- Access to the internet
- Start the required PostgreSQL database:
docker run -it --rm --name tododb -e POSTGRES_USER=todo -e POSTGRES_PASSWORD=todo -e POSTGRES_DB=tododb -p 5432:5432 postgres:14
Note
If you see an error related to rate limits (something like You have reached your pull rate limit
), you need to first do a docker login -u <YOUR_DOCKER_USERNAME>
in order to pull the image. If you don't have a username, you can create a free account.
-
Run the application:
./mvnw clean spring-boot:run
You should see the standard Spring Boot Banner:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.3.1) INFO 71818 --- [ restartedMain] com.acme.todo.TodoApplication : Started TodoApplication in 2.411 seconds (process running for 2.602)
-
Open your browser to http://localhost:8080. You should see
-
Play around with the application a bit. Type a new todo into the text box and press
Enter
. That todo will show up in the list:- Click the empty circle next to a todo to complete it, or uncheck it to mark it as incomplete.
- Click the
X
to remove a todo. - The
OpenAPI
link at the bottom of the page will open the OpenAPI 3.0 specification for the application. - The
Swagger UI
link opens the embedded Swagger UI, which can be used to execute some of the RESTful endpoints directly. - The
Prometheus Metrics
link leads to the Prometheus metrics endpoint, which would be scraped intermittently by Prometheus. - The
Health Check
link opens the built-in health check exposed by Spring Boot.
-
Go ahead and play around a bit to see it all in action. Use
CTRL-C
in the terminal to stop the application before proceeding. -
Run
./mvnw clean
to clean things up. -
IMPORTANT! Also make sure to stop the docker daemon running the PostgreSQL database from step 1 before proceeding (you can use
CTRL-C
to stop it).
Every application should have tests! This application is no different!
Run the command ./mvnw clean verify
to run the tests. You should notice that doing this will automatically start a PostgreSQL database. The application uses the Testcontainers Postgres Module to accomplish this.
- Spring MVC for building the REST layer:
- Open
src/main/java/com/acme/todo/rest/TodoController.java
to find the Spring MVC RESTful controller, exposing the various endpoints available to the user interface.
- Open
- Spring Data JPA for defining relational entities as well as storing and retrieving them:
- Open
src/main/java/com/acme/todo/domain/TodoEntity.java
to find the Java Persistence API (JPA), representing the relational table for storing the todos. - Open
src/main/java/com/acme/todo/repository/TodoRepository.java
to find the Spring Data JPA Repository, exposing all of the create, read, update, and delete operations for theTodoEntity
.
- Open
- Spring Boot Actuators for providing operational capabilities, including health checks and metrics gathering.
- SpringDoc OpenAPI 3 for generating and exposing RESTful API information as well as the embedded Swagger UI endpoint.
Note
Spring Boot on its own does not have a starter providing this capability.
- Prometheus Micrometer Registry for exposing metrics to Prometheus.
- Testing
- Open
src/test/java/com/acme/todo/test/TodoControllerTests.java
to find the rest-assured tests for the controller layer. - Open
src/test/resources/application.properties
to find the Testcontainers Postgres Module configuration.
- Open
- Open
src/main/resources/META-INF/resources
to find the user interface components used. - Open
src/main/resources/application.properties
to find the application configuration. - Open
src/main/resources/import.sql
to find some SQL that will pre-populate the database table with an initial set of data.
We are going to use the Red Hat Migration Toolkit for Applications (MTA) to analyze the application. MTA can be run in a number of different ways:
- A web application running locally or on some remote machine.
- A command line interface.
- A Maven plugin.
- A plugin to most major IDEs
For this exercise we have pre-built a container image that runs the command line interface. This approach was chosen to make it easier to run without having to install anything on a local machine.
Note
The spring-to-quarkus-mta-cli
repository contains the tooling to create the container image being used.
- On the terminal from the project directory, run one of the following commands based on the operating system you are running:
- *nix/macos/Windows Subsystem for Linux (WSL):
docker run -it -v $(pwd):/opt/project:z -u $(id -u):$(id -g) quay.io/rhappsvcs/spring-to-quarkus-mta-cli:latest
- *nix/macos/Windows Subsystem for Linux (WSL):
Tip
If using podman, you could use the command podman run -it -v $(pwd):/opt/project:z,U quay.io/rhappsvcs/spring-to-quarkus-mta-cli:latest
-
Windows:
- cmd (not PowerShell):
docker run -it -v %cd%:/opt/project quay.io/rhappsvcs/spring-to-quarkus-mta-cli:latest
- PowerShell:
docker run -it -v ${PWD}:/opt/project quay.io/rhappsvcs/spring-to-quarkus-mta-cli:latest
- git bash:
winpty docker run -it -v "/$(pwd -W):/opt/project" quay.io/rhappsvcs/spring-to-quarkus-mta-cli:latest
orwinpty docker run -it -v "/$(cmd //c cd):/opt/project" quay.io/rhappsvcs/spring-to-quarkus-mta-cli:latest
If all else fails, you can hard-code the path to your current working directory (i.e.
docker run -it -v c:/path/to/spring-to-quarkus-todo:/opt/project quay.io/rhappsvcs/spring-to-quarkus-mta-cli:latest
).If none of those options work for you, see here for more information on obtaining the current working directory for the
-v
option. - cmd (not PowerShell):
-
Once completed you will see something like:
Report created: /opt/project/windup-report/index.html Access it at this URL: file:///opt/project/windup-report/index.html
-
In your browser, open up the newly-created
windup-report/index.html
page within the project. You should see the Applications page: -
Click on project to move to the Dashboard page:
-
Click on the issues tab at the top to move to the Issues page:
The analysis produced by the Migration Toolkit for Applications contains many links to Quarkus documentation explaining various concepts, like datasource configuration, build tools, etc. Feel free to click around and visit some of these links.
Each issue is something that needs to be dealt with to convert the application from Spring to Quarkus. The majority of the issues presented are related to dependencies within the pom.xml
. Let's fix all of those issues first.
-
In the Category dropdown, select Mandatory to filter the issues by mandatory.
-
Next, find and expand the
Replace the Spring Parent POM with Quarkus BOM
issue. This will expand and explain the issue detail: -
Clicking on
pom.xml
will bring up a window describing all the necessary changes needed to the project'spom.xml
. The window will have a small icon in the gutter on the left-hand side as well as some squiggly lines where issues are found. You can hover your mouse over each of these lines for a detailed description of the issue and how to remediate it.
The first issue is replacing the Spring parent POM with the Quarkus BOM.
Quarkus does not use a parent POM. Instead, Quarkus imports a BOM inside the
<dependencyManagement>
section of the pom
While we're in pom.xml
we may as well fix all the issues related to it.
-
In your editor/IDE, open
pom.xml
-
Find the
<parent>
section and remove it -
In the
<properties>
section, add<quarkus.platform.version>3.11.3</quarkus.platform.version>
-
After the
<properties>
section find the<dependencyManagement>
section and add the following inside the<dependencies>
:<dependency> <groupId>io.quarkus.platform</groupId> <artifactId>quarkus-bom</artifactId> <version>${quarkus.platform.version}</version> <type>pom</type> <scope>import</scope> </dependency>
-
The next issue is
Replace the Spring Web artifact with Quarkus 'spring-web' extension
.In
pom.xml
, find<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
and, according to the Quarkus Spring Web Guide, replace it with
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-spring-web</artifactId> </dependency>
Additionally, the documentation on the issue mentions that Starting with Quarkus version 2.5, the underlying JAX-RS engine must be chosen.
In
pom.xml
, add thequarkus-rest-jackson
extension to the<dependencies>
section:<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-rest-jackson</artifactId> </dependency>
-
The next issue is
Replace the SpringBoot Data JPA artifact with Quarkus 'spring-data-jpa' extension
.In
pom.xml
, find<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
and, according to the Quarkus JPA Guide, replace it with
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-spring-data-jpa</artifactId> </dependency>
-
The next issue is
Spring component spring-boot-starter-validation requires investigation
.In
pom.xml
, find<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
and, according to the Quarkus Validation with Hibernate Validator Guide, replace it with
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-hibernate-validator</artifactId> </dependency>
-
The next issue is
Spring component springdoc-openapi-starter-webmvc-ui requires investigation
. SpringDoc OpenAPI is a 3rd party open source library that isn't part of Spring itself. Luckily, there is the Quarkus OpenAPI extension.In
pom.xml
, find<dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <version>2.5.0</version> </dependency>
and replace it with
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-smallrye-openapi</artifactId> </dependency>
-
The next issue is
Replace the Spring Boot Actuator dependency with Quarkus Smallrye Health extension
.In
pom.xml
, find<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
and, according to the Quarkus - SmallRye Health Guide, replace it with
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-smallrye-health</artifactId> </dependency>
-
The next issue is
Replace the 'micrometer-registry-prometheus' dependency with Quarkus 'quarkus-micrometer-registry-prometheus' extension
. Micrometer Metrics are used in Quarkus as well as in Spring Boot, but Quarkus applications need to use the Quarkus Micrometer Metrics Extension.
In pom.xml
, find
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
and replace it with
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-micrometer-registry-prometheus</artifactId>
</dependency>
-
The next issue is
Replace the 'postgresql' dependency with Quarkus 'quarkus-jdbc-postgresql' extension
. Theorg.postgresql:postgresql
dependency needs to be swapped for the Quarkus PostgreSQL extension.In
pom.xml
, find<dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency>
and replace it with
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-jdbc-postgresql</artifactId> </dependency>
-
The next issue is
Remove spring-boot-devtools dependency
. Theorg.springframework.boot:spring-boot-devtools
isn't needed. The Spring Boot Developer Tools provides features aiming to enhance developer productivity, such as live reload. These features are part of the core of Quarkus.In
pom.xml
, find<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>
and remove it
-
The next issue is
Spring component spring-boot-starter-test requires investigation
.In
pom.xml
, find<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
and, according to the Quarkus testing guide, replace it with both of these dependencies:
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-junit5</artifactId> <scope>test</scope> </dependency>
AND
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-junit5-mockito</artifactId> <scope>test</scope> </dependency>
-
The next issue is
Replace the spring-boot-maven-plugin dependency
. Theorg.springframework.boot:spring-boot-maven-plugin
needs to be changed so that the application is built with Quarkus, both for running on the JVM and in native image.In
pom.xml
, find<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
and replace it with
<build> <plugins> <plugin> <groupId>io.quarkus</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> <parameters>true</parameters> </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> <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> </execution> </executions> </plugin> </plugins> </build> <profiles> <profile> <id>native</id> <activation> <property> <name>native</name> </property> </activation> <properties> <quarkus.native.enabled>true</quarkus.native.enabled> <quarkus.package.jar.enabled>false</quarkus.package.jar.enabled> </properties> </profile> </profiles>
Note
While this replacement might seem like a lot of XML, it also sets up the application to build a native image using the native
Maven profile.
Navigate back to the main issues page and find the Remove the SpringBoot @SpringBootApplication annotation
issue, then expand it.
A Spring Boot application also contains a "main" class with the @SpringBootApplication
annotation. A Quarkus application does not have such a class. There are 2 options that can be taken:
- Remove the
src/main/java/com/acme/todo/TodoApplication.java
class
OR
-
Add the
org.springframework.boot:spring-boot-autoconfigure
dependency as anoptional
Maven dependency. Anoptional
dependency is available when an application compiles but is not packaged with the application at runtime. Doing this would allow the application to compile without modification, but you would also need to maintain a Spring version along with the Quarkus application.To use this option, add this to the
<dependencies>
section ofpom.xml
:<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> <version>3.3.1</version> <optional>true</optional> </dependency>
Tip
This is the option chosen in the solution
branch of this repository. This option was chosen purely because we did not want to have to change any source code within the project. In a more "real world" scenario, the better option would most likely be option 1.
Some issues that weren't caught by the tool but also need to be fixed:
-
The application uses the AssertJ library for testing. The AssertJ version is managed by the Spring Boot BOM but not by the Quarkus BOM, therefore you need to explicitly declare its version.
In
pom.xml
, find<dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <scope>test</scope> </dependency>
and add
<version>3.26.0</version>
because the Quarkus BOM does not manage the version of the AssertJ dependency. The resulting dependency should be<dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>3.26.0</version> <scope>test</scope> </dependency>
-
Remove the Testcontainers dependencies.
In
pom.xml
, find the following within the<dependencyManagement>
section:<dependency> <groupId>org.testcontainers</groupId> <artifactId>testcontainers-bom</artifactId> <version>1.19.8</version> <type>pom</type> <scope>import</scope> </dependency>
and remove it, then in the main
<dependencies>
section find<dependency> <groupId>org.testcontainers</groupId> <artifactId>junit-jupiter</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.testcontainers</groupId> <artifactId>postgresql</artifactId> <scope>test</scope> </dependency>
and remove them as well.
-
Change the rest-assured dependency. Currently the application uses rest-assured's Spring Support. Rest-assured support is supported out-of-the-box in Quarkus and it's version is managed by the Quarkus BOM.
In
pom.xml
, find<dependency> <groupId>io.rest-assured</groupId> <artifactId>spring-mock-mvc</artifactId> <version>5.4.0</version> <scope>test</scope> </dependency>
and replace it with
<dependency> <groupId>io.rest-assured</groupId> <artifactId>rest-assured</artifactId> <scope>test</scope> </dependency>
Now that the changes to pom.xml
are complete, save and close it.
When completed, your pom.xml
should look like the pom.xml
in the solution branch.
Now let's re-analyze the application to see how much of the migration has been completed.
- On the terminal from the project directory, run one of the following commands based on the operating system you are running:
- *nix/macos/Windows Subsystem for Linux (WSL):
docker run -it -v $(pwd):/opt/project:z -u $(id -u):$(id -g) quay.io/rhappsvcs/spring-to-quarkus-mta-cli:latest
- *nix/macos/Windows Subsystem for Linux (WSL):
Tip
If using podman, you could use the command podman run -it -v $(pwd):/opt/project:z,U quay.io/rhappsvcs/spring-to-quarkus-mta-cli:latest
-
Windows:
- cmd (not PowerShell):
docker run -it -v %cd%:/opt/project quay.io/rhappsvcs/spring-to-quarkus-mta-cli:latest
- PowerShell:
docker run -it -v ${PWD}:/opt/project quay.io/rhappsvcs/spring-to-quarkus-mta-cli:latest
- git bash:
winpty docker run -it -v "/$(pwd -W):/opt/project" quay.io/rhappsvcs/spring-to-quarkus-mta-cli:latest
orwinpty docker run -it -v "/$(cmd //c cd):/opt/project" quay.io/rhappsvcs/spring-to-quarkus-mta-cli:latest
If all else fails, you can hard-code the path to your current working directory (i.e.
docker run -it -v c:/path/to/spring-to-quarkus-todo:/opt/project quay.io/rhappsvcs/spring-to-quarkus-mta-cli:latest
).If none of those options work for you, see here for more information on obtaining the current working directory for the
-v
option. - cmd (not PowerShell):
-
Once completed you will see something like:
Report created: /opt/project/windup-report/index.html Access it at this URL: file:///opt/project/windup-report/index.html
-
Refresh the browser and return to the Issues tab as you did before. There should only be a few issues remaining. The remainder of the issues are fixed with configuration in the
application.properties
file. -
Before proceeding, let's start the newly-converted Quarkus application in Quarkus's Dev Mode.
-
In the terminal, run
./mvnw clean quarkus:dev
.You will probably get some test compilation errors. That's ok at this point. We will fix that in a few minutes.
-
The Quarkus application should start up, and you should see the Quarkus banner:
INFO [io.qua.dat.dep.dev.DevServicesDatasourceProcessor] (build-8) Dev Services for default datasource (postgresql) started - container ID is a440b4c6e51e __ ____ __ _____ ___ __ ____ ______ --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \ --\___\_\____/_/ |_/_/|_/_/|_|\____/___/ WARN [org.hib.eng.jdb.spi.SqlExceptionHelper] (JPA Startup Thread) SQL Warning Code: 0, SQLState: 00000 INFO [io.quarkus] (Quarkus Main Thread) spring-to-quarkus-todo 0.0.1-SNAPSHOT on JVM (powered by Quarkus 3.11.3) started in 3.404s. Listening on: http://localhost:8080 INFO [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated. INFO [io.quarkus] (Quarkus Main Thread) Installed features: [agroal, cdi, hibernate-orm, hibernate-orm-panache, hibernate-validator, jdbc-postgresql, micrometer, narayana-jta, rest, rest-jackson, smallrye-context-propagation, smallrye-health, smallrye-openapi, spring-data-jpa, spring-di, spring-web, swagger-ui, vertx]
Notice the line
Dev Services for the default datasource (postgresql) started
. Quarkus Dev Services noticed the PostgreSQL extension on the classpath and started a PostgreSQL container image automatically, while also automatically setting all the configuration properties for the application to communicate with it! -
Re-open your browser to http://localhost:8080.
-
You may (or may not) notice a bunch of exceptions in the console log. This is because we haven't finished converting the application. We still need to migrate some Spring datasource configuration.
The other issues relate to properties within src/main/resources/application.properties
.
-
In your browser tab containing the Migration Toolkit analysis, go back to the Issues tab, expand the
Replace Spring datasource property key/value pairs with Quarkus properties
issue and then click on thesrc/main/resources/application.properties
link. You will see a similar editor as you did previously when you opened thepom.xml
file. -
In your editor/IDE, open
src/main/resources/application.properties
-
The first issue is
Replace Spring JPA Hibernate property with Quarkus property
In
src/main/resources/application.properties
, findspring.jpa.hibernate.ddl-auto=create-drop
and replace withquarkus.hibernate-orm.database.generation=drop-and-create
according to the Quarkus Hibernate ORM and JPA Guide. This will allow Quarkus (& Hibernate under the covers) to automatically create the database schema upon startup. -
The next issue is
Replace Spring datasource property key/value pairs with Quarkus properties
.In
src/main/resources/application.properties
, findspring.datasource.url=jdbc:postgresql://localhost:5432/tododb
and remove it completelyAs you saw, Quarkus Dev Services will automatically create the database for us and bind it to our application.
Similarly, find and remove
spring.datasource.username=todo
andspring.datasource.password=todo
as well -
The next issue is
Replace Spring OpenAPI endpoint mapping
.In
src/main/resources/application.properties
, findspringdoc.api-docs.path=/openapi
and replace it with
quarkus.smallrye-openapi.path=/openapi
to map the default Quarkus OpenAPI endpoint from
/q/openapi
to/openapi
. -
The next issue is
Replace Spring Swagger endpoint mapping
.In
src/main/resources/application.properties
, findspringdoc.swagger-ui.path=/swagger-ui
and replace it with
quarkus.swagger-ui.path=/swagger-ui
to map the default Quarkus Swagger UI page from
/q/swagger-ui
to/swagger-ui
.Additionally, add
quarkus.swagger-ui.always-include=true
so that Quarkus will always expose the Swagger UI endpoint. By default, it is only exposed in Dev Mode. -
The next issue is
Replace Spring Prometheus Metrics endpoint mapping
.In
src/main/resources/application.properties
, addquarkus.micrometer.export.prometheus.path=/actuator/prometheus
to map the default Quarkus Prometheus metrics endpoint from/q/metrics
to/actuator/prometheus
. -
The next issue is
Replace Spring Health endpoint mapping
.In
src/main/resources/application.properties
, addquarkus.smallrye-health.root-path=/actuator/health
to map the default Quarkus health endpoint from/q/health
to/actuator/health
.Additionally, find and remove
management.endpoints.web.exposure.include=prometheus,health
.
There are a couple of other properties that the analysis didn't find.
-
Add
quarkus.datasource.metrics.enabled=true
so that Quarkus will automatically expose datasource-related metrics to Micrometer. -
Now, go back to your browser tab containing the http://localhost:8080 page and refresh it. Magically the page should load and no exceptions in the console! Everything should work as it did before!
Quarkus Dev Mode has seamlessly redeployed your application while also creating the necessary schema and even importing sample data from
src/main/resources/import.sql
. -
Navigate to http://localhost:8080/q/dev (or in your terminal where Dev Mode is running, press
d
) to view the Quarkus Dev UI, a landing page for interacting with your application. All extensions used by the application should show up here along with links to their documentation. Some extensions provide the ability to interact and modify configuration right from the UI.
Now let's fix the tests so that they run.
-
In your terminal where Quarkus dev mode is running, press the
r
key to enable Quarkus Continuous Testing. You should see the console displayNo tests found
. -
Quarkus Dev Services for Databases automatically provisions a database container for the running application AND while executing tests. Therefore, we do not need the
src/test/resources/application.properties
file. Deletesrc/test/resources/application.properties
. -
Open
src/test/java/com/acme/todo/rest/TodoControllerTests.java
.You might find as you move through the following steps that your terminal window starts going crazy with lots of errors. Quarkus Continuous Testing is continually recompiling and re-executing the tests in the background and reporting back to the console. Once you complete the following steps you should get green text at the bottom saying that all tests pass.
-
Remove the
@SpringBootTest
and@AutoConfigureMockMvc
annotations on the class. -
Add the
@QuarkusTest
annotation to the class. -
Find
@Autowired MockMvc mockMvc;
and remove it.
-
Find
@MockBean TodoRepository todoRepository;
and change it to
@InjectMock TodoRepository todoRepository;
Make sure to import
io.quarkus.test.InjectMock
and notio.quarkus.test.junit.mockito
! -
Find
@BeforeEach public void beforeEach() { RestAssuredMockMvc.mockMvc(this.mockMvc); }
and remove it.
-
Find
import static io.restassured.module.mockmvc.RestAssuredMockMvc.*;
and replace it with
import static io.restassured.RestAssured.*;
-
You may need to optimize your imports depending on your IDE. You can inspect the imports on the
solution
branch to see what you need if you run into a problem. -
In the terminal you should now see
All 5 tests are passing (0 skipped), 5 tests were run in 396ms. Tests completed at 14:40:08 due to changes to TodoControllerTests.class.
indicating that all the tests are now passing.If you modify any tests, or any of the source code, the tests should automatically re-run, giving you an instant feedback loop.
-
-
Hit
CTRL-C
(or pressq
) in your terminal once done.
As a bonus exercise, let's create and run a Quarkus native image. Writing this exercise we don't know what host OS each individual is using, so we will use container images to facilitate building the native executable as a Linux executable, and then create a coontainer image from it. This will also alleviate the need to install GraalVM on our local machines.
The easiest way to create a container image containing a native executable is to leverage one of the Quarkus container-image extensions. If one of those extensions is present, then creating a container image for the native executable is essentially a matter of executing a single command.
Note
Native image creation is a CPU and memory-intensive operation. It may or may not work depending on your hardware specs. You may need at lease 6 GB of RAM allocated to your Docker daemon.
Since we already have a Docker runtime we'll use the Docker container image extension to perform the container image build.
-
To install the extension into the project, return to the terminal and run
./mvnw quarkus:add-extension -Dextensions="container-image-docker"
-
Since this is an existing non-Quarkus application, we need to create the
Dockerfile
for the native image.If we had created a new Quarkus application from Code Quarkus, this would have been created for us.
-
Create the directory
src/main/docker
-
Inside
src/main/docker
, create the fileDockerfile.native
-
Paste in the following into
Dockerfile.native
:FROM quay.io/quarkus/quarkus-micro-image:2.0 WORKDIR /work/ RUN chown 1001 /work \ && chmod "g+rwX" /work \ && chown 1001:root /work COPY --chown=1001:root target/*-runner /work/application EXPOSE 8080 USER 1001 CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]
-
Save and close
src/main/docker/Dockerfile.native
-
-
Building a native image can be accomplished by running
./mvnw package -Pnative -Dquarkus.native.container-build=true -Dquarkus.container-image.build=true -Dquarkus.container-image.group=
in the terminal. Building a native image may take several minutes to complete depending on the specs of your machine and how much CPU/RAM is available.There are many container image options available. The
quarkus.container-image.group=
option removes the${user.name}
from the final image name. If we did not include this option, the final image would be created as${user.name}/${quarkus.application.name}:${quarkus.application.version}
. This simply makes it easier to write this tutorial without having to worry about people's usernames!
Note
If the native image build fails due to an out of memory error, you may need to increase the memory size of your docker daemon to a minimum of 6GB.
You could also try adding the parameter -Dquarkus.native.additional-build-args=-J-XX:TieredStopAtLevel=1
to the ./mvnw package
command you ran.
-
Once the native image build is complete, start the PostgreSQL database container needed by the application:
docker run -it --rm --name tododb -e POSTGRES_USER=todo -e POSTGRES_PASSWORD=todo -e POSTGRES_DB=tododb -p 5432:5432 postgres:14
Quarkus Dev Services is only available in development mode. Running a native executable runs in production mode.
-
Before starting the native image container, we first need to get the internal ip address of the running PostgreSQL DB so that our Quarkus application can connect to it.
-
In another terminal, run
docker inspect tododb | grep IPAddress
. You should see something like"SecondaryIPAddresses": null, "IPAddress": "172.17.0.2", "IPAddress": "172.17.0.2",
In this example, the ip address is
172.17.0.2
.
-
-
Now run the native executable image, making sure to substitute the ip address gathered in the previous step
docker run -i --rm -p 8080:8080 -e QUARKUS_DATASOURCE_JDBC_URL=jdbc:postgresql://172.17.0.2:5432/tododb -e QUARKUS_DATASOURCE_USERNAME=todo -e QUARKUS_DATASOURCE_PASSWORD=todo spring-to-quarkus-todo:0.0.1-SNAPSHOT
Tip
If this command didn't work, make sure you substituted the ip address you gathered in step 5 in the command!
Important
Notice the startup time. It should start up in only a few milliseconds!
- Return to your browser to http://localhost:8080
- Everything should work as before! No hassle native image generation!
- Close both the application and the PostgreSQL instances via
CTRL-C
when you're done.