/**
* Copyright 2011 LiveRamp
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.liveramp.hank.zookeeper;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;
import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ZooKeeperPlus {
private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperPlus.class);
private static final List<ACL> DEFAULT_ACL = Ids.OPEN_ACL_UNSAFE;
private static final CreateMode DEFAULT_CREATE_MODE = CreateMode.PERSISTENT;
private ZKPCore conn;
private final String reconnectString;
private final int sessionTimeout;
private final Watcher watcher;
private final Thread reconnectWatcher;
public ZooKeeperPlus(String reconnectString, int sessionTimeout, Watcher watcher){
this.reconnectString = reconnectString;
this.sessionTimeout = sessionTimeout;
this.watcher = watcher;
this.reconnectWatcher = new Thread(new ReconnectWatcher());
this.reconnectWatcher.setDaemon(true);
this.reconnectWatcher.start();
}
public synchronized void reconnect() throws IOException {
conn = new ZKPCore(reconnectString, sessionTimeout, watcher);
}
public class ReconnectWatcher implements Runnable {
private static final long POLL_INTERVAL = 30000; // 30s
@Override
public void run() {
try {
while(true) {
Thread.sleep(POLL_INTERVAL);
// don't start reconnect until we explicitly connect for the first time
if (conn != null && conn.getState() != ZooKeeper.States.CONNECTED) {
LOG.info("ZooKeeper is not connected. Closing connection");
close();
try {
LOG.info("Reconnecting");
reconnect();
LOG.info("Reconnected");
} catch (IOException e) {
LOG.error("Error reconnecting to ZooKeeper", e);
}
}else{
LOG.info("ZooKeeper is connected, sleeping for "+POLL_INTERVAL+"ms");
}
}
} catch (InterruptedException e) {
LOG.error("Interrupting watcher sleep", e);
}
}
}
public void create(String path, byte[] data, CreateMode createMode) throws KeeperException, InterruptedException {
conn.create(path, data, createMode);
}
public void create(String path, byte[] data) throws KeeperException, InterruptedException {
conn.create(path, data);
}
public String create(final String path, byte data[], List<ACL> acl, CreateMode createMode) throws KeeperException, InterruptedException{
return conn.create(path, data, acl, createMode);
}
public void create(final String path, byte data[], List<ACL> acl, CreateMode createMode, AsyncCallback.StringCallback cb, Object ctx){
conn.create(path, data, acl, createMode, cb, ctx);
}
public void createLong(String path, long value) throws KeeperException, InterruptedException {
conn.createLong(path, value);
}
public void createInt(String path, int value) throws KeeperException, InterruptedException {
conn.createInt(path, value);
}
public Integer getIntOrNull(String path) throws KeeperException, InterruptedException {
return conn.getIntOrNull(path);
}
public int getInt(String path) throws KeeperException, InterruptedException {
return conn.getInt(path);
}
public void setString(String path, String value) throws KeeperException, InterruptedException {
conn.setString(path, value);
}
public void setInt(String path, int nextVersion) throws KeeperException, InterruptedException {
conn.setInt(path, nextVersion);
}
public long getLong(String path) throws KeeperException, InterruptedException {
return conn.getLong(path);
}
public Long getLongOrNull(String path) throws KeeperException, InterruptedException {
return conn.getLongOrNull(path);
}
public String getString(String path) throws KeeperException, InterruptedException {
return conn.getString(path);
}
public void deleteIfExists(String path) throws KeeperException, InterruptedException {
conn.deleteIfExists(path);
}
public void setOrCreate(String path, int value, CreateMode createMode) throws KeeperException, InterruptedException {
conn.setOrCreate(path, value, createMode);
}
public void setOrCreate(String path, long value, CreateMode createMode) throws KeeperException, InterruptedException {
conn.setOrCreate(path, value, createMode);
}
public void setOrCreate(String path, String value, CreateMode createMode) throws KeeperException, InterruptedException {
conn.setOrCreate(path, value, createMode);
}
public void ensureCreated(String path, byte[] value) throws InterruptedException, KeeperException {
conn.ensureCreated(path, value);
}
public void ensureCreated(String path, byte[] value, CreateMode createMode) throws InterruptedException, KeeperException {
conn.ensureCreated(path, value, createMode);
}
public void deleteNodeRecursively(String path) throws InterruptedException, KeeperException {
conn.deleteNodeRecursively(path);
}
// Get not hidden children (children that do not start with a period)
public List<String> getChildrenNotHidden(String path, boolean watch) throws InterruptedException, KeeperException {
return conn.getChildrenNotHidden(path, watch);
}
public List<String> getChildrenNotHidden(String path, Watcher watcher) throws InterruptedException, KeeperException {
return conn.getChildrenNotHidden(path, watcher);
}
public int getSessionTimeout() {
return conn.getSessionTimeout();
}
public List<String> getChildren(final String path, Watcher watcher) throws KeeperException, InterruptedException {
return conn.getChildren(path, watcher);
}
public Stat exists(String path, boolean watch) throws KeeperException, InterruptedException {
return conn.exists(path, watch);
}
public void exists(final String path, Watcher watcher, AsyncCallback.StatCallback cb, Object ctx) {
conn.exists(path, watcher, cb, ctx);
}
public void exists(String path, boolean watch, AsyncCallback.StatCallback cb, Object ctx) {
conn.exists(path, watch, cb, ctx);
}
public Stat exists(final String path, Watcher watcher) throws KeeperException, InterruptedException {
return conn.exists(path, watcher);
}
public List<String> getChildren(String path, boolean watch) throws KeeperException, InterruptedException {
return conn.getChildrenNotHidden(path, watch);
}
public void getChildren(final String path, Watcher watcher, AsyncCallback.ChildrenCallback cb, Object ctx) {
conn.getChildren(path, watcher, cb, ctx);
}
public void getChildren(String path, boolean watch, AsyncCallback.ChildrenCallback cb, Object ctx) {
conn.getChildren(path, watch, cb, ctx);
}
public List<String> getChildren(final String path, Watcher watcher, Stat stat) throws KeeperException, InterruptedException {
return conn.getChildren(path, watcher, stat);
}
public List<String> getChildren(String path, boolean watch, Stat stat) throws KeeperException, InterruptedException {
return conn.getChildren(path, watch, stat);
}
public void getChildren(final String path, Watcher watcher, AsyncCallback.Children2Callback cb, Object ctx) {
conn.getChildren(path, watcher, cb, ctx);
}
public void getChildren(String path, boolean watch, AsyncCallback.Children2Callback cb, Object ctx) {
conn.getChildren(path, watch, cb, ctx);
}
public void delete(final String path, int version) throws InterruptedException, KeeperException {
conn.delete(path, version);
}
public void getData(String path, boolean watch, AsyncCallback.DataCallback cb, Object ctx) {
conn.getData(path, watch, cb, ctx);
}
public byte[] getData(String path, boolean watch, Stat stat) throws KeeperException, InterruptedException {
return conn.getData(path, watch, stat);
}
public void getData(final String path, Watcher watcher, AsyncCallback.DataCallback cb, Object ctx) {
conn.getData(path, watcher, cb, ctx);
}
public byte[] getData(final String path, Watcher watcher, Stat stat) throws KeeperException, InterruptedException {
return conn.getData(path, watcher, stat);
}
public Stat setData(final String path, byte data[], int version) throws KeeperException, InterruptedException {
return conn.setData(path, data, version);
}
public void setData(final String path, byte data[], int version, AsyncCallback.StatCallback cb, Object ctx){
conn.setData(path, data, version, cb, ctx);
}
public synchronized void close() throws InterruptedException {
conn.close();
reconnectWatcher.interrupt();
}
public ZooKeeper.States getState() {
return conn.getState();
}
private class ZKPCore extends ZooKeeper {
private ZKPCore(String connectString,
int sessionTimeout,
Watcher watcher,
long sessionId,
byte[] sessionPasswd) throws IOException {
super(connectString, sessionTimeout, watcher, sessionId, sessionPasswd);
}
private ZKPCore(String connectString, int sessionTimeout, Watcher watcher) throws IOException {
super(connectString, sessionTimeout, watcher);
}
public void create(String path, byte[] data, CreateMode createMode) throws KeeperException, InterruptedException {
create(path, data, DEFAULT_ACL, createMode);
}
public void create(String path, byte[] data) throws KeeperException, InterruptedException {
create(path, data, DEFAULT_ACL, DEFAULT_CREATE_MODE);
}
public void createLong(String path, long value) throws KeeperException, InterruptedException {
create(path, (Long.toString(value)).getBytes(), DEFAULT_ACL, DEFAULT_CREATE_MODE);
}
public void createInt(String path, int value) throws KeeperException, InterruptedException {
create(path, (Integer.toString(value)).getBytes(), DEFAULT_ACL, DEFAULT_CREATE_MODE);
}
public Integer getIntOrNull(String path) throws KeeperException, InterruptedException {
Long lvalue = getLongOrNull(path);
if (lvalue == null) {
return null;
}
return lvalue.intValue();
}
public int getInt(String path) throws KeeperException, InterruptedException {
return Integer.parseInt(new String(getData(path, false, new Stat())));
}
public void setString(String path, String value) throws KeeperException, InterruptedException {
setData(path, value.getBytes(), -1);
}
public void setInt(String path, int nextVersion) throws KeeperException, InterruptedException {
setData(path, (Integer.toString(nextVersion)).getBytes(), -1);
}
public long getLong(String path) throws KeeperException, InterruptedException {
return Long.parseLong(new String(getData(path, false, new Stat())));
}
public Long getLongOrNull(String path) throws KeeperException, InterruptedException {
if (exists(path, false) == null) {
return null;
} else {
return Long.parseLong(new String(getData(path, false, new Stat())));
}
}
public String getString(String path) throws KeeperException, InterruptedException {
try {
byte[] data = getData(path, false, null);
if (data == null) {
return null;
}
return new String(data, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
public void deleteIfExists(String path) throws KeeperException, InterruptedException {
if (exists(path, false) != null) {
delete(path, -1);
}
}
public void setOrCreate(String path, int value, CreateMode createMode) throws KeeperException, InterruptedException {
setOrCreate(path, Integer.toString(value), createMode);
}
public void setOrCreate(String path, long value, CreateMode createMode) throws KeeperException, InterruptedException {
setOrCreate(path, Long.toString(value), createMode);
}
public void setOrCreate(String path, String value, CreateMode createMode) throws KeeperException, InterruptedException {
if (exists(path, false) == null) {
create(path, value.getBytes(), DEFAULT_ACL, createMode);
} else {
setData(path, value.getBytes(), -1);
}
}
public void ensureCreated(String path, byte[] value) throws InterruptedException, KeeperException {
ensureCreated(path, value, DEFAULT_CREATE_MODE);
}
public void ensureCreated(String path, byte[] value, CreateMode createMode) throws InterruptedException, KeeperException {
if (!path.isEmpty() && exists(path, false) == null) {
ensureCreated(new File(path).getParent(), null, createMode);
create(path, value, DEFAULT_ACL, createMode);
NodeCreationBarrier.block(ZooKeeperPlus.this, path);
}
}
public void deleteNodeRecursively(String path) throws InterruptedException, KeeperException {
try {
delete(path, -1);
} catch (KeeperException.NotEmptyException e) {
List<String> children = getChildren(path, null);
for (String child : children) {
deleteNodeRecursively(ZkPath.append(path, child));
}
delete(path, -1);
} catch (KeeperException.NoNodeException e) {
// Silently return if the node has already been deleted.
return;
}
}
// Get not hidden children (children that do not start with a period)
public List<String> getChildrenNotHidden(String path, boolean watch) throws InterruptedException, KeeperException {
return ZkPath.filterOutHiddenPaths(super.getChildren(path, watch));
}
public List<String> getChildrenNotHidden(String path, Watcher watcher) throws InterruptedException, KeeperException {
return ZkPath.filterOutHiddenPaths(super.getChildren(path, watcher));
}
}
}