diff --git a/bin/testfiles/JDBC_TESTS.jmx b/bin/testfiles/JDBC_TESTS.jmx index ff1705a823d..66277d3bf8a 100644 --- a/bin/testfiles/JDBC_TESTS.jmx +++ b/bin/testfiles/JDBC_TESTS.jmx @@ -8,7 +8,7 @@ - ../lib/opt/hsqldb-2.4.0.jar + ../lib/opt/hsqldb-2.7.4.jar diff --git a/gradle/verification-keyring.keys b/gradle/verification-keyring.keys index bffc6702e3a..20e89e98240 100644 --- a/gradle/verification-keyring.keys +++ b/gradle/verification-keyring.keys @@ -6813,6 +6813,34 @@ Dw== =QOGP -----END PGP PUBLIC KEY BLOCK----- +pub 4CC08E7F47C3EC76 +uid Brett Wooldridge (Sonatype) + +sub 4D3FB07DD9F19B56 +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBFI4+QsBCAC1+xCdhXj0MuViNA21Pno8bHunP0UIZQmC9BNl7dVuUC8/rg9V +1dlZyXF8SNycOKqT461m41H5VNBmwt64OIuJiUcUreVSs07iLzpn8mQPwyTaRZYd +YlZns5V/3ukfGYUVCWScMdc8WaJwTVxfRwNhnJ2/QbAcIZDypwAd0P03ofpvnt/8 +YqvVyzpJqNTDLFjLpEcditVn4EioVVMcvUu4YVwmUSdBjrMLp0xC8PvbyWiw4dCA +T4C2zFycrr4M2legiZv/N6Tw0fPRE/EALYtIhgLSeqx5Pg9ku/KA6zMraFovnMDM +haJ9+ZsCPxd9JaJ021C9I7EXDA+W5t+DPODDABEBAAG0OEJyZXR0IFdvb2xkcmlk +Z2UgKFNvbmF0eXBlKSA8YnJldHQud29vbGRyaWRnZUBnbWFpbC5jb20+uQENBFI4 ++QsBCAD0Xrq6nXmqubsB+XdgLof14wH7UIw693FUUndKcK+LVaMe7dP8F1Emkorf +YvwTOKQy6L+rUOm23MHuxxwB+msIpMX3WCzFGSq1WjYPd6wVj47yP/wqqhIqQO3q +tUeOVlyTwy8KccrAkXpDjkTlZ0cVP2lqNo6gRTypkvmHYgLYzNNV1GJm+v+t4sm4 +jMepvKLl11/gUNLHx2VpL37w1i6Mm53iiW2GXGin1gPSWB3FtspMLAQdE2Xk0yRk +s+eUJ5e8oj3eJD5w4b3fqsWFCmK8q+/5uPK3Po2xe1oSmpHBm38MFUAxErtabNrB +EioSC5wNER0DhB4gEKVUXLyIDXUDABEBAAGJAR8EGAECAAkFAlI4+QsCGwwACgkQ +TMCOf0fD7HYHrgf9F4p3sWyNApGF8V1GEQEyGUPtfxNd0N1Jzd10MCh470fiBjaQ +IebkCEhneiNBAOWK52HxAlWRINAAa7fVJ8XpmzTbTLDLL1ZrEI//21ZQ3gUxCRgr +shy7lEV1fNJTuZadBbOU/+1L2Bbz9zu7Vju+9DU5sr3c5Byu/E+l/o1i/DvymoFA +OOepAO4IxpEV/ma4d8KYaWhSSb7UgwMjAkPt3YsnxQAnNbApZenVo+NLO+2XRAbd +BzEtRoazqYje0BzSewiTyXruVKK7Ineo6tRWyOMvdYhnoz5EZejp3gkaBmE4U0aa +qOSJ1UKXf8amAyQGcdIzRNx+3apFHn0h2yaMug== +=mIr1 +-----END PGP PUBLIC KEY BLOCK----- + pub 4E066E0459CD109B uid Henri Biestro (CODE SIGNING KEY) diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index d46bb6a739c..a3193356667 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -294,6 +294,7 @@ + diff --git a/lib/aareadme.txt b/lib/aareadme.txt index 96315b8a9e3..ce2e541d979 100644 --- a/lib/aareadme.txt +++ b/lib/aareadme.txt @@ -244,10 +244,6 @@ tika-1.21 http://tika.apache.org/ - Regular Expression Extractor -commons-dbcp2-2.5.0 (org.apache.commons.dbcp2) --------------------------- -- DataSourceElement (JDBC) - Saxon-HE-9.9.1-5 (net.sf.saxon) -------------------------- - XPath2Extractor (XML) diff --git a/src/bom-thirdparty/build.gradle.kts b/src/bom-thirdparty/build.gradle.kts index f26a2b6d191..ca22d231c3d 100644 --- a/src/bom-thirdparty/build.gradle.kts +++ b/src/bom-thirdparty/build.gradle.kts @@ -59,6 +59,7 @@ dependencies { api("com.miglayout:miglayout-swing:5.3") api("com.sun.activation:javax.activation:1.2.0") api("com.thoughtworks.xstream:xstream:1.4.21") + api("com.zaxxer:HikariCP:7.0.2") api("commons-codec:commons-codec:1.19.0") api("commons-collections:commons-collections:3.2.2") api("commons-io:commons-io:2.20.0") @@ -90,7 +91,6 @@ dependencies { api("net.sf.saxon:Saxon-HE:12.9") api("org.apache-extras.beanshell:bsh:2.0b6") api("org.apache.commons:commons-collections4:4.5.0") - api("org.apache.commons:commons-dbcp2:2.9.0") api("org.apache.commons:commons-jexl3:3.5.0") api("org.apache.commons:commons-jexl:2.1.1") api("org.apache.commons:commons-lang3:3.19.0") { diff --git a/src/dist-check/build.gradle.kts b/src/dist-check/build.gradle.kts index 00d5edb3961..18a5eb11a5f 100644 --- a/src/dist-check/build.gradle.kts +++ b/src/dist-check/build.gradle.kts @@ -44,7 +44,7 @@ dependencies { extraTestDependencies(platform(projects.src.bomThirdparty)) extraTestDependencies(platform(projects.src.bomTesting)) - extraTestDependencies("org.hsqldb:hsqldb::jdk8") + extraTestDependencies("org.hsqldb:hsqldb") extraTestDependencies("org.apache.mina:mina-core") extraTestDependencies("org.apache.ftpserver:ftplet-api") extraTestDependencies("org.apache.ftpserver:ftpserver-core") diff --git a/src/dist/src/dist/expected_release_jars.csv b/src/dist/src/dist/expected_release_jars.csv index f7115320585..68a08c083fc 100644 --- a/src/dist/src/dist/expected_release_jars.csv +++ b/src/dist/src/dist/expected_release_jars.csv @@ -69,6 +69,7 @@ 223993,groovy-xml-5.0.2.jar 126373,hamcrest-3.0.jar 2403,hamcrest-core-3.0.jar +172312,HikariCP-7.0.2.jar 181512,httpasyncclient-4.1.5.jar 785639,httpclient-4.5.14.jar 327891,httpcore-4.4.16.jar diff --git a/src/protocol/jdbc/build.gradle.kts b/src/protocol/jdbc/build.gradle.kts index 2b5521c6ca9..f146f158cce 100644 --- a/src/protocol/jdbc/build.gradle.kts +++ b/src/protocol/jdbc/build.gradle.kts @@ -22,7 +22,7 @@ plugins { dependencies { api(projects.src.core) - implementation("org.apache.commons:commons-dbcp2") + implementation("com.zaxxer:HikariCP") implementation("commons-io:commons-io") { because("IOUtils") } diff --git a/src/protocol/jdbc/src/main/java/org/apache/jmeter/protocol/jdbc/AbstractJDBCTestElement.java b/src/protocol/jdbc/src/main/java/org/apache/jmeter/protocol/jdbc/AbstractJDBCTestElement.java index 57521582185..219d4d0b921 100644 --- a/src/protocol/jdbc/src/main/java/org/apache/jmeter/protocol/jdbc/AbstractJDBCTestElement.java +++ b/src/protocol/jdbc/src/main/java/org/apache/jmeter/protocol/jdbc/AbstractJDBCTestElement.java @@ -168,13 +168,11 @@ protected byte[] execute(Connection conn, SampleResult sample) throws SQLExcepti try (Statement stmt = conn.createStatement()) { setQueryTimeout(stmt, getIntegerQueryTimeout()); configureMaxRows(stmt); - ResultSet rs = null; - try { - rs = stmt.executeQuery(getQuery()); + try (ResultSet rs = stmt.executeQuery(getQuery())) { sample.latencyEnd(); return getStringFromResultSet(rs).getBytes(ENCODING); } finally { - close(rs); + commitTransaction(conn); } } } else if (CALLABLE.equals(currentQueryType)) { @@ -186,6 +184,8 @@ protected byte[] execute(Connection conn, SampleResult sample) throws SQLExcepti sample.latencyEnd(); String sb = resultSetsToString(cstmt,hasResultSet, out); return sb.getBytes(ENCODING); + } finally { + commitTransaction(conn); } } else if (UPDATE.equals(currentQueryType)) { try (Statement stmt = conn.createStatement()) { @@ -195,18 +195,18 @@ protected byte[] execute(Connection conn, SampleResult sample) throws SQLExcepti int updateCount = stmt.getUpdateCount(); String results = updateCount + " updates"; return results.getBytes(ENCODING); + } finally { + commitTransaction(conn); } } else if (PREPARED_SELECT.equals(currentQueryType)) { try (PreparedStatement pstmt = getPreparedStatement(conn)) { setArguments(pstmt); configureMaxRows(pstmt); - ResultSet rs = null; - try { - rs = pstmt.executeQuery(); + try (ResultSet rs = pstmt.executeQuery()) { sample.latencyEnd(); return getStringFromResultSet(rs).getBytes(ENCODING); } finally { - close(rs); + commitTransaction(conn); } } } else if (PREPARED_UPDATE.equals(currentQueryType)) { @@ -216,6 +216,8 @@ protected byte[] execute(Connection conn, SampleResult sample) throws SQLExcepti sample.latencyEnd(); String sb = resultSetsToString(pstmt,false,null); return sb.getBytes(ENCODING); + } finally { + commitTransaction(conn); } } else if (ROLLBACK.equals(currentQueryType)){ conn.rollback(); @@ -238,6 +240,13 @@ protected byte[] execute(Connection conn, SampleResult sample) throws SQLExcepti } } + private static void commitTransaction(Connection conn) throws SQLException { + if (!conn.getAutoCommit()) { + // HikariCP rollsback the transaction when a dirty connection returns back to the pool, so we commit it explicitly + conn.commit(); + } + } + private void configureMaxRows(Statement stmt) throws SQLException { int maxRows = getIntegerResultSetMaxRows(); if (maxRows >= 0) { diff --git a/src/protocol/jdbc/src/main/java/org/apache/jmeter/protocol/jdbc/config/DataSourceElement.java b/src/protocol/jdbc/src/main/java/org/apache/jmeter/protocol/jdbc/config/DataSourceElement.java index c074c16027f..964e57be20e 100644 --- a/src/protocol/jdbc/src/main/java/org/apache/jmeter/protocol/jdbc/config/DataSourceElement.java +++ b/src/protocol/jdbc/src/main/java/org/apache/jmeter/protocol/jdbc/config/DataSourceElement.java @@ -19,14 +19,15 @@ import java.sql.Connection; import java.sql.SQLException; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Objects; +import java.util.Properties; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; -import org.apache.commons.dbcp2.BasicDataSource; import org.apache.jmeter.config.ConfigElement; import org.apache.jmeter.gui.TestElementMetadata; import org.apache.jmeter.testbeans.TestBean; @@ -39,6 +40,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.zaxxer.hikari.HikariDataSource; + @TestElementMetadata(labelResource = "displayName") public class DataSourceElement extends AbstractTestElement implements ConfigElement, TestStateListener, TestBean { @@ -46,6 +49,9 @@ public class DataSourceElement extends AbstractTestElement private static final long serialVersionUID = 235L; + private static final AtomicBoolean VALIDATION_QUERY_USED_WARNING = new AtomicBoolean(); + private static final AtomicBoolean POOL_PREPARED_STATEMENTS_WARNING = new AtomicBoolean(); + private transient String dataSource; private transient String driver; private transient String dbUrl; @@ -70,10 +76,10 @@ public class DataSourceElement extends AbstractTestElement * These are called from different threads, so access must be synchronized. * The same instance is called in each case. */ - private transient BasicDataSource dbcpDataSource; + private transient HikariDataSource hikariDataSource; // Keep a record of the pre-thread pools so that they can be disposed of at the end of a test - private transient Set perThreadPoolSet; + private transient Set perThreadPoolSet; public DataSourceElement() { } @@ -81,23 +87,15 @@ public DataSourceElement() { @Override public void testEnded() { synchronized (this) { - if (dbcpDataSource != null) { - try { - dbcpDataSource.close(); - } catch (SQLException ex) { - log.error("Error closing pool: {}", getName(), ex); - } + if (hikariDataSource != null) { + hikariDataSource.close(); } - dbcpDataSource = null; + hikariDataSource = null; } if (perThreadPoolSet != null) {// in case - for(BasicDataSource dsc : perThreadPoolSet){ + for(HikariDataSource dsc : perThreadPoolSet){ log.debug("Closing pool: {}@{}", getDataSourceName(), System.identityHashCode(dsc)); - try { - dsc.close(); - } catch (SQLException ex) { - log.error("Error closing pool:{}", getName(), ex); - } + dsc.close(); } perThreadPoolSet=null; } @@ -124,10 +122,10 @@ public void testStarted() { if (maxPool.equals("0")){ // i.e. if we want per thread pooling variables.putObject(poolName, new DataSourceComponentImpl()); // pool will be created later } else { - BasicDataSource src = initPool(maxPool); + HikariDataSource src = initPool(maxPool); synchronized(this){ - dbcpDataSource = src; - variables.putObject(poolName, new DataSourceComponentImpl(dbcpDataSource)); + hikariDataSource = src; + variables.putObject(poolName, new DataSourceComponentImpl(hikariDataSource)); } } } @@ -142,7 +140,7 @@ public void testStarted(String host) { public Object clone() { DataSourceElement el = (DataSourceElement) super.clone(); synchronized (this) { - el.dbcpDataSource = dbcpDataSource; + el.hikariDataSource = hikariDataSource; el.perThreadPoolSet = perThreadPoolSet; } return el; @@ -208,41 +206,35 @@ public static Connection getConnection(String poolName) throws SQLException{ * Set up the DataSource - maxPool is a parameter, so the same code can * also be used for setting up the per-thread pools. */ - private BasicDataSource initPool(String maxPool) { - BasicDataSource dataSource = new BasicDataSource(); + private HikariDataSource initPool(String maxPool) { + HikariDataSource dataSource = new HikariDataSource(); if (log.isDebugEnabled()) { log.debug("MaxPool: {} Timeout: {} TrimInt: {} Auto-Commit: {} Preinit: {} poolPreparedStatements: {}", maxPool, getTimeout(), getTrimInterval(), isAutocommit(), isPreinit(), poolPreparedStatements); } int poolSize = Integer.parseInt(maxPool); - dataSource.setMinIdle(0); - dataSource.setInitialSize(poolSize); - dataSource.setAutoCommitOnReturn(false); + dataSource.setMinimumIdle(0); if (StringUtilities.isNotEmpty(initQuery)) { String[] sqls = initQuery.split("\n"); - dataSource.setConnectionInitSqls(Arrays.asList(sqls)); - } else { - dataSource.setConnectionInitSqls(Collections.emptyList()); + dataSource.setConnectionInitSql(String.join(";", sqls)); } if (StringUtilities.isNotEmpty(connectionProperties)) { - dataSource.setConnectionProperties(connectionProperties); + dataSource.setDataSourceProperties(parseConnectionProperties(connectionProperties)); } if (StringUtilities.isNotEmpty(poolPreparedStatements)) { - int maxPreparedStatements = Integer.parseInt(poolPreparedStatements); - if (maxPreparedStatements < 0) { - dataSource.setPoolPreparedStatements(false); - } else { - dataSource.setPoolPreparedStatements(true); - dataSource.setMaxOpenPreparedStatements(10); + if (POOL_PREPARED_STATEMENTS_WARNING.compareAndSet(false, true)) { + log.warn( + "Pool prepared statements required in element \"{}\" in is discouraged." + + "Statement cache at the pool level is an anti-pattern, " + + "see https://github.com/brettwooldridge/HikariCP?tab=readme-ov-file#statement-cache", + getName() + ); } } - dataSource.setRollbackOnReturn(false); - dataSource.setMaxIdle(poolSize); - dataSource.setMaxTotal(poolSize); - dataSource.setMaxWaitMillis(Long.parseLong(getTimeout())); - - dataSource.setDefaultAutoCommit(isAutocommit()); + dataSource.setMaximumPoolSize(poolSize); + dataSource.setConnectionTimeout(Long.parseLong(getTimeout())); + dataSource.setAutoCommit(isAutocommit()); if (log.isDebugEnabled()) { StringBuilder sb = new StringBuilder(40); @@ -254,26 +246,26 @@ private BasicDataSource initPool(String maxPool) { sb.append(getCheckQuery()); log.debug(sb.toString()); } - dataSource.setTestOnBorrow(false); - dataSource.setTestOnReturn(false); - dataSource.setTestOnCreate(false); - dataSource.setTestWhileIdle(false); - if(isKeepAlive()) { - dataSource.setTestWhileIdle(true); + if (isKeepAlive()) { + dataSource.setMaxLifetime(Long.parseLong(getConnectionAge())); String validationQuery = getCheckQuery(); - if (StringUtilities.isBlank(validationQuery)) { - dataSource.setValidationQuery(null); - } else { - dataSource.setValidationQuery(validationQuery); + if (StringUtilities.isNotBlank(validationQuery)) { + if (VALIDATION_QUERY_USED_WARNING.compareAndSet(false, true)) { + log.warn( + "Using explicit connection validation query \"{}\" in element \"{}\" in is discouraged." + + "Consider leaving the validation query empty so JDBC's Connection#isValid() would be used for the validation", + validationQuery, getName() + ); + } + dataSource.setConnectionTestQuery(validationQuery); } - dataSource.setSoftMinEvictableIdleTimeMillis(Long.parseLong(getConnectionAge())); - dataSource.setTimeBetweenEvictionRunsMillis(Integer.parseInt(getTrimInterval())); + dataSource.setKeepaliveTime(Long.parseLong(getTrimInterval())); } - int transactionIsolation = DataSourceElementBeanInfo.getTransactionIsolationMode(getTransactionIsolation()); - if (transactionIsolation >= 0) { - dataSource.setDefaultTransactionIsolation(transactionIsolation); + String transactionIsolation = getTransactionIsolation(); + if (!"DEFAULT".equals(transactionIsolation)) { + dataSource.setTransactionIsolation(transactionIsolation); } String _username = getUsername(); @@ -288,7 +280,7 @@ private BasicDataSource initPool(String maxPool) { log.debug(sb.toString()); } dataSource.setDriverClassName(getDriver()); - dataSource.setUrl(getDbUrl()); + dataSource.setJdbcUrl(getDbUrl()); if (!_username.isEmpty()){ dataSource.setUsername(_username); @@ -297,7 +289,7 @@ private BasicDataSource initPool(String maxPool) { if(isPreinit()) { // side effect - connection pool init - that is what we want - // see also https://commons.apache.org/proper/commons-dbcp/apidocs/org/apache/commons/dbcp2/BasicDataSource.html#setInitialSize-int- + // see also https://commons.apache.org/proper/commons-dbcp/apidocs/org/apache/commons/dbcp2/HikariDataSource.html#setInitialSize-int- // it says: "The pool is initialized the first time one of the following methods is invoked: // getConnection, setLogwriter, setLoginTimeout, getLoginTimeout, getLogWriter." // so we get a connection and close it - which releases it back to the pool (but stays open) @@ -317,8 +309,29 @@ private BasicDataSource initPool(String maxPool) { return dataSource; } + private static Properties parseConnectionProperties( String connectionProperties) { + Objects.requireNonNull(connectionProperties, "connectionProperties is null"); + String[] entries = connectionProperties.split(";"); + Properties properties = new Properties(); + for (String entry : entries) { + if (!entry.isEmpty()) { + int index = entry.indexOf('='); + if (index > 0) { + String name = entry.substring(0, index); + String value = entry.substring(index + 1); + properties.setProperty(name, value); + } else { + // no value is empty string which is how + // java.util.Properties works + properties.setProperty(entry, ""); + } + } + } + return properties; + } + // used to hold per-thread singleton connection pools - private static final ThreadLocal> perThreadPoolMap = + private static final ThreadLocal> perThreadPoolMap = ThreadLocal.withInitial(HashMap::new); /** @@ -327,13 +340,13 @@ private BasicDataSource initPool(String maxPool) { */ private class DataSourceComponentImpl { - private final BasicDataSource sharedDSC; + private final HikariDataSource sharedDSC; DataSourceComponentImpl(){ sharedDSC=null; } - DataSourceComponentImpl(BasicDataSource dsc){ + DataSourceComponentImpl(HikariDataSource dsc){ sharedDSC = dsc; } @@ -341,13 +354,11 @@ private class DataSourceComponentImpl { * @return String connection information */ private String getConnectionInfo() { - BasicDataSource dsc = getConfiguredDataSource(); - StringBuilder builder = new StringBuilder(100); - builder.append("shared:").append(sharedDSC != null) - .append(", driver:").append(dsc.getDriverClassName()) - .append(", url:").append(dsc.getUrl()) - .append(", user:").append(dsc.getUsername()); - return builder.toString(); + HikariDataSource dsc = getConfiguredDataSource(); + return "shared:" + (sharedDSC != null) + + ", driver:" + dsc.getDriverClassName() + + ", url:" + dsc.getJdbcUrl() + + ", user:" + dsc.getUsername(); } /** @@ -355,7 +366,7 @@ private String getConnectionInfo() { * @throws SQLException if database access error occurred */ private Connection getConnection() throws SQLException { - BasicDataSource dsc = getConfiguredDataSource(); + HikariDataSource dsc = getConfiguredDataSource(); Connection conn=dsc.getConnection(); int isolation = DataSourceElementBeanInfo.getTransactionIsolationMode(getTransactionIsolation()); if (isolation >= 0 && conn.getTransactionIsolation() != isolation) { @@ -372,12 +383,12 @@ private Connection getConnection() throws SQLException { return conn; } - private BasicDataSource getConfiguredDataSource() { - BasicDataSource dsc; + private HikariDataSource getConfiguredDataSource() { + HikariDataSource dsc; if (sharedDSC != null){ // i.e. shared pool dsc = sharedDSC; } else { - Map poolMap = perThreadPoolMap.get(); + Map poolMap = perThreadPoolMap.get(); dsc = poolMap.get(getDataSourceName()); if (dsc == null){ dsc = initPool("1"); @@ -437,11 +448,11 @@ public String getDataSource() { } /** - * @param dataSource + * @param dataSourceName * The poolname to set. */ - public void setDataSource(String dataSource) { - this.dataSource = dataSource; + public void setDataSource(String dataSourceName) { + this.dataSource = dataSourceName; } private String getDataSourceName() { diff --git a/src/protocol/jdbc/src/main/java/org/apache/jmeter/protocol/jdbc/processor/AbstractJDBCProcessor.java b/src/protocol/jdbc/src/main/java/org/apache/jmeter/protocol/jdbc/processor/AbstractJDBCProcessor.java index ee1cda160fc..7963b6318a8 100644 --- a/src/protocol/jdbc/src/main/java/org/apache/jmeter/protocol/jdbc/processor/AbstractJDBCProcessor.java +++ b/src/protocol/jdbc/src/main/java/org/apache/jmeter/protocol/jdbc/processor/AbstractJDBCProcessor.java @@ -40,17 +40,18 @@ public abstract class AbstractJDBCProcessor extends AbstractJDBCTestElement { * Calls the JDBC code to be executed. */ protected void process() { - if (StringUtilities.isBlank(getDataSource())) { + String dataSourceName = getDataSource(); + if (StringUtilities.isBlank(dataSourceName)) { throw new IllegalArgumentException("Name for DataSource must not be empty in " + getName()); } - try (Connection conn = DataSourceElement.getConnection(getDataSource())){ + try (Connection conn = DataSourceElement.getConnection(dataSourceName)){ execute(conn); } catch (SQLException ex) { - log.warn("SQL Problem in {}: {}", getName(), ex.toString()); + log.warn("SQL Problem in {}, query {}", getName(), getQuery(), ex); } catch (IOException ex) { - log.warn("IO Problem in {}: {}", getName(), ex.toString()); + log.warn("IO Problem in {}", getName(), ex); } catch (UnsupportedOperationException ex) { - log.warn("Execution Problem in {}: {}", getName(), ex.toString()); + log.warn("Execution Problem in {}", getName(), ex); } }