001/*
002 * HA-JDBC: High-Availability JDBC
003 * Copyright (C) 2013  Paul Ferraro
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Lesser General Public License as published by
007 * the Free Software Foundation, either version 3 of the License, or
008 * (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013 * GNU Lesser General Public License for more details.
014 *
015 * You should have received a copy of the GNU Lesser General Public License
016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017 */
018package net.sf.hajdbc.sql.pool;
019
020import java.lang.reflect.Proxy;
021import java.sql.SQLException;
022import java.util.HashMap;
023import java.util.Map;
024
025import javax.sql.ConnectionEvent;
026import javax.sql.ConnectionEventListener;
027import javax.sql.PooledConnection;
028import javax.sql.StatementEvent;
029import javax.sql.StatementEventListener;
030
031import net.sf.hajdbc.Database;
032import net.sf.hajdbc.invocation.Invoker;
033import net.sf.hajdbc.logging.Level;
034import net.sf.hajdbc.sql.AbstractTransactionalProxyFactory;
035import net.sf.hajdbc.sql.LocalTransactionContext;
036import net.sf.hajdbc.sql.ProxyFactory;
037
038/**
039 * 
040 * @author Paul Ferraro
041 */
042public abstract class AbstractPooledConnectionProxyFactory<Z, D extends Database<Z>, C extends PooledConnection> extends AbstractTransactionalProxyFactory<Z, D, Z, C>
043{
044        private Map<ConnectionEventListener, Invoker<Z, D, C, ?, SQLException>> connectionEventListenerInvokers = new HashMap<ConnectionEventListener, Invoker<Z, D, C, ?, SQLException>>();
045        private Map<StatementEventListener, Invoker<Z, D, C, ?, SQLException>> statementEventListenerInvokers = new HashMap<StatementEventListener, Invoker<Z, D, C, ?, SQLException>>();
046        
047        protected AbstractPooledConnectionProxyFactory(Z parentProxy, ProxyFactory<Z, D, Z, SQLException> parent, Invoker<Z, D, Z, C, SQLException> invoker, Map<D, C> map)
048        {
049                super(parentProxy, parent, invoker, map, new LocalTransactionContext<Z, D>(parent.getDatabaseCluster()));
050        }
051
052        public void addConnectionEventListener(ConnectionEventListener listener, Invoker<Z, D, C, ?, SQLException> invoker)
053        {
054                this.connectionEventListenerInvokers.put(listener, invoker);
055        }
056        
057        public void removeConnectionEventListener(ConnectionEventListener listener)
058        {
059                this.connectionEventListenerInvokers.remove(listener);
060        }
061        
062        public void addStatementEventListener(StatementEventListener listener, Invoker<Z, D, C, ?, SQLException> invoker)
063        {
064                this.statementEventListenerInvokers.put(listener, invoker);
065        }
066        
067        public void removeStatementEventListener(StatementEventListener listener)
068        {
069                this.statementEventListenerInvokers.remove(listener);
070        }
071        
072        @Override
073        public void replay(D database, C connection) throws SQLException
074        {
075                super.replay(database, connection);
076                
077                for (Invoker<Z, D, C, ?, SQLException> invoker: this.connectionEventListenerInvokers.values())
078                {
079                        invoker.invoke(database, connection);
080                }
081                for (Invoker<Z, D, C, ?, SQLException> invoker: this.statementEventListenerInvokers.values())
082                {
083                        invoker.invoke(database, connection);
084                }
085        }
086
087        @Override
088        public void close(D database, C connection)
089        {
090                try
091                {
092                        connection.close();
093                }
094                catch (SQLException e)
095                {
096                        this.logger.log(Level.INFO, e);
097                }
098        }
099
100        @SuppressWarnings("unused")
101        private static class ConnectionEventListenerFilter<Z, D extends Database<Z>, C extends PooledConnection> implements ConnectionEventListener
102        {
103                private AbstractPooledConnectionProxyFactory<Z, D, C> proxyFactory;
104                private final D database;
105                private final ConnectionEventListener listener;
106                
107                ConnectionEventListenerFilter(AbstractPooledConnectionProxyFactory<Z, D, C> proxyFactory, D database, ConnectionEventListener listener)
108                {
109                        this.proxyFactory = proxyFactory;
110                        this.database = database;
111                        this.listener = listener;
112                }
113                
114                @Override
115                public void connectionClosed(ConnectionEvent event)
116                {
117                        ConnectionEvent e = this.getEvent(event);
118                        
119                        if (e != null)
120                        {
121                                this.listener.connectionClosed(e);
122                        }
123                }
124
125                @Override
126                public void connectionErrorOccurred(ConnectionEvent event)
127                {
128                        ConnectionEvent e = this.getEvent(event);
129                        
130                        if (e != null)
131                        {
132                                this.listener.connectionErrorOccurred(e);
133                        }
134                }
135                
136                private ConnectionEvent getEvent(ConnectionEvent event)
137                {
138                        Object source = event.getSource();
139                        C connection = this.proxyFactory.get(this.database);
140                        
141                        if (Proxy.isProxyClass(source.getClass()) && Proxy.getInvocationHandler(source) instanceof AbstractPooledConnectionInvocationHandler)
142                        {
143                                return new ConnectionEvent(connection, event.getSQLException());
144                        }
145                        
146                        return event.getSource().equals(connection) ? event : null;
147                }
148        }
149        
150        @SuppressWarnings("unused")
151        private static class StatementEventListenerFilter<Z, D extends Database<Z>, C extends PooledConnection> implements StatementEventListener
152        {
153                private AbstractPooledConnectionProxyFactory<Z, D, C> proxyFactory;
154                private final D database;
155                private final StatementEventListener listener;
156                
157                StatementEventListenerFilter(AbstractPooledConnectionProxyFactory<Z, D, C> proxyFactory, D database, StatementEventListener listener)
158                {
159                        this.proxyFactory = proxyFactory;
160                        this.database = database;
161                        this.listener = listener;
162                }
163                
164                @Override
165                public void statementClosed(StatementEvent event)
166                {
167                        StatementEvent e = this.getEvent(event);
168                        
169                        if (e != null)
170                        {
171                                this.listener.statementClosed(e);
172                        }
173                }
174
175                @Override
176                public void statementErrorOccurred(StatementEvent event)
177                {
178                        StatementEvent e = this.getEvent(event);
179                        
180                        if (e != null)
181                        {
182                                this.listener.statementErrorOccurred(e);
183                        }
184                }
185                
186                private StatementEvent getEvent(StatementEvent event)
187                {
188                        Object source = event.getSource();
189                        C connection = this.proxyFactory.get(this.database);
190                        
191                        if (Proxy.isProxyClass(source.getClass()) && Proxy.getInvocationHandler(source) instanceof AbstractPooledConnectionInvocationHandler)
192                        {
193                                return new StatementEvent(connection, event.getStatement(), event.getSQLException());
194                        }
195                        
196                        return source.equals(connection) ? event : null;
197                }
198        }
199}