From 0d2a54f9e9ac84cd27c4a9d5eee5c54fe0ab5772 Mon Sep 17 00:00:00 2001 From: Thomas Segismont Date: Tue, 22 Jun 2021 15:07:20 +0200 Subject: [PATCH 1/9] Reactive SQL guide: document placeholders Fixes #18056 Reactive SQL clients use different placeholders for prepared queries. Now the database clients details section shows the differences. (cherry picked from commit d7ef14be9293439ec079e81685010cf0504e4ffd) --- docs/src/main/asciidoc/reactive-sql-clients.adoc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/src/main/asciidoc/reactive-sql-clients.adoc b/docs/src/main/asciidoc/reactive-sql-clients.adoc index 7ccc3230fd2b9..bc6c11c98b28e 100644 --- a/docs/src/main/asciidoc/reactive-sql-clients.adoc +++ b/docs/src/main/asciidoc/reactive-sql-clients.adoc @@ -290,7 +290,8 @@ The Reactive PostgreSQL Client can also prepare queries and take parameters that client.preparedQuery("SELECT id, name FROM fruits WHERE id = $1").execute(Tuple.of(id)) ---- -TIP: The SQL string can refer to parameters by position, using $1, $2, ...etc. +TIP: For PostgreSQL, the SQL string can refer to parameters by position, using `$1`, `$2`, ...etc. +Please refer to the <> section for other databases. Similar to the simple `query` method, `preparedQuery` returns an instance of `PreparedQuery>`. Equipped with this tooling, we are able to safely use an `id` provided by the user to get the details of a particular fruit: @@ -467,21 +468,24 @@ Navigate to http://localhost:8080/fruits.html and read/create/delete some fruits == Database Clients details -[cols="15,35,50"] +[cols="10,40,40,10"] |=== -|Database |Extension name |Pool class name +|Database |Extension name |Pool class name |Placeholders |DB2 |`quarkus-reactive-db2-client` |`io.vertx.mutiny.db2client.DB2Pool` +|`?` |MariaDB/MySQL |`quarkus-reactive-mysql-client` |`io.vertx.mutiny.mysqlclient.MySQLPool` +|`?` |PostgreSQL |`quarkus-reactive-pg-client` |`io.vertx.mutiny.pgclient.PgPool` +|`$1`, `$2`, etc. |=== == Transactions From d3efe0a1ddd91f09bc8b947e276c084daf4a1129 Mon Sep 17 00:00:00 2001 From: George Gastaldi Date: Tue, 22 Jun 2021 11:06:36 -0300 Subject: [PATCH 2/9] Flyway devmode: Confirm before cleaning the schema (cherry picked from commit cb67cd31677f428a8c227583361fd2fdc7a0a041) --- .../src/main/resources/dev-templates/datasources.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/flyway/deployment/src/main/resources/dev-templates/datasources.html b/extensions/flyway/deployment/src/main/resources/dev-templates/datasources.html index d09f5167e6f2e..41e7bcddc9b4c 100644 --- a/extensions/flyway/deployment/src/main/resources/dev-templates/datasources.html +++ b/extensions/flyway/deployment/src/main/resources/dev-templates/datasources.html @@ -19,7 +19,7 @@
- +
 
