| /* Copyright (c) 2001-2010, The HSQL Development Group |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * Redistributions of source code must retain the above copyright notice, this |
| * list of conditions and the following disclaimer. |
| * |
| * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * Neither the name of the HSQL Development Group nor the names of its |
| * contributors may be used to endorse or promote products derived from this |
| * software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG, |
| * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| |
| package org.hsqldb.jdbc.pool; |
| |
| import java.lang.reflect.InvocationHandler; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Proxy; |
| import java.sql.Array; |
| import java.sql.CallableStatement; |
| import java.sql.Connection; |
| import java.sql.DatabaseMetaData; |
| import java.sql.PreparedStatement; |
| import java.sql.ResultSet; |
| import java.sql.SQLException; |
| import java.sql.Statement; |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.Set; |
| |
| import org.hsqldb.lib.IntValueHashMap; |
| |
| /** |
| * An <tt>InvocationHandler</tt> that facilitates <tt>Connection</tt> and |
| * <tt>Statement</tt> pooling. |
| * |
| * The primary function is to avoid directly exposing close() and isClosed() |
| * methods on physical <tt>Connection</tt> and <tt>Statement</tt> objects that |
| * are participating in a pooling implementation. |
| * |
| * The secondary function is to assist the pooling mechanism by providing |
| * check in notification for <tt>Connection</tt> objects and |
| * check in / check out notification for derived <tt>Statement</tt> objects. |
| * |
| * @author boucherb@users |
| * @version 1.9.0 |
| * @since 1.9.0 |
| */ |
| public class WrapperInvocationHandler implements InvocationHandler { |
| |
| // First, some helper definitions |
| |
| /** |
| * Uniquely identifies, by the generating invocation signature, a |
| * physical <tt>Statement</tt> object or like collection thereof. <p> |
| */ |
| public final class StatementKey { |
| |
| // assigned in constructor |
| private final Method method; |
| private final Object[] args; |
| |
| // derived |
| private int hashCode; |
| |
| /** |
| * Constructs a new <tt>StatementKey</tt> from the given invocation |
| * signature. |
| * |
| * @param method the invocation's method |
| * @param args the invocation's arguments |
| */ |
| StatementKey(Method method, Object[] args) { |
| |
| this.method = method; |
| this.args = (args == null) ? null |
| : (Object[]) args.clone(); |
| } |
| |
| /** |
| * Returns the hash code value for this object. <p> |
| * |
| * This method is supported to allow semantically correct participation |
| * as a key in a keyed <tt>Collection</tt> (e.g. a <tt>Map</tt>), |
| * <tt>Hashtable</tt> or as an element in a <tt>HashSet</tt>. |
| * |
| * @return The hash code value for this object |
| * @see #equals(java.lang.Object) |
| * @see java.lang.Object#hashCode() |
| */ |
| public int hashCode() { |
| |
| if (hashCode == 0) { |
| int h = method.hashCode(); |
| |
| if (args != null) { |
| for (int i = args.length - 1; i >= 0; i--) { |
| if (args[i] != null) { |
| h = 31 * h + args[i].hashCode(); |
| } |
| } |
| } |
| |
| hashCode = h; |
| } |
| |
| return hashCode; |
| } |
| |
| /** |
| * Indicates whether some other object is "equal to" this one. <p> |
| * |
| * An object is equal to a <tt>StatementKey</tt> if it is the same |
| * object or it is a different StatementKey having an equivalent |
| * method and argument array. |
| * |
| * @param obj the reference object with which to compare. |
| * @return <code>true</code> if this object is the same as the obj |
| * argument; <code>false</code> otherwise. |
| * @see #hashCode() |
| * @see java.lang.Object#equals(java.lang.Object) |
| */ |
| public boolean equals(Object obj) { |
| |
| if (this == obj) { |
| return true; |
| } else if (obj instanceof StatementKey) { |
| StatementKey other = (StatementKey) obj; |
| |
| return (this.method.equals(other.method) |
| && ((this.args == other.args) |
| || Arrays.equals(this.args, other.args))); |
| } else { |
| return false; |
| } |
| } |
| |
| /** |
| * Retrieves the <tt>Method</tt> object with which this key was |
| * constructed. <p> |
| * |
| * @return the <tt>Method</tt> object with which this key was |
| * constructed. |
| */ |
| public Method getMethod() { |
| return this.method; |
| } |
| |
| /** |
| * Retrieves a copy of the argument array which this key was |
| * constructed. <p> |
| * |
| * @return a copy of the argument array which this key was |
| * constructed |
| */ |
| public Object[] getArgs() { |
| return (args == null) ? null |
| : (Object[]) args.clone(); |
| } |
| } |
| |
| |
| /** |
| * Interface required to cooperate with a <tt>Statement</tt> pool |
| * implementation. <p> |
| */ |
| public interface StatementPool { |
| |
| /** |
| * Indicates to an underlying pool that the given physical |
| * <tt>Statement</tt> object is no longer in use by a |
| * surrogate. <p> |
| * |
| * @param key an object representing the invocation signature with |
| * which the surrogate's invocation handler originally generated |
| * the statement. |
| * @param stmt the physical <tt>Statement</tt> object |
| */ |
| public void checkIn(StatementKey key, Statement stmt); |
| |
| /** |
| * Asks an underlying pool for a physical <tt>Statement</tt> object |
| * compatible with the invocation signature represented by the given |
| * key. <p> |
| * |
| * The pool may respond with a <tt>null</tt> value, in which case it is |
| * the job of the invocation handler to delegate to the underlying |
| * physical <tt>Connection</tt>. |
| * |
| * @param key an object representing the invocation signature used |
| * to request a statement. |
| */ |
| public Statement checkOut(StatementKey key); |
| |
| /** |
| * Retrieves whether the given physical <tt>Statement</tt> object is |
| * poolable. <p> |
| * |
| * If it is not, then the invocation handler is free to skip checking |
| * the <tt>Statement</tt> back in when its surrogate is closed, |
| * closing the <tt>Statement</tt> directly, instead. On the other |
| * hand, a well-written statement pool implementation should correctly |
| * handle checkin of non-poolable statements by closing them. |
| */ |
| public boolean isPoolable(Statement stmt); |
| } |
| |
| /** |
| * Interface required to cooperate with a <tt>Connection</tt> pool |
| * implementation. <p> |
| * |
| * A <tt>DataSource</tt> handles checkout of physical connections from an |
| * underlying pool and exposes a <tt>WrapperInvocationHandler.ConnectionPool</tt> |
| * interface to allow each physical <tt>Connection</tt> object's |
| * corresponding <tt>WrapperInvocationHandler</tt> to check it back in to |
| * the underlying pool when the surrogate is closed. |
| */ |
| public interface ConnectionPool { |
| |
| /** |
| * Returns a physical <tt>Connection</tt> to an underlying pool. <p> |
| * |
| * @param connection The physical connection object being returned to |
| * the pool. |
| * @param statementPool The implementation originally provided |
| * by the pooling implementation to facilitate statement reuse |
| * against the given <tt>Connection</tt> object. |
| */ |
| void checkIn(Connection connection, StatementPool statementPool); |
| } |
| |
| // ----- Static computation of Methods that are sensitive to pooling ------- |
| public static final int WIH_NO_SURROGATE = 0; |
| public static final int WIH_CLOSE_SURROGATE = 1; |
| public static final int WIH_IS_CLOSED_SURROGATE = 2; |
| public static final int WIH_GET_PARENT_SURROGATE = 3; |
| public static final int WIH_GET_DATABASEMETADATA_SURROGATE = 4; |
| public static final int WIH_CREATE_OR_PREPARE_STATEMENT_SURROGATE = 5; |
| public static final int WIH_GET_RESULTSET_SURROGATE = 6; |
| public static final int WIH_UNWRAP_SURROGATE = 7; |
| public static final int WIH_GET_ARRAY_SURROGATE = 8; |
| protected static final IntValueHashMap methodMap = new IntValueHashMap(); |
| |
| // ------- Interfaces having methods that are sensitive to pooling --------- |
| protected static final Class[] arrayInterface = new Class[]{ Array.class }; |
| protected static final Class[] connectionInterface = new Class[]{ |
| Connection.class }; |
| protected static final Class[] callableStatementInterface = new Class[]{ |
| CallableStatement.class }; |
| protected static final Class[] databaseMetaDataInterface = new Class[]{ |
| DatabaseMetaData.class }; |
| |
| //protected static final Class[] parameterMetaDataInterface |
| // = new Class[]{ParameterMetaData.class}; |
| protected static final Class[] preparedStatementInterface = new Class[]{ |
| PreparedStatement.class }; |
| |
| //protected static final Class[] resultSetMetaDataInterface |
| // = new Class[]{ResultSetMetaData.class}; |
| protected static final Class[] resultSetInterface = new Class[]{ |
| ResultSet.class }; |
| protected static final Class[] statementInterface = new Class[]{ |
| Statement.class }; |
| |
| // ------------------ Static Initialization Helper Methods ----------------- |
| |
| /** |
| * Simple test used only during static initialization. |
| * |
| * @param clazz reflecting the given public member method |
| * @param method to test |
| * @return true if close() method of poolable class |
| */ |
| protected static boolean _isCloseSurrogateMethod(final Class clazz, |
| final Method method) { |
| |
| return ((Connection.class.isAssignableFrom( |
| clazz) || Statement.class.isAssignableFrom( |
| clazz)) && "close".equals(method.getName())); |
| } |
| |
| /** |
| * Simple test used only during static initialization. |
| * |
| * @param clazz reflecting the given public member method |
| * @param method to test |
| * @return true if isClosed() method of poolable class |
| */ |
| protected static boolean _isIsClosedSurrogateMethod(final Class clazz, |
| final Method method) { |
| |
| return ((Connection.class.isAssignableFrom( |
| clazz) || Statement.class.isAssignableFrom( |
| clazz)) && "isClosed".equals(method.getName())); |
| } |
| |
| // /** |
| // * |
| // * Simple test used only during static initialization. |
| // * |
| // * @param clazz reflecting the given public member method |
| // * @param method to test |
| // * @return true if isWrapperFor() method of class exposes pooling-senstive |
| // * behavior |
| // */ |
| // protected static boolean isIsWrapperForMethod(final Method method) { |
| // return "isWrapperFor".equals(method.getName()); |
| // } |
| |
| /** |
| * Simple test used only during static initialization. |
| * |
| * @param method to test |
| * @return true if method is an unwrap() method |
| */ |
| protected static boolean _isUnwrapMethod(final Method method) { |
| return "unwrap".equals(method.getName()); |
| } |
| |
| // ------------------------ Static Initialization -------------------------- |
| static { |
| Class[] poolingSensitiveInterfaces = new Class[] { |
| java.sql.Array.class, java.sql.CallableStatement.class, |
| java.sql.Connection.class, java.sql.DatabaseMetaData.class, |
| |
| // unlikely to expose raw delegate of interest |
| //java.sql.ParameterMetaData.class, |
| java.sql.PreparedStatement.class, java.sql.ResultSet.class, |
| |
| // unlikely to expose raw delegate of interest |
| //java.sql.ResultSetMetaData.class, |
| java.sql.Statement.class |
| }; |
| |
| for (int i = 0; i < poolingSensitiveInterfaces.length; i++) { |
| Class clazz = poolingSensitiveInterfaces[i]; |
| Method[] methods = clazz.getMethods(); |
| |
| for (int j = 0; j < methods.length; j++) { |
| Method method = methods[j]; |
| Class returnType = method.getReturnType(); |
| |
| if (_isCloseSurrogateMethod(clazz, method)) { |
| methodMap.put(method, WIH_CLOSE_SURROGATE); |
| } else if (_isIsClosedSurrogateMethod(clazz, method)) { |
| methodMap.put(method, WIH_IS_CLOSED_SURROGATE); |
| } else if (Array.class.isAssignableFrom(returnType)) { |
| methodMap.put(method, WIH_GET_ARRAY_SURROGATE); |
| } else if (Connection.class.isAssignableFrom(returnType)) { |
| methodMap.put(method, WIH_GET_PARENT_SURROGATE); |
| } else if (Statement.class.isAssignableFrom(returnType)) { |
| String methodName = method.getName(); |
| |
| if (methodName.startsWith("create") |
| || methodName.startsWith("prepare")) { |
| methodMap.put( |
| method, WIH_CREATE_OR_PREPARE_STATEMENT_SURROGATE); |
| } else { |
| methodMap.put(method, WIH_GET_PARENT_SURROGATE); |
| } |
| } else if (ResultSet.class.isAssignableFrom(returnType)) { |
| methodMap.put(method, WIH_GET_RESULTSET_SURROGATE); |
| } else if (DatabaseMetaData.class.isAssignableFrom( |
| returnType)) { |
| methodMap.put(method, WIH_GET_DATABASEMETADATA_SURROGATE); |
| |
| //} else if (ParameterMetaData.class. |
| // isAssignableFrom(returnType)) { |
| // ************************************************************* |
| //} else if (ResultSetMetaData.class. |
| // isAssignableFrom(returnType)) { |
| // ************************************************************* |
| //} else if (isIsWrapperForMethod(method)) { |
| // ************************************************************* |
| } else if (_isUnwrapMethod(method)) { |
| methodMap.put(method, WIH_UNWRAP_SURROGATE); |
| } |
| } |
| } |
| } |
| |
| // ----------------------- Construction Utility Method --------------------- |
| |
| /** |
| * Given a delegate, retrieves the interface that must be implemented by a |
| * surrogate dynamic proxy to ensure pooling sensitive methods |
| * of the delegate are not exposed directly to clients. |
| * |
| * @param delegate the target delegate of interest |
| * @return the interface that must be implemented by a surrogate dynamic |
| * proxy to ensure pooling sensitive methods of the delegate are |
| * not exposed directly to clients |
| */ |
| protected static Class[] _computeProxiedInterface(Object delegate) { |
| |
| // NOTE: Order is important for XXXStatement. |
| if (delegate instanceof Array) { |
| return arrayInterface; |
| } else if (delegate instanceof Connection) { |
| return connectionInterface; |
| } else if (delegate instanceof CallableStatement) { |
| return callableStatementInterface; |
| } else if (delegate instanceof DatabaseMetaData) { |
| return databaseMetaDataInterface; |
| } else if (delegate instanceof PreparedStatement) { |
| return preparedStatementInterface; |
| } else if (delegate instanceof ResultSet) { |
| return resultSetInterface; |
| } else if (delegate instanceof Statement) { |
| return statementInterface; |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Retrieves a numeric classification of the surrogate method type |
| * corresponding to the given delegate method. |
| * |
| * @param method the method to test |
| * @return the numeric classification |
| */ |
| protected static int _computeSurrogateType(Method method) { |
| return methodMap.get(method, WIH_NO_SURROGATE); |
| } |
| |
| // ---------------------------- Instance Fields ---------------------------- |
| // set in constructor |
| private Object delegate; |
| private Object surrogate; |
| private WrapperInvocationHandler parentHandler; |
| private ConnectionPool connectionPool; |
| private StatementPool statementPool; |
| |
| // derivied |
| private WrapperInvocationHandler dbmdHandler; |
| private boolean surrogateClosed; |
| private StatementKey statementKey; |
| private Set resultSets; //ResultSet invocation handlers |
| private Set statements; //Statement invocation handlers |
| |
| // ------------------------------ Constructors --------------------------------- |
| |
| /** |
| * Constructs a new invocation handler for the given <tt>Connection</tt>. |
| * |
| * @param connection the <tt>Connection</tt> for which to construct an |
| * invocation handler |
| * @param connectionPool interface to an external connection pool; may be null |
| * @param statementPool interface to an external statement pool; may be null |
| * @throws java.lang.IllegalArgumentException if connection is null |
| */ |
| public WrapperInvocationHandler(Connection connection, |
| ConnectionPool connectionPool, |
| StatementPool statementPool) |
| throws IllegalArgumentException { |
| |
| this(connection, null); |
| |
| this.connectionPool = connectionPool; |
| this.statementPool = statementPool; |
| } |
| |
| /** |
| * Constructs a new invocation handler for the given delegate and |
| * parent invocation handler. <p> |
| * |
| * @param delegate the delegate for which to construct the invocation handler |
| * @param parent the invocation handler of the delegate's parent; may be null. |
| * @throws IllegalArgumentException if delegate is null; or if its proxied |
| * interface cannot be determined; or if any of the restrictions on the |
| * parameters that may be passed to <code>Proxy.newProxyInstance</code> |
| * are violated |
| */ |
| public WrapperInvocationHandler(Object delegate, |
| WrapperInvocationHandler parent) |
| throws IllegalArgumentException { |
| |
| if (delegate == null) { |
| throw new IllegalArgumentException("delegate: null"); |
| } |
| |
| Class[] proxiedInterface = _computeProxiedInterface(delegate); |
| |
| if (proxiedInterface == null) { |
| throw new IllegalArgumentException("delegate: " + delegate); |
| } |
| |
| this.delegate = delegate; |
| this.parentHandler = parent; |
| this.surrogate = |
| Proxy.newProxyInstance(proxiedInterface[0].getClassLoader(), |
| proxiedInterface, this); |
| } |
| |
| // ----------------------- Interface Implementation ------------------------ |
| |
| /** |
| * Processes a method invocation on a proxy instance and returns |
| * the result. This method will be invoked on an invocation handler |
| * when a method is invoked on a proxy instance that it is |
| * associated with. |
| * |
| * @param proxy the proxy instance that the method was invoked on |
| * |
| * @param method the <code>Method</code> instance corresponding to |
| * the interface method invoked on the proxy instance. The declaring |
| * class of the <code>Method</code> object will be the interface that |
| * the method was declared in, which may be a superinterface of the |
| * proxy interface that the proxy class inherits the method through. |
| * |
| * @param args an array of objects containing the values of the |
| * arguments passed in the method invocation on the proxy instance, |
| * or <code>null</code> if interface method takes no arguments. |
| * Arguments of primitive types are wrapped in instances of the |
| * appropriate primitive wrapper class, such as |
| * <code>java.lang.Integer</code> or <code>java.lang.Boolean</code>. |
| * |
| * @return the value to return from the method invocation on the |
| * proxy instance. If the declared return type of the interface |
| * method is a primitive type, then the value returned by |
| * this method must be an instance of the corresponding primitive |
| * wrapper class; otherwise, it must be a type assignable to the |
| * declared return type. If the value returned by this method is |
| * <code>null</code> and the interface method's return type is |
| * primitive, then a <code>NullPointerException</code> will be |
| * thrown by the method invocation on the proxy instance. If the |
| * value returned by this method is otherwise not compatible with |
| * the interface method's declared return type as described above, |
| * a <code>ClassCastException</code> will be thrown by the method |
| * invocation on the proxy instance. |
| * |
| * @throws Throwable the exception to throw from the method |
| * invocation on the proxy instance. The exception's type must be |
| * assignable either to any of the exception types declared in the |
| * <code>throws</code> clause of the interface method or to the |
| * unchecked exception types <code>java.lang.RuntimeException</code> |
| * or <code>java.lang.Error</code>. If a checked exception is |
| * thrown by this method that is not assignable to any of the |
| * exception types declared in the <code>throws</code> clause of |
| * the interface method, then undeclared {@link Throwable} containing |
| * the exception that was thrown by this method will be thrown by the |
| * method invocation on the proxy instance. |
| * |
| * @see Throwable |
| */ |
| |
| /** |
| * @todo: - Synchronization can be made more granular if performance suffers. |
| * - Requires some private lock objects and synchronized blocks in |
| * certain methods. |
| * - This was the obvious and easy synchronization point to pick |
| * initially for prototyping purposes |
| */ |
| public synchronized Object invoke(final Object proxy, final Method method, |
| final Object[] args) throws Throwable { |
| |
| Object result; |
| |
| switch (_computeSurrogateType(method)) { |
| |
| case WIH_CLOSE_SURROGATE : { |
| closeSurrogate(); |
| |
| result = null; |
| |
| break; |
| } |
| case WIH_IS_CLOSED_SURROGATE : { |
| result = isClosedSurrogate() ? Boolean.TRUE |
| : Boolean.FALSE; |
| |
| break; |
| } |
| case WIH_GET_PARENT_SURROGATE : { |
| checkSurrogateClosed(); |
| |
| result = getParentSurrogate(method, args); |
| |
| break; |
| } |
| case WIH_GET_DATABASEMETADATA_SURROGATE : { |
| checkSurrogateClosed(); |
| |
| result = getDatabaseMetaDataSurrogate(method, args); |
| |
| break; |
| } |
| case WIH_CREATE_OR_PREPARE_STATEMENT_SURROGATE : { |
| checkSurrogateClosed(); |
| |
| result = getCreatedOrPreparedStatementSurrogate(method, args); |
| |
| break; |
| } |
| case WIH_GET_RESULTSET_SURROGATE : { |
| checkSurrogateClosed(); |
| |
| result = getResultSetSurrogate(method, args); |
| |
| break; |
| } |
| case WIH_UNWRAP_SURROGATE : { |
| checkSurrogateClosed(); |
| |
| result = unwrapSurrogate(method, args); |
| |
| break; |
| } |
| case WIH_GET_ARRAY_SURROGATE : { |
| checkSurrogateClosed(); |
| |
| result = getArraySurrogate(method, args); |
| |
| break; |
| } |
| case WIH_NO_SURROGATE : |
| default : { |
| checkSurrogateClosed(); |
| |
| result = method.invoke(delegate, args); |
| |
| break; |
| } |
| } |
| |
| return result; |
| } |
| |
| // --------------------- java.lang.Object overrides ------------------------ |
| |
| /** |
| * Ensures the identity hash code is returned for all descendents |
| * |
| * @return the identity hashCode for this object. |
| */ |
| public final int hashCode() { |
| return System.identityHashCode(this); |
| } |
| |
| /** |
| * Ensures identity equality semantics are preserved for all descendents. |
| * |
| * @param o the object with which to compare |
| * @return true if (this == o); else false |
| */ |
| public final boolean equals(Object o) { |
| return (this == o); |
| } |
| |
| // -------------------------- Internal Implementation ---------------------- |
| |
| /** |
| * Checks if the surrogate is closed. |
| * |
| * @throws java.lang.Throwable if the surrogate is closed. |
| */ |
| protected void checkSurrogateClosed() throws Throwable { |
| |
| if (isClosedSurrogate()) { |
| throw new SQLException("Surrogate Closed."); // TODO: better msg |
| } |
| } |
| |
| /** |
| * Effectively closes the surrogate, possibly doing work toward |
| * enabling reuse of the delegate. <p> |
| * |
| * @throws java.lang.Throwable if an access error occurs during work to |
| * enable reuse of the delegate |
| */ |
| protected void closeSurrogate() throws Throwable { |
| |
| if (this.surrogateClosed) { |
| return; |
| } |
| |
| if (this.resultSets != null) { |
| Iterator it = this.resultSets.iterator(); |
| |
| // Changed to set of ResultSet invocation handlers so |
| // that handler resources can be cleaned up too. |
| while (it.hasNext()) { |
| WrapperInvocationHandler handler = |
| (WrapperInvocationHandler) it.next(); |
| |
| try { |
| ((ResultSet) handler.delegate).close(); |
| } catch (Exception ex) {} |
| |
| try { |
| handler.closeSurrogate(); |
| } catch (Exception e) {} |
| } |
| } |
| |
| if (this.statements != null) { |
| Iterator it = this.statements.iterator(); |
| |
| while (it.hasNext()) { |
| WrapperInvocationHandler handler = |
| (WrapperInvocationHandler) it.next(); |
| |
| try { |
| handler.closeSurrogate(); |
| } catch (Exception e) {} |
| } |
| } |
| |
| if (this.dbmdHandler != null) { |
| try { |
| this.dbmdHandler.closeSurrogate(); |
| } catch (Throwable ex) {} |
| } |
| |
| Object delegate = this.delegate; |
| |
| try { |
| if (delegate instanceof Connection) { |
| closeConnectionSurrogate(); |
| } else if (delegate instanceof Statement) { |
| closeStatementSurrogate(); |
| } |
| } finally { |
| this.delegate = null; |
| this.surrogate = null; |
| this.dbmdHandler = null; |
| this.parentHandler = null; |
| this.statementKey = null; |
| this.statementPool = null; |
| this.connectionPool = null; |
| this.surrogateClosed = true; |
| } |
| } |
| |
| /** |
| * Does work toward enabling reuse of the delegate, |
| * when it is a Connection. |
| * |
| * @throws java.lang.Throwable the exception, if any, thrown by |
| * returning the delegate connection to the ConnectionPool |
| * designated at construction of the connection's |
| * invocation handler. |
| */ |
| protected void closeConnectionSurrogate() throws Throwable { |
| |
| ConnectionPool connectionPool = this.connectionPool; |
| |
| if (connectionPool == null) { |
| |
| // CHECKME: policy? |
| // pool has "disapeared" or was never provided (why?): should |
| // "really" close the connection since it will no be reused. |
| Connection connection = (Connection) this.delegate; |
| |
| try { |
| connection.close(); |
| } catch (SQLException ex) {} |
| } else { |
| Connection connection = (Connection) this.delegate; |
| StatementPool statementPool = this.statementPool; |
| |
| connectionPool.checkIn(connection, statementPool); |
| } |
| } |
| |
| /** |
| * Does work toward enabling reuse of the delegate, |
| * when it is an instance of <tt>Statement</tt>. |
| * |
| * @throws java.lang.Throwable the exception, if any, thrown by |
| * returning the delegate statement to the StatementPool |
| * designated at construction of the statement's parent connection |
| * invocation handler. |
| */ |
| protected void closeStatementSurrogate() throws Throwable { |
| |
| Statement stmt = (Statement) this.delegate; |
| StatementKey key = this.statementKey; |
| StatementPool statementPool = (this.parentHandler == null) ? null |
| : this.parentHandler |
| .statementPool; |
| |
| if (key == null || statementPool == null |
| || !statementPool.isPoolable(stmt)) { |
| try { |
| stmt.close(); |
| } catch (Exception ex) {} |
| } else { |
| statementPool.checkIn(key, stmt); |
| } |
| } |
| |
| /** |
| * Retrieves whether the surrogate is closed. <p> |
| * |
| * @return true if surrogate is closed; else false |
| * @throws java.lang.Throwable |
| */ |
| protected boolean isClosedSurrogate() throws Throwable { |
| |
| if (this.surrogateClosed) { |
| return true; |
| } |
| |
| // This part is overkill now, but does not introduce |
| // incorrect operation. |
| // |
| |
| /** |
| * @todo: Special handling to check the parent is still desirable |
| * for Array surrogates (and any other proxied delegate |
| * that has a parent whose valid lifetime is at most the |
| * life of the parent and does not expose a public close() |
| * method through the JDBC API |
| */ |
| WrapperInvocationHandler parent = this.parentHandler; |
| |
| if (parent != null && parent.isClosedSurrogate()) { |
| closeSurrogate(); |
| } |
| |
| return this.surrogateClosed; |
| } |
| |
| /** |
| * Retrieves a surrogate for the parent <tt>Statement</tt> or |
| * <tt>Connection</tt> object of this handler's delegate. |
| * |
| * @param method that retrieves the delegate's parent object |
| * @param args required for method invocation |
| * @throws java.lang.Throwable the exception, if any, thrown by invoking |
| * the given method with the given arguments upon the delegate |
| * @return surrogate for the underlying parent object |
| */ |
| protected Object getParentSurrogate(final Method method, |
| final Object[] args) throws Throwable { |
| |
| WrapperInvocationHandler parent = this.parentHandler; |
| |
| return (parent == null) ? null |
| : parent.surrogate; |
| } |
| |
| /** |
| * Surrogate for any method of the delegate that returns |
| * an instance of <tt>DatabaseMetaData</tt> object. <p> |
| * |
| * @param method returning <tt>DatabaseMetaData</tt> |
| * @param args to the method |
| * @throws java.lang.Throwable the exception, if any, thrown by invoking |
| * the given method with the given arguments upon the delegate |
| * @return surrogate for the underlying DatabaseMetaData object |
| */ |
| protected Object getDatabaseMetaDataSurrogate(final Method method, |
| final Object[] args) throws Throwable { |
| |
| if (this.dbmdHandler == null) { |
| Object dbmd = method.invoke(this.delegate, args); |
| |
| this.dbmdHandler = new WrapperInvocationHandler(dbmd, this); |
| } |
| |
| return this.dbmdHandler.surrogate; |
| } |
| |
| /** |
| * Surrogate for any method of the delegate that returns an instance of |
| * <tt>Statement</tt>. <p> |
| * |
| * @param method returning instance of <tt>Statement</tt> |
| * @param args to the method |
| * @throws java.lang.Throwable the exception, if any, thrown by invoking |
| * the given method with the given arguments upon the delegate |
| * @return surrogate for the delegate Statement object |
| */ |
| protected Object getCreatedOrPreparedStatementSurrogate( |
| final Method method, final Object[] args) throws Throwable { |
| |
| WrapperInvocationHandler handler; |
| Object stmt = null; |
| StatementKey key = new StatementKey(method, args); |
| StatementPool pool = this.statementPool; |
| |
| if (pool != null) { |
| stmt = pool.checkOut(key); |
| } |
| |
| if (stmt == null) { |
| stmt = method.invoke(this.delegate, args); |
| } |
| |
| handler = new WrapperInvocationHandler(stmt, this); |
| handler.statementKey = key; |
| |
| if (this.statements == null) { |
| this.statements = new HashSet(); |
| } |
| |
| statements.add(handler); |
| |
| return handler.surrogate; |
| } |
| |
| /** |
| * Surrogate for any method of the delegate that returns an |
| * instance of <tt>ResultSet</tt>. <p> |
| * |
| * @param method returning a <tt>ResultSet</tt> |
| * @param args to the method |
| * @throws java.lang.Throwable the exception, if any, thrown by invoking |
| * the given method with the given arguments upon the delegate |
| * @return surrogate for the underlying ResultSet object |
| */ |
| protected Object getResultSetSurrogate(final Method method, |
| final Object[] args) |
| throws Throwable { |
| |
| Object rs = method.invoke(this.delegate, args); |
| WrapperInvocationHandler handler = new WrapperInvocationHandler(rs, |
| this); |
| |
| if (resultSets == null) { |
| resultSets = new HashSet(); |
| } |
| |
| // Changed to set of ResultSet invocation handlers so |
| // that handler resources can be cleaned up too. |
| resultSets.add(handler); |
| |
| return handler.surrogate; |
| } |
| |
| /** |
| * Surrogate for the delegate's unwrap(...) method. <p> |
| * |
| * If invocation of the method returns the delegate itself, then |
| * the delegate's surrogate is returned instead. |
| * |
| * @param method the unwrap method |
| * @param args the argument(s) to the unwrap method |
| * @throws java.lang.Throwable the exception, if any, thrown by invoking |
| * the given method with the given arguments upon the delegate |
| * @return proxy if the method returns the delegate itself; else the actual |
| * result of invoking the method upon the delegate. |
| */ |
| protected Object unwrapSurrogate(final Method method, |
| final Object[] args) throws Throwable { |
| |
| Object result = method.invoke(this.delegate, args); |
| |
| return (result == this.delegate) ? this.surrogate |
| : result; |
| } |
| |
| /** |
| * Surrogate for any method of the delegate that returns an |
| * instance of <tt>Array</tt>. <p> |
| * |
| * @param method returning an <tt>Array</tt> |
| * @param args to the method |
| * @throws java.lang.Throwable the exception, if any, thrown by invoking |
| * the given method with the given arguments upon the delegate |
| * @return surrogate for the underlying Array object |
| */ |
| protected Object getArraySurrogate(final Method method, |
| final Object[] args) |
| throws java.lang.Throwable { |
| |
| Object array = method.invoke(this.delegate, args); |
| WrapperInvocationHandler handler = new WrapperInvocationHandler(array, |
| this); |
| |
| return handler.surrogate; |
| } |
| } |