Author: kkolinko
Date: Fri Mar 22 13:44:00 2013
New Revision: 1459769
URL: http://svn.apache.org/r1459769
Log:
Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=54732
Fix leak of statements in StatementCache interceptor.
Added:
tomcat/trunk/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCounterInterceptor.java
(with props)
Modified:
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java
tomcat/trunk/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestStatementCache.java
Modified:
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java?rev=1459769&r1=1459768&r2=1459769&view=diff
==============================================================================
---
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java
(original)
+++
tomcat/trunk/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java
Fri Mar 22 13:44:00 2013
@@ -242,11 +242,11 @@ public class StatementCache extends Stat
removeStatement(proxy);
}
}
- closed = true;
- delegate = null;
if (shouldClose) {
super.closeInvoked();
}
+ closed = true;
+ delegate = null;
}
Added:
tomcat/trunk/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCounterInterceptor.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCounterInterceptor.java?rev=1459769&view=auto
==============================================================================
---
tomcat/trunk/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCounterInterceptor.java
(added)
+++
tomcat/trunk/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCounterInterceptor.java
Fri Mar 22 13:44:00 2013
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.jdbc.pool.interceptor;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.sql.Statement;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Interceptor that counts opened Statements. Is used by tests.
+ */
+public class StatementCounterInterceptor extends StatementDecoratorInterceptor
{
+
+ private final AtomicInteger countOpen = new AtomicInteger();
+ private final AtomicInteger countClosed = new AtomicInteger();
+
+ public int getActiveCount() {
+ return countOpen.get() - countClosed.get();
+ }
+
+ @Override
+ protected Object createDecorator(Object proxy, Method method,
+ Object[] args, Object statement, Constructor<?> constructor,
+ String sql) throws InstantiationException, IllegalAccessException,
+ InvocationTargetException {
+ Object result;
+ StatementProxy statementProxy = new StatementProxy(
+ (Statement) statement, sql);
+ result = constructor.newInstance(new Object[] { statementProxy });
+ statementProxy.setActualProxy(result);
+ statementProxy.setConnection(proxy);
+ statementProxy.setConstructor(constructor);
+ countOpen.incrementAndGet();
+ return result;
+ }
+
+ private class StatementProxy extends
+ StatementDecoratorInterceptor.StatementProxy<Statement> {
+ public StatementProxy(Statement delegate, String sql) {
+ super(delegate, sql);
+ }
+
+ @Override
+ public void closeInvoked() {
+ countClosed.incrementAndGet();
+ super.closeInvoked();
+ }
+ }
+}
Propchange:
tomcat/trunk/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCounterInterceptor.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
tomcat/trunk/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestStatementCache.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestStatementCache.java?rev=1459769&r1=1459768&r2=1459769&view=diff
==============================================================================
---
tomcat/trunk/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestStatementCache.java
(original)
+++
tomcat/trunk/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestStatementCache.java
Fri Mar 22 13:44:00 2013
@@ -16,6 +16,7 @@
*/
package org.apache.tomcat.jdbc.test;
+import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.PreparedStatement;
@@ -23,7 +24,9 @@ import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
+import org.apache.tomcat.jdbc.pool.JdbcInterceptor;
import org.apache.tomcat.jdbc.pool.interceptor.StatementCache;
+import org.apache.tomcat.jdbc.pool.interceptor.StatementCounterInterceptor;
public class TestStatementCache extends DefaultTestCase {
@@ -108,6 +111,55 @@ public class TestStatementCache extends
}
@Test
+ public void testStatementClose1() throws Exception {
+ init();
+ datasource.setJdbcInterceptors(
+ TestStatementCacheInterceptor.class.getName()
+ + "(prepared=true,callable=false,max=1);"
+ + StatementCounterInterceptor.class.getName());
+ Connection con = datasource.getConnection();
+ StatementCounterInterceptor counter = findInterceptor(con,
StatementCounterInterceptor.class);
+ PreparedStatement ps1, ps2;
+
+ ps1 = con.prepareStatement("select 1");
+ Assert.assertEquals(1, counter.getActiveCount());
+ ps1.close();
+ Assert.assertEquals("Statement goes into cache, not closed", 1,
counter.getActiveCount());
+
+ ps1 = con.prepareStatement("select 1");
+ Assert.assertEquals("Reusing statement from cache", 1,
counter.getActiveCount());
+ ps2 = con.prepareStatement("select 1");
+ Assert.assertEquals("Reusing statement from cache", 2,
counter.getActiveCount());
+
+ ps2.close();
+ Assert.assertEquals("Statement goes into cache, not closed", 2,
counter.getActiveCount());
+ ps1.close();
+ // Cache has "max=1". The following tests BZ 54732.
+ Assert.assertEquals("Statement does not go into cache, closed", 1,
counter.getActiveCount());
+
+ con.close();
+ Assert.assertEquals("Connection returned to the pool. Statement is in
cache", 1, counter.getActiveCount());
+
+ datasource.close();
+ Assert.assertEquals("Pool cleared. All statements in cache are
closed", 0, counter.getActiveCount());
+ }
+
+ @Test
+ public void testStatementClose2() throws Exception {
+ init();
+ datasource.setJdbcInterceptors(
+ TestStatementCacheInterceptor.class.getName()
+ + "(prepared=false,callable=false,max=10);"
+ + StatementCounterInterceptor.class.getName());
+ Connection con = datasource.getConnection();
+ StatementCounterInterceptor counter = findInterceptor(con,
StatementCounterInterceptor.class);
+ PreparedStatement ps1 = con.prepareStatement("select 1");
+ Assert.assertEquals(1, counter.getActiveCount());
+ ps1.close();
+ Assert.assertEquals("Statement is not pooled, closes immediately", 0,
counter.getActiveCount());
+ }
+
+ @Test
public void testMaxCacheSize() throws Exception {
init();
config(true,false,100);
@@ -130,4 +182,27 @@ public class TestStatementCache extends
TestStatementCache.interceptor = this;
}
}
+
+ /**
+ * Helper method that finds interceptor instance in interceptor chain of a
+ * proxied class.
+ *
+ * @param proxy
+ * Proxy class
+ * @param clazz
+ * Interceptor class that we are looking for
+ * @return Instance of <code>clazz</code>
+ */
+ private static <T extends JdbcInterceptor> T findInterceptor(Object proxy,
+ Class<T> clazz) {
+ JdbcInterceptor interceptor = (JdbcInterceptor) Proxy
+ .getInvocationHandler(proxy);
+ while (interceptor != null) {
+ if (clazz.isInstance(interceptor)) {
+ return clazz.cast(interceptor);
+ }
+ interceptor = interceptor.getNext();
+ }
+ return null;
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]