package org.malagu.multitenant.service; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.sql.DataSource; import org.malagu.multitenant.Constants; import org.malagu.multitenant.MultitenantUtils; import org.malagu.multitenant.domain.DataSourceInfo; import org.malagu.multitenant.domain.Organization; import org.malagu.multitenant.listener.DataSourceCreateListener; import org.malagu.multitenant.listener.OrgDataSourceCreateEvent; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.boot.jdbc.EmbeddedDatabaseConnection; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.jdbc.datasource.SingleConnectionDataSource; import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; /** * @author Kevin Yang (mailto:[email protected]) * @since 2017年11月24日 */ @Service public class DataSourceServiceImpl implements DataSourceService, InitializingBean, ApplicationContextAware { @Autowired private DataSource dataSource; @Autowired private DataSourceProperties properties; @Autowired private DataSourceInfoService dataSourceInfoService; private Map<String, DataSource> dataSourceMap = new ConcurrentHashMap<String, DataSource>(); @Autowired(required = false) private List<DataSourceCreateListener> listeners; @Autowired private DatabaseNameService databaseNameService; private ApplicationContext applicationContext; @Override public DataSource getDataSource(Organization organization) { return dataSourceMap.get(organization.getId()); } @SuppressWarnings("unchecked") @Override public DataSource createDataSource(Organization organization) { return MultitenantUtils.doQuery(() -> { DataSource dataSouce = null; DataSourceInfo dataSourceInfo = dataSourceInfoService.get(organization); if (StringUtils.isEmpty(dataSourceInfo.getJndiName())) { String master = Constants.MASTER; if (EmbeddedDatabaseConnection.isEmbedded(dataSourceInfo.getDriverClassName())) { master = properties.determineDatabaseName(); } DataSourceBuilder<?> factory = this.properties.initializeDataSourceBuilder(); factory.url(dataSourceInfo.getUrl().replace(databaseNameService.getDatabaseName(master), databaseNameService.getDatabaseName(organization.getId()))) .username(dataSourceInfo.getUsername()) .password(dataSourceInfo.getPassword()); if (!StringUtils.isEmpty(dataSourceInfo.getDriverClassName())) { factory.driverClassName(dataSourceInfo.getDriverClassName()); } if (!StringUtils.isEmpty(dataSourceInfo.getType())) { try { factory.type((Class<? extends DataSource>) Class.forName(dataSourceInfo.getType())); } catch (ClassNotFoundException e) { throw new RuntimeException(e.getMessage()); } } publishEvent(organization, dataSourceInfo, factory); dataSouce = factory.build(); } else { JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup(); dataSouce = dataSourceLookup.getDataSource(dataSourceInfo.getJndiName()); } dataSourceMap.put(organization.getId(), dataSouce); this.applicationContext.publishEvent(new OrgDataSourceCreateEvent(dataSouce)); return dataSouce; }); } private void publishEvent(Organization organization, DataSourceInfo dataSourceInfo, DataSourceBuilder<?> dataSourceBuilder) { if (listeners != null) { for (DataSourceCreateListener dataSourceCreateListener : listeners) { dataSourceCreateListener.onCreate(organization, dataSourceInfo, dataSourceBuilder); } } } @Override public SingleConnectionDataSource createSingleConnectionDataSource(Organization organization) { DataSourceInfo dataSourceInfo = dataSourceInfoService.get(organization); if (!StringUtils.isEmpty(dataSourceInfo.getJndiName())) { return null; } SingleConnectionDataSource dataSource = new SingleConnectionDataSource( dataSourceInfo.getUrl(), dataSourceInfo.getUsername(), dataSourceInfo.getPassword(), true); dataSource.setAutoCommit(true); return dataSource; } @Override public DataSource getOrCreateDataSource(String organizationId) { Organization organization = new Organization(); organization.setId(organizationId); return getOrCreateDataSource(organization); } @Override public DataSource getOrCreateDataSource(Organization organization) { DataSource dataSource = getDataSource(organization); if (dataSource == null) { dataSource = createDataSource(organization); dataSourceMap.put(organization.getId(), dataSource); } return dataSource; } @Override public void afterPropertiesSet() throws Exception { dataSourceMap.put(Constants.MASTER, dataSource); if (listeners != null) { AnnotationAwareOrderComparator.sort(listeners); } } @Override public void removeDataSource(Organization organization) { dataSourceMap.remove(organization.getId()); } @Override public void clearDataSource() { dataSourceMap.clear(); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }