All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.smilego.tenant.persistence.hibernate.DynamicDataSourceBasedMultiTenantConnectionProvider Maven / Gradle / Ivy

Go to download

A application used as an example on how to set up pushing its components to the Central Repository.

There is a newer version: 1.2.5
Show newest version
package io.smilego.tenant.persistence.hibernate;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalListener;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import io.smilego.tenant.configuration.ParamConfiguration;
import io.smilego.tenant.model.Param;
import io.smilego.tenant.model.Service;
import io.smilego.tenant.model.Tenant;
import io.smilego.tenant.service.ParamService;
import io.smilego.tenant.service.ServiceService;
import io.smilego.tenant.service.TenantService;
import io.smilego.tenant.util.AESUtils;
import io.smilego.tenant.util.LogBuilder;
import org.hibernate.engine.jdbc.connections.spi.AbstractDataSourceBasedMultiTenantConnectionProviderImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;

import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

public class DynamicDataSourceBasedMultiTenantConnectionProvider
        extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl {

    private static final String TENANT_POOL_NAME_SUFFIX = "DataSource";

    protected Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    @Qualifier("masterDataSource")
    private DataSource masterDataSource;

    @Autowired
    @Qualifier("masterDataSourceProperties")
    private DataSourceProperties dataSourceProperties;

    @Autowired
    @Qualifier("tenantHikariConfig")
    private HikariConfig hikariConfig;

    @Value("${multitenancy.datasource-cache.maximumSize:100}")
    private Long maximumSize;

    @Value("${multitenancy.datasource-cache.expireAfterAccess:10}")
    private Integer expireAfterAccess;

    @Value("${multitenancy.security.encryption-key}")
    private String encryptionKey;

    @Value("${multitenancy.application.name}")
    public String applicationName;

    @Autowired
    private TenantService tenantService;

    @Autowired
    private ServiceService serviceService;

    @Autowired
    private ParamService paramService;

    private LoadingCache tenantDataSources;

    @PostConstruct
    private void createCache() {
        tenantDataSources = CacheBuilder.newBuilder()
                .maximumSize(maximumSize)
                .expireAfterAccess(expireAfterAccess, TimeUnit.MINUTES)
                .removalListener((RemovalListener) removal -> {
                    HikariDataSource ds = (HikariDataSource) removal.getValue();
                    ds.close(); // tear down properly
                    log.info(LogBuilder.of()
                            .header("Closing datasource")
                            .row("Pool name: ", ds.getPoolName()).build());
                })

                .build(new CacheLoader() {
                    public DataSource load(String key) {

                        setParamsService();
                        Tenant tenant = tenantService.getTenant(key);
                        return createAndConfigureDataSource(tenant);
                    }
                });
    }

    private void setParamsService(){
        if (ParamConfiguration.mapParams.isEmpty()){
            ParamConfiguration.applicationName = applicationName;
            Service service = serviceService.getServiceByName(applicationName);
            if(Objects.nonNull(service) && Objects.nonNull(service.getId())){
                List params = paramService.getParamsByServiceId(service.getId());
                if(Objects.nonNull(params) && params.size()>0){
                    ParamConfiguration.mapParams.put(service.getName(), params);
                }
            }
        }
    }

    public void cacheTenants(){
        List tenants = tenantService.getAllTenants();
        try {
            tenants.forEach(f->{
                try {
                    tenantDataSources.get(f.getTenantId());
                } catch (ExecutionException e) {
                    throw new RuntimeException(e);
                }
            });
        } catch (Exception e) {
            log.info(LogBuilder.of()
                    .header("Error getting tenants")
                    .row("Error: ", e.getMessage()).build());
        }
    }

    private DataSource createAndConfigureDataSource(Tenant tenant) {

        HikariConfig config = this.hikariConfig;

        config.setUsername(tenant.getDb());
        config.setPassword(AESUtils.decrypt(encryptionKey, tenant.getPassword()));
        config.setJdbcUrl(tenant.getUrl().concat("/").concat(Tenant.TENANT_DATABASE_PREFIX).concat(applicationName));
        config.setPoolName(tenant.getTenantId() + TENANT_POOL_NAME_SUFFIX);

        HikariDataSource ds = new HikariDataSource(config);

        log.info(LogBuilder.of()
                .header("Configured datasource")
                .row("Pool name: ", ds.getPoolName()).build());
        return ds;
    }
    @Override
    protected DataSource selectAnyDataSource() {
        return masterDataSource;
    }

    @Override
    protected DataSource selectDataSource(String tenantIdentifier) {

        try {
            return tenantDataSources.get(tenantIdentifier);
        } catch (ExecutionException e) {
            throw new RuntimeException("Failed to load DataSource for tenant: " + tenantIdentifier);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy