content: /**
   * 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 com.alibaba.jstorm.cluster;
  
  import java.io.IOException;
  import java.util.List;
  import java.util.Map;
  import java.util.Map.Entry;
  import java.util.UUID;
  import java.util.concurrent.ConcurrentHashMap;
  import java.util.concurrent.atomic.AtomicBoolean;
  
  import org.apache.curator.framework.CuratorFramework;
  import org.apache.zookeeper.CreateMode;
  import org.apache.zookeeper.Watcher.Event.EventType;
  import org.apache.zookeeper.Watcher.Event.KeeperState;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import backtype.storm.Config;
  
  import com.alibaba.jstorm.cache.JStormCache;
  import com.alibaba.jstorm.callback.ClusterStateCallback;
  import com.alibaba.jstorm.callback.WatcherCallBack;
  import com.alibaba.jstorm.utils.JStormUtils;
  import com.alibaba.jstorm.utils.PathUtils;
  import com.alibaba.jstorm.zk.Zookeeper;
  
  /**
   * All ZK interface implementation
   *
   * @author yannian.mu
   */
  public class DistributedClusterState implements ClusterState {
      private static final Logger LOG = LoggerFactory.getLogger(DistributedClusterState.class);
  
      private final Zookeeper zkObj = new Zookeeper();
      private CuratorFramework zk;
      private final WatcherCallBack watcher;
  
      private final ConcurrentHashMap<UUID, ClusterStateCallback> callbacks = new ConcurrentHashMap<>();
  
      private final Map<Object, Object> conf;
      private final AtomicBoolean active;
  
      private JStormCache zkCache;
  
      public DistributedClusterState(Map<Object, Object> conf) throws Exception {
          this.conf = conf;
  
          // just mkdir STORM_ZOOKEEPER_ROOT dir
          CuratorFramework _zk = mkZk();
          String path = String.valueOf(this.conf.get(Config.STORM_ZOOKEEPER_ROOT));
          zkObj.mkdirs(_zk, path);
          _zk.close();
  
          active = new AtomicBoolean(true);
  
          watcher = new WatcherCallBack() {
              @Override
              public void execute(KeeperState state, EventType type, String path) {
                  if (active.get()) {
                      if (!(state.equals(KeeperState.SyncConnected))) {
                          LOG.warn("Received event " + state + ":" + type + ":" + path + " with disconnected Zookeeper.");
                      } else {
                          LOG.info("Received event " + state + ":" + type + ":" + path);
                      }
  
                      if (!type.equals(EventType.None)) {
                          for (Entry<UUID, ClusterStateCallback> e : callbacks.entrySet()) {
                              ClusterStateCallback fn = e.getValue();
                              fn.execute(type, path);
                          }
                      }
                  }
              }
          };
          zk = null;
          zk = mkZk(watcher);
  
      }
  
      @SuppressWarnings("unchecked")
      private CuratorFramework mkZk() throws IOException {
          return zkObj.mkClient(conf, (List<String>) conf.get(Config.STORM_ZOOKEEPER_SERVERS), conf.get(Config.STORM_ZOOKEEPER_PORT), "");
      }
  
      @SuppressWarnings("unchecked")
      private CuratorFramework mkZk(WatcherCallBack watcher) throws NumberFormatException, IOException {
          return zkObj.mkClient(conf, (List<String>) conf.get(Config.STORM_ZOOKEEPER_SERVERS), conf.get(Config.STORM_ZOOKEEPER_PORT),
                  String.valueOf(conf.get(Config.STORM_ZOOKEEPER_ROOT)), watcher);
      }
  
      @Override
      public void close() {
          this.active.set(false);
          zk.close();
      }
  
      @Override
      public void delete_node(String path) throws Exception {
          if (zkCache != null) {
              zkCache.remove(path);
          }
          zkObj.deleteRecursive(zk, path);
      }
  
      @Override
      public List<String> get_children(String path, boolean watch) throws Exception {
          return zkObj.getChildren(zk, path, watch);
      }
  
      @Override
      public byte[] get_data(String path, boolean watch) throws Exception {
          byte[] ret = null;
          if (!watch && zkCache != null) {
              ret = (byte[]) zkCache.get(path);
          }
          if (ret != null) {
              return ret;
          }
  
          ret = zkObj.getData(zk, path, watch);
          if (zkCache != null) {
              zkCache.put(path, ret);
          }
  
          return ret;
      }
  
      /**
       * Note that get_version doesn't use zkCache avoid to conflict with get_data
       */
      @Override
      public Integer get_version(String path, boolean watch) throws Exception {
          return zkObj.getVersion(zk, path, watch);
      }
  
      @Override
      public byte[] get_data_sync(String path, boolean watch) throws Exception {
          byte[] ret;
          ret = zkObj.getData(zk, path, watch);
          if (zkCache != null && ret != null) {
              zkCache.put(path, ret);
          }
          return ret;
      }
  
      @Override
      public void sync_path(String path) throws Exception {
          zkObj.syncPath(zk, path);
      }
  
      @Override
      public void mkdirs(String path) throws Exception {
          zkObj.mkdirs(zk, path);
      }
  
      @Override
      public void set_data(String path, byte[] data) throws Exception {
          if (data.length > (JStormUtils.SIZE_1_K * 800)) {
              throw new Exception("Writing 800k+ data into ZK is not allowed!, data size is " + data.length);
          }
          if (zkObj.exists(zk, path, false)) {
              zkObj.setData(zk, path, data);
          } else {
              zkObj.mkdirs(zk, PathUtils.parent_path(path));
              zkObj.createNode(zk, path, data, CreateMode.PERSISTENT);
          }
  
          if (zkCache != null) {
              zkCache.put(path, data);
          }
      }
  
      @Override
      public void set_ephemeral_node(String path, byte[] data) throws Exception {
          zkObj.mkdirs(zk, PathUtils.parent_path(path));
          if (zkObj.exists(zk, path, false)) {
              zkObj.setData(zk, path, data);
          } else {
              zkObj.createNode(zk, path, data, CreateMode.EPHEMERAL);
          }
  
          if (zkCache != null) {
              zkCache.put(path, data);
          }
      }
  
      @Override
      public UUID register(ClusterStateCallback callback) {
          UUID id = UUID.randomUUID();
          this.callbacks.put(id, callback);
          return id;
      }
  
      @Override
      public ClusterStateCallback unregister(UUID id) {
          return this.callbacks.remove(id);
      }
  
      @Override
      public boolean node_existed(String path, boolean watch) throws Exception {
          return zkObj.existsNode(zk, path, watch);
      }
  
      @Override
      public void tryToBeLeader(String path, byte[] host) throws Exception {
          zkObj.createNode(zk, path, host, CreateMode.EPHEMERAL);
      }
  
      public JStormCache getZkCache() {
          return zkCache;
      }
  
      public void setZkCache(JStormCache zkCache) {
          this.zkCache = zkCache;
      }
  
  }
  