/*
* NOTE: This copyright does *not* cover user programs that use HQ
* program services by normal system calls through the application
* program interfaces provided as part of the Hyperic Plug-in Development
* Kit or the Hyperic Client Development Kit - this is merely considered
* normal use of the program, and does *not* fall under the heading of
* "derived work".
*
* Copyright (C) [2004-2010], VMWare, Inc.
* This file is part of HQ.
*
* HQ is free software; you can redistribute it and/or modify
* it under the terms version 2 of the GNU General Public License as
* published by the Free Software Foundation. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
package org.hyperic.hq.plugin.appha;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperic.hq.product.PluginException;
public class VSphereConnection {
private static final Log _log = LogFactory.getLog(VSphereConnection.class);
private static final Map<String, List<VSphereConnection>> _conns =
new HashMap<String, List<VSphereConnection>>();
private static final int POOL_SIZE =
Integer.valueOf(System.getProperty("vsphere.pool.size", "2"));
final Object LOCK = new Object();
VSphereUtil vim;
private String _user = "", _pass = "";
private String url;
public VSphereConnection(String url) {
this.url = url;
}
private static String address(Object obj) {
if (obj == null) {
return "@NULL";
}
return "@" + Integer.toHexString(obj.hashCode());
}
private static String mask(String val) {
return val.replaceAll(".", "*");
}
private static String diff(String old, String cur) {
return "'" + old + "'->'" + cur + "'";
}
/**
* @return {@link VSphereConnection} from the connection pool. Once a connection is grabbed
* it must be returned to the pool via the release() method
*/
public static VSphereConnection getPooledInstance(Properties props) throws PluginException {
synchronized (_conns) {
VSphereConnection rtn;
String url = VSphereUtil.getURL(props);
List<VSphereConnection> tmp;
if (null == (tmp = _conns.get(url))) {
tmp = new ArrayList<VSphereConnection>();
for (int ii=0; ii<POOL_SIZE; ii++) {
tmp.add(new VSphereConnection(url));
}
_conns.put(url, tmp);
} else {
while (tmp.size() == 0) {
try {
_conns.wait();
} catch (InterruptedException e) {
_log.warn(e,e);
}
}
}
rtn = tmp.remove(0);
rtn.validate(props);
return rtn;
}
}
public static void evict(String url) throws PluginException {
synchronized (_conns) {
try {
List<VSphereConnection> instances = _conns.get(url);
if (instances != null) {
for (VSphereConnection v : instances) {
v.dispose();
}
_conns.remove(url);
}
} catch (Exception e) {
throw new PluginException(e);
}
}
}
public void release() {
synchronized (_conns) {
List<VSphereConnection> tmp;
if (null != (tmp = _conns.get(getUrl()))) {
tmp.add(this);
_conns.notifyAll();
}
}
}
public void dispose() {
if (vim != null) {
VSphereUtil.dispose(vim);
if (_log.isDebugEnabled()) {
_log.debug("Closed previous connection (" +
address(vim) + ") for: " + url);
}
vim = null;
}
}
private String getUrl() {
return url;
}
private void validate(Properties props) throws PluginException {
String url = VSphereUtil.getURL(props);
String username = props.getProperty(VSphereUtil.PROP_USERNAME, "");
String password = props.getProperty(VSphereUtil.PROP_PASSWORD, "");
boolean requiresReconnect = false;
boolean usernameChanged = !username.equals(_user);
boolean passwordChanged = !password.equals(_pass);
if (usernameChanged || passwordChanged) {
requiresReconnect = true;
if (_log.isDebugEnabled()) {
String diff = "";
if (usernameChanged) {
diff += "user:" + diff(_user, username);
}
if (passwordChanged) {
if (diff.length() != 0) {
diff += ",";
}
diff += "pass:" + diff(mask(_pass), mask(password));
}
_log.debug("Credentials changed (" + diff +
") reconnecting cached connection for: " + url);
}
}
else {
if (vim != null) {
requiresReconnect = !vim.isSessionValid();
}
// Else, some previous error must have happened -- expect it to be already logged.
}
if (requiresReconnect) {
dispose();
}
if (vim == null) {
try {
vim = VSphereUtil.getInstance(props);
} catch (Exception e) {
throw new PluginException(e);
}
if (_log.isDebugEnabled()) {
_log.debug("Opened new connection (" +
address(vim) + "/" +
address(LOCK) + ") for: " + url);
}
_user = username;
_pass = password;
}
}
}