Author: fhanik
Date: Wed Apr  7 13:54:21 2010
New Revision: 931550

URL: http://svn.apache.org/viewvc?rev=931550&view=rev
Log:
Start working on a statement cache. Add in disconnect event for interceptors, 
make the statement decorator extensible

Added:
    
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java
   (with props)
Modified:
    
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
    
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/JdbcInterceptor.java
    
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PoolProperties.java
    
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PooledConnection.java
    
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java

Modified: 
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java?rev=931550&r1=931549&r2=931550&view=diff
==============================================================================
--- 
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
 (original)
+++ 
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
 Wed Apr  7 13:54:21 2010
@@ -959,10 +959,26 @@ public class ConnectionPool {
      * @param con
      */
     protected void finalize(PooledConnection con) {
-        // NOOP
+        JdbcInterceptor handler = con.getHandler();
+        while (handler!=null) {
+            handler.reset(null, null);
+            handler=handler.getNext();
+        }
     }
     
     /**
+     * Hook to perform final actions on a pooled connection object once it has 
been disconnected and will be discarded
+     * @param con
+     */
+    protected void disconnectEvent(PooledConnection con, boolean finalizing) {
+        JdbcInterceptor handler = con.getHandler();
+        while (handler!=null) {
+            handler.disconnected(this, con, finalizing);
+            handler=handler.getNext();
+        }
+    }
+
+    /**
      * Return the object that is potentially registered in JMX for 
notifications
      * @return the object implementing the {...@link 
org.apache.tomcat.jdbc.pool.jmx.ConnectionPoolMBean} interface 
      */

Modified: 
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/JdbcInterceptor.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/JdbcInterceptor.java?rev=931550&r1=931549&r2=931550&view=diff
==============================================================================
--- 
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/JdbcInterceptor.java
 (original)
+++ 
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/JdbcInterceptor.java
 Wed Apr  7 13:54:21 2010
@@ -149,6 +149,18 @@ public abstract class JdbcInterceptor im
     public abstract void reset(ConnectionPool parent, PooledConnection con);
     
     /**
+     * Called when {...@link java.sql.Connection#close()} is called on the 
underlying connection.
+     * This is to notify the interceptors, that the physical connection has 
been released.
+     * Implementation of this method should be thought through with care, as 
no actions should trigger an exception.
+     * @param parent - the connection pool that this connection belongs to
+     * @param con    - the pooled connection that holds this connection
+     * @param finalizing - if this connection is finalizing. True means that 
the pooled connection will not reconnect the underlying connection
+     */
+    public void disconnected(ConnectionPool parent, PooledConnection con, 
boolean finalizing) {
+    }
+        
+    
+    /**
      * Returns the properties configured for this interceptor
      * @return the configured properties for this interceptor
      */

Modified: 
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PoolProperties.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PoolProperties.java?rev=931550&r1=931549&r2=931550&view=diff
==============================================================================
--- 
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PoolProperties.java
 (original)
+++ 
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PoolProperties.java
 Wed Apr  7 13:54:21 2010
@@ -811,6 +811,77 @@ public class PoolProperties implements P
         public String getValue() {
             return value;
         }
+        
+        public boolean getValueAsBoolean(boolean def) {
+            if (value==null) return def;
+            if ("true".equals(value)) return true;
+            if ("false".equals(value)) return false;
+            return def;
+        }
+        
+        public int getValueAsInt(int def) {
+            if (value==null) return def;
+            try {
+                int v = Integer.parseInt(value);
+                return v;
+            }catch (NumberFormatException nfe) {
+                return def;
+            }
+        }
+        
+        public long getValueAsLong(long def) {
+            if (value==null) return def;
+            try {
+                return Long.parseLong(value);
+            }catch (NumberFormatException nfe) {
+                return def;
+            }
+        }
+
+        public byte getValueAsByte(byte def) {
+            if (value==null) return def;
+            try {
+                return Byte.parseByte(value);
+            }catch (NumberFormatException nfe) {
+                return def;
+            }
+        }
+        
+        public short getValueAsShort(short def) {
+            if (value==null) return def;
+            try {
+                return Short.parseShort(value);
+            }catch (NumberFormatException nfe) {
+                return def;
+            }
+        }
+
+        public float getValueAsFloat(float def) {
+            if (value==null) return def;
+            try {
+                return Float.parseFloat(value);
+            }catch (NumberFormatException nfe) {
+                return def;
+            }
+        }
+
+        public double getValueAsDouble(double def) {
+            if (value==null) return def;
+            try {
+                return Double.parseDouble(value);
+            }catch (NumberFormatException nfe) {
+                return def;
+            }
+        }
+ 
+        public char getValueAschar(char def) {
+            if (value==null) return def;
+            try {
+                return value.charAt(0);
+            }catch (StringIndexOutOfBoundsException nfe) {
+                return def;
+            }
+        }
         @Override
         public int hashCode() {
             return name.hashCode();

Modified: 
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PooledConnection.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PooledConnection.java?rev=931550&r1=931549&r2=931550&view=diff
==============================================================================
--- 
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PooledConnection.java
 (original)
+++ 
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/PooledConnection.java
 Wed Apr  7 13:54:21 2010
@@ -266,6 +266,7 @@ public class PooledConnection {
         setDiscarded(true);
         if (connection != null) {
             try {
+                parent.disconnectEvent(this, finalize);
                 connection.close();
             }catch (Exception ignore) {
                 if (log.isDebugEnabled()) {

Added: 
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java?rev=931550&view=auto
==============================================================================
--- 
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java
 (added)
+++ 
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java
 Wed Apr  7 13:54:21 2010
@@ -0,0 +1,248 @@
+/* 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.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.tomcat.jdbc.pool.ConnectionPool;
+import org.apache.tomcat.jdbc.pool.JdbcInterceptor;
+import org.apache.tomcat.jdbc.pool.PooledConnection;
+import org.apache.tomcat.jdbc.pool.PoolProperties.InterceptorProperty;
+
+public class StatementCache extends StatementDecoratorInterceptor {
+    protected static final String[] ALL_TYPES = new String[] 
{PREPARE_STATEMENT,PREPARE_CALL}; 
+    protected static final String[] CALLABLE_TYPE = new String[] 
{PREPARE_CALL}; 
+    protected static final String[] PREPARED_TYPE = new String[] 
{PREPARE_STATEMENT}; 
+    protected static final String[] NO_TYPE = new String[] {};
+
+    /*begin properties for the statement cache*/
+    private boolean cachePrepared = true;
+    private boolean cacheCallable = false;
+    private int maxCacheSize = 50;
+    private ConnectionPool parent;
+    private PooledConnection pcon;
+    private String[] types;
+    
+    
+    public void setProperties(Map<String, InterceptorProperty> properties) {
+        super.setProperties(properties);
+        InterceptorProperty p = properties.get("prepared");
+        if (p!=null) cachePrepared = p.getValueAsBoolean(cachePrepared);
+        p = properties.get("callable");
+        if (p!=null) cacheCallable = p.getValueAsBoolean(cacheCallable);
+        p = properties.get("max");
+        if (p!=null) maxCacheSize = p.getValueAsInt(maxCacheSize);
+        if (cachePrepared && cacheCallable) {
+            this.types = ALL_TYPES;
+        } else if (cachePrepared) {
+            this.types = PREPARED_TYPE;
+        } else if (cacheCallable) {
+            this.types = CALLABLE_TYPE;
+        } else {
+            this.types = NO_TYPE;
+        }
+    
+    }
+    /*end properties for the statement cache*/
+    
+    /*begin the cache size*/
+    private static ConcurrentHashMap<ConnectionPool,AtomicInteger> 
cacheSizeMap = 
+        new ConcurrentHashMap<ConnectionPool,AtomicInteger>();
+    
+    private AtomicInteger cacheSize;
+
+    public void poolStarted(ConnectionPool pool) {
+        cacheSizeMap.putIfAbsent(pool, new AtomicInteger(0));
+        super.poolStarted(pool);
+    }
+    
+    public void poolClosed(ConnectionPool pool) {
+        cacheSizeMap.remove(pool);
+        super.poolClosed(pool);
+    }
+    /*end the cache size*/
+    
+    /*begin the actual statement cache*/
+    
+    private static ConcurrentHashMap<PooledConnection, 
ConcurrentHashMap<String,StatementProxy>> statementCache =
+        new ConcurrentHashMap<PooledConnection, 
ConcurrentHashMap<String,StatementProxy>>();
+    
+    
+    public void reset(ConnectionPool parent, PooledConnection con) {
+        super.reset(parent, con);
+        if (parent==null) {
+            cacheSize = null;
+            this.parent = null;
+            this.pcon = null;
+        } else {
+            cacheSize = cacheSizeMap.get(parent);
+            this.parent = parent;
+            this.pcon = con;
+        }
+    }
+
+    public void disconnected(ConnectionPool parent, PooledConnection con, 
boolean finalizing) {
+        ConcurrentHashMap<String,StatementProxy> statements = 
statementCache.get(con);
+        if (statements!=null) {
+            for (Map.Entry<String, StatementProxy> p : statements.entrySet()) {
+                closeStatement(p.getValue());
+            }
+            statements.clear();
+        }
+        super.disconnected(parent, con, finalizing);
+    }
+    
+    public void closeStatement(StatementProxy st) {
+        try {
+            if (st==null) return;
+            if (((PreparedStatement)st).isClosed()) return;
+            cacheSize.decrementAndGet();
+            st.forceClose();
+        }catch (SQLException sqe) {
+            //log debug message
+        }
+    }
+    
+    
+    public Object invoke(Object proxy, Method method, Object[] args) throws 
Throwable {
+        if (compare(CLOSE_VAL,method)) {
+            return super.invoke(proxy, method, args);
+        } else {
+            boolean process = false;
+            process = process(this.types, method, process);
+            
+            if (process) {
+                //check the cache
+                //if (isCached) {
+                    
+                //} else {
+                    return super.invoke(proxy, method, args);
+                //}
+            } else {
+                return super.invoke(proxy,method,args);
+            }
+        }
+    }
+    
+    public boolean isCached(String sql) {
+        ConcurrentHashMap<String,StatementProxy> cache = 
statementCache.get(pcon);
+        return cache.containsKey(sql);
+    }
+    
+    public boolean cacheStatement(StatementProxy proxy) {
+        ConcurrentHashMap<String,StatementProxy> cache = 
statementCache.get(pcon); 
+        if (proxy.getSql()==null) {
+            return false;
+        } else if (cache.containsKey(proxy.getSql())) {
+            cache.put(proxy.getSql(), proxy);
+            return true;
+        } else if (cacheSize.get()>=maxCacheSize) {
+            return false;
+        } else if (cacheSize.incrementAndGet()>maxCacheSize) {
+            cacheSize.decrementAndGet();
+            return false;
+        } else {
+            //cache the statement
+            return true;
+        }
+    }
+    /*end the actual statement cache*/
+
+    
+    protected class StatementProxy extends 
StatementDecoratorInterceptor.StatementProxy {
+        boolean cached = false;
+        public StatementProxy(Object parent, String sql) {
+            super(parent, sql);
+            cached = cacheStatement(this);
+        }
+
+        public void closeInvoked() {
+            super.closedInvoked();
+            if (cached) {
+                //cache a proxy so that we don't reuse the facade
+                StatementProxy proxy = new 
StatementProxy(getDelegate(),getSql());
+                proxy.setActualProxy(getActualProxy());
+                proxy.setConnection(getConnection());
+            }
+           
+        }
+        
+        public void forceClose() throws SQLException {
+            super.closedInvoked();
+            ((Statement)getDelegate()).close();
+        }
+        
+        public Object invoke(Object proxy, Method method, Object[] args) 
throws Throwable {
+            // get the name of the method for comparison
+            final String name = method.getName();
+            // was close invoked?
+            boolean close = compare(JdbcInterceptor.CLOSE_VAL, name);
+            // allow close to be called multiple times
+            if (close && closed)
+                return null;
+            // are we calling isClosed?
+            if (compare(JdbcInterceptor.ISCLOSED_VAL, name))
+                return Boolean.valueOf(closed);
+            // if we are calling anything else, bail out
+            if (closed)
+                throw new SQLException("Statement closed.");
+            if (name.equals("getConnection")){
+                return getConnection();
+            }
+            boolean process = isExecuteQuery(method);
+            // check to see if we are about to execute a query
+            // if we are executing, get the current time
+            Object result = null;
+            try {
+                if (cached && close) {
+                    //dont invoke actual close
+                } else {
+                    // execute the query
+                    result = method.invoke(delegate, args);
+                }
+            } catch (Throwable t) {
+                if (t instanceof InvocationTargetException) {
+                    InvocationTargetException it = (InvocationTargetException) 
t;
+                    throw it.getCause() != null ? it.getCause() : it;
+                } else {
+                    throw t;
+                }
+            }
+            // perform close cleanup
+            if (close) {
+                closeInvoked();
+            }
+            if (process){
+                Constructor<?> cons = getResultSetConstructor();
+                result = cons.newInstance(new Object[]{new 
ResultSetProxy(getActualProxy(), result)});
+            }
+            return result;
+        }
+        
+    }
+    
+}
+
+

Propchange: 
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/StatementCache.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java?rev=931550&r1=931549&r2=931550&view=diff
==============================================================================
--- 
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java
 (original)
+++ 
tomcat/trunk/modules/jdbc-pool/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java
 Wed Apr  7 13:54:21 2010
@@ -97,21 +97,24 @@ public class StatementDecoratorIntercept
             Object result = null;
             String name = method.getName();
             Constructor<?> constructor = null;
+            String sql = null;
             if (compare(CREATE_STATEMENT, name)) {
                 // createStatement
                 constructor = getConstructor(CREATE_STATEMENT_IDX, 
Statement.class);
             } else if (compare(PREPARE_STATEMENT, name)) {
                 // prepareStatement
                 constructor = getConstructor(PREPARE_STATEMENT_IDX, 
PreparedStatement.class);
+                sql = (String)args[0];
             } else if (compare(PREPARE_CALL, name)) {
                 // prepareCall
                 constructor = getConstructor(PREPARE_IDX, 
CallableStatement.class);
+                sql = (String)args[0];
             } else {
                 // do nothing, might be a future unsupported method
                 // so we better bail out and let the system continue
                 return statement;
             }
-            StatementProxy statementProxy = new StatementProxy(statement);
+            StatementProxy statementProxy = new StatementProxy(statement,sql);
             result = constructor.newInstance(new Object[] { statementProxy });
             statementProxy.setActualProxy(result);
             statementProxy.setConnection(proxy);
@@ -142,18 +145,38 @@ public class StatementDecoratorIntercept
         protected Object delegate;
         private Object actualProxy;
         private Object connection;
+        private String sql;
 
-        public StatementProxy(Object parent) {
+        public StatementProxy(Object parent, String sql) {
             this.delegate = parent;
+            this.sql = sql;
+        }
+        public Object getDelegate() {
+            return this.delegate;
+        }
+        
+        public String getSql() {
+            return sql;
         }
 
         public void setConnection(Object proxy) {
             this.connection = proxy;            
         }
+        public Object getConnection() {
+            return this.connection;
+        }
 
         public void setActualProxy(Object proxy){
             this.actualProxy = proxy;
         }
+        public Object getActualProxy() {
+            return this.actualProxy;
+        }
+        
+        public void closedInvoked() {
+            closed = true;
+            delegate = null;
+        }
         
         public Object invoke(Object proxy, Method method, Object[] args) 
throws Throwable {
             // get the name of the method for comparison
@@ -189,8 +212,7 @@ public class StatementDecoratorIntercept
             }
             // perform close cleanup
             if (close) {
-                closed = true;
-                delegate = null;
+                closeInvoked();
             }
             if (process){
                 Constructor<?> cons = getResultSetConstructor();



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to