package com.liveramp.hank.coordinator.zk;
import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
/**
* Abstract Watcher implementation that adds a few features.
* <p/>
* The watch is immediately reset upon notification, *before* the subclass is
* notified, guaranteeing that you won't miss notifications. Though, when you do
* your get() call, you might get get data for events that happened after the
* original notification, and then be notified *again* for the same data.
* <p/>
* The process method is synchronized so that we don't have to worry about
* concurrent notifications
* <p/>
* Calling cancel() on the watcher sets a flag that prevents further
* notifications and watch-setting.
*/
abstract class HankWatcher implements Watcher {
private static final Logger LOG = LoggerFactory.getLogger(HankWatcher.class);
private boolean cancelled = false;
protected HankWatcher() throws KeeperException, InterruptedException {
setWatch();
}
public abstract void setWatch() throws KeeperException, InterruptedException;
protected abstract void realProcess(WatchedEvent event);
@Override
public final void process(WatchedEvent event) {
synchronized (this) {
if (!cancelled) {
if (event.getState() == Event.KeeperState.SyncConnected) {
try {
setWatch();
} catch (KeeperException e) {
LOG.error("Failed to reset watch!", e);
} catch (InterruptedException e) {
// TODO: support retrying here?
LOG.error("Failed to reset watch!", e);
}
realProcess(event);
} else {
// Not sync connected, do nothing
if (LOG.isDebugEnabled()) {
LOG.debug("Not sync connected anymore for watched node.");
}
}
}
}
}
public final void cancel() {
cancelled = true;
}
}