package com.opower.updater;
import com.google.common.io.Closeables;
import org.apache.commons.lang.StringUtils;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessLock;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.kiji.schema.KijiURI;
import java.util.concurrent.TimeUnit;
/**
* Updater lock implementation based on Curator {@link org.apache.curator.framework.recipes.locks.InterProcessMutex}.
*
* @author felix.trepanier
*/
public class ZookeeperUpdaterLocker implements UpdaterLocker {
private static final String PATH = "/kiji/schema/updater/lock";
private static final Integer BASE_SLEEP_TIME_MS = 1000;
private static final Integer MAX_RETRY = 3;
/**
* {@inheritDoc}
*/
@Override
public AcquiredLock acquireLock(KijiURI kijiURI, int maxWaitTime, TimeUnit timeUnit) throws LockNotAcquiredException {
String zookeeperQuorum = StringUtils.join(kijiURI.getZookeeperQuorumOrdered(), ",");
String zookeeperPort = Integer.toString(kijiURI.getZookeeperClientPort());
CuratorFramework curatorFramework =
CuratorFrameworkFactory.newClient(zookeeperQuorum + ":" + zookeeperPort,
new ExponentialBackoffRetry(BASE_SLEEP_TIME_MS, MAX_RETRY));
curatorFramework.start();
InterProcessLock mutex;
mutex = new InterProcessMutex(curatorFramework, PATH + "/" + kijiURI.getInstance());
try {
acquireOrThrow(mutex, maxWaitTime, timeUnit);
return new InternalAcquiredLock(mutex, curatorFramework);
}
catch (LockNotAcquiredException ex) {
curatorFramework.close();
throw ex;
}
catch (Exception ex) {
curatorFramework.close();
throw new LockNotAcquiredException(ex);
}
}
/**
* AcquiredLock implementation that releases the underlying {@link org.apache.curator.framework.recipes.locks.InterProcessLock}
* and closes the {@link org.apache.curator.framework.CuratorFramework}.
*/
private static final class InternalAcquiredLock implements AcquiredLock {
private final InterProcessLock lock;
private final CuratorFramework curatorFramework;
private InternalAcquiredLock(InterProcessLock lock, CuratorFramework curatorFramework) {
this.lock = lock;
this.curatorFramework = curatorFramework;
}
/**
* {@inheritDoc}
*/
@Override
public void release() throws Exception {
try {
lock.release();
}
finally {
Closeables.close(curatorFramework, true);
}
}
}
private static void acquireOrThrow(InterProcessLock mutex, int maxWaitTime, TimeUnit timeUnit)
throws LockNotAcquiredException {
boolean acquired;
try {
acquired = mutex.acquire(maxWaitTime, timeUnit);
}
catch (Exception ex) {
throw new LockNotAcquiredException(ex);
}
if (!acquired) {
throw new LockNotAcquiredException("Could not acquire after " + maxWaitTime + " "
+ timeUnit.toString().toLowerCase() + ".");
}
}
}