/* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/J is licensed under the terms of the GPLv2 <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FLOSS License Exception <http://www.mysql.com/about/legal/licensing/foss-exception.html>. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package com.mysql.jdbc; import java.sql.SQLException; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; public class ConnectionGroup { private String groupName; private long connections = 0; private long activeConnections = 0; private HashMap<Long, LoadBalancingConnectionProxy> connectionProxies = new HashMap<Long, LoadBalancingConnectionProxy>(); private Set<String> hostList = new HashSet<String>(); private boolean isInitialized = false; private long closedProxyTotalPhysicalConnections = 0; private long closedProxyTotalTransactions = 0; private int activeHosts = 0; private Set<String> closedHosts = new HashSet<String>(); ConnectionGroup(String groupName){ this.groupName = groupName; } public long registerConnectionProxy(LoadBalancingConnectionProxy proxy, List<String> localHostList){ long currentConnectionId; synchronized (this){ if(!this.isInitialized){ this.hostList.addAll(localHostList); this.isInitialized = true; this.activeHosts = localHostList.size(); } currentConnectionId = ++connections; this.connectionProxies.put(Long.valueOf(currentConnectionId), proxy); } this.activeConnections++; return currentConnectionId; } /* (non-Javadoc) * @see com.mysql.jdbc.ConnectionGroupMBean#getGroupName() */ public String getGroupName(){ return this.groupName; } /* (non-Javadoc) * @see com.mysql.jdbc.ConnectionGroupMBean#getInitialHostList() */ public Collection<String> getInitialHosts(){ return this.hostList; } /* (non-Javadoc) * @see com.mysql.jdbc.ConnectionGroupMBean#getActiveHostCount() */ public int getActiveHostCount(){ return this.activeHosts; } public Collection<String> getClosedHosts(){ return this.closedHosts; } /* (non-Javadoc) * @see com.mysql.jdbc.ConnectionGroupMBean#getTotalLogicalConnectionCount() */ public long getTotalLogicalConnectionCount(){ return this.connections; } /* (non-Javadoc) * @see com.mysql.jdbc.ConnectionGroupMBean#getActiveLogicalConnectionCount() */ public long getActiveLogicalConnectionCount(){ return this.activeConnections; } /* (non-Javadoc) * @see com.mysql.jdbc.ConnectionGroupMBean#getActivePhysicalConnectionCount() */ public long getActivePhysicalConnectionCount(){ long result = 0; Map<Long, LoadBalancingConnectionProxy> proxyMap = new HashMap<Long, LoadBalancingConnectionProxy>(); synchronized(this.connectionProxies){ proxyMap.putAll(this.connectionProxies); } Iterator<Map.Entry<Long, LoadBalancingConnectionProxy>> i = proxyMap.entrySet().iterator(); while(i.hasNext()){ LoadBalancingConnectionProxy proxy = i.next().getValue(); result += proxy.getActivePhysicalConnectionCount(); } return result; } /* (non-Javadoc) * @see com.mysql.jdbc.ConnectionGroupMBean#getTotalPhysicalConnectionCount() */ public long getTotalPhysicalConnectionCount(){ long allConnections = this.closedProxyTotalPhysicalConnections; Map<Long, LoadBalancingConnectionProxy> proxyMap = new HashMap<Long, LoadBalancingConnectionProxy>(); synchronized(this.connectionProxies){ proxyMap.putAll(this.connectionProxies); } Iterator<Map.Entry<Long, LoadBalancingConnectionProxy>> i = proxyMap.entrySet().iterator(); while(i.hasNext()){ LoadBalancingConnectionProxy proxy = i.next().getValue(); allConnections += proxy.getTotalPhysicalConnectionCount(); } return allConnections; } /* (non-Javadoc) * @see com.mysql.jdbc.ConnectionGroupMBean#getTotalTransactionCount() */ public long getTotalTransactionCount(){ // need to account for closed connection proxies long transactions = this.closedProxyTotalTransactions; Map<Long, LoadBalancingConnectionProxy> proxyMap = new HashMap<Long, LoadBalancingConnectionProxy>(); synchronized(this.connectionProxies){ proxyMap.putAll(this.connectionProxies); } Iterator<Map.Entry<Long, LoadBalancingConnectionProxy>> i = proxyMap.entrySet().iterator(); while(i.hasNext()){ LoadBalancingConnectionProxy proxy = i.next().getValue(); transactions += proxy.getTransactionCount(); } return transactions; } public void closeConnectionProxy(LoadBalancingConnectionProxy proxy){ this.activeConnections--; this.connectionProxies.remove(Long.valueOf(proxy.getConnectionGroupProxyID())); this.closedProxyTotalPhysicalConnections += proxy.getTotalPhysicalConnectionCount(); this.closedProxyTotalTransactions += proxy.getTransactionCount(); } public void removeHost(String host) throws SQLException { removeHost(host, false); } public void removeHost(String host, boolean killExistingConnections) throws SQLException { this.removeHost(host, killExistingConnections, true); } /* (non-Javadoc) * @see com.mysql.jdbc.ConnectionGroupMBean#removeHost(java.lang.String, boolean, boolean) */ public synchronized void removeHost(String host, boolean killExistingConnections, boolean waitForGracefulFailover) throws SQLException { if(this.activeHosts == 1){ throw SQLError.createSQLException("Cannot remove host, only one configured host active.", null); } if(this.hostList.remove(host)){ this.activeHosts--; } else { throw SQLError.createSQLException("Host is not configured: " + host, null); } if(killExistingConnections){ // make a local copy to keep synchronization overhead to minimum Map<Long, LoadBalancingConnectionProxy> proxyMap = new HashMap<Long, LoadBalancingConnectionProxy>(); synchronized(this.connectionProxies){ proxyMap.putAll(this.connectionProxies); } Iterator<Map.Entry<Long, LoadBalancingConnectionProxy>> i = proxyMap.entrySet().iterator(); while(i.hasNext()){ LoadBalancingConnectionProxy proxy = i.next().getValue(); if(waitForGracefulFailover){ proxy.removeHostWhenNotInUse(host); } else { proxy.removeHost(host); } } } this.closedHosts.add(host); } public void addHost(String host){ addHost(host, false); } /* (non-Javadoc) * @see com.mysql.jdbc.ConnectionGroupMBean#addHost(java.lang.String, boolean) */ public void addHost(String host, boolean forExisting){ synchronized(this){ if(this.hostList.add(host)){ this.activeHosts++; } } // all new connections will have this host if(!forExisting){ return; } // make a local copy to keep synchronization overhead to minimum Map<Long, LoadBalancingConnectionProxy> proxyMap = new HashMap<Long, LoadBalancingConnectionProxy>(); synchronized(this.connectionProxies){ proxyMap.putAll(this.connectionProxies); } Iterator<Map.Entry<Long, LoadBalancingConnectionProxy>> i = proxyMap.entrySet().iterator(); while(i.hasNext()){ LoadBalancingConnectionProxy proxy = i.next().getValue(); proxy.addHost(host); } } }