You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metamodel.apache.org by ka...@apache.org on 2014/02/23 19:59:38 UTC
git commit: METAMODEL-38: Added pooling of
Connection+PreparedStatement in JdbcCompiledQuery
Repository: incubator-metamodel
Updated Branches:
refs/heads/master e84630b1d -> 63659d330
METAMODEL-38: Added pooling of Connection+PreparedStatement in
JdbcCompiledQuery
Project: http://git-wip-us.apache.org/repos/asf/incubator-metamodel/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-metamodel/commit/63659d33
Tree: http://git-wip-us.apache.org/repos/asf/incubator-metamodel/tree/63659d33
Diff: http://git-wip-us.apache.org/repos/asf/incubator-metamodel/diff/63659d33
Branch: refs/heads/master
Commit: 63659d33088e97df0cfbb035ccbc45aeae69dc54
Parents: e84630b
Author: Kasper Sørensen <i....@gmail.com>
Authored: Sun Feb 23 19:59:41 2014 +0100
Committer: Kasper Sørensen <i....@gmail.com>
Committed: Sun Feb 23 19:59:41 2014 +0100
----------------------------------------------------------------------
jdbc/pom.xml | 6 +
.../metamodel/jdbc/JdbcCompiledQuery.java | 218 ++++++++++---------
.../jdbc/JdbcCompiledQueryLeaseFactory.java | 19 +-
.../apache/metamodel/jdbc/JdbcDataContext.java | 4 +
.../metamodel/jdbc/JdbcDataContextTest.java | 97 ++++++++-
5 files changed, 233 insertions(+), 111 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/63659d33/jdbc/pom.xml
----------------------------------------------------------------------
diff --git a/jdbc/pom.xml b/jdbc/pom.xml
index 348092f..870968f 100644
--- a/jdbc/pom.xml
+++ b/jdbc/pom.xml
@@ -54,6 +54,12 @@ under the License.
<artifactId>easymock</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>commons-dbcp</groupId>
+ <artifactId>commons-dbcp</artifactId>
+ <version>1.4</version>
+ <scope>test</scope>
+ </dependency>
<!-- Various freely available database drivers -->
<dependency>
http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/63659d33/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcCompiledQuery.java
----------------------------------------------------------------------
diff --git a/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcCompiledQuery.java b/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcCompiledQuery.java
index c69f5df..a78ec1e 100644
--- a/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcCompiledQuery.java
+++ b/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcCompiledQuery.java
@@ -18,12 +18,12 @@
*/
package org.apache.metamodel.jdbc;
-import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;
import org.apache.commons.pool.impl.GenericObjectPool;
+import org.apache.commons.pool.impl.GenericObjectPool.Config;
import org.apache.metamodel.MetaModelException;
import org.apache.metamodel.query.CompiledQuery;
import org.apache.metamodel.query.DefaultCompiledQuery;
@@ -38,103 +38,119 @@ import org.slf4j.LoggerFactory;
*/
final class JdbcCompiledQuery extends DefaultCompiledQuery implements CompiledQuery {
- private static final Logger logger = LoggerFactory.getLogger(JdbcCompiledQuery.class);
-
- private final JdbcDataContext _dataContext;
- private final Connection _connection;
- private final String _sql;
- private final Query _query;
- private final GenericObjectPool<JdbcCompiledQueryLease> _pool;
- private boolean _closed;
-
- public JdbcCompiledQuery(JdbcDataContext dc, Query query) {
- super(query);
- _dataContext = dc;
- _connection = dc.getConnection();
- _query = query;
- _sql = dc.getQueryRewriter().rewriteQuery(query);
- _pool = new GenericObjectPool<JdbcCompiledQueryLease>(new JdbcCompiledQueryLeaseFactory(dc, _connection, _sql));
- _closed = false;
-
- logger.debug("Created compiled JDBC query: {}", _sql);
- }
-
- public JdbcCompiledQueryLease borrowLease() {
- if (logger.isDebugEnabled()) {
- logger.debug("Borrowing lease. Leases (before): Active={}, Idle={}", getActiveLeases(), getIdleLeases());
- }
- try {
- return _pool.borrowObject();
- } catch (Exception e) {
- throw handleError(e, "borrow lease");
- }
- }
-
- public void returnLease(JdbcCompiledQueryLease lease) {
- if (logger.isDebugEnabled()) {
- logger.debug("Returning lease. Leases (before): Active={}, Idle={}", getActiveLeases(), getIdleLeases());
- }
- try {
- _pool.returnObject(lease);
- } catch (Exception e) {
- throw handleError(e, "return lease");
- }
- }
-
- private RuntimeException handleError(Exception e, String message) {
- if (logger.isWarnEnabled()) {
- logger.warn("Unexpected error occurred in compiled JDBC query: " + message, e);
- }
-
- if (e instanceof SQLException) {
- return JdbcUtils.wrapException((SQLException) e, message);
- } else if (e instanceof RuntimeException) {
- return (RuntimeException) e;
- } else {
- return new MetaModelException(message, e);
- }
- }
-
- protected int getActiveLeases() {
- return _pool.getNumActive();
- }
-
- protected int getIdleLeases() {
- return _pool.getNumIdle();
- }
-
- protected Query getQuery() {
- return _query;
- }
-
- @Override
- public String toSql() {
- return _sql;
- }
-
- @Override
- public void close() {
- logger.debug("Closing compiled JDBC query: {}", _sql);
- try {
- _pool.close();
- } catch (Exception e) {
- throw handleError(e, "close pool of leases");
- } finally {
- _dataContext.close(_connection, null, null);
- _closed = true;
- }
- }
-
- protected List<SelectItem> getSelectItems() {
- return _query.getSelectClause().getItems();
- }
-
- @Override
- protected void finalize() throws Throwable {
- super.finalize();
- if (!_closed) {
- logger.warn("finalize() invoked, but DataSet is not closed. Invoking close() on {}", this);
- close();
- }
- }
-}
+ private static final Logger logger = LoggerFactory.getLogger(JdbcCompiledQuery.class);
+
+ private final String _sql;
+ private final Query _query;
+ private final GenericObjectPool<JdbcCompiledQueryLease> _pool;
+ private boolean _closed;
+
+ public JdbcCompiledQuery(JdbcDataContext dc, Query query) {
+ super(query);
+ _query = query;
+ _sql = dc.getQueryRewriter().rewriteQuery(query);
+
+ final Config config = new Config();
+ config.maxActive = getSystemPropertyValue(JdbcDataContext.SYSTEM_PROPERTY_COMPILED_QUERY_POOL_MAX_SIZE, -1);
+ config.minEvictableIdleTimeMillis = getSystemPropertyValue(
+ JdbcDataContext.SYSTEM_PROPERTY_COMPILED_QUERY_POOL_MIN_EVICTABLE_IDLE_TIME_MILLIS, 500);
+ config.timeBetweenEvictionRunsMillis = getSystemPropertyValue(
+ JdbcDataContext.SYSTEM_PROPERTY_COMPILED_QUERY_POOL_TIME_BETWEEN_EVICTION_RUNS_MILLIS, 1000);
+
+ _pool = new GenericObjectPool<JdbcCompiledQueryLease>(new JdbcCompiledQueryLeaseFactory(dc, _sql), config);
+ _closed = false;
+
+ logger.debug("Created compiled JDBC query: {}", _sql);
+ }
+
+ private int getSystemPropertyValue(String property, int defaultValue) {
+ String str = System.getProperty(property);
+ if (str == null) {
+ return defaultValue;
+ }
+ try {
+ return Integer.parseInt(str);
+ } catch (NumberFormatException e) {
+ logger.debug("Failed to parse system property '{}': '{}'", property, str);
+ return defaultValue;
+ }
+ }
+
+ public JdbcCompiledQueryLease borrowLease() {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Borrowing lease. Leases (before): Active={}, Idle={}", getActiveLeases(), getIdleLeases());
+ }
+ try {
+ return _pool.borrowObject();
+ } catch (Exception e) {
+ throw handleError(e, "borrow lease");
+ }
+ }
+
+ public void returnLease(JdbcCompiledQueryLease lease) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Returning lease. Leases (before): Active={}, Idle={}", getActiveLeases(), getIdleLeases());
+ }
+ try {
+ _pool.returnObject(lease);
+ } catch (Exception e) {
+ throw handleError(e, "return lease");
+ }
+ }
+
+ private RuntimeException handleError(Exception e, String message) {
+ if (logger.isWarnEnabled()) {
+ logger.warn("Unexpected error occurred in compiled JDBC query: " + message, e);
+ }
+
+ if (e instanceof SQLException) {
+ return JdbcUtils.wrapException((SQLException) e, message);
+ } else if (e instanceof RuntimeException) {
+ return (RuntimeException) e;
+ } else {
+ return new MetaModelException(message, e);
+ }
+ }
+
+ protected int getActiveLeases() {
+ return _pool.getNumActive();
+ }
+
+ protected int getIdleLeases() {
+ return _pool.getNumIdle();
+ }
+
+ protected Query getQuery() {
+ return _query;
+ }
+
+ @Override
+ public String toSql() {
+ return _sql;
+ }
+
+ @Override
+ public void close() {
+ logger.debug("Closing compiled JDBC query: {}", _sql);
+ try {
+ _pool.close();
+ } catch (Exception e) {
+ throw handleError(e, "close pool of leases");
+ } finally {
+ _closed = true;
+ }
+ }
+
+ protected List<SelectItem> getSelectItems() {
+ return _query.getSelectClause().getItems();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ if (!_closed) {
+ logger.warn("finalize() invoked, but DataSet is not closed. Invoking close() on {}", this);
+ close();
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/63659d33/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcCompiledQueryLeaseFactory.java
----------------------------------------------------------------------
diff --git a/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcCompiledQueryLeaseFactory.java b/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcCompiledQueryLeaseFactory.java
index d4770bb..d973d13 100644
--- a/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcCompiledQueryLeaseFactory.java
+++ b/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcCompiledQueryLeaseFactory.java
@@ -29,13 +29,11 @@ import org.apache.commons.pool.PoolableObjectFactory;
*/
final class JdbcCompiledQueryLeaseFactory implements PoolableObjectFactory<JdbcCompiledQueryLease> {
- private final JdbcDataContext _dataContext;
- private final Connection _connection;
+ private final JdbcDataContext _dataContext;
private final String _sql;
- public JdbcCompiledQueryLeaseFactory(JdbcDataContext dataContext, Connection connection, String sql) {
- _dataContext = dataContext;
- _connection = connection;
+ public JdbcCompiledQueryLeaseFactory(JdbcDataContext dataContext, String sql) {
+ _dataContext = dataContext;
_sql = sql;
}
@@ -43,8 +41,9 @@ final class JdbcCompiledQueryLeaseFactory implements PoolableObjectFactory<JdbcC
@Override
public JdbcCompiledQueryLease makeObject() throws Exception {
try {
- final PreparedStatement statement = _connection.prepareStatement(_sql);
- final JdbcCompiledQueryLease lease = new JdbcCompiledQueryLease(_connection, statement);
+ final Connection connection = _dataContext.getConnection();
+ final PreparedStatement statement = connection.prepareStatement(_sql);
+ final JdbcCompiledQueryLease lease = new JdbcCompiledQueryLease(connection, statement);
return lease;
} catch (SQLException e) {
throw JdbcUtils.wrapException(e, "preparing statement");
@@ -53,7 +52,9 @@ final class JdbcCompiledQueryLeaseFactory implements PoolableObjectFactory<JdbcC
@Override
public void destroyObject(JdbcCompiledQueryLease lease) throws Exception {
- _dataContext.close(null, null, lease.getStatement());
+ final PreparedStatement statement = lease.getStatement();
+ final Connection connection = lease.getConnection();
+ _dataContext.close(connection, null, statement);
}
@Override
@@ -68,4 +69,4 @@ final class JdbcCompiledQueryLeaseFactory implements PoolableObjectFactory<JdbcC
@Override
public void passivateObject(JdbcCompiledQueryLease obj) throws Exception {
}
-}
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/63659d33/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcDataContext.java
----------------------------------------------------------------------
diff --git a/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcDataContext.java b/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcDataContext.java
index ec309ff..6a4b8a0 100644
--- a/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcDataContext.java
+++ b/jdbc/src/main/java/org/apache/metamodel/jdbc/JdbcDataContext.java
@@ -64,6 +64,10 @@ public class JdbcDataContext extends AbstractDataContext implements UpdateableDa
public static final String SYSTEM_PROPERTY_BATCH_UPDATES = "metamodel.jdbc.batch.updates";
public static final String SYSTEM_PROPERTY_CONVERT_LOBS = "metamodel.jdbc.convert.lobs";
+
+ public static final String SYSTEM_PROPERTY_COMPILED_QUERY_POOL_MAX_SIZE = "metamodel.jdbc.compiledquery.pool.max.size";
+ public static final String SYSTEM_PROPERTY_COMPILED_QUERY_POOL_MIN_EVICTABLE_IDLE_TIME_MILLIS = "metamodel.jdbc.compiledquery.pool.idle.timeout";
+ public static final String SYSTEM_PROPERTY_COMPILED_QUERY_POOL_TIME_BETWEEN_EVICTION_RUNS_MILLIS = "metamodel.jdbc.compiledquery.pool.eviction.period.millis";
public static final String DATABASE_PRODUCT_POSTGRESQL = "PostgreSQL";
public static final String DATABASE_PRODUCT_MYSQL = "MySQL";
http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/63659d33/jdbc/src/test/java/org/apache/metamodel/jdbc/JdbcDataContextTest.java
----------------------------------------------------------------------
diff --git a/jdbc/src/test/java/org/apache/metamodel/jdbc/JdbcDataContextTest.java b/jdbc/src/test/java/org/apache/metamodel/jdbc/JdbcDataContextTest.java
index 7a079cd..1746eaa 100644
--- a/jdbc/src/test/java/org/apache/metamodel/jdbc/JdbcDataContextTest.java
+++ b/jdbc/src/test/java/org/apache/metamodel/jdbc/JdbcDataContextTest.java
@@ -22,17 +22,25 @@ import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
import javax.sql.DataSource;
import javax.swing.table.TableModel;
import org.easymock.EasyMock;
+import org.apache.commons.dbcp.BasicDataSource;
import org.apache.metamodel.DataContext;
import org.apache.metamodel.MetaModelException;
import org.apache.metamodel.QueryPostprocessDataContext;
import org.apache.metamodel.data.DataSet;
import org.apache.metamodel.data.DataSetTableModel;
+import org.apache.metamodel.data.Row;
import org.apache.metamodel.jdbc.dialects.DefaultQueryRewriter;
import org.apache.metamodel.jdbc.dialects.IQueryRewriter;
import org.apache.metamodel.query.CompiledQuery;
@@ -272,7 +280,7 @@ public class JdbcDataContextTest extends JdbcTestCase {
public void testMaxRows() throws Exception {
final Connection realCon = getTestDbConnection();
final Statement realStatement = realCon.createStatement();
-
+
final Connection mockCon = EasyMock.createMock(Connection.class);
final Statement mockStatement = EasyMock.createMock(Statement.class);
@@ -425,4 +433,91 @@ public class JdbcDataContextTest extends JdbcTestCase {
assertEquals("Row[values=[Australia, 430300.0]]", data2.getRow().toString());
assertEquals(new DataSetTableModel(data1), new DataSetTableModel(data2));
}
+
+ public void testReleaseConnectionsInCompiledQuery() throws Exception {
+ final int connectionPoolSize = 2;
+ final int threadCount = 4;
+ final int noOfCallsPerThreads = 30;
+
+ final BasicDataSource ds = new BasicDataSource();
+ ds.setDriverClassName("org.hsqldb.jdbcDriver");
+ ds.setUrl("jdbc:hsqldb:res:metamodel");
+ ds.setInitialSize(connectionPoolSize);
+ ds.setMaxActive(connectionPoolSize);
+ ds.setMaxWait(10000);
+ ds.setMinEvictableIdleTimeMillis(1800000);
+ ds.setMinIdle(0);
+ ds.setMaxIdle(connectionPoolSize);
+ ds.setNumTestsPerEvictionRun(3);
+ ds.setTimeBetweenEvictionRunsMillis(-1);
+ ds.setDefaultTransactionIsolation(java.sql.Connection.TRANSACTION_READ_COMMITTED);
+
+ final JdbcDataContext dataContext = new JdbcDataContext(ds,
+ new TableType[] { TableType.TABLE, TableType.VIEW }, null);
+
+ final JdbcCompiledQuery compiledQuery = (JdbcCompiledQuery) dataContext.query().from("CUSTOMERS")
+ .select("CUSTOMERNAME").where("CUSTOMERNUMBER").eq(new QueryParameter()).compile();
+
+ assertEquals(0, compiledQuery.getActiveLeases());
+ assertEquals(0, compiledQuery.getIdleLeases());
+
+ final String compliedQueryString = compiledQuery.toSql();
+
+ assertEquals(
+ "SELECT _CUSTOMERS_._CUSTOMERNAME_ FROM PUBLIC._CUSTOMERS_ WHERE _CUSTOMERS_._CUSTOMERNUMBER_ = ?",
+ compliedQueryString.replace('\"', '_'));
+
+ assertEquals(0, compiledQuery.getActiveLeases());
+ assertEquals(0, compiledQuery.getIdleLeases());
+
+ ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
+ final CountDownLatch latch = new CountDownLatch(threadCount);
+ final List<Throwable> errors = new ArrayList<Throwable>();
+ final Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ for (int i = 0; i < noOfCallsPerThreads; i++) {
+ final DataSet dataSet = dataContext.executeQuery(compiledQuery, new Object[] { 103 });
+ try {
+ assertTrue(dataSet.next());
+ Row row = dataSet.getRow();
+ assertNotNull(row);
+ assertEquals("Atelier graphique", row.getValue(0).toString());
+ assertFalse(dataSet.next());
+ } finally {
+ dataSet.close();
+ }
+ }
+ } catch (Throwable e) {
+ errors.add(e);
+ } finally {
+ latch.countDown();
+ }
+ }
+ };
+
+ for (int i = 0; i < threadCount; i++) {
+ executorService.execute(runnable);
+ }
+
+ try {
+ latch.await(60000, TimeUnit.MILLISECONDS);
+
+ if (errors.size() > 0) {
+ throw new IllegalStateException(errors.get(0));
+ }
+ assertTrue(true);
+ } finally {
+ executorService.shutdownNow();
+ }
+
+ assertEquals(0, compiledQuery.getActiveLeases());
+
+ compiledQuery.close();
+
+ assertEquals(0, compiledQuery.getActiveLeases());
+ assertEquals(0, compiledQuery.getIdleLeases());
+ }
+
}
\ No newline at end of file