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