/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.vplexcontroller;
import java.net.URI;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import org.apache.curator.framework.recipes.locks.InterProcessLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.coordinator.client.service.CoordinatorClient;
/**
* A lock manager for locking calls into the VPLEX API that can have concurrency issues.
*/
public class VPlexApiLockManager {
// Lock name delimiter.
private static final String LOCK_NAME_DELIM = ":";
// A reference to the coordinator.
private CoordinatorClient _coordinator;
// A map of the acquired locks.
private static ConcurrentHashMap<String, InterProcessLock> s_acquiredLocks = new ConcurrentHashMap<String, InterProcessLock>();
// A reference to the logger.
private static final Logger s_logger = LoggerFactory.getLogger(VPlexApiLockManager.class);
/**
* Setter for injecting the coordinator.
*
* @param coordinator A reference to the coordinator.
*/
public void setCoordinator(CoordinatorClient coordinator) {
_coordinator = coordinator;
}
/**
* Attempts to acquire the passed lock.
*
* @param lockName The name of the lock to acquire.
* @param waitInSeconds The amount of time to wait to acquire the lock in
* seconds. A value less than 0 will cause the function
* to wait indefinitely for the lock.
*
* @return true if lock acquired, false otherwise.
*/
public boolean acquireLock(String lockName, long waitInSeconds) {
if (lockName == null || lockName.isEmpty()) {
s_logger.info("No lock name specified.");
return false;
}
try {
InterProcessLock lock = _coordinator.getLock(lockName);
if (lock != null) {
if (waitInSeconds >= 0) {
s_logger.info("Attempting to acquire lock: " + lockName + " for a maximum of " + waitInSeconds + " seconds.");
if (!lock.acquire(waitInSeconds, TimeUnit.SECONDS)) {
s_logger.info("Failed to acquire lock: " + lockName);
return false;
}
} else {
s_logger.info("Attempting to acquire lock: " + lockName + " for as long as it takes.");
lock.acquire(); // will only throw exception or pass
}
s_acquiredLocks.put(lockName, lock);
} else {
return false;
}
s_logger.info("Acquired lock: " + lockName);
return true;
} catch (Exception e) {
s_logger.error("Acquisition of lock: {} failed with Exception: ", lockName, e);
return false;
}
}
/**
* Release the passed lock.
*
* @param lockName The name of the lock to release.
*
* @return true if the lock is released, false otherwise.
*/
public boolean releaseLock(String lockName) {
if (lockName == null || lockName.isEmpty()) {
s_logger.info("No lock name specified.");
return false;
}
try {
InterProcessLock lock = s_acquiredLocks.get(lockName);
if (lock != null) {
s_acquiredLocks.remove(lockName);
lock.release();
s_logger.info("Released lock: " + lockName);
} else {
return false;
}
return true;
} catch (Exception e) {
s_logger.error("Release of lock: {} failed with Exception: ", lockName, e);
return false;
}
}
/**
* Gets a lock name to lock the vplex system on the passed cluster.
*
* @param vplexURI The URI of the VPLEX system.
* @param clusterId The cluster id.
*
* @return The lock name for the given vplex system and cluster.
*/
public String getLockName(URI vplexURI, String clusterId) {
StringBuilder lockNameBuilder = new StringBuilder(vplexURI.toString());
lockNameBuilder.append(LOCK_NAME_DELIM);
lockNameBuilder.append(clusterId);
return lockNameBuilder.toString();
}
/**
* Gets a lock name constructed from the vplex system, cluster, and an array.
*
* @param vplexURI - URI of vplex system
* @param clusterId - String cluster id
* @param arrayURI - Storage array URI
* @return String lock name that was constructed
*/
public String getLockName(URI vplexURI, String clusterId, URI arrayURI) {
StringBuilder lockNameBuilder = new StringBuilder(getLockName(vplexURI, clusterId));
lockNameBuilder.append(LOCK_NAME_DELIM);
lockNameBuilder.append(arrayURI.toString());
return lockNameBuilder.toString();
};
}