From fc9a0143956674ea436ad33bf561a17786552f20 Mon Sep 17 00:00:00 2001 From: maverickstuder Date: Thu, 11 Apr 2024 11:59:53 +0200 Subject: [PATCH] RED-8702: Explore document databases to store entityLog - updated connection logic for clustered deployment - updated dependencies --- build.gradle.kts | 2 +- .../liquibase/MongoSpringLiquibase.java | 99 +++++++++++++++++++ .../TenantMongoLiquibaseExecutor.java | 74 +++++++------- .../commons/service/MongoClientCache.java | 16 +-- .../database/commons/utils/MongoUtils.java | 55 ----------- 5 files changed, 144 insertions(+), 102 deletions(-) create mode 100644 src/main/java/com/knecon/fforesight/mongo/database/commons/liquibase/MongoSpringLiquibase.java delete mode 100644 src/main/java/com/knecon/fforesight/mongo/database/commons/utils/MongoUtils.java diff --git a/build.gradle.kts b/build.gradle.kts index ed03eb2..0f2251a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -25,7 +25,7 @@ repositories { val springBootVersion = "3.1.5" dependencies { - api("com.knecon.fforesight:tenant-commons:0.23.0") + api("com.knecon.fforesight:tenant-commons:0.24.0") api("org.liquibase:liquibase-core:4.20.0") api("org.liquibase.ext:liquibase-mongodb:4.20.0") api("org.springframework.boot:spring-boot-starter-data-mongodb:${springBootVersion}") diff --git a/src/main/java/com/knecon/fforesight/mongo/database/commons/liquibase/MongoSpringLiquibase.java b/src/main/java/com/knecon/fforesight/mongo/database/commons/liquibase/MongoSpringLiquibase.java new file mode 100644 index 0000000..01e25ac --- /dev/null +++ b/src/main/java/com/knecon/fforesight/mongo/database/commons/liquibase/MongoSpringLiquibase.java @@ -0,0 +1,99 @@ +package com.knecon.fforesight.mongo.database.commons.liquibase; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.nio.file.Files; +import java.util.Map; + +import liquibase.Contexts; +import liquibase.GlobalConfiguration; +import liquibase.LabelExpression; +import liquibase.Liquibase; +import liquibase.Scope; +import liquibase.configuration.ConfiguredValue; +import liquibase.database.Database; +import liquibase.exception.LiquibaseException; +import liquibase.integration.commandline.LiquibaseCommandLineConfiguration; +import liquibase.integration.spring.SpringLiquibase; +import liquibase.integration.spring.SpringResourceAccessor; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class MongoSpringLiquibase extends SpringLiquibase { + + protected Database database; + + public MongoSpringLiquibase() { + + super(); + } + + + /** + * Executed automatically when the bean is initialized. + */ + @Override + public void afterPropertiesSet() throws LiquibaseException { + + final ConfiguredValue shouldRunProperty = LiquibaseCommandLineConfiguration.SHOULD_RUN.getCurrentConfiguredValue(); + + if (!(Boolean) shouldRunProperty.getValue()) { + Scope.getCurrentScope().getLog(getClass()).info("Liquibase did not run because " + shouldRunProperty.getProvidedValue().describe() + " was set to false"); + return; + } + if (!shouldRun) { + Scope.getCurrentScope() + .getLog(getClass()) + .info("Liquibase did not run because 'shouldRun' " + "property was set " + "to false on " + getBeanName() + " Liquibase Spring bean."); + return; + } + + try (Liquibase liquibase = createLiquibase(getDatabase())) { + generateRollbackFile(liquibase); + performUpdate(liquibase); + } + } + + + private void generateRollbackFile(Liquibase liquibase) throws LiquibaseException { + + if (rollbackFile != null) { + + try (final OutputStream outputStream = Files.newOutputStream(rollbackFile.toPath()); Writer output = new OutputStreamWriter(outputStream, + GlobalConfiguration.OUTPUT_FILE_ENCODING.getCurrentValue())) { + + if (tag != null) { + liquibase.futureRollbackSQL(tag, new Contexts(getContexts()), new LabelExpression(getLabelFilter()), output); + } else { + liquibase.futureRollbackSQL(new Contexts(getContexts()), new LabelExpression(getLabelFilter()), output); + } + } catch (IOException e) { + throw new LiquibaseException("Unable to generate rollback file.", e); + } + } + } + + + @SuppressWarnings("squid:S2095") + protected Liquibase createLiquibase(Database db) throws LiquibaseException { + + SpringResourceAccessor resourceAccessor = createResourceOpener(); + Liquibase liquibase = new Liquibase(getChangeLog(), resourceAccessor, db); + if (parameters != null) { + for (Map.Entry entry : parameters.entrySet()) { + liquibase.setChangeLogParameter(entry.getKey(), entry.getValue()); + } + } + + if (isDropFirst()) { + liquibase.dropAll(); + } + + return liquibase; + } + +} diff --git a/src/main/java/com/knecon/fforesight/mongo/database/commons/liquibase/TenantMongoLiquibaseExecutor.java b/src/main/java/com/knecon/fforesight/mongo/database/commons/liquibase/TenantMongoLiquibaseExecutor.java index c14a1e1..c096208 100644 --- a/src/main/java/com/knecon/fforesight/mongo/database/commons/liquibase/TenantMongoLiquibaseExecutor.java +++ b/src/main/java/com/knecon/fforesight/mongo/database/commons/liquibase/TenantMongoLiquibaseExecutor.java @@ -7,17 +7,16 @@ import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties; import org.springframework.context.ResourceLoaderAware; import org.springframework.core.io.ResourceLoader; -import com.knecon.fforesight.mongo.database.commons.utils.MongoUtils; import com.knecon.fforesight.tenantcommons.EncryptionDecryptionService; import com.knecon.fforesight.tenantcommons.TenantProvider; import com.knecon.fforesight.tenantcommons.model.MongoDBConnection; import com.knecon.fforesight.tenantcommons.model.TenantResponse; +import com.knecon.fforesight.tenantcommons.utils.MongoConnectionStringHelper; import feign.RetryableException; -import liquibase.Contexts; -import liquibase.Liquibase; -import liquibase.changelog.ChangeSet; +import liquibase.database.Database; import liquibase.database.DatabaseFactory; +import liquibase.exception.LiquibaseException; import liquibase.ext.mongodb.database.MongoLiquibaseDatabase; import liquibase.integration.spring.SpringResourceAccessor; import lombok.RequiredArgsConstructor; @@ -54,41 +53,50 @@ public class TenantMongoLiquibaseExecutor implements InitializingBean, ResourceL @SneakyThrows protected void runOnAllTenants(List tenants) { - tenants.forEach(this::runLiquibase); - } - - - private void runLiquibase(TenantResponse tenant) { - - MongoDBConnection mongoDBConnection = tenant.getMongoDBConnection(); - var mongoUrl = MongoUtils.buildMongoUrl(mongoDBConnection); - log.info("Initializing MongoDB liquibase for tenant {} / {}", tenant.getTenantId(), mongoUrl); - - try (SpringResourceAccessor resourceAccessor = new SpringResourceAccessor(resourceLoader)) { - - try (MongoLiquibaseDatabase database = (MongoLiquibaseDatabase) DatabaseFactory.getInstance() - .openDatabase(mongoUrl, mongoDBConnection.getUsername(), encryptionService.decrypt(mongoDBConnection.getPassword()), null, resourceAccessor)) { - database.setSupportsValidator(false); - try (Liquibase liquibase = new Liquibase(tenantMongoLiquibaseProperties.getChangeLog(), resourceAccessor, database)) { - Contexts contexts = new Contexts(tenantMongoLiquibaseProperties.getContexts()); - List changeSetsList = liquibase.listUnrunChangeSets(contexts, null); - if (!changeSetsList.isEmpty()) { - liquibase.update(contexts); - } - } - } - - } catch (Exception e) { - log.error("Failed to run liquibase migration on MongoDB for tenant: {}", tenant.getTenantId(), e); - } - log.info("Liquibase ran on MongoDB for tenant " + tenant.getTenantId()); + tenants.forEach(this::runMongoLiquibase); } public void initializeTenant(String tenantId) { - runLiquibase(tenantProvider.getTenant(tenantId)); + runMongoLiquibase(tenantProvider.getTenant(tenantId)); } + + protected void runMongoLiquibase(TenantResponse tenant) { + + log.info("Initializing MongoDB liquibase for tenant {}", tenant.getTenantId()); + + try (SpringResourceAccessor resourceAccessor = new SpringResourceAccessor(resourceLoader)) { + + MongoDBConnection mongoDBConnection = tenant.getMongoDBConnection(); + try (MongoLiquibaseDatabase database = (MongoLiquibaseDatabase) DatabaseFactory.getInstance() + .openDatabase(MongoConnectionStringHelper.buildGenericMongoUrl(mongoDBConnection), + mongoDBConnection.getUsername(), + encryptionService.decrypt( mongoDBConnection.getPassword()), + null, + resourceAccessor)) { + database.setSupportsValidator(false); + + executeMongoSpringLiquibase(database); + } + + } catch (Exception e) { + log.error("Failed to run liquibase migration on MongoDB for tenant: {}", tenant.getTenantId(), e); + } log.info("Liquibase ran on MongoDB for tenant " + tenant.getTenantId()); + } + + + protected void executeMongoSpringLiquibase(Database database) throws LiquibaseException { + + MongoSpringLiquibase liquibase = new MongoSpringLiquibase(); + liquibase.setResourceLoader(resourceLoader); + liquibase.setDatabase(database); + liquibase.setChangeLog(tenantMongoLiquibaseProperties.getChangeLog()); + liquibase.setContexts(tenantMongoLiquibaseProperties.getContexts()); + liquibase.setClearCheckSums(tenantMongoLiquibaseProperties.isClearChecksums()); + liquibase.afterPropertiesSet(); + } + } diff --git a/src/main/java/com/knecon/fforesight/mongo/database/commons/service/MongoClientCache.java b/src/main/java/com/knecon/fforesight/mongo/database/commons/service/MongoClientCache.java index 558769f..0e3b1cf 100644 --- a/src/main/java/com/knecon/fforesight/mongo/database/commons/service/MongoClientCache.java +++ b/src/main/java/com/knecon/fforesight/mongo/database/commons/service/MongoClientCache.java @@ -1,6 +1,5 @@ package com.knecon.fforesight.mongo.database.commons.service; -import java.util.Collections; import java.util.concurrent.TimeUnit; import org.jetbrains.annotations.NotNull; @@ -12,9 +11,7 @@ import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.knecon.fforesight.mongo.database.commons.model.MongoClientConnection; import com.knecon.fforesight.tenantcommons.model.MongoDBConnection; -import com.mongodb.MongoClientSettings; -import com.mongodb.MongoCredential; -import com.mongodb.ServerAddress; +import com.knecon.fforesight.tenantcommons.utils.MongoConnectionStringHelper; import com.mongodb.client.MongoClient; import com.mongodb.client.MongoClients; @@ -46,7 +43,7 @@ public class MongoClientCache { var mongoDBConnection = mongoConnectionProvider.getMongoDBConnection(key); if (mongoDBConnection != null) { - return new MongoClientConnection(mongoDBConnection.getDatabase(), buildMongoClient(mongoDBConnection)); + return new MongoClientConnection(key, buildMongoClient(mongoDBConnection)); } throw new RuntimeException("No Connection provided"); } @@ -56,14 +53,7 @@ public class MongoClientCache { private MongoClient buildMongoClient(MongoDBConnection mongoDBConnection) { - MongoCredential credential = MongoCredential.createCredential(mongoDBConnection.getUsername(), - mongoDBConnection.getDatabase(), - mongoDBConnection.getPassword().toCharArray()); - return MongoClients.create(MongoClientSettings.builder() - .applyToClusterSettings(builder -> builder.hosts(Collections.singletonList(new ServerAddress(mongoDBConnection.getHost(), - Integer.parseInt(mongoDBConnection.getPort()))))) - .credential(credential) - .build()); + return MongoClients.create(MongoConnectionStringHelper.buildGenericMongoConnectionString(mongoDBConnection)); } diff --git a/src/main/java/com/knecon/fforesight/mongo/database/commons/utils/MongoUtils.java b/src/main/java/com/knecon/fforesight/mongo/database/commons/utils/MongoUtils.java deleted file mode 100644 index 3c691e0..0000000 --- a/src/main/java/com/knecon/fforesight/mongo/database/commons/utils/MongoUtils.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.knecon.fforesight.mongo.database.commons.utils; - -import com.knecon.fforesight.tenantcommons.model.MongoDBConnection; - -import lombok.experimental.UtilityClass; - -@UtilityClass -public class MongoUtils { - - - private StringBuilder createMongoConnectionStringBuilder(String host, String port, String database) { - - return new StringBuilder("mongodb://").append(host).append(':').append(port).append('/').append(database); - } - - private StringBuilder createMongoConnectionStringBuilder(String host, String port, String username, String password, String database) { - - return createMongoConnectionStringBuilder(host, port, username, password).append('/').append(database); - } - - - private StringBuilder createMongoConnectionStringBuilder(String host, String port, String username, String password) { - - return new StringBuilder("mongodb://").append(username).append(':').append(password).append("@").append(host).append(':').append(port); - } - - - public String buildMongoUrl(MongoDBConnection mongoDBConnection) { - - return createMongoConnectionStringBuilder(mongoDBConnection.getHost(), - mongoDBConnection.getPort(), - mongoDBConnection.getDatabase()).toString(); - } - - - public String buildMongoUrlWithAuth(MongoDBConnection mongoDBConnection) { - - return createMongoConnectionStringBuilder(mongoDBConnection.getHost(), - mongoDBConnection.getPort(), - mongoDBConnection.getUsername(), - mongoDBConnection.getPassword()).toString(); - } - - - public String buildMongoUrlWithAuthForDatabase(MongoDBConnection mongoDBConnection) { - - return createMongoConnectionStringBuilder(mongoDBConnection.getHost(), - mongoDBConnection.getPort(), - mongoDBConnection.getUsername(), - mongoDBConnection.getPassword(), - mongoDBConnection.getDatabase()).toString(); - - } - -}