org.smallmind.persistence.sql.pool.spring.DynamicPooledDataSourceInitializingBean Maven / Gradle / Ivy
/*
* Copyright (c) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 David Berkman
*
* This file is part of the SmallMind Code Project.
*
* The SmallMind Code Project is free software, you can redistribute
* it and/or modify it under either, at your discretion...
*
* 1) The terms of GNU Affero General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* ...or...
*
* 2) The terms of the Apache License, Version 2.0.
*
* The SmallMind Code Project is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License or Apache License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* and the Apache License along with the SmallMind Code Project. If not, see
* or .
*
* Additional permission under the GNU Affero GPL version 3 section 7
* ------------------------------------------------------------------
* If you modify this Program, or any covered work, by linking or
* combining it with other code, such other code is not for that reason
* alone subject to any of the requirements of the GNU Affero GPL
* version 3.
*/
package org.smallmind.persistence.sql.pool.spring;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import javax.sql.CommonDataSource;
import javax.sql.PooledConnection;
import org.smallmind.nutsnbolts.spring.RuntimeBeansException;
import org.smallmind.nutsnbolts.spring.SpringPropertyAccessor;
import org.smallmind.nutsnbolts.spring.SpringPropertyAccessorManager;
import org.smallmind.nutsnbolts.util.Option;
import org.smallmind.persistence.sql.pool.AbstractPooledDataSource;
import org.smallmind.persistence.sql.pool.DataSourceFactory;
import org.smallmind.persistence.sql.pool.PooledDataSourceFactory;
import org.smallmind.persistence.sql.pool.context.ContextualPooledDataSource;
import org.smallmind.persistence.sql.pool.context.DefaultContextualPoolNameTranslator;
import org.smallmind.quorum.pool.ComponentPoolException;
import org.smallmind.quorum.pool.complex.ComplexPoolConfig;
import org.smallmind.quorum.pool.complex.ComponentPool;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class DynamicPooledDataSourceInitializingBean implements InitializingBean, DisposableBean, DataSourceLocator {
/*
jdbc.url...<#> (required, for at least connection '0')
jdbc.user...<#> (required, for at least connection '0')
jdbc.password...<#> (required, for at least connection '0')
jdbc.max_statements. (optional - defaults to '0')
jdbc.validation_query. (optional - defaults to 'select 1')
jdbc.pool.test_on_create. (optional - defaults to 'false')
jdbc.pool.test_on_acquire. (optional - defaults to 'false')
jdbc.pool.initial_size. (optional - defaults to '0')
jdbc.pool.min_size. (optional - defaults to '0')
jdbc.pool.max_size. (optional - defaults to '10')
jdbc.pool.acquire_wait_time_millis. (optional - defaults to '0')
jdbc.pool.connection_timeout_millis. (optional - defaults to '0')
jdbc.pool.max_idle_seconds. (optional - defaults to '0')
jdbc.pool.max_lease_time_seconds. (optional - defaults to '0')
jdbc.mapping. (required for each data source binding)
*/
private final HashMap dataSourceMap = new HashMap();
private final HashMap poolNameMap = new HashMap();
private Map> factoryMap;
public void setFactoryMap (Map> factoryMap) {
this.factoryMap = factoryMap;
}
public CommonDataSource getDataSource (String dataSourceKey) {
CommonDataSource dataSource;
String poolName;
if ((poolName = poolNameMap.get(dataSourceKey)) == null) {
throw new RuntimeBeansException("No mapping definition was provided for data source key(%s)", dataSourceKey);
}
if ((dataSource = dataSourceMap.get(poolName)) == null) {
throw new RuntimeBeansException("No connection pool(%s) definition exists for data source key(%s)", poolName, dataSourceKey);
}
return dataSource;
}
@Override
public void afterPropertiesSet ()
throws SQLException, ComponentPoolException {
SpringPropertyAccessor springPropertyAccessor = SpringPropertyAccessorManager.getSpringPropertyAccessor();
for (String poolName : factoryMap.keySet()) {
dataSourceMap.put(poolName, parsePoolDefinition(springPropertyAccessor, poolName));
}
for (String key : springPropertyAccessor.getKeySet()) {
String dataSourceKey;
String poolName;
if (key.startsWith("jdbc.mapping.") && (!(dataSourceKey = key.substring("jdbc.mapping.".length())).contains("."))) {
poolNameMap.put(dataSourceKey, poolName = springPropertyAccessor.asString("jdbc.mapping." + dataSourceKey));
if (!dataSourceMap.containsKey(poolName)) {
throw new RuntimeBeansException("No connection pool(%s) definition exists for data source key(%s)", poolName, dataSourceKey);
}
}
}
for (AbstractPooledDataSource dataSource : dataSourceMap.values()) {
dataSource.startup();
}
}
@Override
public void destroy ()
throws ComponentPoolException {
for (AbstractPooledDataSource dataSource : dataSourceMap.values()) {
dataSource.shutdown();
}
}
private AbstractPooledDataSource parsePoolDefinition (SpringPropertyAccessor springPropertyAccessor, String poolName)
throws SQLException, ComponentPoolException {
ComplexPoolConfig complexPoolConfig = new ComplexPoolConfig();
HashMap> preContextMap = new HashMap<>();
HashMap postContextMap = new HashMap<>();
Option testOnCreateOption;
Option testOnAcquireOption;
Option acquireWaitTimeMillisOption;
Option connectionTimeoutMillisOption;
Option maxStatementsOption;
Option initialSizeOption;
Option minSizeOption;
Option maxSizeOption;
Option maxIdleSecondsOption;
Option maxLeaseTimeSecondsOption;
String validationQuery;
String urlPrefix = "jdbc.url." + poolName + ".";
String userPrefix = "jdbc.user." + poolName + ".";
String passwordPrefix = "jdbc.password." + poolName + ".";
for (String key : springPropertyAccessor.getKeySet()) {
if (key.startsWith(urlPrefix)) {
getDatabaseConnection(preContextMap, getContextIndex(key.substring(urlPrefix.length()))).setJdbcUrl(springPropertyAccessor.asString(key));
}
if (key.startsWith(userPrefix)) {
getDatabaseConnection(preContextMap, getContextIndex(key.substring(userPrefix.length()))).setUser(springPropertyAccessor.asString(key));
}
if (key.startsWith(passwordPrefix)) {
getDatabaseConnection(preContextMap, getContextIndex(key.substring(passwordPrefix.length()))).setPassword(springPropertyAccessor.asString(key));
}
}
if (preContextMap.isEmpty()) {
throw new RuntimeBeansException("Database connection pool(%s) has no defined connections", poolName);
}
if ((preContextMap.size() == 1) && (!preContextMap.containsKey(null))) {
throw new RuntimeBeansException("Database connection pool(%s) has only a single defined context, so should be defined without any context at all", poolName);
}
for (Map.Entry> contextEntry : preContextMap.entrySet()) {
HashMap connectionMap = contextEntry.getValue();
LinkedList connectionList = new LinkedList<>();
DatabaseConnection[] connections;
int index = 0;
while (!connectionMap.isEmpty()) {
DatabaseConnection connection;
if ((connection = connectionMap.remove(index++)) == null) {
if (contextEntry.getKey() == null) {
throw new RuntimeBeansException("Database connection pool(%s) is missing a connection definition at index(%d)", poolName, index - 1);
}
else {
throw new RuntimeBeansException("Database connection pool(%s) at context(%s) is missing a connection definition at index(%d)", poolName, contextEntry.getKey(), index - 1);
}
}
if (!connection.isComplete()) {
if (contextEntry.getKey() == null) {
throw new RuntimeBeansException("Database connection pool(%s) has an incomplete connection definition at index(%d)", poolName, index - 1);
}
else {
throw new RuntimeBeansException("Database connection pool(%s) at context(%s) has an incomplete connection definition at index(%d)", poolName, contextEntry.getKey(), index - 1);
}
}
connectionList.add(connection);
}
connections = new DatabaseConnection[connectionList.size()];
connectionList.toArray(connections);
postContextMap.put(contextEntry.getKey(), connections);
}
maxStatementsOption = springPropertyAccessor.asInt("jdbc.max_statements." + poolName);
validationQuery = springPropertyAccessor.asString("jdbc.validation_query." + poolName);
if (!(testOnCreateOption = springPropertyAccessor.asBoolean("jdbc.pool.test_on_create." + poolName)).isNone()) {
complexPoolConfig.setTestOnCreate(testOnCreateOption.get());
}
if (!(testOnAcquireOption = springPropertyAccessor.asBoolean("jdbc.pool.test_on_acquire." + poolName)).isNone()) {
complexPoolConfig.setTestOnAcquire(testOnAcquireOption.get());
}
if (!(initialSizeOption = springPropertyAccessor.asInt("jdbc.pool.initial_size." + poolName)).isNone()) {
complexPoolConfig.setInitialPoolSize(initialSizeOption.get());
}
if (!(minSizeOption = springPropertyAccessor.asInt("jdbc.pool.min_size." + poolName)).isNone()) {
complexPoolConfig.setMinPoolSize(minSizeOption.get());
}
if (!(maxSizeOption = springPropertyAccessor.asInt("jdbc.pool.max_size." + poolName)).isNone()) {
complexPoolConfig.setMaxPoolSize(maxSizeOption.get());
}
if (!(acquireWaitTimeMillisOption = springPropertyAccessor.asLong("jdbc.pool.acquire_wait_time_millis." + poolName)).isNone()) {
complexPoolConfig.setAcquireWaitTimeMillis(acquireWaitTimeMillisOption.get());
}
if (!(connectionTimeoutMillisOption = springPropertyAccessor.asLong("jdbc.pool.connection_timeout_millis." + poolName)).isNone()) {
complexPoolConfig.setCreationTimeoutMillis(connectionTimeoutMillisOption.get());
}
if (!(maxIdleSecondsOption = springPropertyAccessor.asInt("jdbc.pool.max_idle_seconds." + poolName)).isNone()) {
complexPoolConfig.setMaxIdleTimeSeconds(maxIdleSecondsOption.get());
}
if (!(maxLeaseTimeSecondsOption = springPropertyAccessor.asInt("jdbc.pool.max_lease_time_seconds." + poolName)).isNone()) {
complexPoolConfig.setMaxLeaseTimeSeconds(maxLeaseTimeSecondsOption.get());
}
if (postContextMap.size() == 1) {
return PooledDataSourceFactory.createPooledDataSource(poolName, factoryMap.get(poolName), validationQuery, maxStatementsOption.isNone() ? 0 : maxStatementsOption.get(), complexPoolConfig, postContextMap.get(null));
}
else {
ComponentPool extends PooledConnection>[] componentPools = new ComponentPool[postContextMap.size()];
DefaultContextualPoolNameTranslator poolNameTranslator = new DefaultContextualPoolNameTranslator(poolName, ':');
int index = 0;
for (Map.Entry contextEntry : postContextMap.entrySet()) {
componentPools[index++] = PooledConnectionComponentPoolFactory.constructComponentPool(poolNameTranslator.getPoolName(contextEntry.getKey()), factoryMap.get(poolName), validationQuery, maxStatementsOption.isNone() ? 0 : maxStatementsOption.get(), complexPoolConfig, contextEntry.getValue());
}
return new ContextualPooledDataSource(poolNameTranslator, componentPools);
}
}
private ContextIndex getContextIndex (String subKey) {
int periodPos;
if ((periodPos = subKey.indexOf('.')) < 0) {
return new ContextIndex(null, Integer.parseInt(subKey));
}
return new ContextIndex(subKey.substring(0, periodPos), Integer.parseInt(subKey.substring(periodPos + 1)));
}
private DatabaseConnection getDatabaseConnection (HashMap> contextMap, ContextIndex contextIndex) {
HashMap connectionMap;
DatabaseConnection databaseConnection;
if ((connectionMap = contextMap.get(contextIndex.getContext())) == null) {
contextMap.put(contextIndex.getContext(), connectionMap = new HashMap<>());
}
if ((databaseConnection = connectionMap.get(contextIndex.getIndex())) == null) {
connectionMap.put(contextIndex.getIndex(), databaseConnection = new DatabaseConnection());
}
return databaseConnection;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy