/* * Copyright (C) 2012 Intel Corporation * All rights reserved. */ package com.intel.mtwilson.agent.vmware; //import java.util.HashMap; import com.intel.dcsg.cpg.x509.X509Util; import com.intel.mtwilson.model.Sha1Digest; import com.intel.dcsg.cpg.tls.policy.TlsConnection; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author jbuhacoff */ public class VMwareConnectionPool { private Logger log = LoggerFactory.getLogger(getClass()); // public static final int DEFAULT_MAX_SIZE = 10; private ConcurrentHashMap<TlsConnection,VMwareClient> pool = new ConcurrentHashMap<TlsConnection,VMwareClient>(); // private ConcurrentHashMap<String,Long> lastAccess = new ConcurrentHashMap<String,Long>(); // private int maxSize = DEFAULT_MAX_SIZE; private VmwareClientFactory factory = null; public VMwareConnectionPool(VmwareClientFactory factory) { this.factory = factory; } /* public VMwareConnectionPool(int maxSize) { // this.maxSize = maxSize; } */ /** * If a client is already open for the given connection string, it will * be returned. Otherwise, a new client is created and added to the pool. * * See also borrowObject() in KeyedObjectPool in apache commons pool * * @param connectionString * @return * @throws VMwareConnectionException */ public VMwareClient getClientForConnection(TlsConnection tlsConnection) throws VMwareConnectionException { VMwareClient client = reuseClientForConnection(tlsConnection); if( client != null ) { // already validated log.debug("Found an already created vcenter connection. Reusing..."); return client; } else { log.debug("Could not find vcenter connection. Creating a new vcenter connection..."); return createClientForConnection(tlsConnection); } } /** * Assumes there is already a client open for the given connection string, * and returns it. If there is not already a client open, this method * returns null. * * You should only call this method if you are interested in the status * of a connection in the pool for reporting purposes - for normal usage * getClientForConnection(String) is must more convenient because it creates * the connection if it is missing and re-creates it if it has been disconnected. * * @param connectionString * @return * @throws VMwareConnectionException */ public VMwareClient reuseClientForConnection(TlsConnection tlsConnection) throws VMwareConnectionException { // log.debug("VMwareConnectionPool searching for existing connection {}", tlsConnection.getConnectionString()); VMwareClient client = pool.get(tlsConnection); if( client == null ) { return null; } // log.debug("VMwareConnectionPool validating existing connection for {}", tlsConnection.getConnectionString()); // lastAccess.put(connectionString, System.currentTimeMillis()); if( factory.validateObject(tlsConnection, client)) { log.debug("Reusing vCenter connection for "+client.getEndpoint()); // client.setTlsPolicy(tlsConnection.getTlsPolicy()); return client; } log.info("Found stale vCenter connection"); try { factory.destroyObject(tlsConnection, client); } catch(Exception e) { log.error("Error while trying to disconnect from vcenter", e); } finally { pool.remove(tlsConnection); // remove it from the pool, we'll recreate it later } return null; } /** * Creates a new client for the given connection string and adds it to the * pool. If there was already an existing client for that connection string, * it is replaced with the new one. * * For normal use you should call getClientForConnection(String) because it * will re-use existing connections and automatically create new ones as needed. * * @param connectionString * @return * @throws VMwareConnectionException */ public VMwareClient createClientForConnection(TlsConnection tlsConnection) throws VMwareConnectionException { try { // log.debug("VMwareConnectionPool create client for connection {}", tlsConnection.getConnectionString()); VMwareClient client = factory.makeObject(tlsConnection); if( factory.validateObject(tlsConnection, client) ) { // log.debug("VMwareConnectionPool caching new connection {}", tlsConnection.getConnectionString()); pool.put(tlsConnection, client); // log.debug("Opening new vCenter connection for "+client.getEndpoint()); return client; } else { throw new Exception("Failed to validate new vmware connection"); } } catch(javax.xml.ws.WebServiceException e) { // is it because of an ssl failure? we're looking for this: com.sun.xml.internal.ws.client.ClientTransportException: HTTP transport error: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: Server certificate is not trusted if( e.getCause() != null && e.getCause() instanceof javax.net.ssl.SSLHandshakeException) { javax.net.ssl.SSLHandshakeException e2 = (javax.net.ssl.SSLHandshakeException)e.getCause(); if( e2.getCause() != null && e2.getCause() instanceof com.intel.dcsg.cpg.tls.policy.UnknownCertificateException ) { com.intel.dcsg.cpg.tls.policy.UnknownCertificateException e3 = (com.intel.dcsg.cpg.tls.policy.UnknownCertificateException)e2.getCause(); log.warn("Failed to connect to vcenter due to unknown certificate exception: {}", e3.toString()); X509Certificate[] chain = e3.getCertificateChain(); if( chain == null || chain.length == 0 ) { log.error("Server certificate is missing"); } else { for(X509Certificate certificate : chain) { try { log.debug("Server certificate fingerprint: {} and subject: {}", new Sha1Digest(X509Util.sha1fingerprint(certificate)), certificate.getSubjectX500Principal().getName()); } catch(NoSuchAlgorithmException e4) { log.error("Cannot read server certificate: {}", e4.toString(), e4); throw new VMwareConnectionException(e4); } catch(CertificateEncodingException e4) { log.error("Cannot read server certificate: {}", e4.toString(), e4); throw new VMwareConnectionException(e4); } } /* try { log.debug("Trust policy: {}", tlsConnection.getTlsPolicy().getClass().getName()); // now show what is in the trusted keystore... to help understand why it didn't match List<X509Certificate> trustedCerts = tlsConnection.getTlsPolicy().getCertificateRepository().getCertificates(); log.debug("There are {} trusted certs in the keystore", trustedCerts.size()); for(X509Certificate trustedCert : trustedCerts) { log.debug("Trusted certificate fingerprint: {} and subject: {}", new Sha1Digest(X509Util.sha1fingerprint(trustedCert)), trustedCert.getSubjectX500Principal().getName()); } } catch(Exception e5) { e5.printStackTrace(System.err); log.error("Cannot enumerate truted certificates: "+e5.toString(), e5); } */ throw new VMwareConnectionException("VMwareConnectionPool not able to read host information: "+ e3.toString()); } } else { throw new VMwareConnectionException("Failed to connect to vcenter due to SSL handshake exception", e2); } } else { throw new VMwareConnectionException("Failed to connect to vcenter due to exception: "+e.toString(), e); } } catch(Exception e) { // log.debug("VMwareConnectionPool failed to create client for connection {}", tlsConnection.getConnectionString()); // removed to prevent leaking secrets log.error("Failed to connect to vcenter: "+e.toString(),e); e.printStackTrace(System.err); throw new VMwareConnectionException("Cannot connect to vcenter: "+tlsConnection.getURL().getHost(), e); } throw new VMwareConnectionException("Failed to connect to vcenter: unknown error"); } public void close() { Set<TlsConnection> tlsConnections = pool.keySet(); for(TlsConnection tlsConnection : tlsConnections) { VMwareClient client = pool.get(tlsConnection); try { factory.destroyObject(tlsConnection, client); } catch(Exception e) { log.error("Failed to disconnect from vcenter: "+tlsConnection.getURL().getHost(), e); } } } /* private void drainPool() { if( pool.size() > maxSize ) { List<String> mostIdleFirst = listIdleConnections(); } } private List<String> * */ }