001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.master;
019
020import java.io.IOException;
021
022import org.apache.hadoop.conf.Configuration;
023import org.apache.hadoop.hbase.Abortable;
024import org.apache.hadoop.hbase.zookeeper.ZKNodeTracker;
025import org.apache.hadoop.hbase.zookeeper.ZKUtil;
026import org.apache.hadoop.hbase.zookeeper.ZNodePaths;
027import org.apache.hadoop.hbase.zookeeper.ZKWatcher;
028import org.apache.hadoop.hbase.client.MasterSwitchType;
029import org.apache.hadoop.hbase.exceptions.DeserializationException;
030import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
031import org.apache.hadoop.hbase.shaded.protobuf.generated.ZooKeeperProtos.SwitchState;
032import org.apache.hadoop.hbase.util.Bytes;
033import org.apache.yetus.audience.InterfaceAudience;
034import org.apache.zookeeper.KeeperException;
035
036
037/**
038 * Tracks the switch of split and merge states in ZK
039 *
040 */
041@InterfaceAudience.Private
042public class SplitOrMergeTracker {
043
044  private String splitZnode;
045  private String mergeZnode;
046
047  private SwitchStateTracker splitStateTracker;
048  private SwitchStateTracker mergeStateTracker;
049
050  public SplitOrMergeTracker(ZKWatcher watcher, Configuration conf,
051                             Abortable abortable) {
052    try {
053      if (ZKUtil.checkExists(watcher, watcher.znodePaths.switchZNode) < 0) {
054        ZKUtil.createAndFailSilent(watcher, watcher.znodePaths.switchZNode);
055      }
056    } catch (KeeperException e) {
057      throw new RuntimeException(e);
058    }
059    splitZnode = ZNodePaths.joinZNode(watcher.znodePaths.switchZNode,
060      conf.get("zookeeper.znode.switch.split", "split"));
061    mergeZnode = ZNodePaths.joinZNode(watcher.znodePaths.switchZNode,
062      conf.get("zookeeper.znode.switch.merge", "merge"));
063    splitStateTracker = new SwitchStateTracker(watcher, splitZnode, abortable);
064    mergeStateTracker = new SwitchStateTracker(watcher, mergeZnode, abortable);
065  }
066
067  public void start() {
068    splitStateTracker.start();
069    mergeStateTracker.start();
070  }
071
072  public boolean isSplitOrMergeEnabled(MasterSwitchType switchType) {
073    switch (switchType) {
074      case SPLIT:
075        return splitStateTracker.isSwitchEnabled();
076      case MERGE:
077        return mergeStateTracker.isSwitchEnabled();
078      default:
079        break;
080    }
081    return false;
082  }
083
084  public void setSplitOrMergeEnabled(boolean enabled, MasterSwitchType switchType)
085    throws KeeperException {
086    switch (switchType) {
087      case SPLIT:
088        splitStateTracker.setSwitchEnabled(enabled);
089        break;
090      case MERGE:
091        mergeStateTracker.setSwitchEnabled(enabled);
092        break;
093      default:
094        break;
095    }
096  }
097
098  private static class SwitchStateTracker extends ZKNodeTracker {
099
100    public SwitchStateTracker(ZKWatcher watcher, String node, Abortable abortable) {
101      super(watcher, node, abortable);
102    }
103
104    /**
105     * Return true if the switch is on, false otherwise
106     */
107    public boolean isSwitchEnabled() {
108      byte [] upData = super.getData(false);
109      try {
110        // if data in ZK is null, use default of on.
111        return upData == null || parseFrom(upData).getEnabled();
112      } catch (DeserializationException dex) {
113        LOG.error("ZK state for LoadBalancer could not be parsed " + Bytes.toStringBinary(upData));
114        // return false to be safe.
115        return false;
116      }
117    }
118
119    /**
120     * Set the switch on/off
121     * @param enabled switch enabled or not?
122     * @throws KeeperException keepException will be thrown out
123     */
124    public void setSwitchEnabled(boolean enabled) throws KeeperException {
125      byte [] upData = toByteArray(enabled);
126      try {
127        ZKUtil.setData(watcher, node, upData);
128      } catch(KeeperException.NoNodeException nne) {
129        ZKUtil.createAndWatch(watcher, node, upData);
130      }
131      super.nodeDataChanged(node);
132    }
133
134    private byte [] toByteArray(boolean enabled) {
135      SwitchState.Builder builder = SwitchState.newBuilder();
136      builder.setEnabled(enabled);
137      return ProtobufUtil.prependPBMagic(builder.build().toByteArray());
138    }
139
140    private SwitchState parseFrom(byte [] bytes)
141      throws DeserializationException {
142      ProtobufUtil.expectPBMagicPrefix(bytes);
143      SwitchState.Builder builder = SwitchState.newBuilder();
144      try {
145        int magicLen = ProtobufUtil.lengthOfPBMagic();
146        ProtobufUtil.mergeFrom(builder, bytes, magicLen, bytes.length - magicLen);
147      } catch (IOException e) {
148        throw new DeserializationException(e);
149      }
150      return builder.build();
151    }
152  }
153}