blob: 9552465c78632153d8caa7298b0afa6cb260c799 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.cn.core.data.jpa.local;
import org.apache.fineract.cn.core.data.jpa.core.MigrationHelper;
import org.apache.fineract.cn.core.data.jpa.local.repository.DataSourceInstance;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.fineract.cn.lang.TenantContextHolder;
import org.slf4j.Logger;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class LocalRoutingDataSource extends AbstractRoutingDataSource {
private final Logger logger;
private final LocalDatabaseProperties localDatabaseProperties;
private final Map<String, DataSource> cachedDataSources = new ConcurrentHashMap<>();
private BasicDataSource managementDataSource;
LocalRoutingDataSource(final Logger logger,
final LocalDatabaseProperties localDatabaseProperties) {
super();
this.logger = logger;
this.localDatabaseProperties = localDatabaseProperties;
}
private DataSource getManagementDataSource() {
if (this.managementDataSource == null) {
this.logger.debug("Creating management data source.");
this.managementDataSource = new BasicDataSource();
this.managementDataSource.setDriverClassName(this.localDatabaseProperties.getManagement().getDriverClass());
this.managementDataSource.setUrl(this.localDatabaseProperties.getManagement().getUrl());
final String username = this.localDatabaseProperties.getManagement().getUsername();
if (username != null) {
this.managementDataSource.setUsername(username);
}
final String password = this.localDatabaseProperties.getManagement().getPassword();
if (password != null) {
this.managementDataSource.setPassword(password);
}
this.managementDataSource.setInitialSize(this.localDatabaseProperties.getPool().getMinSize());
this.managementDataSource.setMaxTotal(this.localDatabaseProperties.getPool().getMaxSize());
this.managementDataSource.setMaxWaitMillis(this.localDatabaseProperties.getPool().getWaitTime());
}
this.logger.debug("Providing management data source.");
return this.managementDataSource;
}
@Override
protected Object determineCurrentLookupKey() {
return TenantContextHolder.identifier().orElse(null);
}
@Override
protected DataSource determineTargetDataSource() {
final Object dataSourceIdentifier = this.determineCurrentLookupKey();
if (dataSourceIdentifier == null) {
return this.getManagementDataSource();
}
final String dataSourceIdentifierAsString = dataSourceIdentifier.toString();
this.cachedDataSources.computeIfAbsent(dataSourceIdentifierAsString, s -> {
this.logger.debug("Creating data source for '{}'.", dataSourceIdentifierAsString);
final DataSourceInstance dataSourceInstanceDetails = this.findDataSourceInstanceDetails(s);
final BasicDataSource basicDataSource = new BasicDataSource();
basicDataSource.setDriverClassName(dataSourceInstanceDetails.getDriverClass());
basicDataSource.setUrl(dataSourceInstanceDetails.getJdbcUrl());
basicDataSource.setUsername(dataSourceInstanceDetails.getUsername());
basicDataSource.setPassword(dataSourceInstanceDetails.getPassword());
basicDataSource.setInitialSize(this.localDatabaseProperties.getPool().getMinSize());
basicDataSource.setMaxTotal(this.localDatabaseProperties.getPool().getMaxSize());
basicDataSource.setMaxWaitMillis(this.localDatabaseProperties.getPool().getWaitTime());
return basicDataSource;
});
this.logger.debug("Providing data source for '{}'.", dataSourceIdentifierAsString);
return this.cachedDataSources.get(dataSourceIdentifierAsString);
}
private DataSourceInstance findDataSourceInstanceDetails(final String dataSourceIdentifier) {
this.logger.debug("Fetching data source details for '{}'.", dataSourceIdentifier);
final String query = "SELECT driver_class, jdbc_url, username, password FROM data_source_instances WHERE identifier = ? ";
final DataSource managementDataSource = this.getManagementDataSource();
try (
final Connection conn = managementDataSource.getConnection();
final PreparedStatement pstmt = conn.prepareCall(query)) {
pstmt.setString(1, dataSourceIdentifier);
try (final ResultSet rset = pstmt.executeQuery()) {
if (rset.next()) {
final DataSourceInstance dataSourceInstance = new DataSourceInstance();
dataSourceInstance.setDriverClass(rset.getString(1));
dataSourceInstance.setJdbcUrl(rset.getString(2));
dataSourceInstance.setUsername(rset.getString(3));
dataSourceInstance.setPassword(rset.getString(4));
return dataSourceInstance;
}
} catch (final SQLException sqlex) {
throw new IllegalStateException(sqlex.getMessage());
}
} catch (final SQLException sqlex) {
throw new IllegalStateException(sqlex.getMessage());
}
throw new IllegalStateException("No data source instance found for identifier '" + dataSourceIdentifier + "'.");
}
@PostConstruct
public void init() throws Exception {
MigrationHelper.execute(this.getManagementDataSource(), "db/changelog/db.changelog-local.yml");
}
}