FINERACT-2389: Fix for rounding mode not being initialized whenever a new tenant is added to the system without a restart

This commit is contained in:
Arnold Galovics
2025-11-06 14:46:25 +01:00
committed by Arnold Gálovics
parent 63c220e5d2
commit 5079203d57
7 changed files with 34 additions and 3 deletions
@@ -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.
*
@@ -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<Long> 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;