View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.zookeeper;
19  
20  import org.apache.hadoop.hbase.classification.InterfaceAudience;
21  import org.apache.hadoop.hbase.Abortable;
22  import org.apache.hadoop.hbase.HConstants;
23  import org.apache.hadoop.hbase.ServerName;
24  import org.apache.hadoop.hbase.exceptions.DeserializationException;
25  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
26  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
27  import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
28  import org.apache.zookeeper.KeeperException;
29  import org.apache.zookeeper.data.Stat;
30  
31  import java.io.IOException;
32  
33  /**
34   * Manages the location of the current active Master for the RegionServer.
35   * <p>
36   * Listens for ZooKeeper events related to the master address. The node
37   * <code>/master</code> will contain the address of the current master.
38   * This listener is interested in
39   * <code>NodeDeleted</code> and <code>NodeCreated</code> events on
40   * <code>/master</code>.
41   * <p>
42   * Utilizes {@link ZooKeeperNodeTracker} for zk interactions.
43   * <p>
44   * You can get the current master via {@link #getMasterAddress()} or via
45   * {@link #getMasterAddress(ZooKeeperWatcher)} if you do not have a running
46   * instance of this Tracker in your context.
47   * <p>
48   * This class also includes utility for interacting with the master znode, for
49   * writing and reading the znode content.
50   */
51  @InterfaceAudience.Private
52  public class MasterAddressTracker extends ZooKeeperNodeTracker {
53    /**
54     * Construct a master address listener with the specified
55     * <code>zookeeper</code> reference.
56     * <p>
57     * This constructor does not trigger any actions, you must call methods
58     * explicitly.  Normally you will just want to execute {@link #start()} to
59     * begin tracking of the master address.
60     *
61     * @param watcher zk reference and watcher
62     * @param abortable abortable in case of fatal error
63     */
64    public MasterAddressTracker(ZooKeeperWatcher watcher, Abortable abortable) {
65      super(watcher, watcher.getMasterAddressZNode(), abortable);
66    }
67  
68    /**
69     * Get the address of the current master if one is available.  Returns null
70     * if no current master.
71     * @return Server name or null if timed out.
72     */
73    public ServerName getMasterAddress() {
74      return getMasterAddress(false);
75    }
76  
77    /**
78     * Get the address of the current master if one is available.  Returns null
79     * if no current master. If refresh is set, try to load the data from ZK again,
80     * otherwise, cached data will be used.
81     *
82     * @param refresh whether to refresh the data by calling ZK directly.
83     * @return Server name or null if timed out.
84     */
85    public ServerName getMasterAddress(final boolean refresh) {
86      try {
87        return ServerName.parseFrom(super.getData(refresh));
88      } catch (DeserializationException e) {
89        LOG.warn("Failed parse", e);
90        return null;
91      }
92    }
93  
94    /**
95     * Get master address.
96     * Use this instead of {@link #getMasterAddress()} if you do not have an
97     * instance of this tracker in your context.
98     * @param zkw ZooKeeperWatcher to use
99     * @return ServerName stored in the the master address znode or null if no
100    * znode present.
101    * @throws KeeperException 
102    * @throws IOException 
103    */
104   public static ServerName getMasterAddress(final ZooKeeperWatcher zkw)
105   throws KeeperException, IOException {
106     byte [] data = ZKUtil.getData(zkw, zkw.getMasterAddressZNode());
107     // TODO javadoc claims we return null in this case. :/
108     if (data == null){
109       throw new IOException("Can't get master address from ZooKeeper; znode data == null");
110     }
111     try {
112       return ServerName.parseFrom(data);
113     } catch (DeserializationException e) {
114       KeeperException ke = new KeeperException.DataInconsistencyException();
115       ke.initCause(e);
116       throw ke;
117     }
118   }
119 
120   /**
121    * Set master address into the <code>master</code> znode or into the backup
122    * subdirectory of backup masters; switch off the passed in <code>znode</code>
123    * path.
124    * @param zkw The ZooKeeperWatcher to use.
125    * @param znode Where to create the znode; could be at the top level or it
126    * could be under backup masters
127    * @param master ServerName of the current master must not be null.
128    * @return true if node created, false if not; a watch is set in both cases
129    * @throws KeeperException
130    */
131   public static boolean setMasterAddress(final ZooKeeperWatcher zkw,
132       final String znode, final ServerName master)
133   throws KeeperException {
134     return ZKUtil.createEphemeralNodeAndWatch(zkw, znode, toByteArray(master));
135   }
136 
137   /**
138    * Check if there is a master available.
139    * @return true if there is a master set, false if not.
140    */
141   public boolean hasMaster() {
142     return super.getData(false) != null;
143   }
144 
145   /**
146    * @param sn must not be null
147    * @return Content of the master znode as a serialized pb with the pb
148    * magic as prefix.
149    */
150    static byte [] toByteArray(final ServerName sn) {
151      ZooKeeperProtos.Master.Builder mbuilder = ZooKeeperProtos.Master.newBuilder();
152      HBaseProtos.ServerName.Builder snbuilder = HBaseProtos.ServerName.newBuilder();
153      snbuilder.setHostName(sn.getHostname());
154      snbuilder.setPort(sn.getPort());
155      snbuilder.setStartCode(sn.getStartcode());
156      mbuilder.setMaster(snbuilder.build());
157      mbuilder.setRpcVersion(HConstants.RPC_CURRENT_VERSION);
158      return ProtobufUtil.prependPBMagic(mbuilder.build().toByteArray());
159    }
160 
161   /**
162    * delete the master znode if its content is same as the parameter
163    * @param zkw must not be null
164    * @param content must not be null
165    */
166   public static boolean deleteIfEquals(ZooKeeperWatcher zkw, final String content) {
167     if (content == null){
168       throw new IllegalArgumentException("Content must not be null");
169     }
170 
171     try {
172       Stat stat = new Stat();
173       byte[] data = ZKUtil.getDataNoWatch(zkw, zkw.getMasterAddressZNode(), stat);
174       ServerName sn = ServerName.parseFrom(data);
175       if (sn != null && content.equals(sn.toString())) {
176         return (ZKUtil.deleteNode(zkw, zkw.getMasterAddressZNode(), stat.getVersion()));
177       }
178     } catch (KeeperException e) {
179       LOG.warn("Can't get or delete the master znode", e);
180     } catch (DeserializationException e) {
181       LOG.warn("Can't get or delete the master znode", e);
182     }
183 
184     return false;
185   }
186 }