/* * Copyright (c) 2013 EMC Corporation * All Rights Reserved */ package com.emc.storageos.recoverpoint.utils; import java.net.URI; import java.util.Iterator; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.fapiclient.ws.FunctionalAPIImpl; import com.emc.storageos.recoverpoint.exceptions.RecoverPointException; import com.emc.storageos.recoverpoint.impl.RecoverPointClient; /** * RecoverPoint API client factory */ public class RecoverPointClientFactory { private static Logger logger = LoggerFactory.getLogger(RecoverPointClientFactory.class); // Stores valid Recover Point connections via a hash map of RecoverPointClient objects private static ConcurrentMap<String, RecoverPointClient> clientMap = new ConcurrentHashMap<String, RecoverPointClient>(); /** * Static method to manage Recover Point connections via the RecoverPointClient class. * * When a valid connection is created the RecoverPointClient object is kept in a ConcurrentHashMap * and referenced by a unique key combination comprised of the endpoint + username + password. * * If a connection key exists, a handle to an existing RecoverPointClient object will * be returned instead of creating a brand new connection. Otherwise, a new connection will be created * and stored (if valid). * * If the connection info is invalid, an exception will be thrown and the object will not be stored. * * The method can potentially be called by multiple threads, therefore synchronization is required * to maintain thread safety. ConcurrentHashMap is, by default, thread safe which is why * it is being used to store valid connections. * * @param protectionSystem for a unique key, if we connect using a secondary address, keep using that address. * @param endpoints URI to the RecoverPoint System * @param username Username to log into the RecoverPoint System * @param password Password to log into the RecoverPoint System * * @return RecoverPointClient with a connection to the RPA. * @throws RecoverPointException */ public static synchronized RecoverPointClient getClient(URI protectionSystem, List<URI> endpoints, String username, String password) throws RecoverPointException { logger.info("Attempting to get RecoverPointClient connection..."); // Throw an exception if null credentials are passed in. if (endpoints == null || username == null || password == null) { throw RecoverPointException.exceptions.invalidCrendentialsPassedIn(username, password); } // Unique key to identify RP connections for different Protection Systems String key = String.valueOf(protectionSystem) + username + password; // See if there is an existing valid RecoverPointClient using the protection system key RecoverPointClient existingClient = clientMap.get(key); if (existingClient != null) { logger.info("Existing RecoverPointClient connection found. Re-using connection: " + existingClient.getEndpoint().toString()); try { // Do a ping check. If this fails, try the other IP addresses. existingClient.ping(); return existingClient; } catch (Exception e) { logger.error("Received " + e.toString() + ". Failed to ping Mgmt IP: " + existingClient.getEndpoint().toString() + ", Cause: " + RecoverPointClient.getCause(e)); // remove the protection system's connection from the factory, and the endpoint from the endpoints list clientMap.remove(key); endpoints.remove(existingClient.getEndpoint()); } } // Now go through the endpoints and try to create a new RP client connection. Iterator<URI> endpointIter = endpoints.iterator(); while (endpointIter.hasNext()) { URI endpoint = endpointIter.next(); // Throw an exception if the endpoint can not be resolved to an ASCII string String mgmtIPAddress = endpoint.toASCIIString(); if (mgmtIPAddress == null) { throw RecoverPointException.exceptions.noRecoverPointEndpoint(); } logger.info("Creating new RecoverPointClient connection to: " + mgmtIPAddress); // If we don't have an existing RecoverPointClient, create a new one and add it to the client map only // if the connection is valid. RecoverPointClient newRecoverpointClient = new RecoverPointClient(endpoint, username, password); FunctionalAPIImpl impl = null; try { // Create the connection impl = new RecoverPointConnection().connect(endpoint, username, password); logger.info("New RecoverPointClient connection created to: " + mgmtIPAddress); // Add the new RecoverPointConnection to the RecoverPointClient newRecoverpointClient.setFunctionalAPI(impl); // We just connected but to be safe, lets do a quick ping to confirm that // we can reach the new RecoverPoint client newRecoverpointClient.ping(); // Update the client map clientMap.put(key, newRecoverpointClient); return newRecoverpointClient; } catch (Exception e) { logger.error("Received " + e.toString() + ". Failed to create new RP connection: " + endpoint.toString() + ", Cause: " + RecoverPointClient.getCause(e)); // Remove invalid entry clientMap.remove(key); if (endpointIter.hasNext()) { logger.info("Trying a different IP address to contact RP..."); } else { throw RecoverPointException.exceptions.failedToPingMgmtIP(mgmtIPAddress, RecoverPointClient.getCause(e)); } } } // You'll never get here. return null; } }