diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 52908a781e9bc..d92c30dcf28b1 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -5236,6 +5236,7 @@ + mysql mysql-connector-java ${mysql-jdbc.version} @@ -5246,6 +5247,17 @@ + + com.mysql + mysql-connector-j + ${mysql-jdbc.version} + + + com.google.protobuf + protobuf-java + + + org.apache.derby derbyclient diff --git a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheResultInterceptor.java b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheResultInterceptor.java index dadc40e07d033..e6e1397b53dd4 100644 --- a/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheResultInterceptor.java +++ b/extensions/cache/runtime/src/main/java/io/quarkus/cache/runtime/CacheResultInterceptor.java @@ -1,6 +1,7 @@ package io.quarkus.cache.runtime; import java.time.Duration; +import java.util.concurrent.Executor; import java.util.function.Function; import java.util.function.Supplier; @@ -16,6 +17,10 @@ import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.TimeoutException; import io.smallrye.mutiny.Uni; +import io.vertx.core.Context; +import io.vertx.core.Handler; +import io.vertx.core.Vertx; +import io.vertx.core.impl.ContextInternal; @CacheResult(cacheName = "") // The `cacheName` attribute is @Nonbinding. @Interceptor @@ -53,6 +58,7 @@ public Object intercept(InvocationContext invocationContext) throws Throwable { try { ReturnType returnType = determineReturnType(invocationContext.getMethod().getReturnType()); if (returnType != ReturnType.NonAsync) { + Context context = Vertx.currentContext(); Uni cacheValue = cache.getAsync(key, new Function>() { @SuppressWarnings("unchecked") @Override @@ -65,11 +71,54 @@ public Uni apply(Object key) { throw new CacheException(e); } } + }).emitOn(new Executor() { + // We need make sure we go back to the original context when the cache value is computed. + // Otherwise, we would always emit on the context having computed the value, which could + // break the duplicated context isolation. + @Override + public void execute(Runnable command) { + Context ctx = Vertx.currentContext(); + if (context == null) { + // We didn't capture a context + if (ctx == null) { + // We are not on a context => we can execute immediately. + command.run(); + } else { + // We are on a context. + // We cannot continue on the current context as we may share a duplicated context. + // We need a new one. Note that duplicate() does not duplicate the duplicated context, + // but the root context. + ((ContextInternal) ctx).duplicate() + .runOnContext(new Handler() { + @Override + public void handle(Void ignored) { + command.run(); + } + }); + } + } else { + // We captured a context. + if (ctx == context) { + // We are on the same context => we can execute immediately + command.run(); + } else { + // 1) We are not on a context (ctx == null) => we need to switch to the captured context. + // 2) We are on a different context (ctx != null) => we need to switch to the captured context. + context.runOnContext(new Handler() { + @Override + public void handle(Void ignored) { + command.run(); + } + }); + } + } + } }); if (binding.lockTimeout() <= 0) { return createAsyncResult(cacheValue, returnType); } + // IMPORTANT: The item/failure are emitted on the captured context. cacheValue = cacheValue.ifNoItem().after(Duration.ofMillis(binding.lockTimeout())) .recoverWithUni(new Supplier>() { @Override diff --git a/extensions/devservices/mysql/pom.xml b/extensions/devservices/mysql/pom.xml index 7aa75b4ff35ae..28b73be41d864 100644 --- a/extensions/devservices/mysql/pom.xml +++ b/extensions/devservices/mysql/pom.xml @@ -41,8 +41,8 @@ test - mysql - mysql-connector-java + com.mysql + mysql-connector-j diff --git a/extensions/jdbc/jdbc-mysql/runtime/pom.xml b/extensions/jdbc/jdbc-mysql/runtime/pom.xml index dae4cc9ab9800..524987f3ac027 100644 --- a/extensions/jdbc/jdbc-mysql/runtime/pom.xml +++ b/extensions/jdbc/jdbc-mysql/runtime/pom.xml @@ -23,8 +23,8 @@ true - mysql - mysql-connector-java + com.mysql + mysql-connector-j org.graalvm.sdk @@ -45,7 +45,7 @@ quarkus-extension-maven-plugin - mysql:mysql-connector-java + com.mysql:mysql-connector-j diff --git a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/test/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilderTest.java b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/test/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilderTest.java index 36556d81add90..16b566efefbe1 100644 --- a/extensions/resteasy-reactive/rest-client-reactive/runtime/src/test/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilderTest.java +++ b/extensions/resteasy-reactive/rest-client-reactive/runtime/src/test/java/io/quarkus/rest/client/reactive/runtime/RestClientCDIDelegateBuilderTest.java @@ -1,9 +1,10 @@ package io.quarkus.rest.client.reactive.runtime; -import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; @@ -21,10 +22,10 @@ import org.eclipse.microprofile.rest.client.ext.QueryParamStyle; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; import org.jboss.resteasy.reactive.client.api.QuarkusRestClientProperties; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; import org.mockito.Mockito; import io.netty.handler.codec.http.multipart.HttpPostRequestEncoder; @@ -38,26 +39,47 @@ public class RestClientCDIDelegateBuilderTest { private static final String TRUSTSTORE_PASSWORD = "truststorePassword"; private static final String KEYSTORE_PASSWORD = "keystorePassword"; - @TempDir - static File tempDir; - private static File truststoreFile; - private static File keystoreFile; + private static Path truststorePath; + private static Path keystorePath; private static Config createdConfig; @BeforeAll public static void beforeAll() throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException { // prepare keystore and truststore - truststoreFile = new File(tempDir, "truststore.jks"); - keystoreFile = new File(tempDir, "keystore.jks"); + truststorePath = Files.createTempFile("truststore", ".jks"); - KeyStore truststore = KeyStore.getInstance("JKS"); - truststore.load(null, TRUSTSTORE_PASSWORD.toCharArray()); - truststore.store(new FileOutputStream(truststoreFile), TRUSTSTORE_PASSWORD.toCharArray()); + try (OutputStream truststoreOs = Files.newOutputStream(truststorePath)) { + KeyStore truststore = KeyStore.getInstance("JKS"); + truststore.load(null, TRUSTSTORE_PASSWORD.toCharArray()); + truststore.store(truststoreOs, TRUSTSTORE_PASSWORD.toCharArray()); + } + + keystorePath = Files.createTempFile("keystore", ".jks"); - KeyStore keystore = KeyStore.getInstance("JKS"); - keystore.load(null, KEYSTORE_PASSWORD.toCharArray()); - keystore.store(new FileOutputStream(keystoreFile), KEYSTORE_PASSWORD.toCharArray()); + try (OutputStream keystoreOs = Files.newOutputStream(keystorePath)) { + KeyStore keystore = KeyStore.getInstance("JKS"); + keystore.load(null, KEYSTORE_PASSWORD.toCharArray()); + keystore.store(keystoreOs, KEYSTORE_PASSWORD.toCharArray()); + } + } + + @AfterAll + public static void afterAll() { + if (truststorePath != null) { + try { + Files.deleteIfExists(truststorePath); + } catch (IOException e) { + // ignore it + } + } + if (keystorePath != null) { + try { + Files.deleteIfExists(keystorePath); + } catch (IOException e) { + // ignore it + } + } } @AfterEach @@ -183,10 +205,10 @@ private static RestClientsConfig createSampleConfigRoot() { .of("io.quarkus.rest.client.reactive.runtime.RestClientCDIDelegateBuilderTest$MyResponseFilter2"); configRoot.queryParamStyle = Optional.of(QueryParamStyle.MULTI_PAIRS); - configRoot.trustStore = Optional.of(truststoreFile.getAbsolutePath()); + configRoot.trustStore = Optional.of(truststorePath.toAbsolutePath().toString()); configRoot.trustStorePassword = Optional.of("truststorePassword"); configRoot.trustStoreType = Optional.of("JKS"); - configRoot.keyStore = Optional.of(keystoreFile.getAbsolutePath()); + configRoot.keyStore = Optional.of(keystorePath.toAbsolutePath().toString()); configRoot.keyStorePassword = Optional.of("keystorePassword"); configRoot.keyStoreType = Optional.of("JKS"); @@ -222,10 +244,10 @@ private static RestClientConfig createSampleClientConfig() { .of("io.quarkus.rest.client.reactive.runtime.RestClientCDIDelegateBuilderTest$MyResponseFilter1"); clientConfig.queryParamStyle = Optional.of(QueryParamStyle.COMMA_SEPARATED); - clientConfig.trustStore = Optional.of(truststoreFile.getAbsolutePath()); + clientConfig.trustStore = Optional.of(truststorePath.toAbsolutePath().toString()); clientConfig.trustStorePassword = Optional.of("truststorePassword"); clientConfig.trustStoreType = Optional.of("JKS"); - clientConfig.keyStore = Optional.of(keystoreFile.getAbsolutePath()); + clientConfig.keyStore = Optional.of(keystorePath.toAbsolutePath().toString()); clientConfig.keyStorePassword = Optional.of("keystorePassword"); clientConfig.keyStoreType = Optional.of("JKS"); diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2RSTFloodProtectionTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2RSTFloodProtectionTest.java index abd5907d75465..991cc1d57771c 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2RSTFloodProtectionTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/http2/Http2RSTFloodProtectionTest.java @@ -14,6 +14,8 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.OS; import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.test.QuarkusUnitTest; @@ -29,6 +31,7 @@ /** * Reproduce CVE-2023-44487. */ +@DisabledOnOs(OS.WINDOWS) public class Http2RSTFloodProtectionTest { @TestHTTPResource(value = "/ping", ssl = true) @@ -81,7 +84,7 @@ void run(HttpClient client, int port, boolean plain) throws InterruptedException .compose(HttpClientRequest::send); } - for (int i = 0; i < 250; i++) { // must be higher thant the NEtty limit (200 / 30s) + for (int i = 0; i < 250; i++) { // must be higher than the Netty limit (200 / 30s) client.request(GET, port, "localhost", "/ping") .onSuccess(req -> req.end().onComplete(v -> req.reset())); } diff --git a/independent-projects/tools/analytics-common/src/test/java/io/quarkus/analytics/AnalyticsServicePromptTest.java b/independent-projects/tools/analytics-common/src/test/java/io/quarkus/analytics/AnalyticsServicePromptTest.java index 07d3df954c868..dec63b23316c8 100644 --- a/independent-projects/tools/analytics-common/src/test/java/io/quarkus/analytics/AnalyticsServicePromptTest.java +++ b/independent-projects/tools/analytics-common/src/test/java/io/quarkus/analytics/AnalyticsServicePromptTest.java @@ -66,25 +66,36 @@ void testConsoleQuestion_no() throws IOException { @Test void testConsoleQuestion_promptTimeout() throws IOException { - System.setProperty("quarkus.analytics.prompt.timeout", "0"); - assertFalse(fileLocations.getLocalConfigFile().toFile().exists()); - service.buildAnalyticsUserInput((String prompt) -> { - assertEquals(ACCEPTANCE_PROMPT, prompt); - return "n"; - }); - assertFalse(fileLocations.getLocalConfigFile().toFile().exists()); - System.clearProperty("quarkus.analytics.prompt.timeout"); + try { + System.setProperty("quarkus.analytics.prompt.timeout", "0"); + assertFalse(fileLocations.getLocalConfigFile().toFile().exists()); + service.buildAnalyticsUserInput((String prompt) -> { + assertEquals(ACCEPTANCE_PROMPT, prompt); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + return "n"; + }); + assertFalse(fileLocations.getLocalConfigFile().toFile().exists()); + } finally { + System.clearProperty("quarkus.analytics.prompt.timeout"); + } } @Test void testConsoleQuestion_AnalyticsDisabled() throws IOException { - System.setProperty("quarkus.analytics.disabled", "true"); - assertFalse(fileLocations.getLocalConfigFile().toFile().exists()); - service.buildAnalyticsUserInput((String prompt) -> { - fail("Prompt should be disabled"); - return "n"; - }); - assertFalse(fileLocations.getLocalConfigFile().toFile().exists()); - System.clearProperty("quarkus.analytics.disabled"); + try { + System.setProperty("quarkus.analytics.disabled", "true"); + assertFalse(fileLocations.getLocalConfigFile().toFile().exists()); + service.buildAnalyticsUserInput((String prompt) -> { + fail("Prompt should be disabled"); + return "n"; + }); + assertFalse(fileLocations.getLocalConfigFile().toFile().exists()); + } finally { + System.clearProperty("quarkus.analytics.disabled"); + } } }