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  import java.io.InterruptedIOException;
33  
34  /**
35   * Manages the location of the current active Master for the RegionServer.
36   * <p>
37   * Listens for ZooKeeper events related to the master address. The node
38   * <code>/master</code> will contain the address of the current master.
39   * This listener is interested in
40   * <code>NodeDeleted</code> and <code>NodeCreated</code> events on
41   * <code>/master</code>.
42   * <p>
43   * Utilizes {@link ZooKeeperNodeTracker} for zk interactions.
44   * <p>
45   * You can get the current master via {@link #getMasterAddress()} or via
46   * {@link #getMasterAddress(ZooKeeperWatcher)} if you do not have a running
47   * instance of this Tracker in your context.
48   * <p>
49   * This class also includes utility for interacting with the master znode, for
50   * writing and reading the znode content.
51   */
52  @InterfaceAudience.Private
53  public class MasterAddressTracker extends ZooKeeperNodeTracker {
54    /**
55     * Construct a master address listener with the specified
56     * <code>zookeeper</code> reference.
57     * <p>
58     * This constructor does not trigger any actions, you must call methods
59     * explicitly.  Normally you will just want to execute {@link #start()} to
60     * begin tracking of the master address.
61     *
62     * @param watcher zk reference and watcher
63     * @param abortable abortable in case of fatal error
64     */
65    public MasterAddressTracker(ZooKeeperWatcher watcher, Abortable abortable) {
66      super(watcher, watcher.getMasterAddressZNode(), abortable);
67    }
68  
69    /**
70     * Get the address of the current master if one is available.  Returns null
71     * if no current master.
72     * @return Server name or null if timed out.
73     */
74    public ServerName getMasterAddress() {
75      return getMasterAddress(false);
76    }
77  
78    /**
79     * Get the address of the current master if one is available.  Returns null
80     * if no current master. If refresh is set, try to load the data from ZK again,
81     * otherwise, cached data will be used.
82     *
83     * @param refresh whether to refresh the data by calling ZK directly.
84     * @return Server name or null if timed out.
85     */
86    public ServerName getMasterAddress(final boolean refresh) {
87      try {
88        return ServerName.parseFrom(super.getData(refresh));
89      } catch (DeserializationException e) {
90        LOG.warn("Failed parse", e);
91        return null;
92      }
93    }
94  
95    /**
96     * Get master address.
97     * Use this instead of {@link #getMasterAddress()} if you do not have an
98     * instance of this tracker in your context.
99     * @param zkw ZooKeeperWatcher to use
100    * @return ServerName stored in the the master address znode or null if no
101    * znode present.
102    * @throws KeeperException 
103    * @throws IOException 
104    */
105   public static ServerName getMasterAddress(final ZooKeeperWatcher zkw)
106   throws KeeperException, IOException {
107     byte [] data;
108     try {
109       data = ZKUtil.getData(zkw, zkw.getMasterAddressZNode());
110     } catch (InterruptedException e) {
111       throw new InterruptedIOException();
112     }
113     if (data == null){
114       throw new IOException("Can't get master address from ZooKeeper; znode data == null");
115     }
116     try {
117       return ServerName.parseFrom(data);
118     } catch (DeserializationException e) {
119       KeeperException ke = new KeeperException.DataInconsistencyException();
120       ke.initCause(e);
121       throw ke;
122     }
123   }
124 
125   /**
126    * Set master address into the <code>master</code> znode or into the backup
127    * subdirectory of backup masters; switch off the passed in <code>znode</code>
128    * path.
129    * @param zkw The ZooKeeperWatcher to use.
130    * @param znode Where to create the znode; could be at the top level or it
131    * could be under backup masters
132    * @param master ServerName of the current master
133    * @return true if node created, false if not; a watch is set in both cases
134    * @throws KeeperException
135    */
136   public static boolean setMasterAddress(final ZooKeeperWatcher zkw,
137       final String znode, final ServerName master)
138   throws KeeperException {
139     return ZKUtil.createEphemeralNodeAndWatch(zkw, znode, toByteArray(master));
140   }
141 
142   /**
143    * Check if there is a master available.
144    * @return true if there is a master set, false if not.
145    */
146   public boolean hasMaster() {
147     return super.getData(false) != null;
148   }
149 
150   /**
151    * @param sn
152    * @return Content of the master znode as a serialized pb with the pb
153    * magic as prefix.
154    */
155    static byte [] toByteArray(final ServerName sn) {
156      ZooKeeperProtos.Master.Builder mbuilder = ZooKeeperProtos.Master.newBuilder();
157      HBaseProtos.ServerName.Builder snbuilder = HBaseProtos.ServerName.newBuilder();
158      snbuilder.setHostName(sn.getHostname());
159      snbuilder.setPort(sn.getPort());
160      snbuilder.setStartCode(sn.getStartcode());
161      mbuilder.setMaster(snbuilder.build());
162      mbuilder.setRpcVersion(HConstants.RPC_CURRENT_VERSION);
163      return ProtobufUtil.prependPBMagic(mbuilder.build().toByteArray());
164    }
165 
166   /**
167    * delete the master znode if its content is same as the parameter
168    */
169   public static boolean deleteIfEquals(ZooKeeperWatcher zkw, final String content) {
170     if (content == null){
171       throw new IllegalArgumentException("Content must not be null");
172     }
173 
174     try {
175       Stat stat = new Stat();
176       byte[] data = ZKUtil.getDataNoWatch(zkw, zkw.getMasterAddressZNode(), stat);
177       ServerName sn = ServerName.parseFrom(data);
178       if (sn != null && content.equals(sn.toString())) {
179         return (ZKUtil.deleteNode(zkw, zkw.getMasterAddressZNode(), stat.getVersion()));
180       }
181     } catch (KeeperException e) {
182       LOG.warn("Can't get or delete the master znode", e);
183     } catch (DeserializationException e) {
184       LOG.warn("Can't get or delete the master znode", e);
185     }
186 
187     return false;
188   }
189 }