diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 5725b7a262a66..6555e477136f5 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -47,7 +47,7 @@ 1.6.0 2.1.0 5.4.1 - 3.5.1 + 3.5.3 1.2.2 1.0.13 2.7.0 diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildRunner.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildRunner.java index 56345395ae014..c5acdbab3a975 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildRunner.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildRunner.java @@ -72,10 +72,9 @@ public Result build(List args, String nativeImageName, String resultingE if (objcopyExists) { if (debugSymbolsEnabled) { splitDebugSymbols(nativeImageName, resultingExecutableName); - } else { - // Strip debug symbols regardless, because the underlying JDK might contain them - objcopy("--strip-debug", resultingExecutableName); } + // Strip debug symbols regardless, because the underlying JDK might contain them + objcopy("--strip-debug", resultingExecutableName); } else if (SystemUtils.IS_OS_LINUX) { log.warn( "objcopy executable not found in PATH. Debug symbols will therefore not be separated from the executable."); diff --git a/core/runtime/src/main/java/io/quarkus/runtime/MockedThroughWrapper.java b/core/runtime/src/main/java/io/quarkus/runtime/MockedThroughWrapper.java index 1cf124ccb6f20..2c11b4683f18e 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/MockedThroughWrapper.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/MockedThroughWrapper.java @@ -8,4 +8,6 @@ */ public interface MockedThroughWrapper { void setMock(Object mock); + + void clearMock(); } diff --git a/devtools/bom-descriptor-json/src/main/resources/catalog-overrides.json b/devtools/bom-descriptor-json/src/main/resources/catalog-overrides.json index 1c102e474cb8e..9bee169af979f 100644 --- a/devtools/bom-descriptor-json/src/main/resources/catalog-overrides.json +++ b/devtools/bom-descriptor-json/src/main/resources/catalog-overrides.json @@ -139,6 +139,7 @@ ], "metadata":{ "project": { + "default-codestart": "resteasy-reactive", "properties": { "doc-root": "https://quarkus.io", "rest-assured-version": "${rest-assured.version}", diff --git a/docs/src/main/asciidoc/getting-started-testing.adoc b/docs/src/main/asciidoc/getting-started-testing.adoc index 1528abf6eb1fa..0f532813fdb65 100644 --- a/docs/src/main/asciidoc/getting-started-testing.adoc +++ b/docs/src/main/asciidoc/getting-started-testing.adoc @@ -1317,6 +1317,9 @@ public class CustomResource implements QuarkusTestResourceLifecycleManager, DevS // apply the network to the container containerNetworkId.ifPresent(container::withNetworkMode); + // start container before retrieving its URL or other properties + container.start(); + String jdbcUrl = container.getJdbcUrl(); if (containerNetworkId.isPresent()) { // Replace hostname + port in the provided JDBC URL with the hostname of the Docker container diff --git a/docs/src/main/asciidoc/getting-started.adoc b/docs/src/main/asciidoc/getting-started.adoc index d898399df3c68..04f5aedb65439 100644 --- a/docs/src/main/asciidoc/getting-started.adoc +++ b/docs/src/main/asciidoc/getting-started.adoc @@ -113,7 +113,7 @@ In addition, you can see the `quarkus-maven-plugin` responsible of the packaging ${quarkus.platform.group-id} quarkus-maven-plugin - ${quarkus-plugin.version} + ${quarkus.platform.version} true diff --git a/docs/src/main/asciidoc/gradle-tooling.adoc b/docs/src/main/asciidoc/gradle-tooling.adoc index 5041db6903372..c77b76b9edc00 100644 --- a/docs/src/main/asciidoc/gradle-tooling.adoc +++ b/docs/src/main/asciidoc/gradle-tooling.adoc @@ -517,7 +517,7 @@ This task depends on both `check` and `quarkusBuild` tasks. The final artifact w `fast-jar` is now the default quarkus package type. The result of `./gradlew build` command is a new directory under `build` named `quarkus-app`. -You can run the application using: `java -jar target/quarkus-app/quarkus-run.jar`. +You can run the application using: `java -jar build/quarkus-app/quarkus-run.jar`. WARNING: In order to successfully run the produced jar, you need to have the entire contents of the `quarkus-app` directory. If any of the files are missing, the application will not start or might not function correctly. diff --git a/docs/src/main/asciidoc/includes/prerequisites.adoc b/docs/src/main/asciidoc/includes/prerequisites.adoc index d49cc9236379c..d739de2f92348 100644 --- a/docs/src/main/asciidoc/includes/prerequisites.adoc +++ b/docs/src/main/asciidoc/includes/prerequisites.adoc @@ -13,7 +13,7 @@ ifndef::prerequisites-no-maven[] * Apache Maven {maven-version} endif::[] ifdef::prerequisites-docker[] -* A working container runtime (Docker or Podman) +* A working container runtime (Docker or xref:podman.adoc[Podman]) endif::[] ifdef::prerequisites-docker-compose[] * Docker and Docker Compose or xref:podman.adoc[Podman], and Docker Compose diff --git a/docs/src/main/asciidoc/security.adoc b/docs/src/main/asciidoc/security.adoc index 8c898a82f42c5..caba8aecf3876 100644 --- a/docs/src/main/asciidoc/security.adoc +++ b/docs/src/main/asciidoc/security.adoc @@ -285,6 +285,19 @@ If you work with the link:https://jeremylong.github.io/DependencyCheck/dependenc You can add `OWASP Dependency Check Plugin` to your project's `pom.xml` like this: +[source,xml] +---- + + org.owasp + dependency-check-maven + ${owasp-dependency-check-plugin.version} + +---- + +where `owasp-dependency-check-plugin.version` should be set to `7.1.1` or later. + +You can configure the plugin like this: + [source,xml] ---- @@ -374,4 +387,3 @@ For example, it can look like this: Such a suppression list has to be carefully prepared and revisited from time to time. You should consider making individual suppressions time limited by adding an `until` tribute, for example: `...`. It will let you doublecheck that only the same known false positives are reported when the suppression period expires, and after reviewing the report you can set a new expiry date. -Note link:https://jeremylong.github.io/DependencyCheck/dependency-check-maven/[OWASP Dependency Check Plugin] `6.5.3` or later should be used with Quarkus. diff --git a/extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcServerProcessor.java b/extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcServerProcessor.java index 09513c8f375af..60584067dc23e 100644 --- a/extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcServerProcessor.java +++ b/extensions/grpc/deployment/src/main/java/io/quarkus/grpc/deployment/GrpcServerProcessor.java @@ -466,7 +466,7 @@ KubernetesPortBuildItem registerGrpcServiceInKubernetes(List { cmd.withEntrypoint("sh"); }); - withCommand("-c", "while [ ! -f " + STARTER_SCRIPT + " ]; do sleep 0.1; done; " + STARTER_SCRIPT); + withCommand("-c", "while [ ! -f " + STARTER_SCRIPT + " ]; do sleep 0.1; done; sleep 0.1; " + + STARTER_SCRIPT); waitingFor(Wait.forLogMessage(".*Started Kafka API server.*", 1)); } diff --git a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerRecorder.java b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerRecorder.java index 12047157ab20b..cb59aa64574b7 100644 --- a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerRecorder.java +++ b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/KeycloakPolicyEnforcerRecorder.java @@ -1,5 +1,6 @@ package io.quarkus.keycloak.pep.runtime; +import java.net.URI; import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -90,8 +91,11 @@ private static PolicyEnforcer createPolicyEnforcer(OidcTenantConfig oidcConfig, adapterConfig.setConnectionPoolSize(keycloakPolicyEnforcerConfig.connectionPoolSize); if (oidcConfig.proxy.host.isPresent()) { - adapterConfig.setProxyUrl(oidcConfig.proxy.host.get() + ":" - + oidcConfig.proxy.port); + String host = oidcConfig.proxy.host.get(); + if (!host.startsWith("http://") && !host.startsWith("https://")) { + host = URI.create(authServerUrl).getScheme() + "://" + host; + } + adapterConfig.setProxyUrl(host + ":" + oidcConfig.proxy.port); } PolicyEnforcerConfig enforcerConfig = getPolicyEnforcerConfig(keycloakPolicyEnforcerConfig, diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeProcessor.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeProcessor.java index 0d47692d9e713..6dfdf57540b47 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeProcessor.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/KnativeProcessor.java @@ -79,6 +79,7 @@ public class KnativeProcessor { private static final String KNATIVE_UTILIZATION_PERCENTAGE = "autoscaling.knative.dev/target-utilization-percentage"; private static final String KNATIVE_AUTOSCALING_TARGET = "autoscaling.knative.dev/target"; private static final String KNATIVE_CONTAINER_CONCURRENCY = "container-concurrency"; + private static final String KNATIVE_DEV_VISIBILITY = "networking.knative.dev/visibility"; @BuildStep public void checkKnative(ApplicationInfoBuildItem applicationInfo, KnativeConfig config, @@ -174,8 +175,12 @@ public List createDecorators(ApplicationInfoBuildItem applic }); if (config.clusterLocal) { - result.add(new DecoratorBuildItem(KNATIVE, - new AddLabelDecorator(name, "networking.knative.dev/visibility", "cluster-local"))); + if (labels.stream().filter(l -> KNATIVE.equals(l.getTarget())) + .noneMatch(l -> l.getKey().equals(KNATIVE_DEV_VISIBILITY))) { + result.add(new DecoratorBuildItem(KNATIVE, + new AddLabelDecorator(name, KNATIVE_DEV_VISIBILITY, "cluster-local"))); + } + } /** diff --git a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftProcessor.java b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftProcessor.java index 1062d9a749a57..b6dd77db59288 100644 --- a/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftProcessor.java +++ b/extensions/kubernetes/vanilla/deployment/src/main/java/io/quarkus/kubernetes/deployment/OpenshiftProcessor.java @@ -67,6 +67,7 @@ public class OpenshiftProcessor { private static final int OPENSHIFT_PRIORITY = DEFAULT_PRIORITY; private static final String OPENSHIFT_INTERNAL_REGISTRY = "image-registry.openshift-image-registry.svc:5000"; private static final String DOCKERIO_REGISTRY = "docker.io"; + private static final String OPENSHIFT_V3_APP = "app"; @BuildStep public void checkOpenshift(ApplicationInfoBuildItem applicationInfo, OpenshiftConfig config, @@ -197,7 +198,10 @@ public List createDecorators(ApplicationInfoBuildItem applic if (config.flavor == v3) { //Openshift 3.x doesn't recognize 'app.kubernetes.io/name', it uses 'app' instead. //The decorator will be applied even on non-openshift resources is it may affect for example: knative - result.add(new DecoratorBuildItem(new AddLabelDecorator(name, "app", name))); + if (labels.stream().filter(l -> OPENSHIFT.equals(l.getTarget())) + .noneMatch(l -> l.getKey().equals(OPENSHIFT_V3_APP))) { + result.add(new DecoratorBuildItem(new AddLabelDecorator(name, OPENSHIFT_V3_APP, name))); + } // The presence of optional is causing issues in OCP 3.11, so we better remove them. // The following 4 decorator will set the optional property to null, so that it won't make it into the file. @@ -247,7 +251,8 @@ public List createDecorators(ApplicationInfoBuildItem applic result.add(new DecoratorBuildItem(OPENSHIFT, new ApplyImagePullPolicyDecorator(name, config.getImagePullPolicy()))); - if (labels.stream().noneMatch(l -> l.getKey().equals(OPENSHIFT_APP_RUNTIME))) { + if (labels.stream().filter(l -> OPENSHIFT.equals(l.getTarget())) + .noneMatch(l -> l.getKey().equals(OPENSHIFT_APP_RUNTIME))) { result.add(new DecoratorBuildItem(OPENSHIFT, new AddLabelDecorator(name, OPENSHIFT_APP_RUNTIME, QUARKUS))); } diff --git a/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/mongodb/LiquibaseMongodbFactory.java b/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/mongodb/LiquibaseMongodbFactory.java index 63a314da7f823..caa81f0ff4750 100644 --- a/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/mongodb/LiquibaseMongodbFactory.java +++ b/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/mongodb/LiquibaseMongodbFactory.java @@ -39,12 +39,15 @@ public Liquibase createLiquibase() { "Config property 'quarkus.mongodb.database' must be defined when no database " + "exist in the connection string")); } + if (mongoClientConfig.credentials.authSource.isPresent()) { + connectionString += "?authSource=" + mongoClientConfig.credentials.authSource.get(); + } + Database database = DatabaseFactory.getInstance().openDatabase(connectionString, this.mongoClientConfig.credentials.username.orElse(null), this.mongoClientConfig.credentials.password.orElse(null), null, resourceAccessor); - ; if (database != null) { liquibaseMongodbConfig.liquibaseCatalogName.ifPresent(database::setLiquibaseCatalogName); liquibaseMongodbConfig.liquibaseSchemaName.ifPresent(database::setLiquibaseSchemaName); diff --git a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientImpl.java b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientImpl.java index 1641510fa8b99..bbb62b55b0c00 100644 --- a/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientImpl.java +++ b/extensions/oidc-client/runtime/src/main/java/io/quarkus/oidc/client/runtime/OidcClientImpl.java @@ -109,7 +109,7 @@ public Uni get() { body.add(OidcConstants.CLIENT_ASSERTION, jwt); } } else if (!OidcCommonUtils.isClientSecretPostAuthRequired(oidcConfig.credentials)) { - body.add(OidcConstants.CLIENT_ID, oidcConfig.clientId.get()); + body = copyMultiMap(body).set(OidcConstants.CLIENT_ID, oidcConfig.clientId.get()); } if (!additionalGrantParameters.isEmpty()) { body = copyMultiMap(body); @@ -122,7 +122,11 @@ public Uni get() { .onFailure(ConnectException.class) .retry() .atMost(oidcConfig.connectionRetryCount) - .onFailure().transform(t -> t.getCause()); + .onFailure().transform(t -> { + LOG.warn("OIDC Server is not available:", t.getCause() != null ? t.getCause() : t); + // don't wrap t to avoid information leak + return new OidcClientException("OIDC Server is not available"); + }); return response.onItem() .transform(resp -> emitGrantTokens(resp, refresh)); } diff --git a/extensions/oidc-common/runtime/pom.xml b/extensions/oidc-common/runtime/pom.xml index 0044d2e98f444..f33160893a635 100644 --- a/extensions/oidc-common/runtime/pom.xml +++ b/extensions/oidc-common/runtime/pom.xml @@ -42,6 +42,11 @@ io.quarkus quarkus-smallrye-jwt-build + + io.quarkus + quarkus-junit5-internal + test + diff --git a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonUtils.java b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonUtils.java index 5ad51f3bc1e1f..c5abaa1879145 100644 --- a/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonUtils.java +++ b/extensions/oidc-common/runtime/src/main/java/io/quarkus/oidc/common/runtime/OidcCommonUtils.java @@ -228,7 +228,14 @@ public static Optional toProxyOptions(OidcCommonConfig.Proxy proxy return Optional.empty(); } JsonObject jsonOptions = new JsonObject(); - jsonOptions.put("host", proxyConfig.host.get()); + // Vert.x Client currently does not expect a host having a scheme but keycloak-authorization expects scheme and host. + // Having a dedicated scheme property is probably better, but since it is property is not taken into account in Vertx Client + // it does not really make sense as it can send a misleading message that users can choose between `http` and `https`. + String host = URI.create(proxyConfig.host.get()).getHost(); + if (host == null) { + host = proxyConfig.host.get(); + } + jsonOptions.put("host", host); jsonOptions.put("port", proxyConfig.port); if (proxyConfig.username.isPresent()) { jsonOptions.put("username", proxyConfig.username.get()); diff --git a/extensions/oidc-common/runtime/src/test/java/io/quarkus/oidc/common/runtime/OidcCommonUtilsTest.java b/extensions/oidc-common/runtime/src/test/java/io/quarkus/oidc/common/runtime/OidcCommonUtilsTest.java new file mode 100644 index 0000000000000..c4ccd112ba24f --- /dev/null +++ b/extensions/oidc-common/runtime/src/test/java/io/quarkus/oidc/common/runtime/OidcCommonUtilsTest.java @@ -0,0 +1,45 @@ +package io.quarkus.oidc.common.runtime; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.net.URI; +import java.util.Optional; + +import org.junit.jupiter.api.Test; + +import io.vertx.core.net.ProxyOptions; + +public class OidcCommonUtilsTest { + + @Test + public void testProxyOptionsWithHostWithoutScheme() throws Exception { + OidcCommonConfig.Proxy config = new OidcCommonConfig.Proxy(); + config.host = Optional.of("localhost"); + config.port = 8080; + config.username = Optional.of("user"); + config.password = Optional.of("password"); + + ProxyOptions options = OidcCommonUtils.toProxyOptions(config).get(); + assertEquals("localhost", options.getHost()); + assertEquals(8080, options.getPort()); + assertEquals("user", options.getUsername()); + assertEquals("password", options.getPassword()); + } + + @Test + public void testProxyOptionsWithHostWithScheme() throws Exception { + OidcCommonConfig.Proxy config = new OidcCommonConfig.Proxy(); + config.host = Optional.of("http://localhost"); + config.port = 8080; + config.username = Optional.of("user"); + config.password = Optional.of("password"); + + assertEquals("http", URI.create(config.host.get()).getScheme()); + + ProxyOptions options = OidcCommonUtils.toProxyOptions(config).get(); + assertEquals("localhost", options.getHost()); + assertEquals(8080, options.getPort()); + assertEquals("user", options.getUsername()); + assertEquals("password", options.getPassword()); + } +} diff --git a/extensions/panache/hibernate-reactive-panache-common/runtime/pom.xml b/extensions/panache/hibernate-reactive-panache-common/runtime/pom.xml index e0ed9b77b4f6b..b600f48dc5044 100644 --- a/extensions/panache/hibernate-reactive-panache-common/runtime/pom.xml +++ b/extensions/panache/hibernate-reactive-panache-common/runtime/pom.xml @@ -10,7 +10,7 @@ quarkus-hibernate-reactive-panache-common Quarkus - Hibernate Reactive with Panache - Common - Runtime - Simplify your persistence code for Hibernate Reactive via the active record or the repository pattern + Common module to simplify your persistence code for Hibernate Reactive via the active record or the repository pattern io.quarkus diff --git a/extensions/panache/mongodb-panache-kotlin/runtime/pom.xml b/extensions/panache/mongodb-panache-kotlin/runtime/pom.xml index c99660041c998..962592a648616 100644 --- a/extensions/panache/mongodb-panache-kotlin/runtime/pom.xml +++ b/extensions/panache/mongodb-panache-kotlin/runtime/pom.xml @@ -11,7 +11,7 @@ quarkus-mongodb-panache-kotlin Quarkus - MongoDB with Panache - Kotlin Runtime - Simplify your persistence code for MongoDB via the active record or the repository pattern + Simplify your persistence code for MongoDB in Kotlin via the active record or the repository pattern io.quarkus diff --git a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientReactiveCDIWrapperBase.java b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientReactiveCDIWrapperBase.java index e436c85ce7164..7ebbb0f7edd71 100644 --- a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientReactiveCDIWrapperBase.java +++ b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/main/java/io/quarkus/rest/client/reactive/runtime/RestClientReactiveCDIWrapperBase.java @@ -47,7 +47,13 @@ public Object getDelegate() { return mock == null ? delegate : mock; } + @Override public void setMock(Object mock) { this.mock = mock; } + + @Override + public void clearMock() { + this.mock = null; + } } diff --git a/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/GraphQLTerminateContextTest.java b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/GraphQLTerminateContextTest.java new file mode 100644 index 0000000000000..ce6c0123bbdf7 --- /dev/null +++ b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/GraphQLTerminateContextTest.java @@ -0,0 +1,78 @@ +package io.quarkus.smallrye.graphql.deployment; + +import static io.quarkus.smallrye.graphql.deployment.AbstractGraphQLTest.MEDIATYPE_JSON; + +import javax.inject.Inject; + +import org.eclipse.microprofile.graphql.GraphQLApi; +import org.eclipse.microprofile.graphql.Query; +import org.hamcrest.Matchers; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; +import io.smallrye.mutiny.Uni; +import io.vertx.ext.web.RoutingContext; + +/** + * Testing that the context terminate + */ +public class GraphQLTerminateContextTest extends AbstractGraphQLTest { + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest() + .withApplicationRoot((JavaArchive jar) -> jar + .addClasses(TestTerminateContextResource.class) + .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml")); + + @Test + public void testWhoAmI() { + runTestWith("user1"); + runTestWith("user2"); + runTestWith("user2"); + } + + public void runTestWith(String expectedUser) { + String fooRequest = getPayload("{\n" + + " whoami {\n" + + " name\n" + + " }\n" + + "}"); + + RestAssured.given().when() + .accept(MEDIATYPE_JSON) + .contentType(MEDIATYPE_JSON) + .body(fooRequest) + .with().header("X-Test", expectedUser) + .post("/graphql") + .then() + .assertThat() + .statusCode(200) + .and() + .log().body().and() + .body("data.whoami.name", Matchers.equalTo(expectedUser)); + } + + @GraphQLApi + public static class TestTerminateContextResource { + + @Inject + RoutingContext ctx; + + @Query + public Uni whoami() { + return Uni.createFrom().item(() -> { + YouAre youAre = new YouAre(); + youAre.name = ctx.request().headers().get("X-Test"); + return youAre; + }); + } + } + + public static class YouAre { + public String name; + } +} diff --git a/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLAbstractHandler.java b/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLAbstractHandler.java index dd57cef207078..3a09077350f90 100644 --- a/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLAbstractHandler.java +++ b/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLAbstractHandler.java @@ -30,7 +30,7 @@ public abstract class SmallRyeGraphQLAbstractHandler implements Handler { + ctx.response() + .endHandler(currentManagedContextTerminationHandler) + .exceptionHandler(currentManagedContextTerminationHandler) + .closeHandler(currentManagedContextTerminationHandler); + + try { + handleWithIdentity(ctx); + } catch (Throwable t) { currentManagedContext.terminate(); - }); + } } } diff --git a/extensions/websockets/client/runtime/pom.xml b/extensions/websockets/client/runtime/pom.xml index 64c99ed63c6fb..588a1e96866b7 100644 --- a/extensions/websockets/client/runtime/pom.xml +++ b/extensions/websockets/client/runtime/pom.xml @@ -11,7 +11,7 @@ quarkus-websockets-client Quarkus - WebSockets Client - Runtime - WebSocket communication channel support + Client for WebSocket communication channel diff --git a/independent-projects/qute/core/src/main/java/io/quarkus/qute/Parser.java b/independent-projects/qute/core/src/main/java/io/quarkus/qute/Parser.java index f79bd1d8b1723..8343980499710 100644 --- a/independent-projects/qute/core/src/main/java/io/quarkus/qute/Parser.java +++ b/independent-projects/qute/core/src/main/java/io/quarkus/qute/Parser.java @@ -1,6 +1,7 @@ package io.quarkus.qute; import io.quarkus.qute.Expression.Part; +import io.quarkus.qute.SectionHelperFactory.BlockInfo; import io.quarkus.qute.SectionHelperFactory.ParametersInfo; import io.quarkus.qute.SectionHelperFactory.ParserDelegate; import io.quarkus.qute.TemplateNode.Origin; @@ -31,7 +32,7 @@ /** * Simple non-reusable parser. */ -class Parser implements Function, ParserHelper, ParserDelegate, WithOrigin, ErrorInitializer { +class Parser implements ParserHelper, ParserDelegate, WithOrigin, ErrorInitializer { private static final Logger LOGGER = Logger.getLogger(Parser.class); static final String ROOT_HELPER_NAME = "$root"; @@ -417,7 +418,7 @@ private void flushTag() { } else { // Expression sectionStack.peek().currentBlock() - .addNode(new ExpressionNode(apply(content), engine)); + .addNode(new ExpressionNode(createExpression(content), engine)); } this.buffer = new StringBuilder(); } @@ -449,7 +450,7 @@ private void sectionStart(String content, String tag) { // => New section block SectionBlock.Builder block = SectionBlock.builder("" + sectionBlockIdx++, this, this) - .setOrigin(origin(0)).setLabel(sectionName); + .setOrigin(origin(tag.length() - 1)).setLabel(sectionName); lastSection.addBlock(block); processParams(tag, sectionName, iter, block); @@ -468,7 +469,7 @@ private void sectionStart(String content, String tag) { .build(); } SectionNode.Builder sectionNode = SectionNode - .builder(sectionName, origin(0), this, this) + .builder(sectionName, origin(tag.length() - 1), this, this) .setEngine(engine) .setHelperFactory(factory); @@ -581,7 +582,8 @@ private void parameterDeclaration(String content, String tag) { String typeInfo = Expressions.typeInfoFrom(value); currentScope.putBinding(key, typeInfo); sectionStack.peek().currentBlock().addNode( - new ParameterDeclarationNode(typeInfo, key, defaultValue != null ? apply(defaultValue) : null, origin(0))); + new ParameterDeclarationNode(typeInfo, key, defaultValue != null ? createExpression(defaultValue) : null, + origin(0))); // If a default value is set we add a synthetic {#let} section to define local variables with default values if (defaultValue != null) { @@ -991,8 +993,11 @@ static boolean isRightBracket(char character) { return character == ')'; } - @Override - public ExpressionImpl apply(String value) { + ExpressionImpl createSectionBlockExpression(BlockInfo block, String value) { + return parseExpression(expressionIdGenerator::incrementAndGet, value, scopeStack.peek(), block.getOrigin()); + } + + ExpressionImpl createExpression(String value) { return parseExpression(expressionIdGenerator::incrementAndGet, value, scopeStack.peek(), origin(value.length() + 1)); } diff --git a/independent-projects/qute/core/src/main/java/io/quarkus/qute/SectionBlock.java b/independent-projects/qute/core/src/main/java/io/quarkus/qute/SectionBlock.java index e73e3875a8883..ccbfeeffc8224 100644 --- a/independent-projects/qute/core/src/main/java/io/quarkus/qute/SectionBlock.java +++ b/independent-projects/qute/core/src/main/java/io/quarkus/qute/SectionBlock.java @@ -8,7 +8,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Function; import java.util.function.Predicate; /** @@ -180,9 +179,9 @@ private void collapseGroup(List group, List finalNodes) } } - static SectionBlock.Builder builder(String id, Function expressionFunc, + static SectionBlock.Builder builder(String id, Parser parser, ErrorInitializer errorInitializer) { - return new Builder(id, expressionFunc, errorInitializer).setLabel(id); + return new Builder(id, parser, errorInitializer).setLabel(id); } static class Builder implements BlockInfo { @@ -193,14 +192,13 @@ static class Builder implements BlockInfo { private Map parameters; private final List nodes; private Map expressions; - private final Function expressionFun; + private final Parser parser; private final ErrorInitializer errorInitializer; - public Builder(String id, Function expressionFun, - ErrorInitializer errorInitializer) { + public Builder(String id, Parser parser, ErrorInitializer errorInitializer) { this.id = id; this.nodes = new ArrayList<>(); - this.expressionFun = expressionFun; + this.parser = parser; this.errorInitializer = errorInitializer; } @@ -229,7 +227,7 @@ SectionBlock.Builder addParameter(String name, String value) { @Override public Expression addExpression(String param, String value) { - Expression expression = expressionFun.apply(value); + Expression expression = parser.createSectionBlockExpression(this, value); if (expressions == null) { expressions = new LinkedHashMap<>(); } diff --git a/independent-projects/qute/core/src/main/java/io/quarkus/qute/SectionHelperFactory.java b/independent-projects/qute/core/src/main/java/io/quarkus/qute/SectionHelperFactory.java index 2af895f7dbcc3..78e902c149940 100644 --- a/independent-projects/qute/core/src/main/java/io/quarkus/qute/SectionHelperFactory.java +++ b/independent-projects/qute/core/src/main/java/io/quarkus/qute/SectionHelperFactory.java @@ -140,6 +140,8 @@ default boolean hasParameter(String name) { * Parse and register an expression for the specified parameter. *

* A registered expression contributes to the {@link Template#getExpressions()}, i.e. can be validated at build time. + *

+ * The origin of the returned expression is the origin of the containing block. * * @param param * @param value diff --git a/independent-projects/qute/core/src/main/java/io/quarkus/qute/SectionNode.java b/independent-projects/qute/core/src/main/java/io/quarkus/qute/SectionNode.java index 0467c432651b6..a00d786e357be 100644 --- a/independent-projects/qute/core/src/main/java/io/quarkus/qute/SectionNode.java +++ b/independent-projects/qute/core/src/main/java/io/quarkus/qute/SectionNode.java @@ -7,7 +7,6 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.CompletionStage; -import java.util.function.Function; import java.util.function.Predicate; import org.jboss.logging.Logger; @@ -18,9 +17,9 @@ class SectionNode implements TemplateNode { private static final Logger LOG = Logger.getLogger("io.quarkus.qute.nodeResolve"); - static Builder builder(String helperName, Origin origin, Function expressionFun, + static Builder builder(String helperName, Origin origin, Parser parser, ErrorInitializer errorFun) { - return new Builder(helperName, origin, expressionFun, errorFun); + return new Builder(helperName, origin, parser, errorFun); } final String name; @@ -110,15 +109,14 @@ static class Builder { private EngineImpl engine; private final ErrorInitializer errorInitializer; - Builder(String helperName, Origin origin, Function expressionFun, - ErrorInitializer errorInitializer) { + Builder(String helperName, Origin origin, Parser parser, ErrorInitializer errorInitializer) { this.helperName = helperName; this.origin = origin; this.blocks = new ArrayList<>(); this.errorInitializer = errorInitializer; // The main block is always present addBlock(SectionBlock - .builder(SectionHelperFactory.MAIN_BLOCK_NAME, expressionFun, errorInitializer) + .builder(SectionHelperFactory.MAIN_BLOCK_NAME, parser, errorInitializer) .setOrigin(origin)); } diff --git a/independent-projects/qute/core/src/test/java/io/quarkus/qute/IfSectionTest.java b/independent-projects/qute/core/src/test/java/io/quarkus/qute/IfSectionTest.java index 7d338ad397802..56964eb816fc6 100644 --- a/independent-projects/qute/core/src/test/java/io/quarkus/qute/IfSectionTest.java +++ b/independent-projects/qute/core/src/test/java/io/quarkus/qute/IfSectionTest.java @@ -240,6 +240,25 @@ public void testFromageCondition() { .data("user", "Stef", "target", new Target(ContentStatus.NEW)).render()); } + @Test + public void testParameterOrigin() { + Engine engine = Engine.builder().addDefaults().build(); + Template template = engine.parse(" {#if item.price > 1}{/if}"); + List expressions = template.getExpressions(); + assertEquals(2, expressions.size()); + for (Expression expression : expressions) { + if (expression.isLiteral()) { + assertEquals(1, expression.getLiteralValue().getNow(false)); + assertEquals(1, expression.getOrigin().getLine()); + assertEquals(3, expression.getOrigin().getLineCharacterStart()); + } else { + assertEquals("item.price", expression.toOriginalString()); + assertEquals(1, expression.getOrigin().getLine()); + assertEquals(3, expression.getOrigin().getLineCharacterStart()); + } + } + } + public static class Target { public ContentStatus status; diff --git a/independent-projects/qute/core/src/test/java/io/quarkus/qute/ParserTest.java b/independent-projects/qute/core/src/test/java/io/quarkus/qute/ParserTest.java index 31b793b7e32a9..48bb33815f9b0 100644 --- a/independent-projects/qute/core/src/test/java/io/quarkus/qute/ParserTest.java +++ b/independent-projects/qute/core/src/test/java/io/quarkus/qute/ParserTest.java @@ -157,7 +157,7 @@ public void testLines() { + "{/}"); Origin fooItemsOrigin = find(template.getExpressions(), "foo.items").getOrigin(); assertEquals(6, fooItemsOrigin.getLine()); - assertEquals(14, fooItemsOrigin.getLineCharacterStart()); + assertEquals(1, fooItemsOrigin.getLineCharacterStart()); assertEquals(24, fooItemsOrigin.getLineCharacterEnd()); Origin itemNameOrigin = find(template.getExpressions(), "item.name").getOrigin(); assertEquals(8, itemNameOrigin.getLine()); diff --git a/independent-projects/qute/core/src/test/java/io/quarkus/qute/SetSectionTest.java b/independent-projects/qute/core/src/test/java/io/quarkus/qute/SetSectionTest.java index 60364077f6c9a..25511136b6c7c 100644 --- a/independent-projects/qute/core/src/test/java/io/quarkus/qute/SetSectionTest.java +++ b/independent-projects/qute/core/src/test/java/io/quarkus/qute/SetSectionTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +import java.util.List; import org.junit.jupiter.api.Test; public class SetSectionTest { @@ -41,4 +42,23 @@ public void testDefaultValues() { .data("bar", "42", "baz", "no").render()); } + @Test + public void testParameterOrigin() { + Engine engine = Engine.builder().addDefaults().build(); + Template template = engine.parse(" {#let item=1 foo=bar}{/let}"); + List expressions = template.getExpressions(); + assertEquals(2, expressions.size()); + for (Expression expression : expressions) { + if (expression.isLiteral()) { + assertEquals(1, expression.getLiteralValue().getNow(false)); + assertEquals(1, expression.getOrigin().getLine()); + assertEquals(3, expression.getOrigin().getLineCharacterStart()); + } else { + assertEquals("bar", expression.toOriginalString()); + assertEquals(1, expression.getOrigin().getLine()); + assertEquals(3, expression.getOrigin().getLineCharacterStart()); + } + } + } + } diff --git a/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/ResteasyReactiveOutputStream.java b/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/ResteasyReactiveOutputStream.java index 824560d0f5d41..6efb3c817439b 100644 --- a/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/ResteasyReactiveOutputStream.java +++ b/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/ResteasyReactiveOutputStream.java @@ -236,22 +236,38 @@ private void prepareWrite(ByteBuf buffer, boolean finished) throws IOException { } else { context.serverResponse().setResponseHeader(HttpHeaderNames.CONTENT_LENGTH, "" + buffer.readableBytes()); } - } else if (!contentLengthSet()) { - request.response().setChunked(true); + } else { + var contentLengthSet = contentLengthSet(); + if (contentLengthSet == ContentLengthSetResult.NOT_SET) { + request.response().setChunked(true); + } else if (contentLengthSet == ContentLengthSetResult.IN_JAX_RS_HEADER) { + // we need to make sure the content-length header is copied to Vert.x headers + // otherwise we could run into a race condition: see https://github.com/quarkusio/quarkus/issues/26599 + Object contentLength = context.getResponse().get().getHeaders().getFirst(HttpHeaders.CONTENT_LENGTH); + context.serverResponse().setResponseHeader(HttpHeaderNames.CONTENT_LENGTH, contentLength.toString()); + } } } } - private boolean contentLengthSet() { + private ContentLengthSetResult contentLengthSet() { if (request.response().headers().contains(HttpHeaderNames.CONTENT_LENGTH)) { - return true; + return ContentLengthSetResult.IN_VERTX_HEADER; } LazyResponse lazyResponse = context.getResponse(); if (!lazyResponse.isCreated()) { - return false; + return ContentLengthSetResult.NOT_SET; } MultivaluedMap responseHeaders = lazyResponse.get().getHeaders(); - return (responseHeaders != null) && responseHeaders.containsKey(HttpHeaders.CONTENT_LENGTH); + return (responseHeaders != null) && responseHeaders.containsKey(HttpHeaders.CONTENT_LENGTH) + ? ContentLengthSetResult.IN_JAX_RS_HEADER + : ContentLengthSetResult.NOT_SET; + } + + private enum ContentLengthSetResult { + NOT_SET, + IN_VERTX_HEADER, + IN_JAX_RS_HEADER } /** diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/response/ContentLengthTest.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/response/ContentLengthTest.java index 2faf6d56d628e..3b2088f806a04 100644 --- a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/response/ContentLengthTest.java +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/response/ContentLengthTest.java @@ -20,7 +20,7 @@ public class ContentLengthTest { - private static final int NUMBER_OF_COPIES = 500; + private static final int NUMBER_OF_COPIES = 1000; @RegisterExtension static ResteasyReactiveUnitTest runner = new ResteasyReactiveUnitTest() diff --git a/integration-tests/hibernate-reactive-postgresql/src/main/java/io/quarkus/it/hibernate/reactive/postgresql/HibernateReactiveTestEndpointFetchLazy.java b/integration-tests/hibernate-reactive-postgresql/src/main/java/io/quarkus/it/hibernate/reactive/postgresql/HibernateReactiveTestEndpointFetchLazy.java index ff1b103592e0a..c0dcda200b2f5 100644 --- a/integration-tests/hibernate-reactive-postgresql/src/main/java/io/quarkus/it/hibernate/reactive/postgresql/HibernateReactiveTestEndpointFetchLazy.java +++ b/integration-tests/hibernate-reactive-postgresql/src/main/java/io/quarkus/it/hibernate/reactive/postgresql/HibernateReactiveTestEndpointFetchLazy.java @@ -34,6 +34,14 @@ public Uni> findBooksWithMutiny(@PathParam("authorId") Integer .chain(author -> Mutiny.fetch(author.getBooks())); } + @GET + @Path("/getReferenceBooksWithMutiny/{authorId}") + public Uni> getReferenceBooksWithMutiny(@PathParam("authorId") Integer authorId) { + return mutinySession + .fetch(mutinySession.getReference(Author.class, authorId)) + .chain(author -> Mutiny.fetch(author.getBooks())); + } + @POST @Path("/prepareDb") public Uni prepareDb() { diff --git a/integration-tests/hibernate-reactive-postgresql/src/test/java/io/quarkus/it/hibernate/reactive/postgresql/HibernateReactiveFetchLazyTest.java b/integration-tests/hibernate-reactive-postgresql/src/test/java/io/quarkus/it/hibernate/reactive/postgresql/HibernateReactiveFetchLazyTest.java index 1cb288ff8602f..f2ee76ed68657 100644 --- a/integration-tests/hibernate-reactive-postgresql/src/test/java/io/quarkus/it/hibernate/reactive/postgresql/HibernateReactiveFetchLazyTest.java +++ b/integration-tests/hibernate-reactive-postgresql/src/test/java/io/quarkus/it/hibernate/reactive/postgresql/HibernateReactiveFetchLazyTest.java @@ -18,6 +18,20 @@ @TestHTTPEndpoint(HibernateReactiveTestEndpointFetchLazy.class) public class HibernateReactiveFetchLazyTest { + @Test + public void fetchAfterGetReferenceWithMutiny() { + RestAssured.when() + .post("/prepareDb") + .then() + .body(is("Neal Stephenson")); + + Response response = RestAssured.when() + .get("/getReferenceBooksWithMutiny/567") + .then() + .extract().response(); + assertTitles(response, "Cryptonomicon", "Snow Crash"); + } + @Test public void fetchAfterFindWithMutiny() { RestAssured.when() diff --git a/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/openshift-v3.properties b/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/openshift-v3.properties index 22bc2bebdb904..dbeb5652f7191 100644 --- a/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/openshift-v3.properties +++ b/integration-tests/kubernetes/quarkus-standard-way/src/test/resources/openshift-v3.properties @@ -1,2 +1,4 @@ quarkus.kubernetes.deployment-target=openshift -quarkus.openshift.flavor=v3 \ No newline at end of file +quarkus.openshift.flavor=v3 +# used in order to ensure that the non-openshift label does not interfere with the openshift manifest generation +quarkus.knative.labels."app.openshift.io/runtime"=test diff --git a/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestCallbacksTestCase.java b/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestCallbacksTestCase.java index 80983c0f53bc0..670422528a9e8 100644 --- a/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestCallbacksTestCase.java +++ b/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestCallbacksTestCase.java @@ -23,7 +23,7 @@ import io.quarkus.test.junit.QuarkusTest; /** - * The purpose of this test is simply to ensure that {@link SimpleAnnotationCheckerBeforeEachCallback} + * The purpose of this test is simply to ensure that {@link TestContextCheckerBeforeEachCallback} * can read {@code @TestAnnotation} without issue. * Also checks that {@link SimpleAnnotationCheckerBeforeClassCallback} is executed properly */ @@ -69,7 +69,7 @@ private void checkBeforeOrAfterEachTestInfo(TestInfo testInfo, String unexpected @TestAnnotation @Order(1) public void testTestMethodHasAnnotation() { - assertTrue(SimpleAnnotationCheckerBeforeEachCallback.testAnnotationChecked); + assertTrue(TestContextCheckerBeforeEachCallback.testAnnotationChecked); } @Test diff --git a/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestNestedTestCase.java b/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestNestedTestCase.java index 57ab941d663c6..479600280ca33 100644 --- a/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestNestedTestCase.java +++ b/integration-tests/main/src/test/java/io/quarkus/it/main/QuarkusTestNestedTestCase.java @@ -59,6 +59,7 @@ void test() { assertEquals(0, COUNT_TEST.getAndIncrement(), "COUNT_TEST"); assertEquals(0, COUNT_AFTER_EACH.get(), "COUNT_AFTER_EACH"); assertEquals(0, COUNT_AFTER_ALL.get(), "COUNT_AFTER_ALL"); + assertEquals(0, TestContextCheckerBeforeEachCallback.OUTER_INSTANCES.size(), "Found unexpected outer instances"); } @Nested @@ -93,6 +94,18 @@ void testTwo() { assertEquals(0, COUNT_AFTER_ALL.get(), "COUNT_AFTER_ALL"); } + @Test + @Order(3) + void testOuterInstancesInBeforeEach() { + assertEquals(1, TestContextCheckerBeforeEachCallback.OUTER_INSTANCES.size()); + } + + @Test + @Order(4) + void testOuterInstancesInAfterEach() { + assertEquals(1, TestContextCheckerAfterEachCallback.OUTER_INSTANCES.size()); + } + @Test void testInnerAndOuterValues() { assertEquals(EXPECTED_INNER_VALUE, innerValue); @@ -122,7 +135,6 @@ void beforeEach() { @Test @Order(1) void testOne() { - // assertEquals(1, SECOND_LEVEL_COUNTER.get(), "SECOND_LEVEL_COUNTER"); assertEquals(1, SECOND_LEVEL_COUNTER.get(), "SECOND_LEVEL_COUNTER"); } @@ -134,6 +146,24 @@ void testSecondLevelAndInnerAndOuterValues() { assertEquals(EXPECTED_OUTER_VALUE, outerValue); assertEquals(EXPECTED_SECOND_LEVEL_FIRST_INNER_VALUE, secondLevelInnerValue); } + + @Test + @Order(3) + void testOuterInstancesInBeforeEach() { + assertEquals(2, TestContextCheckerBeforeEachCallback.OUTER_INSTANCES.size()); + } + + @Test + @Order(4) + void testOuterInstancesInAfterEach() { + assertEquals(2, TestContextCheckerAfterEachCallback.OUTER_INSTANCES.size()); + } + + @Test + @Order(5) + void testOuterInstancesInAfterAll() { + assertEquals(1, TestContextCheckerAfterAllCallback.OUTER_INSTANCES.size()); + } } } @@ -185,9 +215,9 @@ void afterEach() { @AfterAll static void afterAll() { assertEquals(1, COUNT_BEFORE_ALL.get(), "COUNT_BEFORE_ALL"); - assertEquals(15, COUNT_BEFORE_EACH.get(), "COUNT_BEFORE_EACH"); + assertEquals(25, COUNT_BEFORE_EACH.get(), "COUNT_BEFORE_EACH"); assertEquals(4, COUNT_TEST.get(), "COUNT_TEST"); - assertEquals(15, COUNT_AFTER_EACH.get(), "COUNT_AFTER_EACH"); + assertEquals(25, COUNT_AFTER_EACH.get(), "COUNT_AFTER_EACH"); assertEquals(0, COUNT_AFTER_ALL.getAndIncrement(), "COUNT_AFTER_ALL"); } } diff --git a/integration-tests/main/src/test/java/io/quarkus/it/main/TestContextCheckerAfterAllCallback.java b/integration-tests/main/src/test/java/io/quarkus/it/main/TestContextCheckerAfterAllCallback.java new file mode 100644 index 0000000000000..ba8af179ebfc2 --- /dev/null +++ b/integration-tests/main/src/test/java/io/quarkus/it/main/TestContextCheckerAfterAllCallback.java @@ -0,0 +1,18 @@ +package io.quarkus.it.main; + +import java.util.ArrayList; +import java.util.List; + +import io.quarkus.test.junit.callback.QuarkusTestAfterAllCallback; +import io.quarkus.test.junit.callback.QuarkusTestContext; + +public class TestContextCheckerAfterAllCallback implements QuarkusTestAfterAllCallback { + + public static final List OUTER_INSTANCES = new ArrayList<>(); + + @Override + public void afterAll(QuarkusTestContext context) { + OUTER_INSTANCES.clear(); + OUTER_INSTANCES.addAll(context.getOuterInstances()); + } +} diff --git a/integration-tests/main/src/test/java/io/quarkus/it/main/TestContextCheckerAfterEachCallback.java b/integration-tests/main/src/test/java/io/quarkus/it/main/TestContextCheckerAfterEachCallback.java new file mode 100644 index 0000000000000..b28d1511d49ff --- /dev/null +++ b/integration-tests/main/src/test/java/io/quarkus/it/main/TestContextCheckerAfterEachCallback.java @@ -0,0 +1,18 @@ +package io.quarkus.it.main; + +import java.util.ArrayList; +import java.util.List; + +import io.quarkus.test.junit.callback.QuarkusTestAfterEachCallback; +import io.quarkus.test.junit.callback.QuarkusTestMethodContext; + +public class TestContextCheckerAfterEachCallback implements QuarkusTestAfterEachCallback { + + public static final List OUTER_INSTANCES = new ArrayList<>(); + + @Override + public void afterEach(QuarkusTestMethodContext context) { + OUTER_INSTANCES.clear(); + OUTER_INSTANCES.addAll(context.getOuterInstances()); + } +} diff --git a/integration-tests/main/src/test/java/io/quarkus/it/main/SimpleAnnotationCheckerBeforeEachCallback.java b/integration-tests/main/src/test/java/io/quarkus/it/main/TestContextCheckerBeforeEachCallback.java similarity index 72% rename from integration-tests/main/src/test/java/io/quarkus/it/main/SimpleAnnotationCheckerBeforeEachCallback.java rename to integration-tests/main/src/test/java/io/quarkus/it/main/TestContextCheckerBeforeEachCallback.java index 8e9447609046b..6ec5d0c7c8281 100644 --- a/integration-tests/main/src/test/java/io/quarkus/it/main/SimpleAnnotationCheckerBeforeEachCallback.java +++ b/integration-tests/main/src/test/java/io/quarkus/it/main/TestContextCheckerBeforeEachCallback.java @@ -1,17 +1,23 @@ package io.quarkus.it.main; import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; import io.quarkus.test.junit.callback.QuarkusTestBeforeEachCallback; import io.quarkus.test.junit.callback.QuarkusTestMethodContext; -public class SimpleAnnotationCheckerBeforeEachCallback implements QuarkusTestBeforeEachCallback { +public class TestContextCheckerBeforeEachCallback implements QuarkusTestBeforeEachCallback { + public static final List OUTER_INSTANCES = new ArrayList<>(); static boolean testAnnotationChecked; @Override public void beforeEach(QuarkusTestMethodContext context) { - // make sure that this comes into play only for the test we care about + OUTER_INSTANCES.clear(); + OUTER_INSTANCES.addAll(context.getOuterInstances()); + + // continue only if this comes into play only for the test we care about Method testMethod = context.getTestMethod(); if (!testMethod.getDeclaringClass().getName().endsWith("QuarkusTestCallbacksTestCase")) { diff --git a/integration-tests/main/src/test/resources/META-INF/services/io.quarkus.test.junit.callback.QuarkusTestAfterAllCallback b/integration-tests/main/src/test/resources/META-INF/services/io.quarkus.test.junit.callback.QuarkusTestAfterAllCallback new file mode 100644 index 0000000000000..04c5aa035d0ff --- /dev/null +++ b/integration-tests/main/src/test/resources/META-INF/services/io.quarkus.test.junit.callback.QuarkusTestAfterAllCallback @@ -0,0 +1 @@ +io.quarkus.it.main.TestContextCheckerAfterAllCallback diff --git a/integration-tests/main/src/test/resources/META-INF/services/io.quarkus.test.junit.callback.QuarkusTestAfterEachCallback b/integration-tests/main/src/test/resources/META-INF/services/io.quarkus.test.junit.callback.QuarkusTestAfterEachCallback new file mode 100644 index 0000000000000..c9a184678b5c7 --- /dev/null +++ b/integration-tests/main/src/test/resources/META-INF/services/io.quarkus.test.junit.callback.QuarkusTestAfterEachCallback @@ -0,0 +1 @@ +io.quarkus.it.main.TestContextCheckerAfterEachCallback diff --git a/integration-tests/main/src/test/resources/META-INF/services/io.quarkus.test.junit.callback.QuarkusTestBeforeEachCallback b/integration-tests/main/src/test/resources/META-INF/services/io.quarkus.test.junit.callback.QuarkusTestBeforeEachCallback index 18e484433990e..d0f4906056035 100644 --- a/integration-tests/main/src/test/resources/META-INF/services/io.quarkus.test.junit.callback.QuarkusTestBeforeEachCallback +++ b/integration-tests/main/src/test/resources/META-INF/services/io.quarkus.test.junit.callback.QuarkusTestBeforeEachCallback @@ -1 +1 @@ -io.quarkus.it.main.SimpleAnnotationCheckerBeforeEachCallback +io.quarkus.it.main.TestContextCheckerBeforeEachCallback diff --git a/integration-tests/oidc-client-wiremock/src/main/java/io/quarkus/it/keycloak/FrontendResource.java b/integration-tests/oidc-client-wiremock/src/main/java/io/quarkus/it/keycloak/FrontendResource.java index 2c6bacdc96380..a6f9062049194 100644 --- a/integration-tests/oidc-client-wiremock/src/main/java/io/quarkus/it/keycloak/FrontendResource.java +++ b/integration-tests/oidc-client-wiremock/src/main/java/io/quarkus/it/keycloak/FrontendResource.java @@ -63,4 +63,11 @@ public Uni echoRefreshTokenOnly(@QueryParam("refreshToken") String refre return clients.getClient("refresh").refreshTokens(refreshToken) .onItem().transform(t -> t.getAccessToken()); } + + @GET + @Path("password-grant-public-client") + @Produces("text/plain") + public Uni passwordGrantPublicClient() { + return clients.getClient("password-grant-public-client").getTokens().onItem().transform(t -> t.getAccessToken()); + } } diff --git a/integration-tests/oidc-client-wiremock/src/main/resources/application.properties b/integration-tests/oidc-client-wiremock/src/main/resources/application.properties index 8993878e809e6..d8dad093782a0 100644 --- a/integration-tests/oidc-client-wiremock/src/main/resources/application.properties +++ b/integration-tests/oidc-client-wiremock/src/main/resources/application.properties @@ -7,6 +7,12 @@ quarkus.oidc-client.grant.type=password quarkus.oidc-client.grant-options.password.username=alice quarkus.oidc-client.grant-options.password.password=alice +quarkus.oidc-client.password-grant-public-client.token-path=${keycloak.url}/tokens_public_client +quarkus.oidc-client.password-grant-public-client.client-id=quarkus-app +quarkus.oidc-client.password-grant-public-client.grant.type=password +quarkus.oidc-client.password-grant-public-client.grant-options.password.username=alice +quarkus.oidc-client.password-grant-public-client.grant-options.password.password=alice + quarkus.oidc-client.non-standard-response.token-path=${keycloak.url}/non-standard-tokens quarkus.oidc-client.non-standard-response.client-id=quarkus-app quarkus.oidc-client.non-standard-response.credentials.secret=secret diff --git a/integration-tests/oidc-client-wiremock/src/test/java/io/quarkus/it/keycloak/KeycloakRealmResourceManager.java b/integration-tests/oidc-client-wiremock/src/test/java/io/quarkus/it/keycloak/KeycloakRealmResourceManager.java index ea8b059a72201..fceb9735d12f4 100644 --- a/integration-tests/oidc-client-wiremock/src/test/java/io/quarkus/it/keycloak/KeycloakRealmResourceManager.java +++ b/integration-tests/oidc-client-wiremock/src/test/java/io/quarkus/it/keycloak/KeycloakRealmResourceManager.java @@ -35,6 +35,13 @@ public Map start() { .withHeader("Content-Type", MediaType.APPLICATION_JSON) .withBody( "{\"access_token\":\"access_token_1\", \"expires_in\":4, \"refresh_token\":\"refresh_token_1\"}"))); + server.stubFor(WireMock.post("/tokens_public_client") + .withRequestBody(matching("grant_type=password&username=alice&password=alice&client_id=quarkus-app")) + .willReturn(WireMock + .aResponse() + .withHeader("Content-Type", MediaType.APPLICATION_JSON) + .withBody( + "{\"access_token\":\"access_token_public_client\", \"expires_in\":20}"))); server.stubFor(WireMock.post("/non-standard-tokens") .withHeader("X-Custom", matching("XCustomHeaderValue")) .withRequestBody(matching("grant_type=password&username=alice&password=alice&extra_param=extra_param_value")) diff --git a/integration-tests/oidc-client-wiremock/src/test/java/io/quarkus/it/keycloak/OidcClientTest.java b/integration-tests/oidc-client-wiremock/src/test/java/io/quarkus/it/keycloak/OidcClientTest.java index 97c0c709a122b..17b490364fb0a 100644 --- a/integration-tests/oidc-client-wiremock/src/test/java/io/quarkus/it/keycloak/OidcClientTest.java +++ b/integration-tests/oidc-client-wiremock/src/test/java/io/quarkus/it/keycloak/OidcClientTest.java @@ -76,6 +76,18 @@ public Boolean call() throws Exception { }); } + @Test + public void testEchoTokensPasswordGrantPublicClient() { + RestAssured.when().get("/frontend/password-grant-public-client") + .then() + .statusCode(200) + .body(equalTo("access_token_public_client")); + RestAssured.when().get("/frontend/password-grant-public-client") + .then() + .statusCode(200) + .body(equalTo("access_token_public_client")); + } + @Test public void testEchoTokensNonStandardResponse() { RestAssured.when().get("/frontend/echoTokenNonStandardResponse") diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index b0dd7e8e269b9..f89de80a68b31 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -71,6 +71,17 @@ ${quarkus.build.skip} + + org.apache.maven.plugins + maven-surefire-plugin + + + + org.junit.jupiter.api.ClassOrderer$OrderAnnotation + + + + diff --git a/integration-tests/rest-client-reactive/src/test/java/io/quarkus/it/rest/client/InjectMockTest.java b/integration-tests/rest-client-reactive/src/test/java/io/quarkus/it/rest/client/InjectMockTest.java index c8c6a02b7ea43..d82a201b50d41 100644 --- a/integration-tests/rest-client-reactive/src/test/java/io/quarkus/it/rest/client/InjectMockTest.java +++ b/integration-tests/rest-client-reactive/src/test/java/io/quarkus/it/rest/client/InjectMockTest.java @@ -5,6 +5,7 @@ import org.hamcrest.Matchers; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import io.quarkus.it.rest.client.main.ClientWithExceptionMapper; @@ -14,6 +15,7 @@ import io.restassured.RestAssured; @QuarkusTest +@Order(100) // used in order to make sure this is run before ShouldNotUseMockTest public class InjectMockTest { @InjectMock diff --git a/integration-tests/rest-client-reactive/src/test/java/io/quarkus/it/rest/client/ShouldNotUseMockTest.java b/integration-tests/rest-client-reactive/src/test/java/io/quarkus/it/rest/client/ShouldNotUseMockTest.java new file mode 100644 index 0000000000000..dbdb6bf581491 --- /dev/null +++ b/integration-tests/rest-client-reactive/src/test/java/io/quarkus/it/rest/client/ShouldNotUseMockTest.java @@ -0,0 +1,19 @@ +package io.quarkus.it.rest.client; + +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.RestAssured; + +@QuarkusTest +@Order(200) // used in order to make sure this is run after InjectMockTest +public class ShouldNotUseMockTest { + + @Test + void shouldMockClientInTheApp() { + RestAssured.with().post("/call-cdi-client-with-exception-mapper") + .then() + .statusCode(200); + } +} diff --git a/test-framework/common/src/main/java/io/quarkus/test/common/QuarkusTestResourceLifecycleManager.java b/test-framework/common/src/main/java/io/quarkus/test/common/QuarkusTestResourceLifecycleManager.java index 9e0e521caa298..ce5a6a8332810 100644 --- a/test-framework/common/src/main/java/io/quarkus/test/common/QuarkusTestResourceLifecycleManager.java +++ b/test-framework/common/src/main/java/io/quarkus/test/common/QuarkusTestResourceLifecycleManager.java @@ -28,7 +28,7 @@ public interface QuarkusTestResourceLifecycleManager { /** * Start the test resource. * - * @return A map of system properties that should be set for the running test + * @return A map of configuration properties that will be set for the running test */ Map start(); diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/MockSupport.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/MockSupport.java index 1ced5c1988d5e..c730281b34366 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/MockSupport.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/MockSupport.java @@ -24,11 +24,14 @@ static void popContext() { List val = contexts.pop(); for (Object i : val) { try { - i.getClass().getDeclaredMethod("arc$clearMock").invoke(i); - - // Enable all observers declared on the mocked bean - mockObservers(i, false); + if (i instanceof MockedThroughWrapper) { + ((MockedThroughWrapper) i).clearMock(); + } else { + i.getClass().getDeclaredMethod("arc$clearMock").invoke(i); + // Enable all observers declared on the mocked bean + mockObservers(i, false); + } } catch (Exception e) { throw new RuntimeException(e); } @@ -44,6 +47,7 @@ static void installMock(T instance, T mock) { try { if (instance instanceof MockedThroughWrapper) { ((MockedThroughWrapper) instance).setMock(mock); + inst.add(instance); } else { Method setMethod = instance.getClass().getDeclaredMethod("arc$setMock", Object.class); 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 62270cec1b3e9..ab85c6f7ce453 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 @@ -776,6 +776,7 @@ private void initTestState(ExtensionContext extensionContext, ExtensionState sta outerInstances.add(outerInstance); } } else { + outerInstances.clear(); actualTestInstance = runningQuarkusApplication.instance(actualTestClass); }