001/* 002 * HA-JDBC: High-Availability JDBC 003 * Copyright (c) 2004-2008 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.pool; 022 023import java.lang.reflect.Method; 024import java.sql.SQLException; 025import java.util.Arrays; 026import java.util.HashMap; 027import java.util.HashSet; 028import java.util.Map; 029import java.util.Set; 030 031import javax.sql.ConnectionEventListener; 032import javax.sql.PooledConnection; 033import javax.sql.StatementEventListener; 034 035import net.sf.hajdbc.Database; 036import net.sf.hajdbc.sql.AbstractChildInvocationHandler; 037import net.sf.hajdbc.sql.ConnectionInvocationStrategy; 038import net.sf.hajdbc.sql.DriverWriteInvocationStrategy; 039import net.sf.hajdbc.sql.InvocationStrategy; 040import net.sf.hajdbc.sql.Invoker; 041import net.sf.hajdbc.sql.SQLProxy; 042import net.sf.hajdbc.sql.TransactionContext; 043import net.sf.hajdbc.util.reflect.Methods; 044 045/** 046 * @author Paul Ferraro 047 * @param <D> 048 * @param <C> 049 */ 050@SuppressWarnings("nls") 051public abstract class AbstractPooledConnectionInvocationHandler<D, C extends PooledConnection> extends AbstractChildInvocationHandler<D, D, C> 052{ 053 private static final Method addConnectionEventListenerMethod = Methods.getMethod(PooledConnection.class, "addConnectionEventListener", ConnectionEventListener.class); 054 private static final Method addStatementEventListenerMethod = Methods.getMethod(PooledConnection.class, "addStatementEventListener", StatementEventListener.class); 055 private static final Method removeConnectionEventListenerMethod = Methods.getMethod(PooledConnection.class, "removeConnectionEventListener", ConnectionEventListener.class); 056 private static final Method removeStatementEventListenerMethod = Methods.getMethod(PooledConnection.class, "removeStatementEventListener", StatementEventListener.class); 057 058 private static final Set<Method> eventListenerMethodSet = new HashSet<Method>(Arrays.asList(addConnectionEventListenerMethod, addStatementEventListenerMethod, removeConnectionEventListenerMethod, removeStatementEventListenerMethod)); 059 060 private static final Method getConnectionMethod = Methods.getMethod(PooledConnection.class, "getConnection"); 061 private static final Method closeMethod = Methods.getMethod(PooledConnection.class, "close"); 062 063 private Map<Object, Invoker<D, C, ?>> connectionEventListenerInvokerMap = new HashMap<Object, Invoker<D, C, ?>>(); 064 private Map<Object, Invoker<D, C, ?>> statementEventListenerInvokerMap = new HashMap<Object, Invoker<D, C, ?>>(); 065 066 /** 067 * @param dataSource 068 * @param proxy 069 * @param invoker 070 * @param proxyClass 071 * @param objectMap 072 * @throws Exception 073 */ 074 protected AbstractPooledConnectionInvocationHandler(D dataSource, SQLProxy<D, D> proxy, Invoker<D, D, C> invoker, Class<C> proxyClass, Map<Database<D>, C> objectMap) throws Exception 075 { 076 super(dataSource, proxy, invoker, proxyClass, objectMap); 077 } 078 079 @Override 080 protected InvocationStrategy<D, C, ?> getInvocationStrategy(C connection, Method method, Object[] parameters) throws Exception 081 { 082 if (eventListenerMethodSet.contains(method)) 083 { 084 return new DriverWriteInvocationStrategy<D, C, Void>(); 085 } 086 087 if (method.equals(getConnectionMethod)) 088 { 089 return new ConnectionInvocationStrategy<D, C>(this.cluster, connection, this.createTransactionContext()); 090 } 091 092 return super.getInvocationStrategy(connection, method, parameters); 093 } 094 095 @Override 096 protected void postInvoke(C connection, Method method, Object[] parameters) 097 { 098 if (method.equals(closeMethod)) 099 { 100 this.getParentProxy().removeChild(this); 101 } 102 } 103 104 /** 105 * @see net.sf.hajdbc.sql.AbstractChildInvocationHandler#close(java.lang.Object, java.lang.Object) 106 */ 107 @Override 108 protected void close(D dataSource, C connection) throws SQLException 109 { 110 connection.close(); 111 } 112 113 protected abstract TransactionContext<D> createTransactionContext(); 114 115 /** 116 * @see net.sf.hajdbc.sql.AbstractInvocationHandler#record(net.sf.hajdbc.sql.Invoker, java.lang.reflect.Method, java.lang.Object[]) 117 */ 118 @Override 119 protected void record(Invoker<D, C, ?> invoker, Method method, Object[] parameters) 120 { 121 if (method.equals(addConnectionEventListenerMethod)) 122 { 123 synchronized (this.connectionEventListenerInvokerMap) 124 { 125 this.connectionEventListenerInvokerMap.put(parameters[0], invoker); 126 } 127 } 128 else if (method.equals(removeConnectionEventListenerMethod)) 129 { 130 synchronized (this.connectionEventListenerInvokerMap) 131 { 132 this.connectionEventListenerInvokerMap.remove(parameters[0]); 133 } 134 } 135 else if (method.equals(addStatementEventListenerMethod)) 136 { 137 synchronized (this.statementEventListenerInvokerMap) 138 { 139 this.statementEventListenerInvokerMap.put(parameters[0], invoker); 140 } 141 } 142 else if (method.equals(removeStatementEventListenerMethod)) 143 { 144 synchronized (this.statementEventListenerInvokerMap) 145 { 146 this.statementEventListenerInvokerMap.remove(parameters[0]); 147 } 148 } 149 } 150 151 /** 152 * @see net.sf.hajdbc.sql.AbstractInvocationHandler#replay(net.sf.hajdbc.Database, java.lang.Object) 153 */ 154 @Override 155 protected void replay(Database<D> database, C connection) throws Exception 156 { 157 synchronized (this.connectionEventListenerInvokerMap) 158 { 159 for (Invoker<D, C, ?> invoker: this.connectionEventListenerInvokerMap.values()) 160 { 161 invoker.invoke(database, connection); 162 } 163 } 164 165 synchronized (this.statementEventListenerInvokerMap) 166 { 167 for (Invoker<D, C, ?> invoker: this.statementEventListenerInvokerMap.values()) 168 { 169 invoker.invoke(database, connection); 170 } 171 } 172 } 173}