diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 685135974abf1..e2552d58b95e2 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -109,7 +109,7 @@ 2.0.2.Final 2.0.1 1.0.1.Final - 2.7.0.Final + 2.7.1.Final 2.2.3.Final 3.9.1 4.5.22 @@ -117,7 +117,7 @@ 4.4.16 4.1.5 9.2.1 - 2.5.0 + 2.5.2 2.4.240 42.7.8 @@ -139,7 +139,7 @@ 3.6.1.Final 3.0.3 4.0.5 - 4.0.1 + 4.1.1 1.8.0 1.1.10.8 0.113.0 @@ -4561,7 +4561,7 @@ org.apache.kafka kafka-clients - ${kafka3.version} + ${kafka.version} org.lz4 @@ -4586,17 +4586,17 @@ org.apache.kafka kafka-streams - ${kafka3.version} + ${kafka.version} org.apache.kafka kafka-streams-test-utils - ${kafka3.version} + ${kafka.version} org.apache.kafka kafka_2.13 - ${kafka3.version} + ${kafka.version} com.google.code.findbugs diff --git a/build-parent/pom.xml b/build-parent/pom.xml index 79b992b7f3f4a..8ad8f5db2e135 100644 --- a/build-parent/pom.xml +++ b/build-parent/pom.xml @@ -97,7 +97,7 @@ 4.13.2 - 26.4.5 + 26.4.7 quay.io/keycloak/keycloak:${keycloak.server.version} 7.0.3 diff --git a/core/deployment/src/main/java/io/quarkus/deployment/DebugConfig.java b/core/deployment/src/main/java/io/quarkus/deployment/DebugConfig.java index 2006206f2fb97..e780cc6bd2c09 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/DebugConfig.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/DebugConfig.java @@ -21,6 +21,8 @@ public interface DebugConfig { /** * If set to true, writes a list of all reflective classes to META-INF + *

+ * For fast-jar the META-INF is inside of quarkus-app/quarkus/generated-bytecode.jar */ @WithDefault("false") boolean reflection(); diff --git a/core/runtime/pom.xml b/core/runtime/pom.xml index a2747ec95df23..348a1536f85d5 100644 --- a/core/runtime/pom.xml +++ b/core/runtime/pom.xml @@ -236,7 +236,7 @@ org.graalvm.sdk:jniutils org.graalvm.sdk:word org.graalvm.sdk:collections - org.graalvm.sdk:native-bridge + org.graalvm.sdk:nativebridge io.quarkus:quarkus-bootstrap-runner io.quarkus:quarkus-classloader-commons diff --git a/core/runtime/src/main/java/io/quarkus/runtime/logging/LogRuntimeConfig.java b/core/runtime/src/main/java/io/quarkus/runtime/logging/LogRuntimeConfig.java index 751fb233cef29..a79ff82a3a7e8 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/logging/LogRuntimeConfig.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/logging/LogRuntimeConfig.java @@ -39,16 +39,16 @@ public interface LogRuntimeConfig { *

* JBoss Logging supports Apache-style log levels: *

- * * {@link org.jboss.logmanager.Level#FATAL} - * * {@link org.jboss.logmanager.Level#ERROR} - * * {@link org.jboss.logmanager.Level#WARN} - * * {@link org.jboss.logmanager.Level#INFO} - * * {@link org.jboss.logmanager.Level#DEBUG} - * * {@link org.jboss.logmanager.Level#TRACE} + *

    + *
  • {@link org.jboss.logmanager.Level#FATAL}
  • + *
  • {@link org.jboss.logmanager.Level#ERROR}
  • + *
  • {@link org.jboss.logmanager.Level#WARN}
  • + *
  • {@link org.jboss.logmanager.Level#INFO}
  • + *
  • {@link org.jboss.logmanager.Level#DEBUG}
  • + *
  • {@link org.jboss.logmanager.Level#TRACE}
  • + *
* * In addition, it also supports the standard JDK log levels. - * - * @asciidoclet */ @WithDefault("INFO") @WithConverter(LevelConverter.class) diff --git a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/QuarkusPlugin.java b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/QuarkusPlugin.java index 9a636d945e8e2..359b055ed11f3 100644 --- a/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/QuarkusPlugin.java +++ b/devtools/gradle/gradle-application-plugin/src/main/java/io/quarkus/gradle/QuarkusPlugin.java @@ -7,7 +7,6 @@ import java.nio.file.Path; import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -474,7 +473,7 @@ public boolean isSatisfiedBy(Task t) { // add the code gen sources final SourceSet generatedSourceSet = sourceSets .getByName(QuarkusGenerateCode.QUARKUS_GENERATED_SOURCES); - addCodeGenSourceDirs(compileJava, generatedSourceSet, quarkusExt); + addCodeGenSourceDirs(compileJava, generatedSourceSet); // quarkusGenerateCode is a dependency compileJava.dependsOn(quarkusGenerateCode); // quarkusGenerateCodeDev must run before compileJava in case quarkusDev is the target @@ -495,7 +494,7 @@ public boolean isSatisfiedBy(Task t) { // add the code gen test sources final SourceSet generatedSourceSet = sourceSets .getByName(QuarkusGenerateCode.QUARKUS_TEST_GENERATED_SOURCES); - addCodeGenSourceDirs(compileTestJava, generatedSourceSet, quarkusExt); + addCodeGenSourceDirs(compileTestJava, generatedSourceSet); compileTestJava.dependsOn(quarkusGenerateCode, quarkusGenerateCodeTests); if (tasks.contains(new NamedImpl(generatedSourceSet.getCompileJavaTaskName()))) { compileTestJava.mustRunAfter(tasks.named(generatedSourceSet.getCompileJavaTaskName())); @@ -511,7 +510,7 @@ public boolean isSatisfiedBy(Task t) { tasks.named("compileKotlin", task -> { final SourceSet generatedSourceSet = project.getExtensions().getByType(SourceSetContainer.class) .getByName(QuarkusGenerateCode.QUARKUS_GENERATED_SOURCES); - addCodeGenSourceDirs(task, generatedSourceSet, quarkusExt); + addCodeGenSourceDirs(task, generatedSourceSet); task.dependsOn(quarkusGenerateCode); task.mustRunAfter(quarkusGenerateCodeDev); if (tasks.contains(new NamedImpl(generatedSourceSet.getCompileJavaTaskName()))) { @@ -525,7 +524,7 @@ public boolean isSatisfiedBy(Task t) { tasks.named("compileTestKotlin", task -> { final SourceSet generatedSourceSet = project.getExtensions().getByType(SourceSetContainer.class) .getByName(QuarkusGenerateCode.QUARKUS_TEST_GENERATED_SOURCES); - addCodeGenSourceDirs(task, generatedSourceSet, quarkusExt); + addCodeGenSourceDirs(task, generatedSourceSet); task.dependsOn(quarkusGenerateCodeTests); if (tasks.contains(new NamedImpl(generatedSourceSet.getCompileJavaTaskName()))) { task.mustRunAfter(tasks.named(generatedSourceSet.getCompileJavaTaskName())); @@ -538,22 +537,14 @@ public boolean isSatisfiedBy(Task t) { }); } - private static void addCodeGenSourceDirs(JavaCompile compileJava, SourceSet generatedSourceSet, - QuarkusPluginExtension quarkusExt) { + private static void addCodeGenSourceDirs(JavaCompile compileJava, SourceSet generatedSourceSet) { final File baseDir = generatedSourceSet.getJava().getClassesDirectory().get().getAsFile(); - for (String provider : quarkusExt.getCodeGenerationProviders().get()) { - compileJava.source(new File(baseDir, provider)); - } + compileJava.source(baseDir); } - private static void addCodeGenSourceDirs(Task compileKotlin, SourceSet generatedSourceSet, - QuarkusPluginExtension quarkusExt) { + private static void addCodeGenSourceDirs(Task compileKotlin, SourceSet generatedSourceSet) { final File baseDir = generatedSourceSet.getJava().getClassesDirectory().get().getAsFile(); - final List codeGenProviders = quarkusExt.getCodeGenerationProviders().get(); - final Object[] codeGenDirs = new Object[codeGenProviders.size()]; - for (int i = 0; i < codeGenDirs.length; ++i) { - codeGenDirs[i] = new File(baseDir, codeGenProviders.get(i)); - } + final Object[] codeGenDirs = new Object[] { baseDir }; try { var sourcesMethod = compileKotlin.getClass().getMethod("source", Object[].class); sourcesMethod.invoke(compileKotlin, new Object[] { codeGenDirs }); diff --git a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/lgtm-codestart/java/src/test/java/org/acme/SimpleTest.java b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/lgtm-codestart/java/src/test/java/org/acme/SimpleTest.java index 5c62269f9c3a2..c7185c2f0fbd5 100644 --- a/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/lgtm-codestart/java/src/test/java/org/acme/SimpleTest.java +++ b/devtools/project-core-extension-codestarts/src/main/resources/codestarts/quarkus/extension-codestarts/lgtm-codestart/java/src/test/java/org/acme/SimpleTest.java @@ -1,6 +1,6 @@ package org.acme; -import org.eclipse.microprofile.config.inject.ConfigProperty;; +import org.eclipse.microprofile.config.inject.ConfigProperty; import org.jboss.logging.Logger; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; diff --git a/docs/src/main/asciidoc/cdi.adoc b/docs/src/main/asciidoc/cdi.adoc index 9a240de0feed1..f2550ee6820fd 100644 --- a/docs/src/main/asciidoc/cdi.adoc +++ b/docs/src/main/asciidoc/cdi.adoc @@ -443,6 +443,8 @@ import jakarta.decorator.Delegate; import jakarta.annotation.Priority; import jakarta.inject.Inject; import jakarta.enterprise.inject.Any; +import jakarta.enterprise.inject.Decorated; +import jakarta.enterprise.inject.spi.Bean; public interface Account { void withdraw(BigDecimal amount); @@ -461,7 +463,6 @@ public class LargeTxAccount implements Account { <3> @Decorated Bean delegateInfo; <5> - @Inject LogService logService; <6> diff --git a/docs/src/main/asciidoc/rest-client.adoc b/docs/src/main/asciidoc/rest-client.adoc index 78b107fbca2f2..8a3d95935d823 100644 --- a/docs/src/main/asciidoc/rest-client.adoc +++ b/docs/src/main/asciidoc/rest-client.adoc @@ -1341,7 +1341,9 @@ public class TestClientRequestFilter implements ResteasyReactiveClientRequestFil } ---- -== Customizing the ObjectMapper in REST Client Jackson +== Jackson-specific features + +=== Customizing the ObjectMapper in REST Client Jackson The REST Client supports adding a custom ObjectMapper to be used only the Client using the annotation `@ClientObjectMapper`. @@ -1369,6 +1371,60 @@ public interface ExtensionsService { <2> It's must be a static method. Also, the parameter `defaultObjectMapper` will be resolved via CDI. If not found, it will throw an exception at runtime. <3> In this example, we're creating a copy of the default object mapper. You should *NEVER* modify the default object mapper, but create a copy instead. +=== @JsonView support + +Jakarta REST methods can be annotated with https://fasterxml.github.io/jackson-annotations/javadoc/2.10/com/fasterxml/jackson/annotation/JsonView.html[@JsonView] +in order to customize the serialization of the returned POJO, on a per method-basis. This is best explained with an example. + +A typical use of `@JsonView` is to hide certain fields on certain methods. In that vein, let's define two views: + +[source,java] +---- +public class Views { + + public static class Public { + } + + public static class Private extends Public { + } +} +---- + +Let's assume we have the `User` POJO on which we want to hide some field during serialization. A simple example of this is: + +[source,java] +---- +public class User { + + @JsonView(Views.Private.class) + public int id; + + @JsonView(Views.Public.class) + public String name; +} +---- + +The REST Client supports `@JsonView` both for sending content to the REST API and for retrieving data from it: + +[source,java] +---- + @Path("/users") + @RegisterRestClient + public interface UserClient { + @GET + @Path("/{id}") + @Produces(MediaType.APPLICATION_JSON) + @JsonView(Views.Public.class) + User get(@RestPath String id); + + @POST + @Consumes(MediaType.APPLICATION_JSON) + Response create(@JsonView(Views.Public.class) User user); + } +---- + +In the preceding code, the `get` method would return a `User` whose `id` is always `null` while the `create` method would never include `id` in the JSON it sends to the REST API. + == Exception handling The MicroProfile REST Client specification introduces the `org.eclipse.microprofile.rest.client.ext.ResponseExceptionMapper` whose purpose is to convert an HTTP response to an exception. diff --git a/docs/src/main/asciidoc/security-cors.adoc b/docs/src/main/asciidoc/security-cors.adoc index d5bed31d425c3..0d71d854375a7 100644 --- a/docs/src/main/asciidoc/security-cors.adoc +++ b/docs/src/main/asciidoc/security-cors.adoc @@ -34,6 +34,14 @@ The filter then adds CORS headers to the HTTP response, informing browsers about For preflight requests, the filter returns an HTTP response immediately. For regular CORS requests, the filter denies access with an HTTP 403 status if the request violates the configured policy; otherwise, the filter forwards the request to the destination if the policy allows it. +[NOTE] +==== +Despite its name the CORS filter may also prevent CSRF attacks based on link:https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#using-standard-headers-to-verify-origin[Origin verification]. +Therefore, since an [Origin](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Origin) header is expected to be set by the browser for cross-origin JavaScript and HTML form requests, you may want to consider using it instead of the xref:security-csrf-prevention.adoc[REST CSRF filter]. + +You must confirm that the browser does set an `Origin` header for cross-origin requests when accessing your application, especially with HTML forms, before using the CORS filter to prevent CSRF with the link:https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#using-standard-headers-to-verify-origin[Origin verification]. +==== + For detailed configuration options, see the following Configuration Properties section. include::{generated-dir}/config/quarkus-vertx-http_quarkus.http.cors.adoc[leveloffset=+1, opts=optional] diff --git a/docs/src/main/asciidoc/security-csrf-prevention.adoc b/docs/src/main/asciidoc/security-csrf-prevention.adoc index 77349b67b81e1..a7aea5f3aab6b 100644 --- a/docs/src/main/asciidoc/security-csrf-prevention.adoc +++ b/docs/src/main/asciidoc/security-csrf-prevention.adoc @@ -16,6 +16,11 @@ Quarkus Security provides a CSRF prevention feature which implements https://che `Double Submit Cookie` technique requires that the CSRF token sent as `HTTPOnly`, optionally signed, cookie to the client, and directly embedded in a hidden form input of server-side rendered HTML forms, or submitted as a request header value. +[NOTE] +==== +If you are looking for stateless CSRF prevention that does not involve the server creating a cookie, have a look at the xref:security-cors.adoc#cors-filter[CORS filter]. Despite its name it also offers CSRF prevention by checking whether a request's `Origin` either matches the target `Host` or is in a list of allowed origins server-side. +==== + The extension consists of a xref:rest.adoc[Quarkus REST (formerly RESTEasy Reactive)] server filter which creates and verifies CSRF tokens in `application/x-www-form-urlencoded` and `multipart/form-data` forms and a Qute HTML form parameter provider which supports the xref:qute-reference.adoc#injecting-beans-directly-in-templates[injection of CSRF tokens in Qute templates]. The CSRF prevention filter applies to requests using HTTP `POST`, `PUT`, `PATCH`, `DELETE` and other methods that can change the REST application state. diff --git a/docs/src/main/asciidoc/writing-extensions.adoc b/docs/src/main/asciidoc/writing-extensions.adoc index 6f3e6d32bcfbf..51766a9895835 100644 --- a/docs/src/main/asciidoc/writing-extensions.adoc +++ b/docs/src/main/asciidoc/writing-extensions.adoc @@ -1136,7 +1136,7 @@ public interface LogConfiguration { * Enable logging to a file. */ @WithDefault("true") - boolean enable(); + boolean enabled(); /** * The log format. @@ -1172,12 +1172,12 @@ public class LoggingProcessor { ---- A configuration property name can be split into segments. For example, a property name like -`quarkus.log.file.enable` can be split into the following segments: +`quarkus.log.file.enabled` can be split into the following segments: * `quarkus` - a namespace claimed by Quarkus which is a prefix for `@ConfigMapping` interfaces, * `log` - a name segment which corresponds to the prefix set in the interface annotated with `@ConfigMapping`, * `file` - a name segment which corresponds to the `file` field in this class, -* `enable` - a name segment which corresponds to `enable` field in `FileConfig`. +* `enabled` - a name segment which corresponds to `enabled` field in `FileConfig`. <1> The `@ConfigMapping` annotation indicates that the interface is a configuration mapping, in this case one which corresponds to a `quarkus.log` segment. @@ -1189,7 +1189,7 @@ A corresponding `application.properties` for the above example could be: [source%nowrap,properties] ---- -quarkus.log.file.enable=true +quarkus.log.file.enabled=true quarkus.log.file.level=DEBUG quarkus.log.file.path=/tmp/debug.log ---- diff --git a/extensions/amazon-lambda/common-runtime/src/main/java/io/quarkus/amazon/lambda/runtime/LambdaConfig.java b/extensions/amazon-lambda/common-runtime/src/main/java/io/quarkus/amazon/lambda/runtime/LambdaConfig.java index bc73bc8e36f9b..46e6495d4db52 100644 --- a/extensions/amazon-lambda/common-runtime/src/main/java/io/quarkus/amazon/lambda/runtime/LambdaConfig.java +++ b/extensions/amazon-lambda/common-runtime/src/main/java/io/quarkus/amazon/lambda/runtime/LambdaConfig.java @@ -1,5 +1,7 @@ package io.quarkus.amazon.lambda.runtime; +import java.util.Optional; + import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; import io.smallrye.config.ConfigMapping; @@ -7,11 +9,20 @@ @ConfigRoot(phase = ConfigPhase.RUN_TIME) @ConfigMapping(prefix = "quarkus.lambda") public interface LambdaConfig { + /** + * The handler name. Handler names are specified on handler classes using the {@link @jakarta.inject.Named} annotation. + *

+ * If this name is unspecified and there is exactly one unnamed implementation of + * {@link com.amazonaws.services.lambda.runtime.RequestHandler} + * then this unnamed handler will be used. If there is only a single named handler and the name is unspecified + * then the named handler will be used. + * + */ + Optional handler(); /** * Configuration for the mock event server that is run * in dev mode and test mode */ MockEventServerConfig mockEventServer(); - } diff --git a/extensions/amazon-lambda/runtime/src/main/java/io/quarkus/amazon/lambda/runtime/LambdaConfig.java b/extensions/amazon-lambda/runtime/src/main/java/io/quarkus/amazon/lambda/runtime/LambdaConfig.java deleted file mode 100644 index 0aceb7a4f08ef..0000000000000 --- a/extensions/amazon-lambda/runtime/src/main/java/io/quarkus/amazon/lambda/runtime/LambdaConfig.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.quarkus.amazon.lambda.runtime; - -import java.util.Optional; - -import io.quarkus.runtime.annotations.ConfigPhase; -import io.quarkus.runtime.annotations.ConfigRoot; -import io.smallrye.config.ConfigMapping; - -@ConfigRoot(phase = ConfigPhase.RUN_TIME) -@ConfigMapping(prefix = "quarkus.lambda") -public interface LambdaConfig { - - /** - * The handler name. Handler names are specified on handler classes using the {@link @jakarta.inject.Named} annotation. - * - * If this name is unspecified and there is exactly one unnamed implementation of - * {@link com.amazonaws.services.lambda.runtime.RequestHandler} - * then this unnamed handler will be used. If there is only a single named handler and the name is unspecified - * then the named handler will be used. - * - */ - Optional handler(); -} diff --git a/extensions/container-image/deployment/src/main/java/io/quarkus/container/image/deployment/ContainerImageConfig.java b/extensions/container-image/deployment/src/main/java/io/quarkus/container/image/deployment/ContainerImageConfig.java index f97b43b728aff..d24acb240acc7 100644 --- a/extensions/container-image/deployment/src/main/java/io/quarkus/container/image/deployment/ContainerImageConfig.java +++ b/extensions/container-image/deployment/src/main/java/io/quarkus/container/image/deployment/ContainerImageConfig.java @@ -30,8 +30,8 @@ public interface ContainerImageConfig { /** * The tag of the container image. If not set defaults to the application version */ - @WithDefault("${quarkus.application.version:latest}") - String tag(); //used only by ContainerImageProcessor, use ContainerImageInfoBuildItem instead + // keep it Optional as we need the ability to nullify it + Optional tag(); //used only by ContainerImageProcessor, use ContainerImageInfoBuildItem instead /** * Additional tags of the container image. diff --git a/extensions/container-image/deployment/src/main/java/io/quarkus/container/image/deployment/ContainerImageProcessor.java b/extensions/container-image/deployment/src/main/java/io/quarkus/container/image/deployment/ContainerImageProcessor.java index e842306621eda..cffa5aa371dd5 100644 --- a/extensions/container-image/deployment/src/main/java/io/quarkus/container/image/deployment/ContainerImageProcessor.java +++ b/extensions/container-image/deployment/src/main/java/io/quarkus/container/image/deployment/ContainerImageProcessor.java @@ -105,7 +105,7 @@ public void publishImageInfo(ApplicationInfoBuildItem app, + group + "' and name '" + effectiveName + "' is invalid"); } - String effectiveTag = containerImageConfig.tag(); + String effectiveTag = containerImageConfig.tag().orElse(app.getVersion()); if (effectiveTag.equals(UNSET_VALUE)) { effectiveTag = DEFAULT_TAG; } diff --git a/extensions/devservices/oidc/src/main/java/io/quarkus/devservices/oidc/OidcDevServicesConfig.java b/extensions/devservices/oidc/src/main/java/io/quarkus/devservices/oidc/OidcDevServicesConfig.java index e1adb8178adb7..3ebc1f306ac17 100644 --- a/extensions/devservices/oidc/src/main/java/io/quarkus/devservices/oidc/OidcDevServicesConfig.java +++ b/extensions/devservices/oidc/src/main/java/io/quarkus/devservices/oidc/OidcDevServicesConfig.java @@ -1,5 +1,6 @@ package io.quarkus.devservices.oidc; +import java.time.Duration; import java.util.List; import java.util.Map; import java.util.Optional; @@ -8,6 +9,7 @@ import io.quarkus.runtime.annotations.ConfigDocMapKey; import io.quarkus.runtime.annotations.ConfigRoot; import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithDefault; /** * OpenID Connect Dev Services configuration. @@ -22,6 +24,18 @@ public interface OidcDevServicesConfig { @ConfigDocDefault("Enabled when Docker and Podman are not available") Optional enabled(); + /** + * Relative duration the access token will expire in. + */ + @WithDefault("5M") + Duration accessTokenExpiresIn(); + + /** + * Relative duration the ID token will expire in. + */ + @WithDefault("5M") + Duration idTokenExpiresIn(); + /** * A map of roles for OIDC identity provider users. *

diff --git a/extensions/devservices/oidc/src/main/java/io/quarkus/devservices/oidc/OidcDevServicesProcessor.java b/extensions/devservices/oidc/src/main/java/io/quarkus/devservices/oidc/OidcDevServicesProcessor.java index ed9ac598b1274..3689b80f2224a 100644 --- a/extensions/devservices/oidc/src/main/java/io/quarkus/devservices/oidc/OidcDevServicesProcessor.java +++ b/extensions/devservices/oidc/src/main/java/io/quarkus/devservices/oidc/OidcDevServicesProcessor.java @@ -11,7 +11,6 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.interfaces.RSAPublicKey; -import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; @@ -72,6 +71,8 @@ public class OidcDevServicesProcessor { private static volatile String applicationType; private static volatile Map configProperties; private static volatile Map> userToDefaultRoles; + private static volatile Long accessTokenExpiresIn; + private static volatile Long idTokenExpiresIn; private static volatile Runnable closeDevServiceTask; @BuildStep @@ -84,6 +85,8 @@ DevServicesResultBuildItem startServer(CuratedApplicationShutdownBuildItem close } userToDefaultRoles = devServicesConfig.roles(); + accessTokenExpiresIn = devServicesConfig.accessTokenExpiresIn().toSeconds(); + idTokenExpiresIn = devServicesConfig.idTokenExpiresIn().toSeconds(); if (closeDevServiceTask == null) { LOG.info("Starting Dev Services for OIDC"); Vertx vertx = Vertx.vertx(); @@ -571,10 +574,10 @@ private static void passwordTokenEndpoint(RoutingContext rc) { { "access_token":"%s", "token_type":"Bearer", - "expires_in":3600, + "expires_in":%d, "refresh_token":"%s" } - """.formatted(accessToken, refreshToken); + """.formatted(accessToken, accessTokenExpiresIn, refreshToken); rc.response() .putHeader("Content-Type", "application/json") .putHeader("Cache-Control", "no-store") @@ -592,9 +595,9 @@ private static void clientCredentialsTokenEndpoint(RoutingContext rc) { { "access_token": "%s", "token_type": "Bearer", - "expires_in": 3600 + "expires_in": %d } - """.formatted(accessToken); + """.formatted(accessToken, accessTokenExpiresIn); rc.response() .putHeader("Content-Type", "application/json") .putHeader("Cache-Control", "no-store") @@ -669,9 +672,9 @@ private static void refreshTokenEndpoint(RoutingContext rc) { "access_token": "%s", "token_type": "Bearer", "refresh_token": "%s", - "expires_in": 3600 + "expires_in": %d } - """.formatted(accessToken, refreshToken); + """.formatted(accessToken, refreshToken, accessTokenExpiresIn); rc.response() .putHeader("Content-Type", "application/json") .putHeader("Cache-Control", "no-store") @@ -689,8 +692,8 @@ private static void refreshTokenEndpoint(RoutingContext rc) { * { * "token_type":"Bearer", * "scope":"openid email profile", - * "expires_in":3600, - * "ext_expires_in":3600, + * "expires_in":EXPIRES_IN, + * "ext_expires_in":EXPIRES_IN, * "access_token":TOKEN, * "id_token":JWT * } @@ -734,13 +737,14 @@ private static void authorizationCodeFlowTokenEndpoint(RoutingContext rc) { { "token_type":"Bearer", "scope":"openid email profile", - "expires_in":3600, - "ext_expires_in":3600, + "expires_in":%d, + "ext_expires_in":%d, "access_token":"%s", "id_token":"%s", "refresh_token": "%s" } - """.formatted(accessToken, idToken, userAndRoles.encode()); + """.formatted(accessTokenExpiresIn, accessTokenExpiresIn, accessToken, idToken, + userAndRoles.encode()); rc.response() .putHeader("Content-Type", "application/json") .putHeader("Cache-Control", "no-store") @@ -761,7 +765,7 @@ private static void invalidTokenResponse(RoutingContext rc) { private static String createIdToken(String user, Set roles, String clientId) { return Jwt.claims() - .expiresIn(Duration.ofDays(1)) + .expiresIn(idTokenExpiresIn) .issuedAt(Instant.now()) .issuer(baseURI) .audience(clientId) @@ -778,7 +782,7 @@ private static String createIdToken(String user, Set roles, String clien private static String createAccessToken(String user, Set roles, Set scope) { return Jwt.claims() - .expiresIn(Duration.ofDays(1)) + .expiresIn(accessTokenExpiresIn) .issuedAt(Instant.now()) .issuer(baseURI) .subject(user) diff --git a/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/deployment/HibernateSearchElasticsearchProcessor.java b/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/deployment/HibernateSearchElasticsearchProcessor.java index ed5306bdb8b0a..3f5357726b665 100644 --- a/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/deployment/HibernateSearchElasticsearchProcessor.java +++ b/extensions/hibernate-search-orm-elasticsearch/deployment/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/deployment/HibernateSearchElasticsearchProcessor.java @@ -29,6 +29,8 @@ import org.jboss.logging.Logger; import io.quarkus.arc.deployment.UnremovableBeanBuildItem; +import io.quarkus.deployment.Capabilities; +import io.quarkus.deployment.Capability; import io.quarkus.deployment.IsDevServicesSupportedByLaunchMode; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; @@ -320,10 +322,15 @@ void devServicesDropAndCreateAndDropByDefault( @Record(ExecutionTime.RUNTIME_INIT) @BuildStep(onlyIf = HibernateSearchManagementEnabled.class) - void createManagementRoutes(BuildProducer routes, + void createManagementRoutes(Capabilities capabilities, + BuildProducer routes, HibernateSearchElasticsearchRecorder recorder, HibernateSearchElasticsearchBuildTimeConfig hibernateSearchElasticsearchBuildTimeConfig) { - + if (capabilities.isMissing(Capability.VERTX_HTTP)) { + throw new IllegalArgumentException( + "Enabling Hibernate Search management endpoints, while there are no Vert.x HTTP capabilities " + + "in the application, is not allowed."); + } String managementRootPath = hibernateSearchElasticsearchBuildTimeConfig.management().rootPath(); routes.produce(RouteBuildItem.newManagementRoute( diff --git a/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/root/ApplicationPathHttpRootTest.java b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/root/ApplicationPathHttpRootTest.java index 7d9bc1e7c85c1..7f288518bcdcc 100644 --- a/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/root/ApplicationPathHttpRootTest.java +++ b/extensions/resteasy-classic/resteasy/deployment/src/test/java/io/quarkus/resteasy/test/root/ApplicationPathHttpRootTest.java @@ -26,7 +26,7 @@ public class ApplicationPathHttpRootTest { @Test public void testResources() { - // Note that /foo is added automatically by RestAssuredURLManager + // Note that /foo is added automatically by RestAssuredStateManager RestAssured.when().get("/hello/world").then().body(Matchers.is("hello world")); RestAssured.when().get("/world").then().statusCode(404); } diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/beanparam/EmptyBeanParamRecordTest.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/beanparam/EmptyBeanParamRecordTest.java new file mode 100644 index 0000000000000..8dcf587c88efe --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/beanparam/EmptyBeanParamRecordTest.java @@ -0,0 +1,45 @@ +package io.quarkus.resteasy.reactive.server.test.beanparam; + +import jakarta.ws.rs.BeanParam; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class EmptyBeanParamRecordTest { + @RegisterExtension + static final QuarkusUnitTest TEST = new QuarkusUnitTest() + .setArchiveProducer(() -> { + return ShrinkWrap.create(JavaArchive.class) + .addClasses(EmptyBeanParam.class, Resource.class); + }).assertException(x -> { + x.printStackTrace(); + Assertions.assertEquals( + "Body parameters (or non-annotated fields) are not allowed for records. Make sure to annotate your record components with @Rest* or @*Param or that they can be injected as context objects.", + x.getMessage()); + }); + + @Test + void empty() { + Assertions.fail(); + } + + public record EmptyBeanParam(String something, Integer other) { + } + + @Path("/") + public static class Resource { + + @Path("/record") + @GET + public String beanParamRecord(@BeanParam EmptyBeanParam param) { + return "OK"; + } + } +} diff --git a/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/beanparam/ReallyEmptyBeanParamRecordTest.java b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/beanparam/ReallyEmptyBeanParamRecordTest.java new file mode 100644 index 0000000000000..facdbda9d0912 --- /dev/null +++ b/extensions/resteasy-reactive/rest/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/beanparam/ReallyEmptyBeanParamRecordTest.java @@ -0,0 +1,46 @@ +package io.quarkus.resteasy.reactive.server.test.beanparam; + +import jakarta.ws.rs.BeanParam; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class ReallyEmptyBeanParamRecordTest { + @RegisterExtension + static final QuarkusUnitTest TEST = new QuarkusUnitTest() + .setArchiveProducer(() -> { + return ShrinkWrap.create(JavaArchive.class) + .addClasses(EmptyBeanParam.class, Resource.class); + }).assertException(x -> { + x.printStackTrace(); + Assertions.assertEquals( + "No annotations found on fields at 'io.quarkus.resteasy.reactive.server.test.beanparam.ReallyEmptyBeanParamRecordTest$EmptyBeanParam'. Annotations like `@QueryParam` should be used in fields, not in methods.", + x.getMessage()); + }); + + @Test + void empty() { + Assertions.fail(); + } + + // This one does not even have body params + public record EmptyBeanParam() { + } + + @Path("/") + public static class Resource { + + @Path("/record") + @GET + public String beanParamRecord(@BeanParam EmptyBeanParam param) { + return "OK"; + } + } +} diff --git a/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/SmallRyeReactiveMessagingProcessor.java b/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/SmallRyeReactiveMessagingProcessor.java index 9e1eb7a88ccf8..1e2055163f05b 100644 --- a/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/SmallRyeReactiveMessagingProcessor.java +++ b/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/SmallRyeReactiveMessagingProcessor.java @@ -54,6 +54,7 @@ import io.quarkus.deployment.builditem.GeneratedClassBuildItem; import io.quarkus.deployment.builditem.RunTimeConfigurationDefaultBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; +import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; import io.quarkus.deployment.metrics.MetricsCapabilityBuildItem; import io.quarkus.deployment.recording.RecorderContext; import io.quarkus.gizmo.ClassCreator; @@ -78,6 +79,7 @@ import io.quarkus.smallrye.reactivemessaging.runtime.HealthCenterInterceptor; import io.quarkus.smallrye.reactivemessaging.runtime.QuarkusMediatorConfiguration; import io.quarkus.smallrye.reactivemessaging.runtime.QuarkusWorkerPoolRegistry; +import io.quarkus.smallrye.reactivemessaging.runtime.ReactiveMessagingConfigBuilderCustomizer; import io.quarkus.smallrye.reactivemessaging.runtime.ReactiveMessagingConfiguration; import io.quarkus.smallrye.reactivemessaging.runtime.RequestScopedDecorator; import io.quarkus.smallrye.reactivemessaging.runtime.SmallRyeReactiveMessagingLifecycle; @@ -546,6 +548,13 @@ void produceCoroutineScope( } } + @BuildStep + void configCustomizer(BuildProducer serviceProvider) { + // Config mapping between SmallRye / MP and Quarkus + serviceProvider.produce(ServiceProviderBuildItem + .allProvidersFromClassPath(ReactiveMessagingConfigBuilderCustomizer.class.getName())); + } + private void ensureKotlinCoroutinesEnabled(CoroutineConfigurationBuildItem coroutineConfigurationBuildItem, MethodInfo method) { if (!coroutineConfigurationBuildItem.isEnabled()) { diff --git a/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/WiringHelper.java b/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/WiringHelper.java index c1962a6b10523..9379eb24d5ccf 100644 --- a/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/WiringHelper.java +++ b/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/WiringHelper.java @@ -81,7 +81,7 @@ static void produceOutgoingChannel(BuildProducer producer, Str */ static Optional getManagingConnector(ChannelDirection direction, String channel) { return ConfigProvider.getConfig().getOptionalValue( - "mp.messaging." + direction.name().toLowerCase() + "." + normalizeChannelName(channel) + ".connector", + "quarkus.messaging." + direction.name().toLowerCase() + "." + normalizeChannelName(channel) + ".connector", String.class); } @@ -95,7 +95,8 @@ static Optional getManagingConnector(ChannelDirection direction, String static boolean isChannelEnabled(ChannelDirection direction, String channel) { return ConfigProvider.getConfig() .getOptionalValue( - "mp.messaging." + direction.name().toLowerCase() + "." + normalizeChannelName(channel) + ".enabled", + "quarkus.messaging." + direction.name().toLowerCase() + "." + normalizeChannelName(channel) + + ".enabled", Boolean.class) .orElse(true); } diff --git a/extensions/smallrye-reactive-messaging/runtime/src/main/java/io/quarkus/smallrye/reactivemessaging/runtime/ReactiveMessagingConfigBuilderCustomizer.java b/extensions/smallrye-reactive-messaging/runtime/src/main/java/io/quarkus/smallrye/reactivemessaging/runtime/ReactiveMessagingConfigBuilderCustomizer.java new file mode 100644 index 0000000000000..41c39764fe4cb --- /dev/null +++ b/extensions/smallrye-reactive-messaging/runtime/src/main/java/io/quarkus/smallrye/reactivemessaging/runtime/ReactiveMessagingConfigBuilderCustomizer.java @@ -0,0 +1,46 @@ +package io.quarkus.smallrye.reactivemessaging.runtime; + +import java.util.function.Function; + +import io.smallrye.config.FallbackConfigSourceInterceptor; +import io.smallrye.config.RelocateConfigSourceInterceptor; +import io.smallrye.config.SmallRyeConfigBuilder; +import io.smallrye.config.SmallRyeConfigBuilderCustomizer; + +public class ReactiveMessagingConfigBuilderCustomizer implements SmallRyeConfigBuilderCustomizer { + + @Override + public void configBuilder(SmallRyeConfigBuilder builder) { + builder.withInterceptors(new FallbackConfigSourceInterceptor(new Fallback())); + builder.withInterceptors(new RelocateConfigSourceInterceptor(new Relocate())); + } + + private static final String REACTIVE_MESSAGING_PREFIX = "quarkus.messaging."; + private static final String MP_MESSAGING_PREFIX = "mp.messaging."; + private static final String INCOMING = "incoming."; + private static final String OUTGOING = "outgoing."; + + private record Fallback() implements Function { + @Override + public String apply(String name) { + if (name.startsWith(REACTIVE_MESSAGING_PREFIX) + && (name.regionMatches(18, INCOMING, 0, 9) || name.regionMatches(18, OUTGOING, 0, 9))) { + // replaces quarkus with mp + return "mp" + name.substring(7); + } + return name; + } + } + + private record Relocate() implements Function { + @Override + public String apply(String name) { + if (name.startsWith(MP_MESSAGING_PREFIX) + && (name.regionMatches(13, INCOMING, 0, 9) || name.regionMatches(13, OUTGOING, 0, 9))) { + // replaces mp with quarkus + return "quarkus" + name.substring(2); + } + return name; + } + } +} diff --git a/extensions/smallrye-reactive-messaging/runtime/src/main/java/io/quarkus/smallrye/reactivemessaging/runtime/ReactiveMessagingRuntimeConfig.java b/extensions/smallrye-reactive-messaging/runtime/src/main/java/io/quarkus/smallrye/reactivemessaging/runtime/ReactiveMessagingRuntimeConfig.java index c90e8b4144e95..8c3a020c1c23a 100644 --- a/extensions/smallrye-reactive-messaging/runtime/src/main/java/io/quarkus/smallrye/reactivemessaging/runtime/ReactiveMessagingRuntimeConfig.java +++ b/extensions/smallrye-reactive-messaging/runtime/src/main/java/io/quarkus/smallrye/reactivemessaging/runtime/ReactiveMessagingRuntimeConfig.java @@ -1,12 +1,16 @@ package io.quarkus.smallrye.reactivemessaging.runtime; import java.util.List; +import java.util.Map; import java.util.Optional; +import io.quarkus.runtime.annotations.ConfigDocIgnore; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithDefault; import io.smallrye.config.WithName; +import io.smallrye.config.WithParentName; @ConfigRoot(phase = ConfigPhase.RUN_TIME) @ConfigMapping(prefix = "quarkus.messaging") @@ -17,4 +21,46 @@ public interface ReactiveMessagingRuntimeConfig { */ @WithName("connector-context-propagation") Optional> connectorContextPropagation(); + + /** + * Used internally only. Users use mp.messaging. + */ + @ConfigDocIgnore + Map incoming(); + + /** + * Used internally only. Users use mp.messaging. + */ + @ConfigDocIgnore + Map outgoing(); + + interface ChannelDirection { + /** + * Used internally only. Users use mp.messaging. + */ + @ConfigDocIgnore + Optional connector(); + + /** + * Used internally only. Users use mp.messaging. + */ + @ConfigDocIgnore + @WithDefault("true") + boolean enabled(); + + /** + * Used internally only. + */ + @ConfigDocIgnore + @WithParentName + Map channelConfig(); + } + + interface Incoming extends ChannelDirection { + + } + + interface Outgoing extends ChannelDirection { + + } } diff --git a/extensions/smallrye-reactive-messaging/runtime/src/main/resources/META-INF/services/io.smallrye.config.SmallRyeConfigBuilderCustomizer b/extensions/smallrye-reactive-messaging/runtime/src/main/resources/META-INF/services/io.smallrye.config.SmallRyeConfigBuilderCustomizer new file mode 100644 index 0000000000000..04d34ef7943cd --- /dev/null +++ b/extensions/smallrye-reactive-messaging/runtime/src/main/resources/META-INF/services/io.smallrye.config.SmallRyeConfigBuilderCustomizer @@ -0,0 +1 @@ +io.quarkus.smallrye.reactivemessaging.runtime.ReactiveMessagingConfigBuilderCustomizer diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/management/ManagementInterfaceBuildTimeConfig.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/management/ManagementInterfaceBuildTimeConfig.java index 1e7491ffd4c72..4a3283cd027b5 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/management/ManagementInterfaceBuildTimeConfig.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/management/ManagementInterfaceBuildTimeConfig.java @@ -18,7 +18,7 @@ public interface ManagementInterfaceBuildTimeConfig { /** * Enables / Disables the usage of a separate interface/port to expose the management endpoints. * If sets to {@code true}, the management endpoints will be exposed to a different HTTP server. - * This avoids exposing the management endpoints on a y available server(. + * This avoids exposing the management endpoints on a publicly available server. */ @WithDefault("false") boolean enabled(); diff --git a/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/ServerEndpointIndexer.java b/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/ServerEndpointIndexer.java index a1a12f54d8864..8113cea9f6016 100644 --- a/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/ServerEndpointIndexer.java +++ b/independent-projects/resteasy-reactive/server/processor/src/main/java/org/jboss/resteasy/reactive/server/processor/ServerEndpointIndexer.java @@ -395,6 +395,10 @@ protected InjectableBean scanInjectableBean(ClassInfo currentClassInfo, ClassInf annotations, field.type(), "%s", new Object[] { field }, applyFieldRules, hasRuntimeConverters, // We don't support annotation-less path params in injectable beans: only annotations Collections.emptySet(), field.name(), EMPTY_STRING_ARRAY, new HashMap<>()); + if (currentClassInfo.isRecord() && result.getType() == ParameterType.BODY) { + throw new DeploymentException( + "Body parameters (or non-annotated fields) are not allowed for records. Make sure to annotate your record components with @Rest* or @*Param or that they can be injected as context objects."); + } if ((result.getType() != null) && (result.getType() != ParameterType.BEAN)) { //BODY means no annotation, so for fields not injectable fieldExtractors.put(field, result); diff --git a/integration-tests/devtools/src/test/resources/__snapshots__/LgtmCodestartTest/testContent/src_test_java_ilove_quark_us_SimpleTest.java b/integration-tests/devtools/src/test/resources/__snapshots__/LgtmCodestartTest/testContent/src_test_java_ilove_quark_us_SimpleTest.java index 813fd23e2ab58..5bf7be8cced6b 100644 --- a/integration-tests/devtools/src/test/resources/__snapshots__/LgtmCodestartTest/testContent/src_test_java_ilove_quark_us_SimpleTest.java +++ b/integration-tests/devtools/src/test/resources/__snapshots__/LgtmCodestartTest/testContent/src_test_java_ilove_quark_us_SimpleTest.java @@ -1,6 +1,6 @@ package ilove.quark.us; -import org.eclipse.microprofile.config.inject.ConfigProperty;; +import org.eclipse.microprofile.config.inject.ConfigProperty; import org.jboss.logging.Logger; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; diff --git a/integration-tests/oidc-dev-services/src/main/java/io/quarkus/it/oidc/dev/services/SecuredResource.java b/integration-tests/oidc-dev-services/src/main/java/io/quarkus/it/oidc/dev/services/SecuredResource.java index 124482eba00aa..03e21c71494fc 100644 --- a/integration-tests/oidc-dev-services/src/main/java/io/quarkus/it/oidc/dev/services/SecuredResource.java +++ b/integration-tests/oidc-dev-services/src/main/java/io/quarkus/it/oidc/dev/services/SecuredResource.java @@ -22,6 +22,9 @@ public class SecuredResource { @IdToken JsonWebToken idToken; + @Inject + JsonWebToken accessToken; + @Inject UserInfo userInfo; @@ -47,6 +50,13 @@ public String getUserOnly() { return userInfo.getPreferredUserName() + " " + securityIdentity.getRoles() + " " + userInfo.getName(); } + @RolesAllowed("user") + @GET + @Path("expires-in") + public String getExpiresIn() { + return String.valueOf(accessToken.getExpirationTime() - accessToken.getIssuedAtTime()); + } + @GET @Path("auth-server-url") public String getAuthServerUrl() { diff --git a/integration-tests/oidc-dev-services/src/test/java/io/quarkus/it/oidc/dev/services/BearerAuthenticationOidcDevServicesTest.java b/integration-tests/oidc-dev-services/src/test/java/io/quarkus/it/oidc/dev/services/BearerAuthenticationOidcDevServicesTest.java index b12d6ac4f9b7c..fe043beb2ca55 100644 --- a/integration-tests/oidc-dev-services/src/test/java/io/quarkus/it/oidc/dev/services/BearerAuthenticationOidcDevServicesTest.java +++ b/integration-tests/oidc-dev-services/src/test/java/io/quarkus/it/oidc/dev/services/BearerAuthenticationOidcDevServicesTest.java @@ -91,6 +91,16 @@ void testEmailAndName() { .body(Matchers.containsString(" Bob")); } + @Test + void testTokenExpiration() { + RestAssured.given() + .auth().oauth2(getAccessToken("bob")) + .get("/secured/expires-in") + .then() + .statusCode(200) + .body(Matchers.is(String.valueOf(5 * 60L))); // The default token expiration is 5 minutes + } + private String getAccessToken(String user) { return oidcTestClient.getAccessToken(user, user); } diff --git a/pom.xml b/pom.xml index e6219b530d901..19b1bb9821fc1 100644 --- a/pom.xml +++ b/pom.xml @@ -71,15 +71,15 @@ 0.8.14 7.4.0 5.5.6 - 7.1.10.Final + 7.1.11.Final 3.2.0 4.13.2 1.17.6 1.0.1 - 3.1.9.Final + 3.1.10.Final 9.1.0.Final 8.1.2.Final - 7.1.10.Final + 7.1.11.Final 1.76.0 diff --git a/test-framework/common/src/main/java/io/quarkus/test/common/RestAssuredStateManager.java b/test-framework/common/src/main/java/io/quarkus/test/common/RestAssuredStateManager.java new file mode 100644 index 0000000000000..0b60b150920b0 --- /dev/null +++ b/test-framework/common/src/main/java/io/quarkus/test/common/RestAssuredStateManager.java @@ -0,0 +1,178 @@ +package io.quarkus.test.common; + +import java.time.Duration; +import java.util.Optional; + +import org.eclipse.microprofile.config.ConfigProvider; + +import io.restassured.RestAssured; +import io.restassured.config.HttpClientConfig; +import io.restassured.config.RestAssuredConfig; +import io.restassured.internal.path.json.ConfigurableJsonSlurper; +import io.restassured.path.json.JsonPath; +import io.restassured.specification.RequestSpecification; + +/** + * Utility class that sets the rest assured port to the default test port and meaningful timeouts. + *

+ * This class works whether RestAssured is on the classpath or not - if it is not, invoking the methods of the class are + * essentially NO-OPs + *

+ * TODO: do we actually want this here, or should it be in a different module? + */ +public class RestAssuredStateManager { + + private static final int DEFAULT_HTTP_PORT = 8081; + private static final int DEFAULT_HTTPS_PORT = 8444; + + private static int oldPort; + private static String oldBaseURI; + private static String oldBasePath; + private static Object oldRestAssuredConfig; // we can't declare the type here as that would prevent this class for being loaded if RestAssured is not present + private static Object oldRequestSpecification; + + private static final boolean REST_ASSURED_PRESENT; + + static { + boolean present = false; + try { + Class.forName("io.restassured.RestAssured"); + present = true; + } catch (ClassNotFoundException ignored) { + } + REST_ASSURED_PRESENT = present; + } + + private RestAssuredStateManager() { + + } + + private static int getPortFromConfig(int defaultValue, String... keys) { + for (String key : keys) { + Optional port = ConfigProvider.getConfig().getOptionalValue(key, Integer.class); + if (port.isPresent()) + return port.get(); + } + return defaultValue; + } + + public static void setURL(boolean useSecureConnection) { + setURL(useSecureConnection, null, null); + } + + public static void setURL(boolean useSecureConnection, String additionalPath) { + setURL(useSecureConnection, null, additionalPath); + } + + public static void setURL(boolean useSecureConnection, Integer port) { + setURL(useSecureConnection, port, null); + } + + public static void setURL(boolean useSecureConnection, Integer port, String additionalPath) { + if (!REST_ASSURED_PRESENT) { + return; + } + + oldPort = RestAssured.port; + if (port == null) { + port = useSecureConnection ? getPortFromConfig(DEFAULT_HTTPS_PORT, "quarkus.http.test-ssl-port") + : getPortFromConfig(DEFAULT_HTTP_PORT, "quarkus.lambda.mock-event-server.test-port", + "quarkus.http.test-port"); + } + RestAssured.port = port; + + oldBaseURI = RestAssured.baseURI; + final String protocol = useSecureConnection ? "https://" : "http://"; + String host = ConfigProvider.getConfig().getOptionalValue("quarkus.http.host", String.class) + .orElse("localhost"); + if (host.equals("0.0.0.0")) { + host = "localhost"; + } + RestAssured.baseURI = protocol + host; + + oldBasePath = RestAssured.basePath; + Optional basePath = ConfigProvider.getConfig().getOptionalValue("quarkus.http.root-path", + String.class); + if (basePath.isPresent() || additionalPath != null) { + StringBuilder bp = new StringBuilder(); + if (basePath.isPresent()) { + if (basePath.get().startsWith("/")) { + bp.append(basePath.get().substring(1)); + } else { + bp.append(basePath.get()); + } + if (bp.toString().endsWith("/")) { + bp.setLength(bp.length() - 1); + } + } + if (additionalPath != null) { + if (!additionalPath.startsWith("/")) { + bp.append("/"); + } + bp.append(additionalPath); + if (bp.toString().endsWith("/")) { + bp.setLength(bp.length() - 1); + } + } + RestAssured.basePath = bp.toString(); + } + + oldRestAssuredConfig = RestAssured.config(); + + Duration timeout = ConfigProvider.getConfig() + .getOptionalValue("quarkus.http.test-timeout", Duration.class).orElse(Duration.ofSeconds(30)); + configureTimeouts(timeout); + + oldRequestSpecification = RestAssured.requestSpecification; + if (ConfigProvider.getConfig() + .getOptionalValue("quarkus.test.rest-assured.enable-logging-on-failure", Boolean.class).orElse(true)) { + RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); + } + } + + private static void configureTimeouts(Duration d) { + RestAssured.config = RestAssured.config().httpClient(new HttpClientConfig() + .setParam("http.conn-manager.timeout", d.toMillis()) // this needs to be long + .setParam("http.connection.timeout", (int) d.toMillis()) // this needs to be int + .setParam("http.socket.timeout", (int) d.toMillis())); // same here + } + + public static void clearState() { + if (!REST_ASSURED_PRESENT) { + return; + } + + clearURL(); + + JsonPath.config = null; + // Reset the numberReturnType ThreadLocal in ConfigurableJsonSlurper as it is causing class loader leaks + try { + java.lang.reflect.Field numberReturnTypeField = ConfigurableJsonSlurper.class.getDeclaredField("numberReturnType"); + numberReturnTypeField.setAccessible(true); + ThreadLocal threadLocal = (ThreadLocal) numberReturnTypeField.get(null); + if (threadLocal != null) { + threadLocal.remove(); + } + } catch (Exception e) { + // Ignore if the field doesn't exist or can't be accessed + } + } + + /** + * @deprecated most probably, you want to call {@link #clearState()}. If it's not the case, please let us know so that we + * can reconsider the deprecation. + * Note: when removing, please move the code to {@link #clearState()}. + */ + @Deprecated(forRemoval = true, since = "3.31") + static void clearURL() { + if (!REST_ASSURED_PRESENT) { + return; + } + + RestAssured.port = oldPort; + RestAssured.baseURI = oldBaseURI; + RestAssured.basePath = oldBasePath; + RestAssured.config = (RestAssuredConfig) oldRestAssuredConfig; + RestAssured.requestSpecification = (RequestSpecification) oldRequestSpecification; + } +} diff --git a/test-framework/common/src/main/java/io/quarkus/test/common/RestAssuredURLManager.java b/test-framework/common/src/main/java/io/quarkus/test/common/RestAssuredURLManager.java index ec615d334a041..975d325a147e3 100644 --- a/test-framework/common/src/main/java/io/quarkus/test/common/RestAssuredURLManager.java +++ b/test-framework/common/src/main/java/io/quarkus/test/common/RestAssuredURLManager.java @@ -1,59 +1,11 @@ package io.quarkus.test.common; -import java.time.Duration; -import java.util.Optional; - -import org.eclipse.microprofile.config.ConfigProvider; - -import io.restassured.RestAssured; -import io.restassured.config.HttpClientConfig; -import io.restassured.config.RestAssuredConfig; -import io.restassured.specification.RequestSpecification; - /** - * Utility class that sets the rest assured port to the default test port and meaningful timeouts. - *

- * This class works whether RestAssured is on the classpath or not - if it is not, invoking the methods of the class are - * essentially NO-OPs - *

- * TODO: do we actually want this here, or should it be in a different module? + * @deprecated use {@link RestAssuredStateManager} instead */ +@Deprecated(forRemoval = true, since = "3.31") public class RestAssuredURLManager { - private static final int DEFAULT_HTTP_PORT = 8081; - private static final int DEFAULT_HTTPS_PORT = 8444; - - private static int oldPort; - private static String oldBaseURI; - private static String oldBasePath; - private static Object oldRestAssuredConfig; // we can't declare the type here as that would prevent this class for being loaded if RestAssured is not present - private static Object oldRequestSpecification; - - private static final boolean REST_ASSURED_PRESENT; - - static { - boolean present = false; - try { - Class.forName("io.restassured.RestAssured"); - present = true; - } catch (ClassNotFoundException ignored) { - } - REST_ASSURED_PRESENT = present; - } - - private RestAssuredURLManager() { - - } - - private static int getPortFromConfig(int defaultValue, String... keys) { - for (String key : keys) { - Optional port = ConfigProvider.getConfig().getOptionalValue(key, Integer.class); - if (port.isPresent()) - return port.get(); - } - return defaultValue; - } - public static void setURL(boolean useSecureConnection) { setURL(useSecureConnection, null, null); } @@ -67,83 +19,10 @@ public static void setURL(boolean useSecureConnection, Integer port) { } public static void setURL(boolean useSecureConnection, Integer port, String additionalPath) { - if (!REST_ASSURED_PRESENT) { - return; - } - - oldPort = RestAssured.port; - if (port == null) { - port = useSecureConnection ? getPortFromConfig(DEFAULT_HTTPS_PORT, "quarkus.http.test-ssl-port") - : getPortFromConfig(DEFAULT_HTTP_PORT, "quarkus.lambda.mock-event-server.test-port", - "quarkus.http.test-port"); - } - RestAssured.port = port; - - oldBaseURI = RestAssured.baseURI; - final String protocol = useSecureConnection ? "https://" : "http://"; - String host = ConfigProvider.getConfig().getOptionalValue("quarkus.http.host", String.class) - .orElse("localhost"); - if (host.equals("0.0.0.0")) { - host = "localhost"; - } - RestAssured.baseURI = protocol + host; - - oldBasePath = RestAssured.basePath; - Optional basePath = ConfigProvider.getConfig().getOptionalValue("quarkus.http.root-path", - String.class); - if (basePath.isPresent() || additionalPath != null) { - StringBuilder bp = new StringBuilder(); - if (basePath.isPresent()) { - if (basePath.get().startsWith("/")) { - bp.append(basePath.get().substring(1)); - } else { - bp.append(basePath.get()); - } - if (bp.toString().endsWith("/")) { - bp.setLength(bp.length() - 1); - } - } - if (additionalPath != null) { - if (!additionalPath.startsWith("/")) { - bp.append("/"); - } - bp.append(additionalPath); - if (bp.toString().endsWith("/")) { - bp.setLength(bp.length() - 1); - } - } - RestAssured.basePath = bp.toString(); - } - - oldRestAssuredConfig = RestAssured.config(); - - Duration timeout = ConfigProvider.getConfig() - .getOptionalValue("quarkus.http.test-timeout", Duration.class).orElse(Duration.ofSeconds(30)); - configureTimeouts(timeout); - - oldRequestSpecification = RestAssured.requestSpecification; - if (ConfigProvider.getConfig() - .getOptionalValue("quarkus.test.rest-assured.enable-logging-on-failure", Boolean.class).orElse(true)) { - RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); - } - } - - private static void configureTimeouts(Duration d) { - RestAssured.config = RestAssured.config().httpClient(new HttpClientConfig() - .setParam("http.conn-manager.timeout", d.toMillis()) // this needs to be long - .setParam("http.connection.timeout", (int) d.toMillis()) // this needs to be int - .setParam("http.socket.timeout", (int) d.toMillis())); // same here + RestAssuredStateManager.setURL(useSecureConnection, port, additionalPath); } public static void clearURL() { - if (!REST_ASSURED_PRESENT) { - return; - } - - RestAssured.port = oldPort; - RestAssured.baseURI = oldBaseURI; - RestAssured.basePath = oldBasePath; - RestAssured.config = (RestAssuredConfig) oldRestAssuredConfig; - RestAssured.requestSpecification = (RequestSpecification) oldRequestSpecification; + RestAssuredStateManager.clearURL(); } } diff --git a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/ComponentContainer.java b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/ComponentContainer.java index 90a6bbd059bd9..2c5309415f78b 100644 --- a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/ComponentContainer.java +++ b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/ComponentContainer.java @@ -59,6 +59,7 @@ import org.jboss.jandex.DotName; import org.jboss.jandex.IndexView; import org.jboss.jandex.Indexer; +import org.jboss.jandex.MethodInfo; import org.jboss.jandex.Type; import org.jboss.jandex.Type.Kind; import org.jboss.logging.Logger; @@ -357,6 +358,19 @@ public void register(RegistrationContext registrationContext) { .add(new TypeAndQualifiers(Types.jandexType(param.getParameterizedType()), requiredQualifiers)); } + // We need to remove duplicate unsatisfied injection points + // More specifically, injection points with the the same required type + // and the same qualifiers (with non-binding members ignored) + if (!unsatisfiedInjectionPoints.isEmpty()) { + Map> qualifierToNonbindingMembers = findQualifierToNonbindingMembers( + beanDeployment); + if (!qualifierToNonbindingMembers.isEmpty()) { + findDuplicateInjectionPoints(beanDeployment, unsatisfiedInjectionPoints, + qualifierToNonbindingMembers) + .forEach(unsatisfiedInjectionPoints::remove); + } + } + for (TypeAndQualifiers unsatisfied : unsatisfiedInjectionPoints) { ClassInfo implementationClass = computingIndex.getClassByName(unsatisfied.type.name()); BeanConfigurator configurator = registrationContext.configure(implementationClass.name()) @@ -882,6 +896,94 @@ private static Set findBindings(Method method, Set bindings) .collect(Collectors.toSet()); } + private static Map> findQualifierToNonbindingMembers(BeanDeployment beanDeployment) { + Map> ret = new HashMap<>(); + for (ClassInfo qualifier : beanDeployment.getQualifiers()) { + Set nonbinding = new HashSet<>(); + for (MethodInfo m : qualifier.methods()) { + if (m.hasAnnotation(DotNames.NONBINDING)) { + nonbinding.add(m.name()); + } + } + nonbinding.addAll(beanDeployment.getQualifierNonbindingMembers(qualifier.name())); + if (!nonbinding.isEmpty()) { + ret.put(qualifier.name(), nonbinding); + } + } + return ret; + } + + private static List findDuplicateInjectionPoints(BeanDeployment beanDeployment, + Set unsatisfiedInjectionPoints, Map> qualifierToNonbindingMembers) { + List ret = new ArrayList<>(); + + for (List groupByType : unsatisfiedInjectionPoints.stream() + .collect(Collectors.groupingBy(ip -> ip.type)).values()) { + + if (groupByType.size() > 1) { + for (List groupByNumberOfQualifiers : groupByType.stream() + .collect(Collectors.groupingBy(tq -> tq.qualifiers.size())).values()) { + + if (groupByNumberOfQualifiers.size() > 1) { + for (Entry, List> e : groupByNumberOfQualifiers.stream() + .collect(Collectors.groupingBy(tq -> { + return tq.qualifiers.stream().map(AnnotationInstance::name).collect(Collectors.toSet()); + })).entrySet()) { + + List byQualiferNames = e.getValue(); + if (byQualiferNames.size() > 1) { + Set qualifiers = e.getKey(); + boolean nonbinding = false; + for (DotName q : qualifiers) { + if (qualifierToNonbindingMembers.containsKey(q)) { + nonbinding = true; + break; + } + } + if (nonbinding) { + // At least two unsatisfied injection points with + // - the same required type + // - the same qualifier annotations + // - where at least one qualifier has a @Nonbinding member + for (List maybeDuplicit : byQualiferNames.stream() + .collect(Collectors.groupingBy( + tq -> new MatchingQualifiers(tq, qualifierToNonbindingMembers))) + .values()) { + if (maybeDuplicit.size() > 1) { + maybeDuplicit.subList(1, maybeDuplicit.size()).forEach(ret::add); + } + } + } + } + } + } + } + } + } + return ret; + } + + record MatchingQualifier(DotName name, Set values) { + + public MatchingQualifier(AnnotationInstance annotation, Map> qualifierToNonbindingMembers) { + this(annotation.name(), + annotation.values().stream() + .filter(v -> !qualifierToNonbindingMembers.get(annotation.name()).contains(v.name())) + .collect(Collectors.toSet())); + } + + } + + record MatchingQualifiers(Set qualifiers) { + + public MatchingQualifiers(TypeAndQualifiers typeAndQualifiers, Map> qualifierToNonbindingMembers) { + this(typeAndQualifiers.qualifiers.stream() + .map(q -> new MatchingQualifier(q, qualifierToNonbindingMembers)) + .collect(Collectors.toSet())); + } + + } + @SuppressWarnings("unchecked") static T cast(Object obj) { return (T) obj; diff --git a/test-framework/junit5-component/src/test/java/io/quarkus/test/component/unsatisfied/UnsatisfiedDependencyClassHierarchyTest.java b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/unsatisfied/UnsatisfiedDependencyClassHierarchyTest.java new file mode 100644 index 0000000000000..4446a0fb5cfff --- /dev/null +++ b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/unsatisfied/UnsatisfiedDependencyClassHierarchyTest.java @@ -0,0 +1,62 @@ +package io.quarkus.test.component.unsatisfied; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import jakarta.inject.Inject; +import jakarta.inject.Singleton; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import io.quarkus.test.InjectMock; +import io.quarkus.test.component.QuarkusComponentTest; + +@QuarkusComponentTest +public class UnsatisfiedDependencyClassHierarchyTest { + + @Inject + Consumer consumer; + + @InjectMock + Foo fooMock; + + @InjectMock + Bar barMock; + + @Test + public void testAutoMocks() { + Mockito.when(fooMock.val()).thenReturn("fooMock"); + Mockito.when(barMock.val()).thenReturn("barMock"); + assertEquals("fooMock", consumer.foo.val()); + assertEquals("barMock", consumer.bar.val()); + } + + @Singleton + public static class Consumer { + + @Inject + Foo foo; // -> register mock synthetic bean for bean types [Foo] and default qualifiers + + @Inject + Bar bar; // -> register mock synthetic bean for bean types [Bar] and default qualifiers + + } + + public static class Foo { + + String val() { + return "foo"; + } + + } + + public static class Bar extends Foo { + + @Override + String val() { + return super.val() + "bar"; + } + + } + +} diff --git a/test-framework/junit5-component/src/test/java/io/quarkus/test/component/unsatisfied/UnsatisfiedDependencyNonbindingQualifierMemberTest.java b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/unsatisfied/UnsatisfiedDependencyNonbindingQualifierMemberTest.java new file mode 100644 index 0000000000000..6250b81f814ed --- /dev/null +++ b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/unsatisfied/UnsatisfiedDependencyNonbindingQualifierMemberTest.java @@ -0,0 +1,66 @@ +package io.quarkus.test.component.unsatisfied; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.lang.annotation.Retention; + +import jakarta.enterprise.util.Nonbinding; +import jakarta.inject.Inject; +import jakarta.inject.Qualifier; +import jakarta.inject.Singleton; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import io.quarkus.test.InjectMock; +import io.quarkus.test.component.QuarkusComponentTest; + +@QuarkusComponentTest +public class UnsatisfiedDependencyNonbindingQualifierMemberTest { + + @Inject + FooConsumer consumer; + + @InjectMock + @FooQualifier("ignored") + Foo mock; // this mock is used for both FooConsumer#alpha nad FooConsumer#bravo + + @Test + public void testAutoMocks() { + Mockito.when(mock.val()).thenReturn("dummy"); + assertEquals("dummydummy", consumer.ping()); + } + + @Singleton + public static class FooConsumer { + + @FooQualifier("alpha") + Foo alpha; + + @FooQualifier("bravo") + Foo bravo; + + public String ping() { + return alpha.val() + bravo.val(); + } + + } + + public static class Foo { + + String val() { + return "foo"; + } + + } + + @Qualifier + @Retention(RUNTIME) + public @interface FooQualifier { + + @Nonbinding + String value(); + } + +} diff --git a/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusProdModeTest.java b/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusProdModeTest.java index 51360724ef940..5aa5a514fa65c 100644 --- a/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusProdModeTest.java +++ b/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusProdModeTest.java @@ -57,7 +57,7 @@ import io.quarkus.deployment.util.FileUtil; import io.quarkus.maven.dependency.Dependency; import io.quarkus.test.common.PathTestHelper; -import io.quarkus.test.common.RestAssuredURLManager; +import io.quarkus.test.common.RestAssuredStateManager; import io.quarkus.test.common.TestConfigUtil; import io.quarkus.test.common.TestResourceManager; import io.smallrye.common.process.ProcessUtil; @@ -642,7 +642,7 @@ public void stop() { } if (clearRestAssuredURL) { - RestAssuredURLManager.clearURL(); + RestAssuredStateManager.clearState(); clearRestAssuredURL = false; } } @@ -653,12 +653,12 @@ private void setupRestAssured() { .orElse(DEFAULT_HTTP_PORT_INT); // If http port is 0, then we need to set the port to null in order to use the `quarkus.http.test-ssl-port` property - // which is done in `RestAssuredURLManager.setURL`. + // which is done in `RestAssuredStateManager.setURL`. if (httpPort == 0) { httpPort = null; } - RestAssuredURLManager.setURL(false, httpPort); + RestAssuredStateManager.setURL(false, httpPort); } private void ensureApplicationStartupOrFailure() throws IOException { diff --git a/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusUnitTest.java b/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusUnitTest.java index 8f2ce5ff2a1e5..ff65e7525a124 100644 --- a/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusUnitTest.java +++ b/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusUnitTest.java @@ -71,7 +71,7 @@ import io.quarkus.test.common.GroovyClassValue; import io.quarkus.test.common.PathTestHelper; import io.quarkus.test.common.PropertyTestUtil; -import io.quarkus.test.common.RestAssuredURLManager; +import io.quarkus.test.common.RestAssuredStateManager; import io.quarkus.test.common.TestConfigUtil; import io.quarkus.test.common.TestResourceManager; import io.quarkus.test.common.http.TestHTTPResourceManager; @@ -851,8 +851,8 @@ public void afterAll(ExtensionContext extensionContext) throws Exception { public void afterEach(ExtensionContext context) throws Exception { if (runningQuarkusApplication != null) { //this kinda sucks, but everything is isolated, so we need to hook into everything via reflection - runningQuarkusApplication.getClassLoader().loadClass(RestAssuredURLManager.class.getName()) - .getDeclaredMethod("clearURL") + runningQuarkusApplication.getClassLoader().loadClass(RestAssuredStateManager.class.getName()) + .getDeclaredMethod("clearState") .invoke(null); } } @@ -864,7 +864,7 @@ public void beforeEach(ExtensionContext context) throws Exception { return; } if (runningQuarkusApplication != null) { - runningQuarkusApplication.getClassLoader().loadClass(RestAssuredURLManager.class.getName()) + runningQuarkusApplication.getClassLoader().loadClass(RestAssuredStateManager.class.getName()) .getDeclaredMethod("setURL", boolean.class).invoke(null, useSecureConnection); } else { Optional> testClass = context.getTestClass(); diff --git a/test-framework/junit5-internal/src/test/java/io/quarkus/test/QuarkusProdModeTestExpectExitTest.java b/test-framework/junit5-internal/src/test/java/io/quarkus/test/QuarkusProdModeTestExpectExitTest.java index 95d7b883e3eba..0ce37b6f80253 100644 --- a/test-framework/junit5-internal/src/test/java/io/quarkus/test/QuarkusProdModeTestExpectExitTest.java +++ b/test-framework/junit5-internal/src/test/java/io/quarkus/test/QuarkusProdModeTestExpectExitTest.java @@ -13,7 +13,7 @@ import org.mockito.Mockito; import io.quarkus.runtime.annotations.QuarkusMain; -import io.quarkus.test.common.RestAssuredURLManager; +import io.quarkus.test.common.RestAssuredStateManager; @TestMethodOrder(OrderAnnotation.class) public class QuarkusProdModeTestExpectExitTest { @@ -40,7 +40,7 @@ public void shouldStartManually() { thenAppIsNotRunning(); thenAppWasNotRestarted(); - try (var urlMgrMock = Mockito.mockStatic(RestAssuredURLManager.class)) { + try (var urlMgrMock = Mockito.mockStatic(RestAssuredStateManager.class)) { whenStartApp(); thenAppIsNotRunning(); thenAppWasRestarted(); diff --git a/test-framework/junit5-internal/src/test/java/io/quarkus/test/QuarkusProdModeTestTest.java b/test-framework/junit5-internal/src/test/java/io/quarkus/test/QuarkusProdModeTestTest.java index 354c3cf97791e..a2ba296c9c41f 100644 --- a/test-framework/junit5-internal/src/test/java/io/quarkus/test/QuarkusProdModeTestTest.java +++ b/test-framework/junit5-internal/src/test/java/io/quarkus/test/QuarkusProdModeTestTest.java @@ -11,7 +11,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.Mockito; -import io.quarkus.test.common.RestAssuredURLManager; +import io.quarkus.test.common.RestAssuredStateManager; @TestMethodOrder(OrderAnnotation.class) public class QuarkusProdModeTestTest { @@ -27,7 +27,7 @@ public class QuarkusProdModeTestTest { public void shouldStopAndStartManually() { thenAppIsRunning(); - try (var urlMgrMock = Mockito.mockStatic(RestAssuredURLManager.class)) { + try (var urlMgrMock = Mockito.mockStatic(RestAssuredStateManager.class)) { whenStopApp(); thenAppIsNotRunning(); @@ -37,8 +37,8 @@ public void shouldStopAndStartManually() { whenStopApp(); // stop again to verify in next test method that app was auto-restarted thenAppIsNotRunning(); - urlMgrMock.verify(() -> RestAssuredURLManager.setURL(false, 8081)); - urlMgrMock.verify(RestAssuredURLManager::clearURL, times(2)); + urlMgrMock.verify(() -> RestAssuredStateManager.setURL(false, 8081)); + urlMgrMock.verify(RestAssuredStateManager::clearState, times(2)); } } diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusIntegrationTestExtension.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusIntegrationTestExtension.java index d04e80920a7c1..f856ddbf1a2e9 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusIntegrationTestExtension.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusIntegrationTestExtension.java @@ -48,7 +48,7 @@ import io.quarkus.runtime.test.TestHttpEndpointProvider; import io.quarkus.test.common.ArtifactLauncher; import io.quarkus.test.common.DevServicesContext; -import io.quarkus.test.common.RestAssuredURLManager; +import io.quarkus.test.common.RestAssuredStateManager; import io.quarkus.test.common.RunCommandLauncher; import io.quarkus.test.common.TestConfigUtil; import io.quarkus.test.common.TestHostLauncher; @@ -91,7 +91,7 @@ public void afterEach(ExtensionContext context) throws Exception { invokeAfterEachCallbacks(createQuarkusTestMethodContext(context)); } - RestAssuredURLManager.clearURL(); + RestAssuredStateManager.clearState(); TestScopeManager.tearDown(true); } } @@ -117,7 +117,7 @@ public void beforeEach(ExtensionContext context) throws Exception { invokeBeforeEachCallbacks(createQuarkusTestMethodContext(context)); } - RestAssuredURLManager.setURL(ssl, QuarkusTestExtension.getEndpointPath(context, testHttpEndpointProviders)); + RestAssuredStateManager.setURL(ssl, QuarkusTestExtension.getEndpointPath(context, testHttpEndpointProviders)); TestScopeManager.setup(true); } } diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java index 6912253090149..e08b1ca27e3c9 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java @@ -69,7 +69,7 @@ import io.quarkus.runtime.test.TestHttpEndpointProvider; import io.quarkus.test.TestMethodInvoker; import io.quarkus.test.common.GroovyClassValue; -import io.quarkus.test.common.RestAssuredURLManager; +import io.quarkus.test.common.RestAssuredStateManager; import io.quarkus.test.common.RestorableSystemProperties; import io.quarkus.test.common.TestClassIndexer; import io.quarkus.test.common.TestResourceManager; @@ -404,7 +404,7 @@ public void beforeEach(ExtensionContext context) throws Exception { if (insecureAllowed.isPresent()) { secure = !insecureAllowed.get().toLowerCase(Locale.ENGLISH).equals("enabled"); } - runningQuarkusApplication.getClassLoader().loadClass(RestAssuredURLManager.class.getName()) + runningQuarkusApplication.getClassLoader().loadClass(RestAssuredStateManager.class.getName()) .getDeclaredMethod("setURL", boolean.class, String.class).invoke(null, secure, endpointPath); runningQuarkusApplication.getClassLoader().loadClass(TestScopeManager.class.getName()) .getDeclaredMethod("setup", boolean.class).invoke(null, false); @@ -524,8 +524,8 @@ public void afterEach(ExtensionContext context) throws Exception { popMockContext(); Map.Entry, ?> tuple = createQuarkusTestMethodContextTuple(context); invokeAfterEachCallbacks(tuple.getKey(), tuple.getValue()); - runningQuarkusApplication.getClassLoader().loadClass(RestAssuredURLManager.class.getName()) - .getDeclaredMethod("clearURL").invoke(null); + runningQuarkusApplication.getClassLoader().loadClass(RestAssuredStateManager.class.getName()) + .getDeclaredMethod("clearState").invoke(null); runningQuarkusApplication.getClassLoader().loadClass(TestScopeManager.class.getName()) .getDeclaredMethod("tearDown", boolean.class).invoke(null, false);