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: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org