From 7d345c7a6746131f9765d90088774f6a1a54905e Mon Sep 17 00:00:00 2001 From: Mazen Khalil Date: Tue, 15 Nov 2022 18:13:38 +0300 Subject: [PATCH 1/4] Utilize MongoClients, allow configurable DB & changeLog, enhance the API --- .../mongodb/LiquibaseMongodbFactory.java | 124 +++++++++++------- .../runtime/LiquibaseMongodbConfig.java | 7 + .../runtime/LiquibaseMongodbRecorder.java | 31 +++-- 3 files changed, 105 insertions(+), 57 deletions(-) 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 e8744a52b193a..3f9dd6968b8e1 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 @@ -1,77 +1,109 @@ package io.quarkus.liquibase.mongodb; import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.Optional; +import javax.enterprise.inject.Default; +import javax.enterprise.inject.literal.NamedLiteral; +import javax.enterprise.util.AnnotationLiteral; + +import com.mongodb.client.MongoClient; + +import io.quarkus.arc.Arc; import io.quarkus.liquibase.mongodb.runtime.LiquibaseMongodbBuildTimeConfig; import io.quarkus.liquibase.mongodb.runtime.LiquibaseMongodbConfig; -import io.quarkus.mongodb.runtime.MongoClientConfig; +import io.quarkus.mongodb.runtime.MongoClientBeanUtil; import liquibase.Contexts; import liquibase.LabelExpression; import liquibase.Liquibase; import liquibase.database.Database; -import liquibase.database.DatabaseFactory; +import liquibase.exception.DatabaseException; +import liquibase.ext.mongodb.database.MongoConnection; +import liquibase.ext.mongodb.database.MongoLiquibaseDatabase; import liquibase.resource.ClassLoaderResourceAccessor; public class LiquibaseMongodbFactory { - private final MongoClientConfig mongoClientConfig; + private final String mongoClientName; + private final Optional defaultDatabaseName; private final LiquibaseMongodbConfig liquibaseMongodbConfig; private final LiquibaseMongodbBuildTimeConfig liquibaseMongodbBuildTimeConfig; - //connection-string format, see https://docs.mongodb.com/manual/reference/connection-string/ - Pattern HAS_DB = Pattern - .compile("(?mongodb://|mongodb\\+srv://)(?[^/]*)(?[/]?)(?[^?]*)(?\\??.*)"); - public LiquibaseMongodbFactory(LiquibaseMongodbConfig config, - LiquibaseMongodbBuildTimeConfig liquibaseMongodbBuildTimeConfig, MongoClientConfig mongoClientConfig) { + LiquibaseMongodbBuildTimeConfig liquibaseMongodbBuildTimeConfig, String mongoClientName, + Optional defaultDatabaseName) { this.liquibaseMongodbConfig = config; this.liquibaseMongodbBuildTimeConfig = liquibaseMongodbBuildTimeConfig; - this.mongoClientConfig = mongoClientConfig; + this.mongoClientName = mongoClientName; + this.defaultDatabaseName = defaultDatabaseName; } public Liquibase createLiquibase() { - try (ClassLoaderResourceAccessor resourceAccessor = new ClassLoaderResourceAccessor( - Thread.currentThread().getContextClassLoader())) { - String connectionString = this.mongoClientConfig.connectionString.orElse("mongodb://localhost:27017"); - - Matcher matcher = HAS_DB.matcher(connectionString); - if (!matcher.matches() || matcher.group("db") == null || matcher.group("db").isEmpty()) { - connectionString = matcher.replaceFirst( - "${prefix}${hosts}/" - + this.mongoClientConfig.database - .orElseThrow(() -> new IllegalArgumentException("Config property " + - "'quarkus.mongodb.database' must be defined when no database exist in the connection string")) - + "${options}"); - } - if (mongoClientConfig.credentials.authSource.isPresent()) { - boolean alreadyHasQueryParams = connectionString.contains("?"); - connectionString += (alreadyHasQueryParams ? "&" : "?") + "authSource=" - + mongoClientConfig.credentials.authSource.get(); - } + String databaseName = this.defaultDatabaseName.orElseThrow(() -> { + String propertyName = MongoClientBeanUtil.isDefault(this.liquibaseMongodbConfig.mongoClientName) + ? "quarkus.mongodb.database" + : "quarkus.mongodb." + this.mongoClientName + ".database"; + return new IllegalArgumentException("Config property '" + propertyName + "' must be defined"); + }); + + return createLiquibase(databaseName); + } + + public Liquibase createLiquibase(String databaseName) { + return createLiquibase(databaseName, liquibaseMongodbBuildTimeConfig.changeLog); + } - Database database = DatabaseFactory.getInstance().openDatabase(connectionString, - this.mongoClientConfig.credentials.username.orElse(null), - this.mongoClientConfig.credentials.password.orElse(null), - null, resourceAccessor); + public Liquibase createLiquibase(String databaseName, String changeLog) { + return createLiquibase(databaseName, changeLog, liquibaseMongodbConfig.changeLogParameters); + } - if (database != null) { - liquibaseMongodbConfig.liquibaseCatalogName.ifPresent(database::setLiquibaseCatalogName); - liquibaseMongodbConfig.liquibaseSchemaName.ifPresent(database::setLiquibaseSchemaName); - liquibaseMongodbConfig.liquibaseTablespaceName.ifPresent(database::setLiquibaseTablespaceName); + public Liquibase createLiquibase(String databaseName, String changeLog, Map changeLogParameters) { + if (databaseName == null) { + throw new IllegalArgumentException("'databaseName' cannot be null"); + } - if (liquibaseMongodbConfig.defaultCatalogName.isPresent()) { - database.setDefaultCatalogName(liquibaseMongodbConfig.defaultCatalogName.get()); + if (changeLog == null) { + throw new IllegalArgumentException("'changelog' file cannot be null"); + } + + MongoClient mongoClient = Arc.container() + .instance(MongoClient.class, getLiteral()).get(); + + try (ClassLoaderResourceAccessor resourceAccessor = new ClassLoaderResourceAccessor( + Thread.currentThread().getContextClassLoader())) { + MongoConnection connection = new MongoConnection() { + @Override + public void close() throws DatabaseException { + // Ignore } - if (liquibaseMongodbConfig.defaultSchemaName.isPresent()) { - database.setDefaultSchemaName(liquibaseMongodbConfig.defaultSchemaName.get()); + }; + connection.setMongoClient(mongoClient); + connection.setMongoDatabase(mongoClient.getDatabase(databaseName)); + + Database database = new MongoLiquibaseDatabase() { + @Override + public void close() throws DatabaseException { + // Ignore } + }; + database.setConnection(connection); + + liquibaseMongodbConfig.liquibaseCatalogName.ifPresent(database::setLiquibaseCatalogName); + liquibaseMongodbConfig.liquibaseSchemaName.ifPresent(database::setLiquibaseSchemaName); + liquibaseMongodbConfig.liquibaseTablespaceName.ifPresent(database::setLiquibaseTablespaceName); + + if (liquibaseMongodbConfig.defaultCatalogName.isPresent()) { + database.setDefaultCatalogName(liquibaseMongodbConfig.defaultCatalogName.get()); + } + if (liquibaseMongodbConfig.defaultSchemaName.isPresent()) { + database.setDefaultSchemaName(liquibaseMongodbConfig.defaultSchemaName.get()); } - Liquibase liquibase = new Liquibase(liquibaseMongodbBuildTimeConfig.changeLog, resourceAccessor, database); + Liquibase liquibase = new Liquibase(changeLog, resourceAccessor, database); - for (Map.Entry entry : liquibaseMongodbConfig.changeLogParameters.entrySet()) { - liquibase.getChangeLogParameters().set(entry.getKey(), entry.getValue()); + if (changeLogParameters != null) { + for (Map.Entry entry : changeLogParameters.entrySet()) { + liquibase.getChangeLogParameters().set(entry.getKey(), entry.getValue()); + } } return liquibase; @@ -81,6 +113,10 @@ public Liquibase createLiquibase() { } } + private AnnotationLiteral getLiteral() { + return MongoClientBeanUtil.isDefault(mongoClientName) ? Default.Literal.INSTANCE : NamedLiteral.of(mongoClientName); + } + public LiquibaseMongodbConfig getConfiguration() { return liquibaseMongodbConfig; } diff --git a/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/mongodb/runtime/LiquibaseMongodbConfig.java b/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/mongodb/runtime/LiquibaseMongodbConfig.java index b9db00bb17e8d..20f64725e994b 100644 --- a/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/mongodb/runtime/LiquibaseMongodbConfig.java +++ b/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/mongodb/runtime/LiquibaseMongodbConfig.java @@ -5,6 +5,7 @@ import java.util.Map; import java.util.Optional; +import io.quarkus.mongodb.runtime.MongoClientBeanUtil; import io.quarkus.runtime.annotations.ConfigItem; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; @@ -15,6 +16,12 @@ @ConfigRoot(name = "liquibase-mongodb", phase = ConfigPhase.RUN_TIME) public class LiquibaseMongodbConfig { + /** + * The name of mongo client + */ + @ConfigItem(defaultValue = MongoClientBeanUtil.DEFAULT_MONGOCLIENT_NAME) + public String mongoClientName; + /** * The migrate at start flag */ diff --git a/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/mongodb/runtime/LiquibaseMongodbRecorder.java b/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/mongodb/runtime/LiquibaseMongodbRecorder.java index 179b48fa4746d..fb9a914f85342 100644 --- a/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/mongodb/runtime/LiquibaseMongodbRecorder.java +++ b/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/mongodb/runtime/LiquibaseMongodbRecorder.java @@ -9,6 +9,8 @@ import io.quarkus.arc.InjectableInstance; import io.quarkus.arc.InstanceHandle; import io.quarkus.liquibase.mongodb.LiquibaseMongodbFactory; +import io.quarkus.mongodb.runtime.MongoClientBeanUtil; +import io.quarkus.mongodb.runtime.MongoClientConfig; import io.quarkus.mongodb.runtime.MongodbConfig; import io.quarkus.runtime.annotations.Recorder; import liquibase.Liquibase; @@ -18,11 +20,11 @@ public class LiquibaseMongodbRecorder { public Supplier liquibaseSupplier(LiquibaseMongodbConfig config, LiquibaseMongodbBuildTimeConfig buildTimeConfig, MongodbConfig mongodbConfig) { - return new Supplier() { - @Override - public LiquibaseMongodbFactory get() { - return new LiquibaseMongodbFactory(config, buildTimeConfig, mongodbConfig.defaultMongoClientConfig); - } + return () -> { + MongoClientConfig mongoClientConfig = MongoClientBeanUtil.isDefault(config.mongoClientName) + ? mongodbConfig.defaultMongoClientConfig + : mongodbConfig.mongoClientConfigs.get(config.mongoClientName); + return new LiquibaseMongodbFactory(config, buildTimeConfig, config.mongoClientName, mongoClientConfig.database); }; } @@ -37,18 +39,21 @@ public void doStartActions() { for (InstanceHandle liquibaseFactoryHandle : liquibaseFactoryInstance.handles()) { try { LiquibaseMongodbFactory liquibaseFactory = liquibaseFactoryHandle.get(); - if (liquibaseFactory.getConfiguration().cleanAtStart) { - try (Liquibase liquibase = liquibaseFactory.createLiquibase()) { + + if (!liquibaseFactory.getConfiguration().cleanAtStart + && !liquibaseFactory.getConfiguration().migrateAtStart) { + // Don't initialize if no clean or migration required at start + return; + } + + try (Liquibase liquibase = liquibaseFactory.createLiquibase()) { + if (liquibaseFactory.getConfiguration().cleanAtStart) { liquibase.dropAll(); } - } - if (liquibaseFactory.getConfiguration().migrateAtStart) { - if (liquibaseFactory.getConfiguration().validateOnMigrate) { - try (Liquibase liquibase = liquibaseFactory.createLiquibase()) { + if (liquibaseFactory.getConfiguration().migrateAtStart) { + if (liquibaseFactory.getConfiguration().validateOnMigrate) { liquibase.validate(); } - } - try (Liquibase liquibase = liquibaseFactory.createLiquibase()) { liquibase.update(liquibaseFactory.createContexts(), liquibaseFactory.createLabels()); } } From 1292a8ca661875889e32dd28c48aa10cc81fdb40 Mon Sep 17 00:00:00 2001 From: Mazen Khalil Date: Tue, 15 Nov 2022 18:48:36 +0300 Subject: [PATCH 2/4] Fix mongoClientName property read --- .../io/quarkus/liquibase/mongodb/LiquibaseMongodbFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3f9dd6968b8e1..097983463a9e0 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 @@ -40,7 +40,7 @@ public LiquibaseMongodbFactory(LiquibaseMongodbConfig config, public Liquibase createLiquibase() { String databaseName = this.defaultDatabaseName.orElseThrow(() -> { - String propertyName = MongoClientBeanUtil.isDefault(this.liquibaseMongodbConfig.mongoClientName) + String propertyName = MongoClientBeanUtil.isDefault(this.mongoClientName) ? "quarkus.mongodb.database" : "quarkus.mongodb." + this.mongoClientName + ".database"; return new IllegalArgumentException("Config property '" + propertyName + "' must be defined"); From 3d4d5ca5b7ebddaf93ccc97cc65874b1ccbafbd0 Mon Sep 17 00:00:00 2001 From: Mazen Khalil Date: Mon, 5 Dec 2022 00:57:53 +0300 Subject: [PATCH 3/4] Replace lambda expression with method reference --- .../mongodb/runtime/LiquibaseMongodbRecorder.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/mongodb/runtime/LiquibaseMongodbRecorder.java b/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/mongodb/runtime/LiquibaseMongodbRecorder.java index fb9a914f85342..3a1544d028e70 100644 --- a/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/mongodb/runtime/LiquibaseMongodbRecorder.java +++ b/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/mongodb/runtime/LiquibaseMongodbRecorder.java @@ -20,11 +20,14 @@ public class LiquibaseMongodbRecorder { public Supplier liquibaseSupplier(LiquibaseMongodbConfig config, LiquibaseMongodbBuildTimeConfig buildTimeConfig, MongodbConfig mongodbConfig) { - return () -> { - MongoClientConfig mongoClientConfig = MongoClientBeanUtil.isDefault(config.mongoClientName) - ? mongodbConfig.defaultMongoClientConfig - : mongodbConfig.mongoClientConfigs.get(config.mongoClientName); - return new LiquibaseMongodbFactory(config, buildTimeConfig, config.mongoClientName, mongoClientConfig.database); + return new Supplier() { + @Override + public LiquibaseMongodbFactory get() { + MongoClientConfig mongoClientConfig = MongoClientBeanUtil.isDefault(config.mongoClientName) + ? mongodbConfig.defaultMongoClientConfig + : mongodbConfig.mongoClientConfigs.get(config.mongoClientName); + return new LiquibaseMongodbFactory(config, buildTimeConfig, config.mongoClientName, mongoClientConfig.database); + } }; } From b414ef89b55af2eb12903d313d8f7d36a9258788 Mon Sep 17 00:00:00 2001 From: Mazen Khalil Date: Fri, 9 Dec 2022 11:51:52 +0300 Subject: [PATCH 4/4] Fallback to the connectionString if no database defined in the config --- .../mongodb/LiquibaseMongodbFactory.java | 30 ++++++++++++++----- .../runtime/LiquibaseMongodbRecorder.java | 2 +- 2 files changed, 24 insertions(+), 8 deletions(-) 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 097983463a9e0..31d9f8135ece2 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 @@ -2,6 +2,8 @@ import java.util.Map; import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.enterprise.inject.Default; import javax.enterprise.inject.literal.NamedLiteral; @@ -13,6 +15,7 @@ import io.quarkus.liquibase.mongodb.runtime.LiquibaseMongodbBuildTimeConfig; import io.quarkus.liquibase.mongodb.runtime.LiquibaseMongodbConfig; import io.quarkus.mongodb.runtime.MongoClientBeanUtil; +import io.quarkus.mongodb.runtime.MongoClientConfig; import liquibase.Contexts; import liquibase.LabelExpression; import liquibase.Liquibase; @@ -25,27 +28,31 @@ public class LiquibaseMongodbFactory { private final String mongoClientName; - private final Optional defaultDatabaseName; + private final MongoClientConfig mongoClientConfig; private final LiquibaseMongodbConfig liquibaseMongodbConfig; private final LiquibaseMongodbBuildTimeConfig liquibaseMongodbBuildTimeConfig; + //connection-string format, see https://docs.mongodb.com/manual/reference/connection-string/ + Pattern HAS_DB = Pattern + .compile("(?mongodb://|mongodb\\+srv://)(?[^/]*)(?[/]?)(?[^?]*)(?\\??.*)"); + public LiquibaseMongodbFactory(LiquibaseMongodbConfig config, LiquibaseMongodbBuildTimeConfig liquibaseMongodbBuildTimeConfig, String mongoClientName, - Optional defaultDatabaseName) { + MongoClientConfig mongoClientConfig) { this.liquibaseMongodbConfig = config; this.liquibaseMongodbBuildTimeConfig = liquibaseMongodbBuildTimeConfig; this.mongoClientName = mongoClientName; - this.defaultDatabaseName = defaultDatabaseName; + this.mongoClientConfig = mongoClientConfig; } public Liquibase createLiquibase() { - String databaseName = this.defaultDatabaseName.orElseThrow(() -> { + String databaseName = this.mongoClientConfig.database.orElseGet(() -> getConnectionStringDatabase().orElseThrow(() -> { String propertyName = MongoClientBeanUtil.isDefault(this.mongoClientName) ? "quarkus.mongodb.database" : "quarkus.mongodb." + this.mongoClientName + ".database"; - return new IllegalArgumentException("Config property '" + propertyName + "' must be defined"); - }); - + return new IllegalArgumentException( + "Config property '" + propertyName + "' must be defined when no database exist in the connection string"); + })); return createLiquibase(databaseName); } @@ -113,6 +120,15 @@ public void close() throws DatabaseException { } } + private Optional getConnectionStringDatabase() { + String connectionString = this.mongoClientConfig.connectionString.orElse("mongodb://localhost:27017"); + Matcher matcher = HAS_DB.matcher(connectionString); + if (!matcher.matches() || matcher.group("db") == null || matcher.group("db").isEmpty()) { + return Optional.empty(); + } + return Optional.of(matcher.group("db")); + } + private AnnotationLiteral getLiteral() { return MongoClientBeanUtil.isDefault(mongoClientName) ? Default.Literal.INSTANCE : NamedLiteral.of(mongoClientName); } diff --git a/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/mongodb/runtime/LiquibaseMongodbRecorder.java b/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/mongodb/runtime/LiquibaseMongodbRecorder.java index 3a1544d028e70..955bed33a540c 100644 --- a/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/mongodb/runtime/LiquibaseMongodbRecorder.java +++ b/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/mongodb/runtime/LiquibaseMongodbRecorder.java @@ -26,7 +26,7 @@ public LiquibaseMongodbFactory get() { MongoClientConfig mongoClientConfig = MongoClientBeanUtil.isDefault(config.mongoClientName) ? mongodbConfig.defaultMongoClientConfig : mongodbConfig.mongoClientConfigs.get(config.mongoClientName); - return new LiquibaseMongodbFactory(config, buildTimeConfig, config.mongoClientName, mongoClientConfig.database); + return new LiquibaseMongodbFactory(config, buildTimeConfig, config.mongoClientName, mongoClientConfig); } }; }