package com.adobe.prefs.zookeeper;
import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.BoundedExponentialBackoffRetry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.adobe.prefs.zookeeper.ZkUtils.namespace;
/**
* Class responsible with creating (and destroying on shutdown) a zookeeper client that
* can (and <b>is recommended to be</b>) shared throughout the application.
* <p/>
* This is because the CuratorFramework instance is supposed to only be instantiated
* once per application and the preferences mechanism (service loader) requires this
* instance to be available when the factory class is loaded.
* <p/>
* The reason why this code is factored out of the {@link ZkPreferencesFactory}
* is to provide this class as the single one in this package which is meant to be called directly by application code,
* since everything else in this package is just SPI implementation for Preferences.
*/
public final class ZkManager {
private ZkManager() {
}
private static final Logger logger = LoggerFactory.getLogger(ZkManager.class);
private static final Supplier<CuratorFramework> curatorSupplier = Suppliers.memoize(curatorSupplier());
private static final Supplier<CuratorFramework> rootFacadeSupplier = Suppliers.memoize(curatorFacadeSupplier("/"));
/**
* Shares the Zookeeper client used by the Preferences integration with the application code.
* However, this method returns a facade of the original curator, so that clients cannot shut it down
* or otherwise modify it.
*
* @return a singleton curator framework instance
*/
public static CuratorFramework curatorFramework() {
return rootFacadeSupplier.get();
}
static Supplier<CuratorFramework> curatorFacadeSupplier(final String rootPath) {
return Suppliers.compose(new Function<CuratorFramework, CuratorFramework>() {
final String namespace = namespace(rootPath);
@Override
public CuratorFramework apply(final CuratorFramework curator) {
return curator.usingNamespace(namespace);
}
}, curatorSupplier);
}
private static Supplier<CuratorFramework> curatorSupplier() {
return new Supplier<CuratorFramework>() {
@Override public CuratorFramework get() {
final String quorum = System.getProperty("zk.quorum", "localhost");
final int sessionTimeout = Integer.parseInt(
System.getProperty("zk.session.timeout", "30000"));
final int connectionTimeout = Integer.parseInt(
System.getProperty("zk.connection.timeout", "15000"));
final int initialDelay = Integer.parseInt(
System.getProperty("zk.retry.initialDelay", "10"));
final int maxDelay = Integer.parseInt(
System.getProperty("zk.retry.maxDelay", "200"));
final int maxCount = Integer.parseInt(
System.getProperty("zk.retry.maxCount", "10"));
logger.info("Initializing the Zookeeper client for quorum: {}", quorum);
final CuratorFramework curator = CuratorFrameworkFactory.newClient(quorum, sessionTimeout, connectionTimeout,
new BoundedExponentialBackoffRetry(initialDelay, maxDelay, maxCount));
curator.start();
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
logger.info("Shutting down the Zookeeper client...");
curator.close();
}
});
return curator;
}
};
}
}