Index: hbase-server/src/test/java/org/apache/hadoop/hbase/TestZooKeeper.java =================================================================== --- hbase-server/src/test/java/org/apache/hadoop/hbase/TestZooKeeper.java (revision 1483633) +++ hbase-server/src/test/java/org/apache/hadoop/hbase/TestZooKeeper.java (working copy) @@ -494,6 +494,8 @@ // The recovered master should not call retainAssignment, as it is not a // clean startup. assertFalse("Retain assignment should not be called", MockLoadBalancer.retainAssignCalled); + // number of listeners should be same as the value before master aborted + assertEquals(expectedNumOfListeners, zkw.getNumberOfListeners()); } finally { admin.close(); } Index: hbase-server/src/main/java/org/apache/hadoop/hbase/procedure/ZKProcedureUtil.java =================================================================== --- hbase-server/src/main/java/org/apache/hadoop/hbase/procedure/ZKProcedureUtil.java (revision 1483633) +++ hbase-server/src/main/java/org/apache/hadoop/hbase/procedure/ZKProcedureUtil.java (working copy) @@ -82,7 +82,7 @@ super(watcher); this.memberName = memberName; // make sure we are listening for events - watcher.registerListener(this); + watcher.registerListener(this, false); // setup paths for the zknodes used in procedures this.baseZNode = ZKUtil.joinZNode(watcher.baseZNode, procDescription); acquiredZnode = ZKUtil.joinZNode(baseZNode, ACQUIRED_BARRIER_ZNODE_DEFAULT); Index: hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java =================================================================== --- hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java (revision 1483633) +++ hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java (working copy) @@ -2048,7 +2048,6 @@ private boolean tryRecoveringExpiredZKSession() throws InterruptedException, IOException, KeeperException, ExecutionException { - this.zooKeeper.unregisterAllListeners(); this.zooKeeper.reconnectAfterExpiration(); Callable callable = new Callable () { Index: hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java =================================================================== --- hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java (revision 1483633) +++ hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java (working copy) @@ -239,29 +239,64 @@ * @param listener */ public void registerListener(ZooKeeperListener listener) { + registerListener(listener, true); + } + + /** + * Register the specified listener to receive ZooKeeper events. + * @param listener + * @param replace When true, the function will firstly remove all existing listeners with the same + * class of the passed in listener and then register the new listener + */ + public void registerListener(ZooKeeperListener listener, boolean replace) { + if (replace) { + unregisterListenerByClass(listener.getClass()); + } listeners.add(listener); } /** - * Register the specified listener to receive ZooKeeper events and add it as - * the first in the list of current listeners. + * Register the specified listener to receive ZooKeeper events and add it as the first in the list + * of current listeners. * @param listener */ public void registerListenerFirst(ZooKeeperListener listener) { - listeners.add(0, listener); + registerListenerFirst(listener, true); } - public void unregisterListener(ZooKeeperListener listener) { - listeners.remove(listener); + /** + * Register the specified listener to receive ZooKeeper events and add it as the first in the list + * of current listeners. + * @param listener + * @param replace When true, the function will firstly remove all existing listeners with the same + * class of the passed in listener and then register the new listener + */ + public void registerListenerFirst(ZooKeeperListener listener, boolean replace) { + if (replace) { + unregisterListenerByClass(listener.getClass()); + } + listeners.add(0, listener); } /** - * Clean all existing listeners + * Clean existing listener instances of same class that the passed in. */ - public void unregisterAllListeners() { - listeners.clear(); + private void unregisterListenerByClass(Class classType) { + List toBeRemovedList = new ArrayList(); + for (ZooKeeperListener curListener : listeners) { + if (curListener.getClass().equals(classType)) { + toBeRemovedList.add(curListener); + } + } + if (!toBeRemovedList.isEmpty()) { + listeners.removeAll(toBeRemovedList); + } } + public void unregisterListener(ZooKeeperListener listener) { + listeners.remove(listener); + } + /** * @return The number of currently registered listeners */