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}