From 23c6d6442c0a07a1eb74374c22c32297bc588329 Mon Sep 17 00:00:00 2001 From: George Gastaldi Date: Tue, 22 Jun 2021 11:29:24 -0300 Subject: [PATCH 3/9] Liquibase devmode: Prompt before cleaning the schema (cherry picked from commit 8f45989b23bf857ddd4c7e2477a5d8de7ab92b48) --- .../src/main/resources/dev-templates/datasources.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/liquibase/deployment/src/main/resources/dev-templates/datasources.html b/extensions/liquibase/deployment/src/main/resources/dev-templates/datasources.html index fe82b15924087..f31ec795c7e1d 100644 --- a/extensions/liquibase/deployment/src/main/resources/dev-templates/datasources.html +++ b/extensions/liquibase/deployment/src/main/resources/dev-templates/datasources.html @@ -19,7 +19,7 @@ - +  
From 52fe228e04eddc7b94153d03bf3040e7ea4d1954 Mon Sep 17 00:00:00 2001 From: Alexey Loubyansky Date: Tue, 22 Jun 2021 14:32:00 +0200 Subject: [PATCH 4/9] Extension registry transfer listener (cherry picked from commit 555e11b1de7e696792f1f521e9a64d362538dd07) --- ...enRegistryArtifactResolverWithCleanup.java | 6 +- .../maven/MavenRegistryClientFactory.java | 71 ++++++++++++++++++- 2 files changed, 72 insertions(+), 5 deletions(-) diff --git a/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/client/maven/MavenRegistryArtifactResolverWithCleanup.java b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/client/maven/MavenRegistryArtifactResolverWithCleanup.java index 4ca003847f1ff..5dffc6cbe6e0d 100644 --- a/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/client/maven/MavenRegistryArtifactResolverWithCleanup.java +++ b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/client/maven/MavenRegistryArtifactResolverWithCleanup.java @@ -48,7 +48,7 @@ public String getLatestVersionFromRange(Artifact artifact, String versionRange) */ protected static ArtifactResult resolveAndCleanupOldTimestampedVersions(MavenArtifactResolver resolver, Artifact artifact, boolean cleanupOldTimestampedVersions) throws BootstrapMavenException { - if (!cleanupOldTimestampedVersions) { + if (!artifact.isSnapshot() || !cleanupOldTimestampedVersions) { return resolver.resolve(artifact); } @@ -63,7 +63,9 @@ protected static ArtifactResult resolveAndCleanupOldTimestampedVersions(MavenArt if (jsonDirContent != null && jsonDirContent.length > existingFiles.size()) { final String fileName = result.getArtifact().getFile().getName(); for (File c : jsonDirContent) { - if (c.getName().length() > fileName.length() && c.getName().startsWith(artifact.getArtifactId()) + if (c.getName().length() > fileName.length() + && c.getName().startsWith(artifact.getArtifactId()) + && c.getName().endsWith(artifact.getClassifier()) && existingFiles.contains(c.getName())) { c.deleteOnExit(); } diff --git a/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/client/maven/MavenRegistryClientFactory.java b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/client/maven/MavenRegistryClientFactory.java index 4284d21e4ec3b..2c9e8129ac54c 100644 --- a/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/client/maven/MavenRegistryClientFactory.java +++ b/independent-projects/tools/registry-client/src/main/java/io/quarkus/registry/client/maven/MavenRegistryClientFactory.java @@ -33,11 +33,16 @@ import java.util.List; import java.util.Objects; import java.util.Set; +import org.eclipse.aether.DefaultRepositorySystemSession; +import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.artifact.DefaultArtifact; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.repository.RepositoryPolicy; import org.eclipse.aether.resolution.ArtifactResult; +import org.eclipse.aether.transfer.TransferCancelledException; +import org.eclipse.aether.transfer.TransferEvent; +import org.eclipse.aether.transfer.TransferListener; public class MavenRegistryClientFactory implements RegistryClientFactory { @@ -70,7 +75,9 @@ public RegistryClient buildRegistryClient(RegistryConfig config) throws Registry Collections.emptyList(), singleRegistryRepos, true); aggregatedRepos = resolver.getRemoteRepositoryManager().aggregateRepositories(resolver.getSession(), aggregatedRepos, resolver.getRepositories(), false); - resolver = newResolver(resolver, aggregatedRepos); + resolver = newResolver(resolver, aggregatedRepos, config, log); + } else { + resolver = newResolver(resolver, resolver.getRepositories(), config, log); } final boolean cleanupTimestampedArtifacts = isCleanupTimestampedArtifacts(config); @@ -326,12 +333,13 @@ private static boolean isComplete(RegistryMavenRepoConfig config) { return true; } - private static MavenArtifactResolver newResolver(MavenArtifactResolver resolver, List aggregatedRepos) { + private static MavenArtifactResolver newResolver(MavenArtifactResolver resolver, List aggregatedRepos, + RegistryConfig config, MessageWriter log) { try { final BootstrapMavenContext mvnCtx = new BootstrapMavenContext( BootstrapMavenContext.config() .setRepositorySystem(resolver.getSystem()) - .setRepositorySystemSession(resolver.getSession()) + .setRepositorySystemSession(setRegistryTransferListener(config, log, resolver.getSession())) .setRemoteRepositoryManager(resolver.getRemoteRepositoryManager()) .setRemoteRepositories(aggregatedRepos) .setLocalRepository(resolver.getMavenContext().getLocalRepo()) @@ -342,6 +350,63 @@ private static MavenArtifactResolver newResolver(MavenArtifactResolver resolver, } } + private static DefaultRepositorySystemSession setRegistryTransferListener(RegistryConfig config, MessageWriter log, + RepositorySystemSession session) { + final DefaultRepositorySystemSession newSession = new DefaultRepositorySystemSession(session); + final TransferListener tl = newSession.getTransferListener(); + newSession.setTransferListener(new TransferListener() { + + boolean refreshingLocalCache; + + @Override + public void transferInitiated(TransferEvent event) throws TransferCancelledException { + if (!refreshingLocalCache) { + refreshingLocalCache = true; + log.info("Refreshing the local extension catalog cache of " + config.getId()); + } + if (tl != null) { + tl.transferInitiated(event); + } + } + + @Override + public void transferStarted(TransferEvent event) throws TransferCancelledException { + if (tl != null) { + tl.transferStarted(event); + } + } + + @Override + public void transferProgressed(TransferEvent event) throws TransferCancelledException { + if (tl != null) { + tl.transferProgressed(event); + } + } + + @Override + public void transferCorrupted(TransferEvent event) throws TransferCancelledException { + if (tl != null) { + tl.transferCorrupted(event); + } + } + + @Override + public void transferSucceeded(TransferEvent event) { + if (tl != null) { + tl.transferSucceeded(event); + } + } + + @Override + public void transferFailed(TransferEvent event) { + if (tl != null) { + tl.transferFailed(event); + } + } + }); + return newSession; + } + private void determineExtraRepos(RegistryConfig config, List configuredRepos) { final RegistryMavenConfig mavenConfig = config.getMaven() == null ? null : config.getMaven(); From edc9daa18af4176b75196f1d7f64e2d30071f185 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Tue, 22 Jun 2021 17:22:59 +0300 Subject: [PATCH 5/9] Provide actionable message when @QuarkusIntegrationTest is run before the Quarkus build (cherry picked from commit a6a7fa3a508f2e8288a5107b459507301963df7c) --- .../test/junit/IntegrationTestUtil.java | 45 ++++++++++++++++++- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/IntegrationTestUtil.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/IntegrationTestUtil.java index 4c80c084153fd..3798574327458 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/IntegrationTestUtil.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/IntegrationTestUtil.java @@ -232,8 +232,22 @@ static Properties readQuarkusArtifactProperties(ExtensionContext context) { Path buildOutputDirectory = determineBuildOutputDirectory(context); Path artifactProperties = buildOutputDirectory.resolve("quarkus-artifact.properties"); if (!Files.exists(artifactProperties)) { - throw new IllegalStateException( - "Unable to locate the artifact metadata file created that must be created by Quarkus in order to run integration tests."); + TestLauncher testLauncher = determineTestLauncher(); + String errorMessage = "Unable to locate the artifact metadata file created that must be created by Quarkus in order to run integration tests. "; + if (testLauncher == TestLauncher.MAVEN) { + errorMessage += "Make sure this test is run after 'mvn package'. "; + if (context.getTestClass().isPresent()) { + String testClassName = context.getTestClass().get().getName(); + if (testClassName.endsWith("Test")) { + errorMessage += "The easiest way to ensure this is by having the 'maven-failsafe-plugin' run the test instead of the 'maven-surefire-plugin'."; + } + } + } else if (testLauncher == TestLauncher.GRADLE) { + errorMessage += "Make sure this test is run after the 'quarkusBuild' Gradle task."; + } else { + errorMessage += "Make sure this test is run after the Quarkus artifact is built from your build tool."; + } + throw new IllegalStateException(errorMessage); } try { Properties properties = new Properties(); @@ -246,6 +260,33 @@ static Properties readQuarkusArtifactProperties(ExtensionContext context) { } } + private static TestLauncher determineTestLauncher() { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + int i = stackTrace.length - 1; + TestLauncher testLauncher = TestLauncher.UNKNOWN; + while (true) { + StackTraceElement element = stackTrace[i--]; + String className = element.getClassName(); + if (className.startsWith("org.apache.maven")) { + testLauncher = TestLauncher.MAVEN; + break; + } + if (className.startsWith("org.gradle")) { + testLauncher = TestLauncher.GRADLE; + } + if (i == 0) { + break; + } + } + return testLauncher; + } + + private enum TestLauncher { + MAVEN, + GRADLE, + UNKNOWN + } + static Path determineBuildOutputDirectory(ExtensionContext context) { String buildOutputDirStr = System.getProperty("build.output.directory"); Path result = null; From c507390e915aba9ddbe6d595d062251554e48166 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Tue, 22 Jun 2021 15:31:01 +0100 Subject: [PATCH 6/9] Upgrade to Hibernate ORM 5.5.3.Final (cherry picked from commit b5f0266931651038029cbd20daa90eba09b64073) --- bom/application/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 134c31a9d5a49..e6027f09778c7 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -94,7 +94,7 @@ 3.12.0 1.15 1.5.1 - 5.5.2.Final + 5.5.3.Final 1.0.0.CR7 6.2.0.Final 6.0.4.Final From db377c381a4504f772a5cd2e73bbdf6e585f3bed Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Tue, 22 Jun 2021 16:58:04 +0100 Subject: [PATCH 7/9] Disable AvailableSettings.HBM2DDL_SCRIPTS_CREATE_APPEND (cherry picked from commit 7a7d3f3384d4ada1dc75c13800a3d49b10c3d86b) --- .../orm/runtime/FastBootHibernatePersistenceProvider.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java index 34858d5c20b04..e57ed4a5f33dd 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/FastBootHibernatePersistenceProvider.java @@ -340,6 +340,9 @@ private static void injectRuntimeConfiguration(String persistenceUnitName, runtimeSettingsBuilder.put(AvailableSettings.HBM2DDL_HALT_ON_ERROR, "true"); } + //Never append on existing scripts: + runtimeSettingsBuilder.put(AvailableSettings.HBM2DDL_SCRIPTS_CREATE_APPEND, "false"); + runtimeSettingsBuilder.put(AvailableSettings.HBM2DDL_SCRIPTS_ACTION, persistenceUnitConfig.scripts.generation.generation); From 4e2342332178a10d58e9710ffaa868443e7426e7 Mon Sep 17 00:00:00 2001 From: Eric Deandrea Date: Tue, 22 Jun 2021 09:19:26 -0400 Subject: [PATCH 8/9] Validate option for hibernate database generation In the [hibernate-orm guide](https://quarkus.io/guides/hibernate-orm#quarkus-hibernate-orm_quarkus.hibernate-orm.database.generation),`validate` is not shown as a valid value for the `quarkus.hibernate-orm.database.generation` property. (cherry picked from commit 5503b466044bed0b3bccc6d7d991c90451e51a6d) --- .../orm/runtime/HibernateOrmRuntimeConfigPersistenceUnit.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRuntimeConfigPersistenceUnit.java b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRuntimeConfigPersistenceUnit.java index f55957d183657..d16fb2304d5f2 100644 --- a/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRuntimeConfigPersistenceUnit.java +++ b/extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/HibernateOrmRuntimeConfigPersistenceUnit.java @@ -72,7 +72,7 @@ public static class HibernateOrmConfigPersistenceUnitDatabaseGeneration { * * `drop-and-create` is awesome in development mode. * - * Accepted values: `none`, `create`, `drop-and-create`, `drop`, `update`. + * Accepted values: `none`, `create`, `drop-and-create`, `drop`, `update`, `validate`. */ @ConfigItem(name = ConfigItem.PARENT, defaultValue = "none") public String generation = "none"; @@ -102,7 +102,7 @@ public static class HibernateOrmConfigPersistenceUnitScriptGeneration { /** * Select whether the database schema DDL files are generated or not. * - * Accepted values: `none`, `create`, `drop-and-create`, `drop`, `update`. + * Accepted values: `none`, `create`, `drop-and-create`, `drop`, `update`, `validate`. */ @ConfigItem(name = ConfigItem.PARENT, defaultValue = "none") public String generation = "none"; From 78a9136902875e62e9c1ac27f68a97762c795ec1 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Tue, 22 Jun 2021 16:22:02 +0100 Subject: [PATCH 9/9] H2 DevServices should not attempt to stop a stopped server (cherry picked from commit 17eb2fba302b1c56489e26bb8ddfb04d992f0287) --- .../h2/deployment/H2DevServicesProcessor.java | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/extensions/devservices/h2/src/main/java/io/quarkus/devservices/h2/deployment/H2DevServicesProcessor.java b/extensions/devservices/h2/src/main/java/io/quarkus/devservices/h2/deployment/H2DevServicesProcessor.java index bd0889a21e54e..68cdf2f167672 100644 --- a/extensions/devservices/h2/src/main/java/io/quarkus/devservices/h2/deployment/H2DevServicesProcessor.java +++ b/extensions/devservices/h2/src/main/java/io/quarkus/devservices/h2/deployment/H2DevServicesProcessor.java @@ -51,17 +51,28 @@ public RunningDevServicesDatasource startDatabase(Optional username, Opt new Closeable() { @Override public void close() throws IOException { - //make sure the DB is removed on close - try (Connection connection = DriverManager.getConnection(connectionUrl, "sa", "sa")) { - try (Statement statement = connection.createStatement()) { - statement.execute("SET DB_CLOSE_DELAY 0"); + //Test first, to not make too much noise if the Server is dead already + //(perhaps we failed to start?) + if (tcpServer.isRunning(false)) { + //make sure the DB is removed on close + try (Connection connection = DriverManager.getConnection( + connectionUrl, + "sa", + "sa")) { + try (Statement statement = connection.createStatement()) { + statement.execute("SET DB_CLOSE_DELAY 0"); + } + } catch (SQLException t) { + t.printStackTrace(); } - } catch (SQLException t) { - t.printStackTrace(); + tcpServer.stop(); + System.out.println( + "[INFO] H2 database was shut down; server status: " + tcpServer.getStatus()); + } else { + System.out.println( + "[INFO] H2 database was NOT shut down as it appears it was down already; server status: " + + tcpServer.getStatus()); } - tcpServer.stop(); - System.out.println( - "[INFO] H2 database was shut down; server status: " + tcpServer.getStatus()); } }); } catch (SQLException throwables) {