diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/ZKPermissionWatcher.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/ZKPermissionWatcher.java index d5fdd41..2ea8601 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/ZKPermissionWatcher.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/ZKPermissionWatcher.java @@ -24,6 +24,7 @@ import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.util.Bytes; +import org.apache.hadoop.hbase.util.Threads; import org.apache.hadoop.hbase.zookeeper.ZKUtil; import org.apache.hadoop.hbase.zookeeper.ZooKeeperListener; import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; @@ -32,6 +33,7 @@ import org.apache.zookeeper.KeeperException; import java.io.IOException; import java.util.List; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; /** * Handles synchronization of access control list entries and updates @@ -50,6 +52,9 @@ public class ZKPermissionWatcher extends ZooKeeperListener { TableAuthManager authManager; String aclZNode; CountDownLatch initialized = new CountDownLatch(1); + AtomicReference> nodes = + new AtomicReference>(null); + PermissionRefresher refresher; public ZKPermissionWatcher(ZooKeeperWatcher watcher, TableAuthManager authManager, Configuration conf) { @@ -57,6 +62,40 @@ public class ZKPermissionWatcher extends ZooKeeperListener { this.authManager = authManager; String aclZnodeParent = conf.get("zookeeper.znode.acl.parent", ACL_NODE); this.aclZNode = ZKUtil.joinZNode(watcher.baseZNode, aclZnodeParent); + this.refresher = new PermissionRefresher(); + } + + /* + * This class' thread runs refreshNodes() + */ + class PermissionRefresher extends Thread { + PermissionRefresher() { + Thread.UncaughtExceptionHandler handler = new Thread.UncaughtExceptionHandler() { + @Override + public void uncaughtException(final Thread t, final Throwable e) { + LOG.error("Unexpected exception in PermissionRefresher", e); + } + }; + Threads.setDaemonThreadRunning(this, "ZK-Permission-Refresher", handler); + } + + @Override + public void run() { + while (true) { + List nodeList = nodes.get(); + if (nodeList == null) { + try { + Thread.sleep(2); + continue; + } catch (InterruptedException e) { + LOG.warn("Interrupted while waiting for node list", e); + Thread.currentThread().interrupt(); + } + } + nodes.set(null); + refreshNodes(nodeList, nodes); + } + } } public void start() throws KeeperException { @@ -66,7 +105,7 @@ public class ZKPermissionWatcher extends ZooKeeperListener { List existing = ZKUtil.getChildDataAndWatchForNewChildren(watcher, aclZNode); if (existing != null) { - refreshNodes(existing); + refreshNodes(existing, null); } } } finally { @@ -78,7 +117,7 @@ public class ZKPermissionWatcher extends ZooKeeperListener { try { initialized.await(); } catch (InterruptedException e) { - LOG.warn("Interrupted while waiting", e); + LOG.warn("Interrupted while waiting for start", e); Thread.currentThread().interrupt(); } } @@ -90,7 +129,7 @@ public class ZKPermissionWatcher extends ZooKeeperListener { try { List nodes = ZKUtil.getChildDataAndWatchForNewChildren(watcher, aclZNode); - refreshNodes(nodes); + refreshNodes(nodes, null); } catch (KeeperException ke) { LOG.error("Error reading data from zookeeper", ke); // only option is to abort @@ -137,9 +176,16 @@ public class ZKPermissionWatcher extends ZooKeeperListener { if (path.equals(aclZNode)) { // table permissions changed try { - List nodes = + List nodeList = ZKUtil.getChildDataAndWatchForNewChildren(watcher, aclZNode); - refreshNodes(nodes); + while (!nodes.compareAndSet(null, nodeList)) { + try { + Thread.sleep(2); + } catch (InterruptedException e) { + LOG.warn("Interrupted while setting node list", e); + Thread.currentThread().interrupt(); + } + } } catch (KeeperException ke) { LOG.error("Error reading data from zookeeper for path "+path, ke); watcher.abort("Zookeeper error get node children for path "+path, ke); @@ -147,9 +193,13 @@ public class ZKPermissionWatcher extends ZooKeeperListener { } } - private void refreshNodes(List nodes) { + private void refreshNodes(List nodes, AtomicReference ref) { for (ZKUtil.NodeAndData n : nodes) { if (n.isEmpty()) continue; + if (ref != null && ref.get() != null) { + // there is a newer list + continue; + } String path = n.getNode(); String entry = (ZKUtil.getNodeName(path)); try {