package nl.tno.storm.configuration.impl;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import nl.tno.storm.configuration.api.ConfigurationListener;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
public class WatcherHelper implements Watcher {
private final String mapPath;
private final List<ConfigurationListener> listeners = new CopyOnWriteArrayList<>();
private final Set<String> watchedChildren = new HashSet<String>();
private final ZookeeperStormConfiguration zkConfiguration;
public WatcherHelper(ZookeeperStormConfiguration zkConfiguration, String mapPath) throws Exception {
this.zkConfiguration = zkConfiguration;
this.mapPath = mapPath;
watchMap();
watchChildren();
}
/**
* Create a watcher for the parent node; triggers
* EventType.NodeChildrenChanged and EventType.NodeDeleted
*
* @throws Exception
*/
private void watchMap() throws Exception {
this.zkConfiguration.zkClient.getChildren().usingWatcher(this).forPath(this.mapPath);
}
/**
* Set a data watcher on all children. By keeping track of the watchers we
* already set we avoid setting double watchers on the same node.
*
* @throws Exception
*/
private void watchChildren() throws Exception {
for (String childKey : this.zkConfiguration.zkClient.getChildren().forPath(mapPath)) {
String path = mapPath + "/" + childKey;
if (!watchedChildren.contains(path)) {
this.zkConfiguration.zkClient.getData().usingWatcher(this).forPath(path);
watchedChildren.add(path);
}
}
}
public void addListener(ConfigurationListener listener) {
this.listeners.add(listener);
}
public void removeListener(ConfigurationListener listener) {
this.listeners.remove(listener);
}
@Override
public void process(WatchedEvent e) {
try {
if (e.getType() == EventType.NodeChildrenChanged) {
// Reset the children watcher
this.zkConfiguration.zkClient.getChildren().usingWatcher(this).forPath(e.getPath());
} else if (e.getType() == EventType.NodeDataChanged) {
// Data of a node changed. We reset the watcher in
// watchChildren()
watchedChildren.remove(e.getPath());
} else if (e.getType() == EventType.NodeDeleted) {
// When a node is deleted the NodeChildrenChanged event also
// fires. By ignoring this event we avoid have double events.
// When the node comes back we have to create a new watcher, so
// we remove it form watchedChildren.
watchedChildren.remove(e.getPath());
return;
}
watchChildren();
// Notify all listeners
for (ConfigurationListener listener : listeners) {
listener.configurationChanged(zkConfiguration.pathToMap(mapPath));
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}