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

Reply via email to