diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/TomcatJdbcDataSourcePerTenantService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/TomcatJdbcDataSourcePerTenantService.java index b01d6999d..fb829d664 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/TomcatJdbcDataSourcePerTenantService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/database/TomcatJdbcDataSourcePerTenantService.java @@ -18,14 +18,21 @@ */ package org.apache.fineract.infrastructure.core.service.database; -import java.util.HashMap; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import javax.sql.DataSource; +import lombok.extern.slf4j.Slf4j; import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant; import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenantConnection; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; +import org.apache.fineract.infrastructure.core.service.tenant.TenantDetailsService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.stereotype.Service; /** @@ -34,19 +41,22 @@ import org.springframework.stereotype.Service; * * {@link ThreadLocalContextUtil} is used to retrieve the {@link FineractPlatformTenant} for the request. */ +@Slf4j @Service -public class TomcatJdbcDataSourcePerTenantService implements RoutingDataSourceService { +public class TomcatJdbcDataSourcePerTenantService implements RoutingDataSourceService, ApplicationListener { - private static final Map TENANT_TO_DATA_SOURCE_MAP = new HashMap<>(1); + private static final Map TENANT_TO_DATA_SOURCE_MAP = new ConcurrentHashMap<>(); private final DataSource tenantDataSource; + private final TenantDetailsService tenantDetailsService; private final DataSourcePerTenantServiceFactory dataSourcePerTenantServiceFactory; @Autowired public TomcatJdbcDataSourcePerTenantService(final @Qualifier("hikariTenantDataSource") DataSource tenantDataSource, - final DataSourcePerTenantServiceFactory dataSourcePerTenantServiceFactory) { + final DataSourcePerTenantServiceFactory dataSourcePerTenantServiceFactory, final TenantDetailsService tenantDetailsService) { this.tenantDataSource = tenantDataSource; this.dataSourcePerTenantServiceFactory = dataSourcePerTenantServiceFactory; + this.tenantDetailsService = tenantDetailsService; } @Override @@ -57,20 +67,42 @@ public class TomcatJdbcDataSourcePerTenantService implements RoutingDataSourceSe final FineractPlatformTenant tenant = ThreadLocalContextUtil.getTenant(); if (tenant != null) { final FineractPlatformTenantConnection tenantConnection = tenant.getConnection(); + Long tenantConnectionKey = tenantConnection.getConnectionId(); + // if tenantConnection information available switch to the + // appropriate datasource for that tenant. + actualDataSource = TENANT_TO_DATA_SOURCE_MAP.computeIfAbsent(tenantConnectionKey, (key) -> { + DataSource tenantSpecificDataSource = dataSourcePerTenantServiceFactory.createNewDataSourceFor(tenantConnection); + return tenantSpecificDataSource; + }); - synchronized (TENANT_TO_DATA_SOURCE_MAP) { - // if tenantConnection information available switch to the - // appropriate datasource for that tenant. - DataSource possibleDS = TENANT_TO_DATA_SOURCE_MAP.get(tenantConnection.getConnectionId()); - if (possibleDS != null) { - actualDataSource = possibleDS; - } else { - actualDataSource = dataSourcePerTenantServiceFactory.createNewDataSourceFor(tenantConnection); - TENANT_TO_DATA_SOURCE_MAP.put(tenantConnection.getConnectionId(), actualDataSource); - } - } } return actualDataSource; } + + @Override + public void onApplicationEvent(ContextRefreshedEvent event) { + final List allTenants = tenantDetailsService.findAllTenants(); + for (final FineractPlatformTenant tenant : allTenants) { + initializeDataSourceConnection(tenant); + } + } + + private void initializeDataSourceConnection(FineractPlatformTenant tenant) { + log.debug("Initializing database connection for {}", tenant.getName()); + final FineractPlatformTenantConnection tenantConnection = tenant.getConnection(); + Long tenantConnectionKey = tenantConnection.getConnectionId(); + TENANT_TO_DATA_SOURCE_MAP.computeIfAbsent(tenantConnectionKey, (key) -> { + DataSource tenantSpecificDataSource = dataSourcePerTenantServiceFactory.createNewDataSourceFor(tenantConnection); + try (Connection connection = tenantSpecificDataSource.getConnection()) { + String url = connection.getMetaData().getURL(); + log.debug("Established database connection with URL {}", url); + } catch (SQLException e) { + log.error("Error while initializing database connection for {}", tenant.getName(), e); + } + return tenantSpecificDataSource; + }); + log.debug("Database connection for {} initialized", tenant.getName()); + + } }