001/*
002 * Copyright The Apache Software Foundation
003 *
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *     http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020package org.apache.hadoop.hbase.zookeeper;
021
022import java.util.concurrent.CountDownLatch;
023
024import org.apache.yetus.audience.InterfaceAudience;
025import org.apache.zookeeper.KeeperException;
026import org.slf4j.Logger;
027import org.slf4j.LoggerFactory;
028
029/**
030 * A ZooKeeper watcher meant to detect deletions of ZNodes.
031 */
032@InterfaceAudience.Private
033public class DeletionListener extends ZKListener {
034
035  private static final Logger LOG = LoggerFactory.getLogger(DeletionListener.class);
036
037  private final String pathToWatch;
038  private final CountDownLatch deletedLatch;
039
040  private volatile Throwable exception;
041
042  /**
043   * Create a new instance of the deletion watcher.
044   * @param zkWatcher ZookeeperWatcher instance
045   * @param pathToWatch (Fully qualified) ZNode path that we are waiting to
046   *                    be deleted.
047   * @param deletedLatch Count down on this latch when deletion has occurred.
048   */
049  public DeletionListener(ZKWatcher zkWatcher, String pathToWatch,
050                          CountDownLatch deletedLatch) {
051    super(zkWatcher);
052    this.pathToWatch = pathToWatch;
053    this.deletedLatch = deletedLatch;
054    exception = null;
055  }
056
057  /**
058   * Check if an exception has occurred when re-setting the watch.
059   * @return True if we were unable to re-set a watch on a ZNode due to
060   *         an exception.
061   */
062  public boolean hasException() {
063    return exception != null;
064  }
065
066  /**
067   * Get the last exception which has occurred when re-setting the watch.
068   * Use hasException() to check whether or not an exception has occurred.
069   * @return The last exception observed when re-setting the watch.
070   */
071  public Throwable getException() {
072    return exception;
073  }
074
075  @Override
076  public void nodeDataChanged(String path) {
077    if (!path.equals(pathToWatch)) {
078      return;
079    }
080    try {
081      if (!(ZKUtil.setWatchIfNodeExists(watcher, pathToWatch))) {
082        deletedLatch.countDown();
083      }
084    } catch (KeeperException ex) {
085      exception = ex;
086      deletedLatch.countDown();
087      LOG.error("Error when re-setting the watch on " + pathToWatch, ex);
088    }
089  }
090
091  @Override
092  public void nodeDeleted(String path) {
093    if (!path.equals(pathToWatch)) {
094      return;
095    }
096    if (LOG.isDebugEnabled()) {
097      LOG.debug("Processing delete on {}", pathToWatch);
098    }
099    deletedLatch.countDown();
100  }
101}