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}