001/* 002 * HA-JDBC: High-Availability JDBC 003 * Copyright (c) 2004-2007 Paul Ferraro 004 * 005 * This library is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU Lesser General Public License as published by the 007 * Free Software Foundation; either version 2.1 of the License, or (at your 008 * option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, but WITHOUT 011 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 012 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License 013 * for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public License 016 * along with this library; if not, write to the Free Software Foundation, 017 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018 * 019 * Contact: ferraro@users.sourceforge.net 020 */ 021package net.sf.hajdbc.sql; 022 023import java.lang.reflect.Method; 024import java.lang.reflect.Proxy; 025import java.sql.Clob; 026import java.sql.Connection; 027import java.sql.SQLException; 028import java.sql.Savepoint; 029import java.util.Map; 030import java.util.Set; 031 032import net.sf.hajdbc.Database; 033import net.sf.hajdbc.util.reflect.Methods; 034 035/** 036 * @author Paul Ferraro 037 * @param <D> 038 * @param <P> 039 */ 040@SuppressWarnings("nls") 041public class ConnectionInvocationHandler<D, P> extends AbstractChildInvocationHandler<D, P, Connection> 042{ 043 private static final Set<Method> driverReadMethodSet = Methods.findMethods(Connection.class, "create(ArrayOf|Blob|Clob|NClob|SQLXML|Struct)", "getAutoCommit", "getCatalog", "getClientInfo", "getHoldability", "getTypeMap", "getWarnings", "isClosed", "isReadOnly", "nativeSQL"); 044 private static final Set<Method> databaseReadMethodSet = Methods.findMethods(Connection.class, "getTransactionIsolation", "isValid"); 045 private static final Set<Method> driverWriteMethodSet = Methods.findMethods(Connection.class, "clearWarnings", "setAutoCommit", "setClientInfo", "setHoldability", "setTypeMap"); 046 private static final Set<Method> endTransactionMethodSet = Methods.findMethods(Connection.class, "commit", "rollback"); 047 private static final Set<Method> createStatementMethodSet = Methods.findMethods(Connection.class, "createStatement"); 048 private static final Set<Method> prepareStatementMethodSet = Methods.findMethods(Connection.class, "prepareStatement"); 049 private static final Set<Method> prepareCallMethodSet = Methods.findMethods(Connection.class, "prepareCall"); 050 private static final Set<Method> setSavepointMethodSet = Methods.findMethods(Connection.class, "setSavepoint"); 051 private static final Set<Method> createClobMethodSet = Methods.findMethods(Connection.class, "createN?Clob"); 052 053 private static final Method getMetaDataMethod = Methods.getMethod(Connection.class, "getMetaData"); 054 private static final Method releaseSavepointMethod = Methods.getMethod(Connection.class, "releaseSavepoint", Savepoint.class); 055 private static final Method rollbackSavepointMethod = Methods.getMethod(Connection.class, "rollback", Savepoint.class); 056 private static final Method closeMethod = Methods.getMethod(Connection.class, "close"); 057 private static final Method createArrayOfMethod = Methods.findMethod(Connection.class, "createArrayOf"); 058 private static final Method createBlobMethod = Methods.findMethod(Connection.class, "createBlob"); 059 private static final Method createSQLXMLMethod = Methods.findMethod(Connection.class, "createSQLXML"); 060 061 private TransactionContext<D> transactionContext; 062 063 /** 064 * @param proxy 065 * @param handler 066 * @param invoker 067 * @param connectionMap 068 * @param transactionContext 069 * @throws Exception 070 */ 071 public ConnectionInvocationHandler(P proxy, SQLProxy<D, P> handler, Invoker<D, P, Connection> invoker, Map<Database<D>, Connection> connectionMap, TransactionContext<D> transactionContext) throws Exception 072 { 073 super(proxy, handler, invoker, Connection.class, connectionMap); 074 075 this.transactionContext = transactionContext; 076 } 077 078 /** 079 * @see net.sf.hajdbc.sql.AbstractChildInvocationHandler#getInvocationStrategy(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) 080 */ 081 @Override 082 protected InvocationStrategy<D, Connection, ?> getInvocationStrategy(Connection connection, Method method, Object[] parameters) throws Exception 083 { 084 if (driverReadMethodSet.contains(method)) 085 { 086 return new DriverReadInvocationStrategy<D, Connection, Object>(); 087 } 088 089 if (databaseReadMethodSet.contains(method)) 090 { 091 return new DatabaseReadInvocationStrategy<D, Connection, Object>(); 092 } 093 094 if (driverWriteMethodSet.contains(method) || method.equals(closeMethod)) 095 { 096 return new DriverWriteInvocationStrategy<D, Connection, Object>(); 097 } 098 099 if (endTransactionMethodSet.contains(method)) 100 { 101 return this.transactionContext.end(new DatabaseWriteInvocationStrategy<D, Connection, Void>(this.cluster.getTransactionalExecutor())); 102 } 103 104 if (method.equals(rollbackSavepointMethod) || method.equals(releaseSavepointMethod)) 105 { 106 return new DatabaseWriteInvocationStrategy<D, Connection, Void>(this.cluster.getTransactionalExecutor()); 107 } 108 109 if (createStatementMethodSet.contains(method)) 110 { 111 return new StatementInvocationStrategy<D>(connection, this.transactionContext); 112 } 113 114 if (prepareStatementMethodSet.contains(method)) 115 { 116 return new PreparedStatementInvocationStrategy<D>(this.cluster, connection, this.transactionContext, (String) parameters[0]); 117 } 118 119 if (prepareCallMethodSet.contains(method)) 120 { 121 return new CallableStatementInvocationStrategy<D>(this.cluster, connection, this.transactionContext); 122 } 123 124 if (setSavepointMethodSet.contains(method)) 125 { 126 return new SavepointInvocationStrategy<D>(this.cluster, connection); 127 } 128 129 if (method.equals(getMetaDataMethod)) 130 { 131 return new DatabaseMetaDataInvocationStrategy<D>(connection); 132 } 133 134 if ((createBlobMethod != null) && method.equals(createBlobMethod)) 135 { 136 return new BlobInvocationStrategy<D, Connection>(this.cluster, connection); 137 } 138 139 if (createClobMethodSet.contains(method)) 140 { 141 return new ClobInvocationStrategy<D, Connection>(this.cluster, connection, method.getReturnType().asSubclass(Clob.class)); 142 } 143 144 if ((createArrayOfMethod != null) && method.equals(createArrayOfMethod)) 145 { 146 return new ArrayInvocationStrategy<D, Connection>(this.cluster, connection); 147 } 148 149 if ((createSQLXMLMethod != null) && method.equals(createSQLXMLMethod)) 150 { 151 return new SQLXMLInvocationStrategy<D, Connection>(this.cluster, connection); 152 } 153 154 return super.getInvocationStrategy(connection, method, parameters); 155 } 156 157 /** 158 * @see net.sf.hajdbc.sql.AbstractChildInvocationHandler#getInvoker(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) 159 */ 160 @SuppressWarnings("unchecked") 161 @Override 162 protected Invoker<D, Connection, ?> getInvoker(Connection connection, Method method, Object[] parameters) throws Exception 163 { 164 if (method.equals(releaseSavepointMethod)) 165 { 166 final SQLProxy<D, Savepoint> proxy = (SQLProxy) Proxy.getInvocationHandler(parameters[0]); 167 168 return new Invoker<D, Connection, Void>() 169 { 170 public Void invoke(Database<D> database, Connection connection) throws SQLException 171 { 172 connection.releaseSavepoint(proxy.getObject(database)); 173 174 return null; 175 } 176 }; 177 } 178 179 if (method.equals(rollbackSavepointMethod)) 180 { 181 final SQLProxy<D, Savepoint> proxy = (SQLProxy) Proxy.getInvocationHandler(parameters[0]); 182 183 return new Invoker<D, Connection, Void>() 184 { 185 public Void invoke(Database<D> database, Connection connection) throws SQLException 186 { 187 connection.rollback(proxy.getObject(database)); 188 189 return null; 190 } 191 }; 192 } 193 194 return super.getInvoker(connection, method, parameters); 195 } 196 197 /** 198 * @see net.sf.hajdbc.sql.AbstractChildInvocationHandler#isSQLMethod(java.lang.reflect.Method) 199 */ 200 @Override 201 protected boolean isSQLMethod(Method method) 202 { 203 return prepareStatementMethodSet.contains(method); 204 } 205 206 /** 207 * @see net.sf.hajdbc.sql.AbstractChildInvocationHandler#postInvoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) 208 */ 209 @SuppressWarnings("unchecked") 210 @Override 211 protected void postInvoke(Connection object, Method method, Object[] parameters) 212 { 213 if (method.equals(closeMethod)) 214 { 215 this.transactionContext.close(); 216 217 this.getParentProxy().removeChild(this); 218 } 219 else if (method.equals(releaseSavepointMethod)) 220 { 221 SQLProxy<D, Savepoint> proxy = (SQLProxy) Proxy.getInvocationHandler(parameters[0]); 222 223 this.removeChild(proxy); 224 } 225 } 226 227 /** 228 * @see net.sf.hajdbc.sql.AbstractChildInvocationHandler#close(java.lang.Object, java.lang.Object) 229 */ 230 @Override 231 protected void close(P parent, Connection connection) throws SQLException 232 { 233 connection.close(); 234 } 235 236 /** 237 * @see net.sf.hajdbc.sql.AbstractInvocationHandler#isRecordable(java.lang.reflect.Method) 238 */ 239 @Override 240 protected boolean isRecordable(Method method) 241 { 242 return driverWriteMethodSet.contains(method); 243 } 244}