diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/GlobalConfigurationProperty.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/configuration/domain/GlobalConfigurationProperty.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/GlobalConfigurationProperty.java rename to fineract-core/src/main/java/org/apache/fineract/infrastructure/configuration/domain/GlobalConfigurationProperty.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/GlobalConfigurationRepository.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/configuration/domain/GlobalConfigurationRepository.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/GlobalConfigurationRepository.java rename to fineract-core/src/main/java/org/apache/fineract/infrastructure/configuration/domain/GlobalConfigurationRepository.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/GlobalConfigurationRepositoryWrapper.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/configuration/domain/GlobalConfigurationRepositoryWrapper.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/GlobalConfigurationRepositoryWrapper.java rename to fineract-core/src/main/java/org/apache/fineract/infrastructure/configuration/domain/GlobalConfigurationRepositoryWrapper.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/exception/GlobalConfigurationPropertyNotFoundException.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/configuration/exception/GlobalConfigurationPropertyNotFoundException.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/exception/GlobalConfigurationPropertyNotFoundException.java rename to fineract-core/src/main/java/org/apache/fineract/infrastructure/configuration/exception/GlobalConfigurationPropertyNotFoundException.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/MoneyHelperInitializationService.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/configuration/service/MoneyHelperInitializationService.java similarity index 87% rename from fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/MoneyHelperInitializationService.java rename to fineract-core/src/main/java/org/apache/fineract/infrastructure/configuration/service/MoneyHelperInitializationService.java index 04eac2f3d..34fea1c7d 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/MoneyHelperInitializationService.java +++ b/fineract-core/src/main/java/org/apache/fineract/infrastructure/configuration/service/MoneyHelperInitializationService.java @@ -26,6 +26,8 @@ import org.apache.fineract.infrastructure.configuration.domain.GlobalConfigurati import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; import org.apache.fineract.organisation.monetary.domain.MoneyHelper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; /** @@ -40,7 +42,10 @@ import org.springframework.stereotype.Service; @RequiredArgsConstructor public class MoneyHelperInitializationService { - private final GlobalConfigurationRepositoryWrapper globalConfigurationRepository; + // TODO: this is preventing the circular dependency... + @Lazy + @Autowired + private GlobalConfigurationRepositoryWrapper globalConfigurationRepository; /** * Initialize MoneyHelper for a specific tenant. This method should be called during tenant setup and whenever @@ -56,6 +61,7 @@ public class MoneyHelperInitializationService { String tenantIdentifier = tenant.getTenantIdentifier(); + FineractPlatformTenant originalTenant = ThreadLocalContextUtil.getTenant(); try { // Set tenant context to read configuration ThreadLocalContextUtil.setTenant(tenant); @@ -69,8 +75,7 @@ public class MoneyHelperInitializationService { log.error("Failed to initialize MoneyHelper for tenant '{}'", tenantIdentifier, e); throw new RuntimeException("Failed to initialize MoneyHelper for tenant: " + tenantIdentifier, e); } finally { - // Clear tenant context - ThreadLocalContextUtil.clearTenant(); + ThreadLocalContextUtil.setTenant(originalTenant); } } @@ -85,6 +90,10 @@ public class MoneyHelperInitializationService { return MoneyHelper.isTenantInitialized(tenantIdentifier); } + public boolean isTenantInitialized(FineractPlatformTenant tenant) { + return isTenantInitialized(tenant.getTenantIdentifier()); + } + /** * Get the rounding mode from configuration with fallback to default. * diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/MoneyHelperStartupInitializationService.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/configuration/service/MoneyHelperStartupInitializationService.java similarity index 100% rename from fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/MoneyHelperStartupInitializationService.java rename to fineract-core/src/main/java/org/apache/fineract/infrastructure/configuration/service/MoneyHelperStartupInitializationService.java diff --git a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/database/TomcatJdbcDataSourcePerTenantService.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/database/TomcatJdbcDataSourcePerTenantService.java index e3b792e1b..a6c1889dd 100644 --- a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/database/TomcatJdbcDataSourcePerTenantService.java +++ b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/database/TomcatJdbcDataSourcePerTenantService.java @@ -18,14 +18,17 @@ */ package org.apache.fineract.infrastructure.core.service.database; +import com.google.common.collect.Sets; import java.sql.Connection; import java.sql.SQLException; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import javax.sql.DataSource; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.fineract.infrastructure.configuration.service.MoneyHelperInitializationService; import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant; import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenantConnection; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; @@ -53,6 +56,9 @@ public class TomcatJdbcDataSourcePerTenantService implements RoutingDataSourceSe private final DataSourcePerTenantServiceFactory dataSourcePerTenantServiceFactory; + private final MoneyHelperInitializationService moneyHelperInitializationService; + private final Set tenantMoneyInitializingSet = Sets.newConcurrentHashSet(); + @Override public DataSource retrieveDataSource() { // default to tenant database datasource @@ -66,7 +72,23 @@ public class TomcatJdbcDataSourcePerTenantService implements RoutingDataSourceSe // appropriate datasource for that tenant. actualDataSource = TENANT_TO_DATA_SOURCE_MAP.computeIfAbsent(tenantConnectionKey, (key) -> dataSourcePerTenantServiceFactory.createNewDataSourceFor(tenant, tenantConnection)); + } + // TODO: This is definitely not the optimal place to initialize the rounding modes + // Preferably nothing should use a statically referenced context and the initialization + // should happen within the rounding mode retrieval + if (tenant != null) { + Long connectionId = tenant.getConnection().getConnectionId(); + if (!tenantMoneyInitializingSet.contains(connectionId) && !moneyHelperInitializationService.isTenantInitialized(tenant)) { + // Double check to prevent visibility and race-condition issues + synchronized (tenantMoneyInitializingSet) { + if (!tenantMoneyInitializingSet.contains(connectionId)) { + tenantMoneyInitializingSet.add(connectionId); + moneyHelperInitializationService.initializeTenantRoundingMode(tenant); + tenantMoneyInitializingSet.remove(connectionId); + } + } + } } return actualDataSource;