Pools.java
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (c) 2012-2014 Monty Program Ab
// Copyright (c) 2015-2025 MariaDB Corporation Ab
package org.mariadb.jdbc.pool;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.mariadb.jdbc.Configuration;
/** Pools */
public final class Pools {
private static final AtomicInteger poolIndex = new AtomicInteger();
private static final Map<Configuration, PoolHolder> poolMap = new ConcurrentHashMap<>();
private static ScheduledThreadPoolExecutor poolExecutor = null;
static class PoolHolder {
private final Configuration conf;
private final int poolIndex;
private final ScheduledThreadPoolExecutor executor;
private Pool pool;
PoolHolder(Configuration conf, int poolIndex, ScheduledThreadPoolExecutor executor) {
this.conf = conf;
this.poolIndex = poolIndex;
this.executor = executor;
}
synchronized Pool getPool() {
if (pool == null) {
pool = new Pool(conf, poolIndex, executor);
}
return pool;
}
}
/**
* Get existing pool for a configuration. Create it if it doesn't exist.
*
* @param conf configuration parser
* @return pool
*/
public static Pool retrievePool(Configuration conf) {
PoolHolder holder = poolMap.get(conf);
if (holder == null) {
synchronized (poolMap) {
holder = poolMap.get(conf);
if (holder == null) {
if (poolExecutor == null) {
poolExecutor =
new ScheduledThreadPoolExecutor(
1, new PoolThreadFactory("MariaDbPool-maxTimeoutIdle-checker"));
}
holder = new PoolHolder(conf, poolIndex.incrementAndGet(), poolExecutor);
poolMap.put(conf, holder);
}
}
}
// Don't initialize a pool while holding a lock on `poolMap`.
return holder.getPool();
}
/**
* Remove pool.
*
* @param pool pool to remove
*/
public static void remove(Pool pool) {
if (poolMap.containsKey(pool.getConf())) {
synchronized (poolMap) {
PoolHolder previous = poolMap.remove(pool.getConf());
if (previous != null && poolMap.isEmpty()) {
shutdownExecutor();
}
}
}
}
/** Close all pools. */
public static void close() {
synchronized (poolMap) {
for (PoolHolder holder : poolMap.values()) {
try {
holder.getPool().close();
} catch (Exception exception) {
// eat
}
}
shutdownExecutor();
poolMap.clear();
}
}
/**
* Closing a pool with name defined in url.
*
* @param poolName the option "poolName" value
*/
public static void close(String poolName) {
if (poolName == null) {
return;
}
synchronized (poolMap) {
for (PoolHolder holder : poolMap.values()) {
if (poolName.equals(holder.conf.poolName())) {
try {
holder
.getPool()
.close(); // Pool.close() calls Pools.remove(), which does the rest of the cleanup
} catch (Exception exception) {
// eat
}
return;
}
}
}
}
private static void shutdownExecutor() {
if (poolExecutor != null) {
poolExecutor.shutdown();
try {
poolExecutor.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException interrupted) {
// eat
}
poolExecutor = null;
}
}
}