Compare commits

...

10 commits

26 changed files with 274 additions and 277 deletions

View file

@ -15,10 +15,10 @@ ext {
description = 'JDBC connection pool and utilities' description = 'JDBC connection pool and utilities'
inceptionYear = '2018' inceptionYear = '2018'
url = 'https://xbib.org/' + user + '/' + name url = 'https://xbib.org/' + user + '/' + name
scmUrl = 'https://xbib.org/' + user + '/' + name scmUrl = url
scmConnection = 'scm:git:git://xbib.org/' + user + '/' + name + '.git' scmConnection = 'scm:git:git://xbib.org/' + user + '/' + name + '.git'
scmDeveloperConnection = 'scm:git:ssh://forgejo@xbib.org:' + user + '/' + name + '.git' scmDeveloperConnection = 'scm:git:ssh://forgejo@xbib.org:' + user + '/' + name + '.git'
issueManagementSystem = 'Github' issueManagementSystem = 'Forgejo'
issueManagementUrl = ext.scmUrl + '/issues' issueManagementUrl = ext.scmUrl + '/issues'
licenseName = 'The Apache License, Version 2.0' licenseName = 'The Apache License, Version 2.0'
licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt'

View file

@ -1,5 +1,3 @@
group = org.xbib group = org.xbib
name = database name = database
version = 2.0.2 version = 2.2.0
org.gradle.warning.mode = ALL

View file

@ -19,7 +19,7 @@ jar {
tasks.withType(JavaCompile) { tasks.withType(JavaCompile) {
options.fork = true options.fork = true
options.forkOptions.jvmArgs += ['-Duser.language=en','-Duser.country=US'] options.forkOptions.jvmArgs += ['-Duser.language=en','-Duser.country=US']
options.compilerArgs << '-Xlint:all' options.compilerArgs << '-Xlint:all,-exports'
options.encoding = 'UTF-8' options.encoding = 'UTF-8'
} }

View file

@ -2,7 +2,7 @@ if (project.hasProperty('forgeJoToken')) {
publishing { publishing {
repositories { repositories {
maven { maven {
url 'https://xbib.org/api/packages/joerg/maven' url project.property('forgeJoUrl')
credentials(HttpHeaderCredentials) { credentials(HttpHeaderCredentials) {
name = "Authorization" name = "Authorization"
value = "token ${project.property('forgeJoToken')}" value = "token ${project.property('forgeJoToken')}"

View file

@ -1,27 +0,0 @@
apply plugin: 'ivy-publish'
publishing {
repositories {
ivy {
url = "https://xbib.org/repo"
}
}
publications {
ivy(IvyPublication) {
from components.java
descriptor {
license {
name = 'The Apache License, Version 2.0'
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
author {
name = 'Jörg Prante'
url = 'http://example.com/users/jane'
}
descriptor.description {
text = rootProject.ext.description
}
}
}
}
}

View file

@ -19,7 +19,7 @@ publishing {
id = 'jprante' id = 'jprante'
name = 'Jörg Prante' name = 'Jörg Prante'
email = 'joergprante@gmail.com' email = 'joergprante@gmail.com'
url = 'https://github.com/jprante' url = 'https://xbib.org/joerg'
} }
} }
scm { scm {

Binary file not shown.

View file

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

20
gradlew.bat vendored
View file

@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute if %ERRORLEVEL% equ 0 goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail goto fail
@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute if exist "%JAVA_EXE%" goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail goto fail

View file

@ -118,7 +118,8 @@ public class Pool implements BagStateListener {
* *
* @param config the config * @param config the config
*/ */
public Pool(PoolConfig config) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { public Pool(PoolConfig config) throws ClassNotFoundException,
NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
config.validate(); config.validate();
this.config = config; this.config = config;
logger.log(Level.INFO, () -> "starting new pool: " + config.getPoolName()); logger.log(Level.INFO, () -> "starting new pool: " + config.getPoolName());
@ -146,16 +147,26 @@ public class Pool implements BagStateListener {
checkFailFast(initializationTimeout); checkFailFast(initializationTimeout);
} }
ThreadFactory threadFactory = config.getThreadFactory(); ThreadFactory threadFactory = config.getThreadFactory();
ClassLoader contextClassLoader = config.getContextClassLoader();
int maxPoolSize = config.getMaximumPoolSize(); int maxPoolSize = config.getMaximumPoolSize();
LinkedBlockingQueue<Runnable> addConnectionQueue = new LinkedBlockingQueue<>(maxPoolSize); LinkedBlockingQueue<Runnable> addConnectionQueue = new LinkedBlockingQueue<>(maxPoolSize);
this.addConnectionQueueReadOnlyView = Collections.unmodifiableCollection(addConnectionQueue); this.addConnectionQueueReadOnlyView = Collections.unmodifiableCollection(addConnectionQueue);
this.addConnectionExecutor = createThreadPoolExecutor(addConnectionQueue, poolName + " connection adder", threadFactory, new ThreadPoolExecutor.DiscardOldestPolicy()); this.addConnectionExecutor = createThreadPoolExecutor(addConnectionQueue,
this.closeConnectionExecutor = createThreadPoolExecutor(maxPoolSize, poolName + " connection closer", threadFactory, new ThreadPoolExecutor.CallerRunsPolicy()); poolName + " connection adder",
threadFactory,
contextClassLoader,
new ThreadPoolExecutor.DiscardOldestPolicy());
this.closeConnectionExecutor = createThreadPoolExecutor(maxPoolSize,
poolName + " connection closer",
threadFactory,
contextClassLoader,
new ThreadPoolExecutor.CallerRunsPolicy());
this.leakTaskFactory = new ProxyLeakTaskFactory(config.getLeakDetectionThreshold(), houseKeepingExecutorService); this.leakTaskFactory = new ProxyLeakTaskFactory(config.getLeakDetectionThreshold(), houseKeepingExecutorService);
this.houseKeeperTask = houseKeepingExecutorService.scheduleWithFixedDelay(new HouseKeeper(this), 100L, config.getHousekeepingPeriodMs(), TimeUnit.MILLISECONDS); this.houseKeeperTask = houseKeepingExecutorService.scheduleWithFixedDelay(new HouseKeeper(this), 100L, config.getHousekeepingPeriodMs(), TimeUnit.MILLISECONDS);
if (Boolean.getBoolean("org.xbib.jdbc.connection.pool.blockUntilFilled") && config.getInitializationFailTimeout() > 1) { if (Boolean.getBoolean("org.xbib.jdbc.connection.pool.blockUntilFilled") && config.getInitializationFailTimeout() > 1) {
addConnectionExecutor.setCorePoolSize(Math.min(16, Runtime.getRuntime().availableProcessors())); int procs = Math.min(16, Runtime.getRuntime().availableProcessors());
addConnectionExecutor.setMaximumPoolSize(Math.min(16, Runtime.getRuntime().availableProcessors())); addConnectionExecutor.setCorePoolSize(procs);
addConnectionExecutor.setMaximumPoolSize(procs);
final long startTime = ClockSource.currentTime(); final long startTime = ClockSource.currentTime();
while (ClockSource.elapsedMillis(startTime) < config.getInitializationFailTimeout() && getTotalConnections() < config.getMinimumIdle()) { while (ClockSource.elapsedMillis(startTime) < config.getInitializationFailTimeout() && getTotalConnections() < config.getMinimumIdle()) {
quietlySleep(TimeUnit.MILLISECONDS.toMillis(100)); quietlySleep(TimeUnit.MILLISECONDS.toMillis(100));
@ -303,8 +314,11 @@ public class Pool implements BagStateListener {
addConnectionExecutor.awaitTermination(getLoginTimeout(), TimeUnit.SECONDS); addConnectionExecutor.awaitTermination(getLoginTimeout(), TimeUnit.SECONDS);
destroyHouseKeepingExecutorService(); destroyHouseKeepingExecutorService();
bag.close(); bag.close();
final ExecutorService assassinExecutor = createThreadPoolExecutor(config.getMaximumPoolSize(), poolName + " connection assassinator", final ExecutorService assassinExecutor = createThreadPoolExecutor(config.getMaximumPoolSize(),
config.getThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy()); poolName + " connection assassinator",
config.getThreadFactory(),
config.getContextClassLoader(),
new ThreadPoolExecutor.CallerRunsPolicy());
try { try {
final long start = ClockSource.currentTime(); final long start = ClockSource.currentTime();
do { do {
@ -413,6 +427,7 @@ public class Pool implements BagStateListener {
if (ds == null) { if (ds == null) {
String dsClassName = config.getDataSourceClassName(); String dsClassName = config.getDataSourceClassName();
if (dsClassName != null) { if (dsClassName != null) {
// we must use the system class loader
Class<?> clazz = Class.forName(dsClassName, true, ClassLoader.getSystemClassLoader()); Class<?> clazz = Class.forName(dsClassName, true, ClassLoader.getSystemClassLoader());
ds = (DataSource) clazz.getDeclaredConstructor().newInstance(); ds = (DataSource) clazz.getDeclaredConstructor().newInstance();
} else if (url != null) { } else if (url != null) {
@ -814,7 +829,8 @@ public class Pool implements BagStateListener {
private ScheduledExecutorService initializeHouseKeepingExecutorService() { private ScheduledExecutorService initializeHouseKeepingExecutorService() {
if (config.getScheduledExecutor() == null) { if (config.getScheduledExecutor() == null) {
ThreadFactory threadFactory = Optional.ofNullable(config.getThreadFactory()).orElseGet(() -> ThreadFactory threadFactory = Optional.ofNullable(config.getThreadFactory()).orElseGet(() ->
new DefaultThreadFactory(poolName + "-housekeeper", true)); new DefaultThreadFactory(poolName + "-housekeeper",
Thread.currentThread().getContextClassLoader(), true));
ScheduledThreadPoolExecutor executor = ScheduledThreadPoolExecutor executor =
new ScheduledThreadPoolExecutor(1, threadFactory, new ScheduledThreadPoolExecutor(1, threadFactory,
new ThreadPoolExecutor.DiscardPolicy()); new ThreadPoolExecutor.DiscardPolicy());
@ -867,16 +883,18 @@ public class Pool implements BagStateListener {
* *
* @param queueSize the queue size * @param queueSize the queue size
* @param threadName the thread name * @param threadName the thread name
* @param threadFactory an optional ThreadFactory * @param threadFactory an optional ThreadFactory, if null, a DefaultThreadFactory will be used
* @param contextClassLoader the context class loader if the thread factory is null
* @param policy the RejectedExecutionHandler policy * @param policy the RejectedExecutionHandler policy
* @return a ThreadPoolExecutor * @return a ThreadPoolExecutor
*/ */
private ThreadPoolExecutor createThreadPoolExecutor(int queueSize, private ThreadPoolExecutor createThreadPoolExecutor(int queueSize,
String threadName, String threadName,
ThreadFactory threadFactory, ThreadFactory threadFactory,
ClassLoader contextClassLoader,
RejectedExecutionHandler policy) { RejectedExecutionHandler policy) {
if (threadFactory == null) { if (threadFactory == null) {
threadFactory = new DefaultThreadFactory(threadName, true); threadFactory = new DefaultThreadFactory(threadName, contextClassLoader, true);
} }
LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(queueSize); LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(queueSize);
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1,
@ -897,9 +915,10 @@ public class Pool implements BagStateListener {
private ThreadPoolExecutor createThreadPoolExecutor(BlockingQueue<Runnable> queue, private ThreadPoolExecutor createThreadPoolExecutor(BlockingQueue<Runnable> queue,
String threadName, String threadName,
ThreadFactory threadFactory, ThreadFactory threadFactory,
ClassLoader contextClassLoader,
RejectedExecutionHandler policy) { RejectedExecutionHandler policy) {
if (threadFactory == null) { if (threadFactory == null) {
threadFactory = new DefaultThreadFactory(threadName, true); threadFactory = new DefaultThreadFactory(threadName, contextClassLoader, true);
} }
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1,
5, TimeUnit.SECONDS, queue, threadFactory, policy); 5, TimeUnit.SECONDS, queue, threadFactory, policy);
@ -975,7 +994,7 @@ public class Pool implements BagStateListener {
* *
* @return true if we should create a connection, false if the need has disappeared * @return true if we should create a connection, false if the need has disappeared
*/ */
synchronized boolean shouldCreateAnotherConnection() { public synchronized boolean shouldCreateAnotherConnection() {
return getTotalConnections() < config.getMaximumPoolSize() && return getTotalConnections() < config.getMaximumPoolSize() &&
(bag.getWaitingThreadCount() > 0 || getIdleConnections() < config.getMinimumIdle()); (bag.getWaitingThreadCount() > 0 || getIdleConnections() < config.getMinimumIdle());
} }

View file

@ -1,5 +1,6 @@
package org.xbib.jdbc.connection.pool; package org.xbib.jdbc.connection.pool;
import java.lang.reflect.InvocationTargetException;
import java.sql.Connection; import java.sql.Connection;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
@ -74,6 +75,8 @@ public class PoolConfig {
private ThreadFactory threadFactory; private ThreadFactory threadFactory;
private ClassLoader contextClassLoader;
private ScheduledExecutorService scheduledExecutor; private ScheduledExecutorService scheduledExecutor;
/** /**
@ -297,15 +300,6 @@ public class PoolConfig {
this.dataSource = dataSource; this.dataSource = dataSource;
} }
/**
* Get the name of the JDBC {@link DataSource} class used to create Connections.
*
* @return the fully qualified name of the JDBC {@link DataSource} class
*/
public String getDataSourceClassName() {
return dataSourceClassName;
}
/** /**
* Set the fully qualified class name of the JDBC {@link DataSource} that will be used create Connections. * Set the fully qualified class name of the JDBC {@link DataSource} that will be used create Connections.
* *
@ -315,6 +309,15 @@ public class PoolConfig {
this.dataSourceClassName = className; this.dataSourceClassName = className;
} }
/**
* Get the name of the JDBC {@link DataSource} class used to create Connections.
*
* @return the fully qualified name of the JDBC {@link DataSource} class
*/
public String getDataSourceClassName() {
return dataSourceClassName;
}
public Properties getProperties() { public Properties getProperties() {
return properties; return properties;
} }
@ -324,23 +327,47 @@ public class PoolConfig {
} }
public void setDriverClassName(String driverClassName) { public void setDriverClassName(String driverClassName) {
Class<?> driverClass = attemptFromContextLoader(driverClassName); Class<?> driverClass = null;
ClassLoader threadContextClassLoader = Thread.currentThread().getContextClassLoader();
if (threadContextClassLoader != null) {
try { try {
if (driverClass == null) { driverClass = threadContextClassLoader.loadClass(driverClassName);
driverClass = this.getClass().getClassLoader().loadClass(driverClassName); if (driverClass != null) {
logger.log(Level.FINE, () -> "driver class found in the PoolConfig class classloader: " + driverClassName + " " + this.getClass().getClassLoader()); logger.log(Level.FINE, () -> "driver class found in thread context class loader:" + driverClassName);
} }
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
logger.log(Level.SEVERE, "failed to load driver class from PoolConfig class classloader: " + driverClassName + " " + this.getClass().getClassLoader()); logger.log(Level.FINE, () -> "failed to load driver class from thread context class loader" + driverClassName);
}
}
try {
if (driverClass == null) {
driverClass = getClass().getClassLoader().loadClass(driverClassName);
if (driverClass != null) {
logger.log(Level.FINE, () -> "driver class found in the class classloader: " + driverClassName);
}
}
} catch (ClassNotFoundException e) {
logger.log(Level.FINE, () -> "failed to load driver class from class classloader: " + driverClassName);
}
try {
if (driverClass == null) {
driverClass = ClassLoader.getSystemClassLoader().loadClass(driverClassName);
if (driverClass != null) {
logger.log(Level.FINE, () -> "driver class found in the system class classloader: " + driverClassName);
}
}
} catch (ClassNotFoundException e) {
logger.log(Level.SEVERE, () -> "failed to load driver class from system class classloader: " + driverClassName);
} }
if (driverClass == null) { if (driverClass == null) {
throw new RuntimeException("failed to load driver class " + driverClassName + " in either of PoolConfig class loader or Thread context classloader"); throw new IllegalStateException("failed to load driver class " + driverClassName);
} }
try { try {
driverClass.getConstructor().newInstance(); driverClass.getConstructor().newInstance();
this.driverClassName = driverClassName; this.driverClassName = driverClassName;
} catch (Exception e) { } catch (InstantiationException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException("Failed to instantiate class " + driverClassName, e); throw new IllegalStateException("failed to instantiate class " + driverClassName, e);
} }
} }
@ -549,15 +576,6 @@ public class PoolConfig {
return housekeepingPeriodMs; return housekeepingPeriodMs;
} }
/**
* Get the thread factory used to create threads.
*
* @return the thread factory (may be null, in which case the default thread factory is used)
*/
public ThreadFactory getThreadFactory() {
return threadFactory;
}
/** /**
* Set the thread factory to be used to create threads. * Set the thread factory to be used to create threads.
* *
@ -567,20 +585,21 @@ public class PoolConfig {
this.threadFactory = threadFactory; this.threadFactory = threadFactory;
} }
private Class<?> attemptFromContextLoader(String driverClassName) { /**
ClassLoader threadContextClassLoader = Thread.currentThread().getContextClassLoader(); * Get the thread factory used to create threads.
if (threadContextClassLoader != null) { *
try { * @return the thread factory (may be null, in which case the default thread factory is used)
Class<?> driverClass = threadContextClassLoader.loadClass(driverClassName); */
logger.log(Level.FINE, "Driver class found in Thread context class loader:" + public ThreadFactory getThreadFactory() {
driverClassName + " " + threadContextClassLoader); return threadFactory;
return driverClass;
} catch (ClassNotFoundException e) {
logger.log(Level.FINE, "Driver class not found in Thread context class loader, trying classloader: " +
driverClassName + " " + threadContextClassLoader + " " + this.getClass().getClassLoader());
} }
public void setContextClassLoader(ClassLoader contextClassLoader) {
this.contextClassLoader = contextClassLoader;
} }
return null;
public ClassLoader getContextClassLoader() {
return contextClassLoader != null ? contextClassLoader : Thread.currentThread().getContextClassLoader();
} }
@SuppressWarnings("StatementWithEmptyBody") @SuppressWarnings("StatementWithEmptyBody")

View file

@ -31,7 +31,8 @@ public class PoolEntryCreator implements Callable<Boolean> {
final PoolEntry poolEntry = pool.createPoolEntry(); final PoolEntry poolEntry = pool.createPoolEntry();
if (poolEntry != null) { if (poolEntry != null) {
pool.bag.add(poolEntry); pool.bag.add(poolEntry);
logger.log(Level.FINE, () -> MessageFormat.format("{0}: added connection {1} ", pool.poolName, poolEntry.getConnection())); logger.log(Level.FINE, () -> MessageFormat.format("{0}: added connection {1} ",
pool.poolName, poolEntry.getConnection()));
if (loggingPrefix != null) { if (loggingPrefix != null) {
pool.logPoolState(loggingPrefix); pool.logPoolState(loggingPrefix);
} }

View file

@ -6,10 +6,15 @@ public class DefaultThreadFactory implements ThreadFactory {
private final String threadName; private final String threadName;
private final ClassLoader contextClassLoader;
private final boolean daemon; private final boolean daemon;
public DefaultThreadFactory(String threadName, boolean daemon) { public DefaultThreadFactory(String threadName,
ClassLoader contextClassLoader,
boolean daemon) {
this.threadName = threadName; this.threadName = threadName;
this.contextClassLoader = contextClassLoader;
this.daemon = daemon; this.daemon = daemon;
} }
@ -17,6 +22,7 @@ public class DefaultThreadFactory implements ThreadFactory {
public Thread newThread(Runnable r) { public Thread newThread(Runnable r) {
Thread thread = new Thread(r, threadName); Thread thread = new Thread(r, threadName);
thread.setDaemon(daemon); thread.setDaemon(daemon);
thread.setContextClassLoader(contextClassLoader);
return thread; return thread;
} }
} }

View file

@ -17,24 +17,36 @@ public class DriverDataSource implements DataSource {
private static final Logger logger = Logger.getLogger(DriverDataSource.class.getName()); private static final Logger logger = Logger.getLogger(DriverDataSource.class.getName());
static {
DriverManager.setLogWriter(new PrintWriter(new LoggingOutputStream(logger)));
DriverManager.drivers().forEach(d -> logger.log(Level.INFO, "found driver " + d));
}
private static final String PASSWORD = "password"; private static final String PASSWORD = "password";
private static final String USER = "user"; private static final String USER = "user";
private static final String AUTOCOMMIT = "autocommit"; private static final String AUTOCOMMIT = "autocommit";
private String jdbcUrl;
private final Properties driverProperties; private final Properties driverProperties;
private String jdbcUrl;
private Driver driver; private Driver driver;
public DriverDataSource(String jdbcUrl, String driverClassName, Properties properties, String username, String password) { public DriverDataSource(String jdbcUrl,
String driverClassName,
Properties properties,
String username,
String password) {
this.jdbcUrl = jdbcUrl; this.jdbcUrl = jdbcUrl;
this.driverProperties = new Properties(); this.driverProperties = new Properties();
for (Entry<Object, Object> entry : properties.entrySet()) { for (Entry<Object, Object> entry : properties.entrySet()) {
driverProperties.setProperty(entry.getKey().toString(), entry.getValue().toString()); driverProperties.setProperty(entry.getKey().toString(), entry.getValue().toString());
} }
if (jdbcUrl != null) {
logger.log(Level.INFO, () -> "DriverManager looking for JDBC URL " + jdbcUrl);
}
if (username != null) { if (username != null) {
setUser(username); setUser(username);
} }
@ -51,25 +63,25 @@ public class DriverDataSource implements DataSource {
} }
} }
if (driver == null) { if (driver == null) {
logger.warning("Registered driver with driverClassName was not found, trying direct instantiation: " + driverClassName); logger.log(Level.WARNING, "Registered driver with driverClassName was not found, trying direct instantiation: " + driverClassName);
Class<?> driverClass = null; Class<?> driverClass = null;
ClassLoader threadContextClassLoader = Thread.currentThread().getContextClassLoader(); ClassLoader threadContextClassLoader = Thread.currentThread().getContextClassLoader();
try { try {
if (threadContextClassLoader != null) { if (threadContextClassLoader != null) {
try { try {
driverClass = threadContextClassLoader.loadClass(driverClassName); driverClass = threadContextClassLoader.loadClass(driverClassName);
logger.fine("Driver class found in Thread context class loader: " + driverClassName + " " + threadContextClassLoader); logger.log(Level.FINE, "Driver class found in Thread context class loader: " + driverClassName + " " + threadContextClassLoader);
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
logger.fine("Driver class not found in Thread context class loader, trying classloader: " + logger.log(Level.FINE, "Driver class not found in Thread context class loader, trying classloader: " +
driverClassName + " " + threadContextClassLoader + " " + this.getClass().getClassLoader()); driverClassName + " " + threadContextClassLoader + " " + this.getClass().getClassLoader());
} }
} }
if (driverClass == null) { if (driverClass == null) {
driverClass = this.getClass().getClassLoader().loadClass(driverClassName); driverClass = this.getClass().getClassLoader().loadClass(driverClassName);
logger.fine("Driver class found in the PoolConfig class classloader:" + driverClassName + " " + this.getClass().getClassLoader()); logger.log(Level.FINE, "Driver class found in the PoolConfig class classloader:" + driverClassName + " " + this.getClass().getClassLoader());
} }
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
logger.fine("Failed to load driver class from PoolConfig class classloader: " + driverClassName + " " + this.getClass().getClassLoader()); logger.log(Level.FINE, "Failed to load driver class from PoolConfig class classloader: " + driverClassName + " " + this.getClass().getClassLoader());
} }
if (driverClass != null) { if (driverClass != null) {
try { try {
@ -80,18 +92,19 @@ public class DriverDataSource implements DataSource {
} }
} }
} }
if (driver == null && jdbcUrl != null) {
final String sanitizedUrl = jdbcUrl.replaceAll("([?&;][^&#;=]*[pP]assword=)[^&#;]*", "$1<masked>"); final String sanitizedUrl = jdbcUrl.replaceAll("([?&;][^&#;=]*[pP]assword=)[^&#;]*", "$1<masked>");
try { try {
if (driver == null) {
driver = DriverManager.getDriver(jdbcUrl); driver = DriverManager.getDriver(jdbcUrl);
logger.fine("Loaded driver with class name for jdbcUrl " + driver.getClass().getName() + " " + sanitizedUrl); logger.log(Level.FINE, "Loaded driver with class name for jdbcUrl " + driver.getClass().getName() + " " + sanitizedUrl);
} else if (!driver.acceptsURL(jdbcUrl)) { if (!driver.acceptsURL(sanitizedUrl)) {
throw new RuntimeException("Driver " + driverClassName + " claims to not accept jdbcUrl " + sanitizedUrl); throw new RuntimeException("Driver " + driverClassName + " claims to not accept jdbcUrl " + sanitizedUrl);
} }
} catch (SQLException e) { } catch (SQLException e) {
throw new RuntimeException("Failed to get driver instance for jdbcUrl " + sanitizedUrl, e); throw new RuntimeException("Failed to get driver instance for jdbcUrl " + sanitizedUrl, e);
} }
} }
}
@Override @Override
public Connection getConnection() throws SQLException { public Connection getConnection() throws SQLException {

View file

@ -0,0 +1,26 @@
package org.xbib.jdbc.connection.pool.util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class LoggingOutputStream extends ByteArrayOutputStream {
private final Logger logger;
public LoggingOutputStream(Logger logger) {
super();
this.logger = logger;
}
@Override
public void flush() throws IOException {
super.flush();
String s = new String(buf, 0, count);
if (!s.isEmpty()) {
logger.log(Level.FINE, s);
}
reset();
}
}

View file

@ -17,8 +17,10 @@ public class HouseKeeperCleanupTest {
private ScheduledThreadPoolExecutor executor; private ScheduledThreadPoolExecutor executor;
@BeforeEach @BeforeEach
public void before() throws Exception { public void before() {
ThreadFactory threadFactory = new DefaultThreadFactory("global-housekeeper", true); ThreadFactory threadFactory = new DefaultThreadFactory("global-housekeeper",
Thread.currentThread().getContextClassLoader(),
true);
executor = new ScheduledThreadPoolExecutor(1, threadFactory, executor = new ScheduledThreadPoolExecutor(1, threadFactory,
new ThreadPoolExecutor.DiscardPolicy()); new ThreadPoolExecutor.DiscardPolicy());
executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);

View file

@ -27,7 +27,6 @@ public class MariaDBTest extends CommonTest {
static MariaDBContainer<?> mariaDBContainer; static MariaDBContainer<?> mariaDBContainer;
static { static {
// mariadb 10.3.6
mariaDBContainer = new MariaDBContainer<>("mariadb") mariaDBContainer = new MariaDBContainer<>("mariadb")
.withDatabaseName("testDB") .withDatabaseName("testDB")
.withUsername("testUser") .withUsername("testUser")
@ -45,7 +44,7 @@ public class MariaDBTest extends CommonTest {
} }
@Override @Override
protected DatabaseProvider createDatabaseProvider(OptionsOverride options) throws Exception { protected DatabaseProvider createDatabaseProvider(OptionsOverride options) {
Config config = ConfigSupplier.of() Config config = ConfigSupplier.of()
.property("database.url", mariaDBContainer.getJdbcUrl()) .property("database.url", mariaDBContainer.getJdbcUrl())
.property("database.user", "testUser") .property("database.user", "testUser")
@ -95,12 +94,14 @@ public class MariaDBTest extends CommonTest {
} }
@Disabled("MariaDB temporarily disabled") @Disabled("MariaDB temporarily disabled")
@Test
@Override @Override
public void intervals() { public void intervals() {
super.intervals(); super.intervals();
} }
@Disabled("MariaDB temporarily disabled") @Disabled("MariaDB temporarily disabled")
@Test
@Override @Override
public void metadataColumnTypes() { public void metadataColumnTypes() {
super.metadataColumnTypes(); super.metadataColumnTypes();
@ -251,5 +252,4 @@ public class MariaDBTest extends CommonTest {
+ " str_varchar, str_fixed, str_lob, bin_blob, boolean_flag, date_millis, local_date from dbtest2 order by 1") + " str_varchar, str_fixed, str_lob, bin_blob, boolean_flag, date_millis, local_date from dbtest2 order by 1")
.queryMany(SqlArgs::readRow)); .queryMany(SqlArgs::readRow));
} }
} }

View file

@ -14,6 +14,9 @@ import org.xbib.jdbc.query.OptionsOverride;
import org.xbib.jdbc.query.Schema; import org.xbib.jdbc.query.Schema;
import org.xbib.jdbc.test.CommonTest; import org.xbib.jdbc.test.CommonTest;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
/** /**
* Exercise Database functionality with a real Oracle database. * Exercise Database functionality with a real Oracle database.
*/ */
@ -78,4 +81,16 @@ public class OracleTest extends CommonTest {
return null; return null;
}); });
} }
@Test
public void testTableExists() {
db.dropTableQuietly("dbtest");
new Schema().addTable("dbtest").addColumn("pk").primaryKey().schema().execute(db);
assertTrue(db.tableExists("dbtest"));
db.dropTableQuietly("dbtest");
assertFalse(db.tableExists("dbtest"));
db.ddl("create table \"dbtest\" (pk varchar(1))").execute();
assertTrue(db.tableExists("dbtest"));
db.dropTableQuietly("dbtest");
}
} }

View file

@ -4,7 +4,7 @@ import org.xbib.jdbc.postgresql.Postgresql;
module org.xbib.jdbc.postgresql { module org.xbib.jdbc.postgresql {
requires org.xbib.jdbc.query; requires org.xbib.jdbc.query;
requires java.sql; requires java.sql;
requires org.postgresql.jdbc; requires org.postgresql.jdbc; // automatic module
uses Flavor; uses Flavor;
exports org.xbib.jdbc.postgresql; exports org.xbib.jdbc.postgresql;
provides Flavor with Postgresql; provides Flavor with Postgresql;

View file

@ -35,6 +35,7 @@ public class Listener implements Runnable {
while (true) { while (true) {
try (Statement stmt = connection.createStatement(); try (Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("SELECT 1")) { ResultSet rs = stmt.executeQuery("SELECT 1")) {
//
} catch (SQLException e) { } catch (SQLException e) {
errorConsumer.accept(e); errorConsumer.accept(e);
} }

View file

@ -102,7 +102,7 @@ public class DatabaseImpl implements Database {
@Override @Override
public void commitNow() { public void commitNow() {
if (options.ignoreTransactionControl()) { if (options.ignoreTransactionControl()) {
logger.fine("Ignoring call to commitNow()"); logger.log(Level.FINE, "Ignoring call to commitNow()");
return; return;
} }
if (!options.allowTransactionControl()) { if (!options.allowTransactionControl()) {
@ -118,7 +118,7 @@ public class DatabaseImpl implements Database {
@Override @Override
public void rollbackNow() { public void rollbackNow() {
if (options.ignoreTransactionControl()) { if (options.ignoreTransactionControl()) {
logger.fine("Ignoring call to rollbackNow()"); logger.log(Level.FINE, "Ignoring call to rollbackNow()");
return; return;
} }
if (!options.allowTransactionControl()) { if (!options.allowTransactionControl()) {
@ -200,14 +200,24 @@ public class DatabaseImpl implements Database {
public boolean tableExists(String tableName, String schemaName) throws DatabaseException { public boolean tableExists(String tableName, String schemaName) throws DatabaseException {
if (tableName != null && connection != null) { if (tableName != null && connection != null) {
try { try {
// schema based check
DatabaseMetaData metadata = connection.getMetaData(); DatabaseMetaData metadata = connection.getMetaData();
String normalizedTable = flavor().normalizeTableName(tableName); String normalizedTable = flavor().normalizeTableName(tableName);
ResultSet resultSet = metadata.getTables(connection.getCatalog(), schemaName, normalizedTable, new String[]{"TABLE", "VIEW"}); try (ResultSet resultSet = metadata.getTables(connection.getCatalog(), schemaName, normalizedTable, new String[]{"TABLE", "VIEW"})) {
while (resultSet.next()) { while (resultSet.next()) {
if (normalizedTable.equals(resultSet.getString("TABLE_NAME"))) { if (normalizedTable.equals(resultSet.getString("TABLE_NAME"))) {
return true; return true;
} }
} }
}
// otherwise, do a simple check
try (ResultSet resultSet = metadata.getTables(null, null, tableName, null) ) {
while (resultSet.next()) {
if (tableName.equals(resultSet.getString("TABLE_NAME"))) {
return true;
}
}
}
} catch (SQLException exc) { } catch (SQLException exc) {
throw new DatabaseException("Unable to look up table " + tableName throw new DatabaseException("Unable to look up table " + tableName
+ " in schema " + schemaName + " : " + exc.getMessage(), + " in schema " + schemaName + " : " + exc.getMessage(),
@ -455,19 +465,14 @@ public class DatabaseImpl implements Database {
return; return;
} }
params.forEach((k, v) -> { params.forEach((k, v) -> {
if (v instanceof String) { switch (v) {
sql.argString(k, (String) v); case String s -> sql.argString(k, s);
} else if (v instanceof Integer) { case Integer i -> sql.argInteger(k, i);
sql.argInteger(k, (Integer) v); case Long l -> sql.argLong(k, l);
} else if (v instanceof Long) { case Boolean b -> sql.argBoolean(k, b);
sql.argLong(k, (Long) v); case LocalDate localDate -> sql.argLocalDate(k, localDate);
} else if (v instanceof Boolean) { case LocalDateTime localDateTime -> sql.argLocalDateTime(k, localDateTime);
sql.argBoolean(k, (Boolean) v); case null, default ->
} else if (v instanceof LocalDate) {
sql.argLocalDate(k, (LocalDate) v);
} else if (v instanceof LocalDateTime) {
sql.argLocalDateTime(k, (LocalDateTime) v);
} else {
throw new DatabaseException("unknown type for param: " + (v != null ? v.getClass() : "null")); throw new DatabaseException("unknown type for param: " + (v != null ? v.getClass() : "null"));
} }
}); });
@ -478,22 +483,15 @@ public class DatabaseImpl implements Database {
return; return;
} }
params.forEach((k, v) -> { params.forEach((k, v) -> {
if (v instanceof String) { switch (v) {
sql.argString(k, (String) v); case String s -> sql.argString(k, s);
} else if (v instanceof Integer) { case Integer i -> sql.argInteger(k, i);
sql.argInteger(k, (Integer) v); case Long l -> sql.argLong(k, l);
} else if (v instanceof Long) { case Boolean b -> sql.argBoolean(k, b);
sql.argLong(k, (Long) v); case LocalDate localDate -> sql.argLocalDate(k, localDate);
} else if (v instanceof Boolean) { case LocalDateTime localDateTime -> sql.argLocalDateTime(k, localDateTime);
sql.argBoolean(k, (Boolean) v); case null -> sql.argNull(k);
} else if (v instanceof LocalDate) { default -> throw new DatabaseException("unknown type for param: " + v.getClass());
sql.argLocalDate(k, (LocalDate) v);
} else if (v instanceof LocalDateTime) {
sql.argLocalDateTime(k, (LocalDateTime) v);
} else if (v == null) {
sql.argNull(k);
} else {
throw new DatabaseException("unknown type for param: " + v.getClass());
} }
}); });
} }
@ -503,22 +501,15 @@ public class DatabaseImpl implements Database {
return; return;
} }
params.forEach((k, v) -> { params.forEach((k, v) -> {
if (v instanceof String) { switch (v) {
sql.argString(k, (String) v); case String s -> sql.argString(k, s);
} else if (v instanceof Integer) { case Integer i -> sql.argInteger(k, i);
sql.argInteger(k, (Integer) v); case Long l -> sql.argLong(k, l);
} else if (v instanceof Long) { case Boolean b -> sql.argBoolean(k, b);
sql.argLong(k, (Long) v); case LocalDate localDate -> sql.argLocalDate(k, localDate);
} else if (v instanceof Boolean) { case LocalDateTime localDateTime -> sql.argLocalDateTime(k, localDateTime);
sql.argBoolean(k, (Boolean) v); case null -> sql.argNull(k);
} else if (v instanceof LocalDate) { default -> throw new DatabaseException("unknown type for param: " + v.getClass());
sql.argLocalDate(k, (LocalDate) v);
} else if (v instanceof LocalDateTime) {
sql.argLocalDateTime(k, (LocalDateTime) v);
} else if (v == null) {
sql.argNull(k);
} else {
throw new DatabaseException("unknown type for param: " + v.getClass());
} }
}); });
} }
@ -559,7 +550,10 @@ public class DatabaseImpl implements Database {
} }
@Override @Override
public void consumeQueue(String table, String channel, int limit, Consumer<String> consumer) throws SQLException { public void consumeQueue(String table,
String channel,
int limit,
Consumer<String> consumer) throws SQLException {
List<Long> consumedKeys = new ArrayList<>(); List<Long> consumedKeys = new ArrayList<>();
try { try {
connection.setAutoCommit(false); connection.setAutoCommit(false);
@ -572,13 +566,12 @@ public class DatabaseImpl implements Database {
consumer.accept(resultSet.getString("data")); consumer.accept(resultSet.getString("data"));
} }
succeedInQueue(connection, table, key); succeedInQueue(connection, table, key);
} catch (QueueException e) { } catch (Exception e) {
connection.rollback(); connection.rollback();
failInQueue(connection, table, key); failInQueue(connection, table, key);
throw e; throw e;
} }
} }
} finally {
connection.commit(); connection.commit();
} }
} catch (Exception e) { } catch (Exception e) {

View file

@ -5,13 +5,10 @@ import org.xbib.jdbc.connection.pool.PoolDataSource;
import org.xbib.jdbc.query.util.Metric; import org.xbib.jdbc.query.util.Metric;
import javax.sql.DataSource; import javax.sql.DataSource;
import java.io.Closeable;
import java.io.IOException;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DriverManager; import java.sql.DriverManager;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.logging.Level; import java.util.logging.Level;
@ -23,7 +20,7 @@ import java.util.logging.Logger;
* of this laziness, the underlying resources require explicit cleanup by calling either * of this laziness, the underlying resources require explicit cleanup by calling either
* commitAndClose() or rollbackAndClose(). * commitAndClose() or rollbackAndClose().
*/ */
public final class DatabaseProvider implements Supplier<Database>, Closeable { public final class DatabaseProvider implements Supplier<Database> {
private static final Logger logger = Logger.getLogger(DatabaseProvider.class.getName()); private static final Logger logger = Logger.getLogger(DatabaseProvider.class.getName());
@ -47,7 +44,7 @@ public final class DatabaseProvider implements Supplier<Database>, Closeable {
return new DatabaseProviderBuilderImpl(ds, () -> { return new DatabaseProviderBuilderImpl(ds, () -> {
try { try {
if (ds == null) { if (ds == null) {
throw new NullPointerException(); throw new IllegalArgumentException("data source must not be null");
} }
return ds.getConnection(); return ds.getConnection();
} catch (Exception e) { } catch (Exception e) {
@ -170,7 +167,7 @@ public final class DatabaseProvider implements Supplier<Database>, Closeable {
try { try {
code.run(this); code.run(this);
complete = true; complete = true;
} catch (ThreadDeath | DatabaseException t) { } catch (DatabaseException t) {
throw t; throw t;
} catch (Throwable t) { } catch (Throwable t) {
throw new DatabaseException("Error during transaction", t); throw new DatabaseException("Error during transaction", t);
@ -188,7 +185,7 @@ public final class DatabaseProvider implements Supplier<Database>, Closeable {
try { try {
code.run(this); code.run(this);
complete = true; complete = true;
} catch (ThreadDeath | DatabaseException t) { } catch (DatabaseException t) {
throw t; throw t;
} catch (Throwable t) { } catch (Throwable t) {
throw new DatabaseException("Error during transaction", t); throw new DatabaseException("Error during transaction", t);
@ -207,7 +204,7 @@ public final class DatabaseProvider implements Supplier<Database>, Closeable {
try { try {
result = code.run(this); result = code.run(this);
complete = true; complete = true;
} catch (ThreadDeath | DatabaseException t) { } catch (DatabaseException t) {
throw t; throw t;
} catch (Throwable t) { } catch (Throwable t) {
throw new DatabaseException("Error during transaction", t); throw new DatabaseException("Error during transaction", t);
@ -227,7 +224,7 @@ public final class DatabaseProvider implements Supplier<Database>, Closeable {
try { try {
result = code.run(this); result = code.run(this);
complete = true; complete = true;
} catch (ThreadDeath | DatabaseException t) { } catch (DatabaseException t) {
throw t; throw t;
} catch (Throwable t) { } catch (Throwable t) {
throw new DatabaseException("Error during transaction", t); throw new DatabaseException("Error during transaction", t);
@ -249,7 +246,7 @@ public final class DatabaseProvider implements Supplier<Database>, Closeable {
try { try {
code.run(this, tx); code.run(this, tx);
complete = true; complete = true;
} catch (ThreadDeath | DatabaseException t) { } catch (DatabaseException t) {
throw t; throw t;
} catch (Throwable t) { } catch (Throwable t) {
throw new DatabaseException("Error during transaction", t); throw new DatabaseException("Error during transaction", t);
@ -270,7 +267,7 @@ public final class DatabaseProvider implements Supplier<Database>, Closeable {
try { try {
code.run(this, tx); code.run(this, tx);
complete = true; complete = true;
} catch (ThreadDeath | DatabaseException t) { } catch (DatabaseException t) {
throw t; throw t;
} catch (Throwable t) { } catch (Throwable t) {
throw new DatabaseException("Error during transaction", t); throw new DatabaseException("Error during transaction", t);
@ -287,9 +284,6 @@ public final class DatabaseProvider implements Supplier<Database>, Closeable {
if (database != null) { if (database != null) {
return database; return database;
} }
if (builder.isClosed()) {
throw new DatabaseException("Called get() on a DatabaseProvider after close()");
}
Metric metric = new Metric(logger.isLoggable(Level.FINE)); Metric metric = new Metric(logger.isLoggable(Level.FINE));
try { try {
connection = builder.connectionProvider.get(); connection = builder.connectionProvider.get();
@ -299,7 +293,7 @@ public final class DatabaseProvider implements Supplier<Database>, Closeable {
// Don't try to be clever about clearing it conditionally. // Don't try to be clever about clearing it conditionally.
if (!builder.options.flavor().isAutoCommitOnly()) { if (!builder.options.flavor().isAutoCommitOnly()) {
connection.setAutoCommit(false); connection.setAutoCommit(false);
metric.checkpoint("setAutoCommit"); metric.checkpoint("setAutoCommit false");
} }
} catch (SQLException e) { } catch (SQLException e) {
throw new DatabaseException("Unable to set autoCommit for the connection", e); throw new DatabaseException("Unable to set autoCommit for the connection", e);
@ -393,8 +387,7 @@ public final class DatabaseProvider implements Supplier<Database>, Closeable {
} }
} }
@Override public void close() {
public void close() throws IOException {
if (connection != null) { if (connection != null) {
try { try {
connection.close(); connection.close();
@ -404,26 +397,22 @@ public final class DatabaseProvider implements Supplier<Database>, Closeable {
} }
connection = null; connection = null;
database = null; database = null;
builder.close();
} }
private static class DatabaseProviderBuilderImpl implements DatabaseProviderBuilder, Closeable { private static class DatabaseProviderBuilderImpl implements DatabaseProviderBuilder {
private DataSource dataSource; private final DataSource dataSource;
private final Supplier<Connection> connectionProvider; private final Supplier<Connection> connectionProvider;
private final Options options; private final Options options;
private final AtomicBoolean closed;
private DatabaseProviderBuilderImpl(DataSource dataSource, private DatabaseProviderBuilderImpl(DataSource dataSource,
Supplier<Connection> connectionProvider, Supplier<Connection> connectionProvider,
Options options) { Options options) {
this.dataSource = dataSource; this.dataSource = dataSource;
this.connectionProvider = connectionProvider; this.connectionProvider = connectionProvider;
this.options = options; this.options = options;
this.closed = new AtomicBoolean(false);
} }
@Override @Override
@ -511,20 +500,5 @@ public final class DatabaseProvider implements Supplier<Database>, Closeable {
this.build().transact(tx); this.build().transact(tx);
} }
@Override
public void close() throws IOException {
if (closed.compareAndSet(false, true)) {
if (dataSource != null) {
if (dataSource instanceof Closeable) {
((Closeable) dataSource).close();
}
dataSource = null;
}
}
}
public boolean isClosed() {
return closed.get();
}
} }
} }

View file

@ -1,8 +0,0 @@
package org.xbib.jdbc.query;
public class QueueException extends DatabaseException {
public QueueException(String message, Throwable cause) {
super(message, cause);
}
}

View file

@ -1,6 +1,5 @@
package org.xbib.jdbc.query; package org.xbib.jdbc.query;
import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -15,10 +14,6 @@ public class Table extends ArrayList<List<Object>> implements List<List<Object>>
private String search; private String search;
private LocalDateTime from;
private LocalDateTime to;
private Integer offset; private Integer offset;
private Integer size; private Integer size;
@ -29,11 +24,12 @@ public class Table extends ArrayList<List<Object>> implements List<List<Object>>
private String groupByClause; private String groupByClause;
private final List<Map.Entry<String, Boolean>> sort = new ArrayList<>(); private final List<Map.Entry<String, Boolean>> sort;
private long total; private long total;
public Table() { public Table() {
this.sort = new ArrayList<>();
} }
public void setStatement(String statement) { public void setStatement(String statement) {
@ -60,22 +56,6 @@ public class Table extends ArrayList<List<Object>> implements List<List<Object>>
return search; return search;
} }
public void setFromDate(LocalDateTime from) {
this.from = from;
}
public LocalDateTime getFromDate() {
return from;
}
public void setToDate(LocalDateTime to) {
this.to = to;
}
public LocalDateTime getToDate() {
return to;
}
public void setOffset(Integer offset) { public void setOffset(Integer offset) {
this.offset = offset; this.offset = offset;
} }

View file

@ -18,13 +18,8 @@ import java.util.logging.Logger;
* to prevent SQL injection or any other bad things. * to prevent SQL injection or any other bad things.
*/ */
public class DebugSql { public class DebugSql {
public static final String PARAM_SQL_SEPARATOR = "\tParamSql:\t";
public static String printDebugOnlySqlString(String sql, Object[] args, Options options) { public static final String PARAM_SQL_SEPARATOR = "\tParamSql:\t";
StringBuilder buf = new StringBuilder();
printSql(buf, sql, args, false, true, options);
return buf.toString();
}
public static void printSql(StringBuilder buf, String sql, Object[] args, Options options) { public static void printSql(StringBuilder buf, String sql, Object[] args, Options options) {
printSql(buf, sql, args, true, options.isLogParameters(), options); printSql(buf, sql, args, true, options.isLogParameters(), options);
@ -65,8 +60,7 @@ public class DebugSql {
for (int i = 0; i < argsToPrint.length; i++) { for (int i = 0; i < argsToPrint.length; i++) {
buf.append(removeTabs(sqlParts[i])); buf.append(removeTabs(sqlParts[i]));
Object argToPrint = argsToPrint[i]; Object argToPrint = argsToPrint[i];
if (argToPrint instanceof String) { if (argToPrint instanceof String argToPrintString) {
String argToPrintString = (String) argToPrint;
int maxLength = options.maxStringLengthParam(); int maxLength = options.maxStringLengthParam();
if (argToPrintString.length() > maxLength && maxLength > 0) { if (argToPrintString.length() > maxLength && maxLength > 0) {
buf.append("'").append(argToPrintString, 0, maxLength).append("...'"); buf.append("'").append(argToPrintString, 0, maxLength).append("...'");
@ -77,16 +71,12 @@ public class DebugSql {
} }
} else if (argToPrint instanceof SqlNull || argToPrint == null) { } else if (argToPrint instanceof SqlNull || argToPrint == null) {
buf.append("null"); buf.append("null");
} else if (argToPrint instanceof java.sql.Timestamp) { } else if (argToPrint instanceof java.sql.Timestamp timestamp) {
java.sql.Timestamp timestamp = (java.sql.Timestamp) argToPrint;
LocalDateTime localDateTime = timestamp.toLocalDateTime(); LocalDateTime localDateTime = timestamp.toLocalDateTime();
buf.append(localDateTime.toString()); buf.append(localDateTime.toString());
//buf.append(options.flavor().dateAsSqlFunction((Timestamp) argToPrint, options.calendarForTimestamps())); } else if (argToPrint instanceof java.sql.Date date) {
} else if (argToPrint instanceof java.sql.Date) {
java.sql.Date date = (java.sql.Date) argToPrint;
LocalDate localDate = date.toLocalDate(); LocalDate localDate = date.toLocalDate();
buf.append(localDate.toString()); buf.append(localDate.toString());
//buf.append(options.flavor().localDateAsSqlFunction((java.sql.Date) argToPrint));
} else if (argToPrint instanceof Number) { } else if (argToPrint instanceof Number) {
buf.append(argToPrint); buf.append(argToPrint);
} else if (argToPrint instanceof Boolean) { } else if (argToPrint instanceof Boolean) {
@ -144,25 +134,22 @@ public class DebugSql {
} }
public static void logSuccess(String sqlType, Logger log, Metric metric, String sql, Object[] args, Options options) { public static void logSuccess(String sqlType, Logger log, Metric metric, String sql, Object[] args, Options options) {
if (log.isLoggable(Level.FINE)) { if (log.isLoggable(Level.FINEST)) {
String msg = logMiddle('\t', sqlType, metric, null, sql, args, options); log.log(Level.FINEST, () -> logMiddle('\t', sqlType, metric, null, sql, args, options));
log.fine(msg);
} }
} }
public static void logWarning(String sqlType, Logger log, Metric metric, String errorCode, String sql, Object[] args, public static void logWarning(String sqlType, Logger log, Metric metric, String errorCode, String sql, Object[] args,
Options options, Throwable t) { Options options, Throwable t) {
if (log.isLoggable(Level.WARNING)) { if (log.isLoggable(Level.WARNING)) {
String msg = logMiddle(' ', sqlType, metric, errorCode, sql, args, options); log.log(Level.WARNING, logMiddle(' ', sqlType, metric, errorCode, sql, args, options), t);
log.log(Level.WARNING, msg, t);
} }
} }
public static void logError(String sqlType, Logger log, Metric metric, String errorCode, String sql, Object[] args, public static void logError(String sqlType, Logger log, Metric metric, String errorCode, String sql, Object[] args,
Options options, Throwable t) { Options options, Throwable t) {
if (log.isLoggable(Level.SEVERE)) { if (log.isLoggable(Level.SEVERE)) {
String msg = logMiddle(' ', sqlType, metric, errorCode, sql, args, options); log.log(Level.SEVERE, logMiddle(' ', sqlType, metric, errorCode, sql, args, options), t);
log.log(Level.SEVERE, msg, t);
} }
} }

View file

@ -1,26 +1,23 @@
dependencyResolutionManagement { dependencyResolutionManagement {
versionCatalogs { versionCatalogs {
libs { libs {
version('gradle', '8.5') version('gradle', '8.7')
library('mariadb', 'org.mariadb.jdbc', 'mariadb-java-client').version('3.1.3') library('mariadb', 'org.mariadb.jdbc', 'mariadb-java-client').version('3.4.1')
library('oracle', 'com.oracle.database.jdbc','ojdbc11').version('23.2.0.0') library('oracle', 'com.oracle.database.jdbc', 'ojdbc11').version('23.4.0.24.05')
library('postgresql', 'org.postgresql', 'postgresql').version('42.6.0') library('postgresql', 'org.postgresql', 'postgresql').version('42.7.3')
} }
testLibs { testLibs {
version('junit', '5.10.1') version('junit', '5.10.2')
version('testcontainers', '1.19.1') version('testcontainers', '1.20.0')
library('junit-jupiter-api', 'org.junit.jupiter', 'junit-jupiter-api').versionRef('junit') library('junit-jupiter-api', 'org.junit.jupiter', 'junit-jupiter-api').versionRef('junit')
library('junit-jupiter-params', 'org.junit.jupiter', 'junit-jupiter-params').versionRef('junit') library('junit-jupiter-params', 'org.junit.jupiter', 'junit-jupiter-params').versionRef('junit')
library('junit-jupiter-engine', 'org.junit.jupiter', 'junit-jupiter-engine').versionRef('junit') library('junit-jupiter-engine', 'org.junit.jupiter', 'junit-jupiter-engine').versionRef('junit')
library('junit-jupiter-platform-launcher', 'org.junit.platform', 'junit-platform-launcher').version('1.10.1') library('junit-jupiter-platform-launcher', 'org.junit.platform', 'junit-platform-launcher').version('1.10.1')
library('hamcrest', 'org.hamcrest', 'hamcrest-library').version('2.2') library('hamcrest', 'org.hamcrest', 'hamcrest-library').version('2.2')
library('junit4', 'junit', 'junit').version('4.13.2') library('junit4', 'junit', 'junit').version('4.13.2')
library('derby', 'org.apache.derby', 'derby').version('10.16.1.1') library('derby', 'org.apache.derby', 'derby').version('10.17.1.0')
library('hsqldb', 'org.hsqldb', 'hsqldb').version('2.7.1') library('hsqldb', 'org.hsqldb', 'hsqldb').version('2.7.3')
library('h2', 'com.h2database', 'h2').version('2.1.214') library('h2', 'com.h2database', 'h2').version('2.3.230')
library('mariadb', 'org.mariadb.jdbc', 'mariadb-java-client').version('3.1.3')
library('oracle', 'com.oracle.database.jdbc','ojdbc11').version('23.2.0.0')
library('postgresql', 'org.postgresql', 'postgresql').version('42.6.0')
library('mockito-core', 'org.mockito', 'mockito-core').version('5.3.1') library('mockito-core', 'org.mockito', 'mockito-core').version('5.3.1')
library('testcontainers', 'org.testcontainers', 'testcontainers').versionRef('testcontainers') library('testcontainers', 'org.testcontainers', 'testcontainers').versionRef('testcontainers')
library('testcontainers-junit-jupiter', 'org.testcontainers', 'junit-jupiter').versionRef('testcontainers') library('testcontainers-junit-jupiter', 'org.testcontainers', 'junit-jupiter').versionRef('testcontainers')
@ -32,6 +29,7 @@ dependencyResolutionManagement {
} }
include 'jdbc-connection-pool' include 'jdbc-connection-pool'
include 'jdbc-pool'
include 'jdbc-query' include 'jdbc-query'
include 'jdbc-test' include 'jdbc-test'
include 'jdbc-mariadb' include 'jdbc-mariadb'