| /** |
| * Copyright 2010 The Apache Software Foundation |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 org.apache.hadoop.hbase.zookeeper; |
| |
| import org.apache.hadoop.hbase.Abortable; |
| import org.apache.zookeeper.KeeperException; |
| |
| /** |
| * Tracks the availability and value of a single ZooKeeper node. |
| * |
| * <p>Utilizes the {@link ZooKeeperListener} interface to get the necessary |
| * ZooKeeper events related to the node. |
| * |
| * <p>This is the base class used by trackers in both the Master and |
| * RegionServers. |
| */ |
| public abstract class ZooKeeperNodeTracker extends ZooKeeperListener { |
| /** Path of node being tracked */ |
| protected final String node; |
| |
| /** Data of the node being tracked */ |
| private byte [] data; |
| |
| /** Used to abort if a fatal error occurs */ |
| protected final Abortable abortable; |
| |
| private boolean stopped = false; |
| |
| /** |
| * Constructs a new ZK node tracker. |
| * |
| * <p>After construction, use {@link #start} to kick off tracking. |
| * |
| * @param watcher |
| * @param node |
| * @param abortable |
| */ |
| public ZooKeeperNodeTracker(ZooKeeperWatcher watcher, String node, |
| Abortable abortable) { |
| super(watcher); |
| this.node = node; |
| this.abortable = abortable; |
| this.data = null; |
| } |
| |
| /** |
| * Starts the tracking of the node in ZooKeeper. |
| * |
| * <p>Use {@link #blockUntilAvailable()} to block until the node is available |
| * or {@link #getData()} to get the data of the node if it is available. |
| */ |
| public synchronized void start() { |
| this.watcher.registerListener(this); |
| try { |
| if(ZKUtil.watchAndCheckExists(watcher, node)) { |
| byte [] data = ZKUtil.getDataAndWatch(watcher, node); |
| if(data != null) { |
| this.data = data; |
| } else { |
| // It existed but now does not, try again to ensure a watch is set |
| start(); |
| } |
| } |
| } catch (KeeperException e) { |
| abortable.abort("Unexpected exception during initialization, aborting", e); |
| } |
| } |
| |
| public synchronized void stop() { |
| this.stopped = true; |
| notifyAll(); |
| } |
| |
| /** |
| * Gets the data of the node, blocking until the node is available. |
| * |
| * @return data of the node |
| * @throws InterruptedException if the waiting thread is interrupted |
| */ |
| public synchronized byte [] blockUntilAvailable() |
| throws InterruptedException { |
| return blockUntilAvailable(0); |
| } |
| |
| /** |
| * Gets the data of the node, blocking until the node is available or the |
| * specified timeout has elapsed. |
| * |
| * @param timeout maximum time to wait for the node data to be available, |
| * n milliseconds. Pass 0 for no timeout. |
| * @return data of the node |
| * @throws InterruptedException if the waiting thread is interrupted |
| */ |
| public synchronized byte [] blockUntilAvailable(long timeout) |
| throws InterruptedException { |
| if (timeout < 0) throw new IllegalArgumentException(); |
| boolean notimeout = timeout == 0; |
| long startTime = System.currentTimeMillis(); |
| long remaining = timeout; |
| while (!this.stopped && (notimeout || remaining > 0) && this.data == null) { |
| if (notimeout) { |
| wait(); |
| continue; |
| } |
| wait(remaining); |
| remaining = timeout - (System.currentTimeMillis() - startTime); |
| } |
| return data; |
| } |
| |
| /** |
| * Gets the data of the node. |
| * |
| * <p>If the node is currently available, the most up-to-date known version of |
| * the data is returned. If the node is not currently available, null is |
| * returned. |
| * |
| * @return data of the node, null if unavailable |
| */ |
| public synchronized byte [] getData() { |
| return data; |
| } |
| |
| public String getNode() { |
| return this.node; |
| } |
| |
| @Override |
| public synchronized void nodeCreated(String path) { |
| if (!path.equals(node)) return; |
| try { |
| byte [] data = ZKUtil.getDataAndWatch(watcher, node); |
| if (data != null) { |
| this.data = data; |
| notifyAll(); |
| } else { |
| nodeDeleted(path); |
| } |
| } catch(KeeperException e) { |
| abortable.abort("Unexpected exception handling nodeCreated event", e); |
| } |
| } |
| |
| @Override |
| public synchronized void nodeDeleted(String path) { |
| if(path.equals(node)) { |
| try { |
| if(ZKUtil.watchAndCheckExists(watcher, node)) { |
| nodeCreated(path); |
| } else { |
| this.data = null; |
| } |
| } catch(KeeperException e) { |
| abortable.abort("Unexpected exception handling nodeDeleted event", e); |
| } |
| } |
| } |
| |
| @Override |
| public synchronized void nodeDataChanged(String path) { |
| if(path.equals(node)) { |
| nodeCreated(path); |
| } |
| } |
| } |