/*
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);
}
}
}