-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Migration Guide 3.0
- Jakarta EE 10
- Automatic update tool
- CDI
- RESTEasy Reactive
- JPA / Hibernate ORM
- Hibernate Reactive
- Hibernate Reactive Panache
- Kubernetes/OpenShift
- OIDC
- SmallRye Reactive Messaging
- JAXB
- Testing
- Keystore default password
- Management interface
- OpenTelemetry
- OpenTracing
- OpenAPI
- Scheduler/Quartz
- Dev tools
- MapStruct
Quarkus 3.0 is based on Jakarta EE 10, which means that:
-
Many Jakarta EE packages have been renamed from
javax.*
tojakarta.*
.For example you will have to use
jakarta.inject.Inject
instead ofjavax.inject.Inject
,jakarta.persistence.EntityManager
instead ofjavax.persistence.EntityManager
,jakarta.validation.Validator
instead ofjavax.validation.Validator
, etc. -
Many corresponding implementations have been upgraded to a new major version, which implies differences in behavior, most notably for Hibernate ORM (see below).
We recommend you use the automatic update tool (see below) to ease the upgrade to Quarkus 3.0.
Quarkus 3.0 introduces an update tool that can help you update your projects to Quarkus 3.
This upgrade tool will, among other tasks:
-
Update the Quarkus version
-
Adjust the packages to use the
jakarta.*
packages -
Adjust your dependencies in some cases
-
Upgrade your Quarkiverse extensions to versions compatible with Quarkus 3.0
-
Adjust your configuration files when configuration properties have changed
It doesn’t handle everything (typically, Hibernate ORM API changes are not covered by the update tool) but it should handle most of the tedious work.
This update tool can be used for both Quarkus applications and Quarkus extensions, be they Maven or Gradle projects using Java or Kotlin.
If you are using the Quarkus CLI - which is recommended - upgrade it to the latest and run:
quarkus update --stream=3.0
If you are not using the CLI and using Maven, use the Quarkus Maven plugin to update your projects:
./mvnw io.quarkus.platform:quarkus-maven-plugin:3.0.1.Final:update -N -Dstream=3.0
If you are not using the CLI and using Gradle, use the Quarkus Gradle plugin to do so:
./gradlew -PquarkusPluginVersion=3.0.1.Final quarkusUpdate --stream=3.0
For more information, consult the dedicated guide.
Adding a CDI interceptor annotation such as @Transactional
to a private method was never supported, and used to result in a warning in logs because the annotation is ignored.
When such an annotation is ignored, Quarkus will now trigger a build failure instead:
jakarta.enterprise.inject.spi.DeploymentException: @Transactional will have no effect on method com.acme.MyBean.myMethod() because the method is private. [...]
Ideally you should remove such annotations since they are ignored, but if that’s not possible, set the configuration property quarkus.arc.fail-on-intercepted-private-method
to false
to revert to the previous behavior (warnings in logs).
The @AlternativePriority
annotation has been deprecated since Quarkus 2.6 and is removed in Quarkus 3.0.
Replace all usages with the 2 annotations @Alternative
and @Priority
.
Before:
@AlternativePriority(1)
After:
@Alternative
@Priority(1)
It is preferable to use jakarta.annotation.Priority
, but if you need to maintain compatibility with Quarkus 2.x and 3.x through mechanical transformation, you can use io.quarkus.arc.Priority
as well.
These 2 annotations are equivalent.
Note that the io.quarkus.arc.Priority
annotation is getting deprecated in Quarkus 3.0 and will be removed in the future.
-
Class
org.jboss.resteasy.reactive.server.core.multipart.MultipartFormDataOutput
has been moved toorg.jboss.resteasy.reactive.server.multipart.MultipartFormDataOutput
-
Class
org.jboss.resteasy.reactive.server.core.multipart.PartItem
has been moved to ` org.jboss.resteasy.reactive.server.multipart.PartItem` -
Class
org.jboss.resteasy.reactive.server.core.multipart.FormData.FormValue
has been moved toorg.jboss.resteasy.reactive.server.multipart.FormValue
The REST Client no longer uses the server specific MessageBodyReader and MessageBodyWriter classes associated with Jackson (which used to be the case, but was unintentional).
The result is that applications that use both quarkus-resteasy-reactive-jackson
and quarkus-rest-client-reactive
now have to include quarkus-rest-client-reactive-jackson
Quarkus now depends on Hibernate ORM 6.2 instead of Hibernate ORM 5.6.
This implies a noticeable amount of backwards-incompatible changes, be it in APIs, behavior, or database schema expectations. In particular, but not only:
Refer to this dedicated guide for more information.
Using persistence.xml
files and quarkus.hibernate-orm.*
configuration properties in the same application will fail
When configuring the Hibernate ORM extension through both a persistence.xml
file and quarkus.hibernate-orm.
properties in application.properties
, Quarkus used to ignore quarkus.hibernate-orm.
properties, even though documentation stated the application would fail to start.
Quarkus will now fail as expected when it can detect such situations.
You can still chose between persistence.xml
and quarkus.hibernate-orm.*
properties:
-
To ignore
persistence.xml
files, set the configuration propertyquarkus.hibernate-orm.persistence-xml.ignore
totrue
. -
To use
persistence.xml
files, remove allquarkus.hibernate-orm.*
properties fromapplication.properties
.
In order to mitigate some incompatibilities
caused by the migration to Hibernate ORM 6,
and also to simplify sequence reset requirements in import scripts in general,
the default ID generation optimizer has changed from pooled
to pooled-lo
.
This change is backwards-compatible, but if you need to revert to the pooled
optimizer,
just set quarkus.hibernate-orm.id.optimizer.default = pooled
.
Quarkus now depends on Hibernate Reactive 2 instead of Hibernate Reactive 1.
This implies a noticeable amount of backwards-incompatible changes, be it in behavior or database schema expectations.
Most of the changes are related to Hibernate Reactive 2 depending on Hibernate ORM 6.2 instead of Hibernate ORM 5.6. Refer to this dedicated guide for more information about the migration from Hibernate ORM 5.6 to 6.2 (and thus, from Hibernate Reactive 1 to 2).
It is no longer possible to inject a Mutiny.Session
in a CDI bean.
The main reason for this change is that the lifecycle of a reactive session does not fit the lifecycle of the CDI request context.
And this mismatch can result in tricky errors.
Users are encouraged to inject a Mutiny.SessionFactory
instead and control the session lifecycle through the SessionFactory#withSession()
and SessionFactory#withTransaction()
methods.
In order to mitigate some incompatibilities
caused by the migration to Hibernate Reactive 2,
and also to simplify sequence reset requirements in import scripts in general,
the default ID generation optimizer has changed from pooled
to pooled-lo
.
This change is backwards-compatible, but if you need to revert to the pooled
optimizer,
just set quarkus.hibernate-orm.id.optimizer.default = pooled
.
This extension has undergone extensive refactoring. However, most of the changes do not affect the API.
Two major internal changes include:
-
The current reactive
Mutiny.Session
is no longer stored in the CDI request context, -
A Panache entity method execution is not offloaded on the current Vert.x context anymore.
The consequence of these changes is that a user might need to take care of marking reactive session boundaries.
For example most of the methods of a Hibernate Reactive Panache entity must be invoked within the scope of a reactive Mutiny.Session
.
In some cases, the session is opened automatically on demand.
For example, if a Panache entity method is invoked in a JAX-RS resource method in an application that includes the quarkus-resteasy-reactive
extension.
For other cases, there are both a declarative and a programmatic way to ensure the session is opened.
You can annotate a CDI business method that returns Uni
with the @WithSession
annotation.
The method will be intercepted and the returned Uni
will be triggered within a scope of a reactive session.
Alternatively, you can use the Panache.withSession()
method to achieve the same effect.
Also make sure to wrap methods that modify the database or involve multiple queries within a transaction.
You can annotate a CDI business method that returns Uni
with the @WithTransaction
annotation.
The method will be intercepted and the returned Uni
is triggered within a transaction boundary.
Alternatively, you can use the Panache.withTransaction()
method for the same effect.
The @ReactiveTransactional
annotation is deprecated and can only be used for methods that return Uni
; this is validated at build time.
Users are encouraged to use @WithTransaction
instead.
Note
|
Sometimes it’s necessary to execute an asynchronous code from a blocking thread. Quarkus provides the VertxContextSupport#subscribeAndAwait() util method which subscribes to the supplied io.smallrye.mutiny.Uni on a Vert.x duplicated context, then blocks the current thread and waits for the result.
|
Neither Hibernate Reactive nor reactive SQL clients support streaming.
Furthermore, we are not able to provide a Panache#withTransaction()
alternative for io.smallrye.mutiny.Multi
without bypassing the Hibernate Reactive API.
Therefore, we decided to remove the stream()
methods from the PanacheEntityBase
, PanacheQuery
and PanacheRepositoryBase
.
You can replace the code like MyEntity.<MyEntity> streamAll()
with something similar to MyEntity.<MyEntity> listAll()).toMulti().chain(list → Multi.createFrom().iterable(list))
(which is by the way very similar to the original internal implementation).
Deprecated Property | Property to use |
---|---|
quarkus.kubernetes.expose |
quarkus.kubernetes.ingress.expose |
quarkus.openshift.expose |
quarkus.openshift.route.expose |
quarkus.kubernetes.host |
quarkus.kubernetes.ingress.host |
quarkus.openshift.host |
quarkus.openshift.route.host |
quarkus.kubernetes.group |
quarkus.kubernetes.part-of |
quarkus.openshift.group |
quarkus.openshift.part-of |
Plus, properties without the quarkus.
prefix will now be ignored. For example, before this version, we could add the property kubernetes.name
and this property was mapped to quarkus.kubernetes.name
. After this version, we’re not going to do this any longer to avoid issues like https://github.com/quarkusio/quarkus/issues/30850.
-
Before, the generated container and service resources were only mapping the HTTP port of the Quarkus application. Now, the HTTPS port is also being mapped unless SSL is explicitly disabled using the property
quarkus.http.insecure-requests=disabled
. -
New property to select the port name to be used by the generated Ingress resource:
quarkus.kubernetes.ingress.target-port=https
(by default, its value ishttp
).
OIDC session cookie which is created after an OIDC authorization code flow has completed, will now be encrypted by default starting from 3.0.2.Final
. Users are not expected to notice it in most cases.
However, only if either mTLS
or private_key_jwt
(OIDC client private key is used to sign a JWT token) authentication methods are used between Quarkus and OpenId Connect Provider, then an in-memory encryption key will be generated, which might cause some pods in the application dealing with a very large number of requests failing to decrypt the session cookie, because a given pod trying to decrypt it might not be the one which encrypted it.
In such cases one can register an encryption secret which should be 32 characters long, for example:
quarkus.oidc.token-state-manager.encryption-secret=eUk1p7UB3nFiXZGUXi0uph1Y9p34YhBU
Also note that an encrypted session cookie might exceed a 4096
bytes limit which will cause some browsers ignoring it. Try one of the following in such cases:
-
Set
quarkus.oidc.token-state-manager.split-tokens=true
to have the ID, access and refresh tokens stored in separate cookies -
Set
quarkus.oidc.token-state-manager.strategy=id-refresh-tokens
if you do not need to use the access token as a source of roles or to requestUserInfo
or propagate it to the downstream services -
Register a custom
quarkus.oidc.TokenStateManager
CDI bean with the alternative priority set to 1. For example, customquarkus.oidc.TokenStateManager
can store all the tokens in a database and return a short DB pointer which Quarkus will use as a session cookie value.
If application users access the Quarkus application from within the trusted network, the session cookie encryption can be disabled:
quarkus.oidc.token-state-manager.encryption-required=false
In the 2.16.0
and 2.16.1
releases, in OIDC web-app
applications, OIDC session cookie had a SameSite
attribute set to Strict
by default. However SameSite=Strict
introduced unpredictability in the way the session cookie can be handled by different browsers.
Therefore starting from 3.0
, the session cookie will again have a SameSite=Lax
attribute set by default.
If you do have a 2.16.0
or 2.16.1
based application working with the session cookie having SameSite=Strict
attribute, then please add the following configuration: quarkus.oidc.authentication.cookie-same-site=strict
Since the 2.12.0
release the vertx-kafka-client
dependency from the smallrye-reactive-messaging-kafka extension is marked for removal.
While not used for client implementations, this dependency provided default Kafka serdes for io.vertx.core.buffer.Buffer
, io.vertx.core.json.JsonObject
and io.vertx.core.json.JsonArray
types, from the io.vertx.kafka.client.serialization package.
The 3.0
release removes this dependency. Serdes mentioned above are still provided from the io.quarkus.kafka.client.serialization package.
SmallRye Reactive Messaging proposes an in-memory connector for testing purposes.
The usage of this connector caused a split-package issue because its classes are provided from the io.smallrye.reactive.messaging.providers.connectors
. This is resolved by moving these classes to io.smallrye.reactive.messaging.memory
package.
The JAXB extension automatically detects the classes that are using JAXB annotations and registers these classes into the default JAXBContext
. This default JAXBContext
instance is validated at runtime when used, so if there are issues or conflicts with the classes and JAXB, you will get a JAXB exception with a proper description to help you troubleshoot the issue. In this release, you can validate the JAXBContext
instance at build time to detect and fix the JAXB errors by adding the property quarkus.jaxb.validate-jaxb-context=true
.
Moreover, we have added the property quarkus.jaxb.exclude-classes
to exclude classes to be bounded to the JAXBContext
. This property accepts either a comma-separated list of fully qualified class names, for example:
quarkus.jaxb.exclude-classes=org.acme.one.Model,org.acme.two.Model
Or a list of packages, for example:
quarkus.jaxb.exclude-classes=org.acme.*
In this example, the classes org.acme.one.Model
and org.acme.two.Model
won’t be automatically bounded to the default JAXBContext
instance.
Removal of @io.quarkus.test.junit.NativeImageTest
and @io.quarkus.test.junit.DisabledOnNativeImageTest
annotations
These annotations were marked as deprecated for removal since Quarkus 2.8.0.Final and they were finally removed.
Use @io.quarkus.test.junit.QuarkusIntegrationTest
and @io.quarkus.test.junit.DisabledOnIntegrationTest
respectively instead.
Quarkus 3.0 updates Mockito to 5.x and in 5.0.0 Mockito switched to the more flexible inline
mockmaker by default.
To preserve the mocking behavior users are used to since Quarkus 1.x and to avoid memory leaks for big test suites, Quarkus 3.0 fixates the mockmaker to subclass
instead of inline
until the latter is fully supported in later Quarkus 3.x releases.
If you really want to force the inline
mockmaker:
-
add the following exclusion to your
pom.xml
:<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-junit5-mockito</artifactId> <exclusions> <exclusion> <groupId>org.mockito</groupId> <artifactId>mockito-subclass</artifactId> </exclusion> </exclusions> </dependency>
-
add
mockito-core
to your dependencies (note: themockito-inline
artifact was removed in Mockito 5.3)
Quarkus used "password" as the default password for JWT key and keystores. This default value has been removed. So, if you used "password" you now need to configure that password in the application.properties file:
quarkus.oidc-client.credentials.jwt.key-store-password=password
quarkus.oidc-client.credentials.jwt.key-password=password
You can now expose the metrics and health endpoint on a separate HTTP server using quarkus.management.enabled=true
.
Note that for the endpoint exposed on that management interface, the paths are resolved differently:
-
the root path is configured using
quarkus.management.root-path
;quarkus.http.root-path
is only used for the main HTTP server -
the
quarkus.http.non-application-root-path
is not used for endpoints exposed on the management interface.
There are some major changes in the OpenTelemetry extension on Quarkus 3.0.
Before 3.0 the OpenTelemetry SDK (OTel SDK) was created at build time and had limited configuration options, most notably, it could not be disabled at runtime. This can now be done by setting: quarkus.otel.sdk.disabled=true
Now, after some build time preparation steps, the OTel SDK itself is wired at runtime using the standard OTel Auto-configuration feature. This enables the usage of all Java OTel properties from upstream.
We tried to maximise backyards compatibility as much as possible.
Old properties are deprecated but, apart from the ones related with sampling, they will work transparently along with the new ones. We are mapping them during a short transition period.
These are the property changes:
Deprecated attribute | Property to use |
---|---|
quarkus.opentelemetry.enabled |
quarkus.otel.enabled |
quarkus.opentelemetry.tracer.enabled |
quarkus.otel.traces.enabled |
quarkus.opentelemetry.propagators |
quarkus.otel.propagators |
quarkus.opentelemetry.tracer.suppress-non-application-uris |
quarkus.otel.traces.suppress-non-application-uris |
quarkus.opentelemetry.tracer.include-static-resources |
quarkus.otel.traces.include-static-resources |
quarkus.opentelemetry.tracer.sampler |
quarkus.otel.traces.sampler |
quarkus.opentelemetry.tracer.sampler.ratio |
quarkus.otel.traces.sampler.arg |
quarkus.opentelemetry.tracer.exporter.otlp.enabled |
quarkus.otel.exporter.otlp.enabled |
quarkus.opentelemetry.tracer.exporter.otlp.headers |
quarkus.otel.exporter.otlp.traces.headers |
quarkus.opentelemetry.tracer.exporter.otlp.endpoint |
quarkus.otel.exporter.otlp.traces.legacy-endpoint |
For samplers the changes are:
If the sampler is parent based, there is no need to set, the now dropped property, quarkus.opentelemetry.tracer.sampler.parent-based
.
The values you need to set on quarkus.opentelemetry.tracer.sampler
are now:
Old Sampler config value | New Sampler config value | New Sampler config value (If Parent based) |
---|---|---|
on |
always_on |
parentbased_always_on |
off |
always_off |
parentbased_always_off |
ratio |
traceidratio |
parentbased_traceidratio |
Many new properties are now available. Please check the guide.
We allowed the CDI configuration of many classes: IdGenerator, Resource attributes, Sampler and SpanProcessor. This is not available in standard OTel and we continue to provide this handy feature. However, we are deprecating the CDI creation of the SpanProcessor through our LateBoundBatchSpanProcessor. If you are overriding or customising it, please let us know. Currently we continue to use this processor to make sure backwards compatibility exists but we will soon move to use the standard exportes bundled with the OTel SDK.
This means default backwards compatible exporter is using this configuration:
quarkus.otel.traces.exporter=cdi
As a preview, the stock OTLP exporter is now availably by setting:
quarkus.otel.traces.exporter=otlp
We now provide additional configurations of the OTel SDK using their standard SPI hooks for Sampler and SpanExporter. The remaining SPIs are available but require testing to validate compatibility.
The OpenTelemetry Guide was also updated.
OpenTelemetry (OTel) 1.23.1 introduced breaking changes. Some of them are:
- HTTP span names are now "{http.method} {http.route}"
instead of just "{http.route}"
.
- All methods in all Getter
classes in instrumentation-api-semconv have been renamed to use the get()
naming scheme
- Semantic conventions changes:
Deprecated attribute | Property to use |
---|---|
messaging.destination_kind |
messaging.destination.kind |
messaging.destination |
messaging.destination.name |
messaging.consumer_id |
messaging.consumer.id |
messaging.kafka.consumer_group |
messaging.kafka.consumer.group |
Before 3.0, to activate JDBC tracing, this configuration was used:
quarkus.datasource.jdbc.url=jdbc:otel:postgresql://localhost:5432/mydatabase
# use the 'OpenTelemetryDriver' instead of the one for your database
quarkus.datasource.jdbc.driver=io.opentelemetry.instrumentation.jdbc.OpenTelemetryDriver
Now, a much simpler configuration is required:
quarkus.datasource.jdbc.telemetry=true
-
OpenTracing support has been deprecated since Quarkus 2.14. We encourage moving to OpenTelemetry as soon as possible. The OpenTracing support will be removed soon.
-
OpenAPI no longer enables a wildcard
*
CORS
Origin
support by default as it can leak OpenAPI documents. If you’d like, you can enable a wildcardOrigin
support in devmode.
The quarkus.quartz.start-mode
property is deprecated and should be replaced with quarkus.scheduler.start-mode
. Note that the new Programmatic Scheduling API works in both: the quarkus-scheduler
and the quarkus-quartz
extensions, i.e. the start mode is a shared feature available in these extensions.
-
The lowest supported Maven version has changed from 3.6.2 to 3.8.2 following a refactoring of the Quarkus Maven plugins to support Maven 3.9.
-
The
io.quarkus:quarkus-bootstrap-maven-plugin
Maven plugin has been deprecated since 2.10.0.Final and no longer exists. If your extension uses it, you must change the artifact ID toio.quarkus:quarkus-extension-maven-plugin
. The update recipe should also perform this change (see here)