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.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 if (data == null){
108 throw new IOException("Can't get master address from ZooKeeper; znode data == null");
109 }
110 try {
111 return ServerName.parseFrom(data);
112 } catch (DeserializationException e) {
113 KeeperException ke = new KeeperException.DataInconsistencyException();
114 ke.initCause(e);
115 throw ke;
116 }
117 }
118
119 /**
120 * Set master address into the <code>master</code> znode or into the backup
121 * subdirectory of backup masters; switch off the passed in <code>znode</code>
122 * path.
123 * @param zkw The ZooKeeperWatcher to use.
124 * @param znode Where to create the znode; could be at the top level or it
125 * could be under backup masters
126 * @param master ServerName of the current master
127 * @return true if node created, false if not; a watch is set in both cases
128 * @throws KeeperException
129 */
130 public static boolean setMasterAddress(final ZooKeeperWatcher zkw,
131 final String znode, final ServerName master)
132 throws KeeperException {
133 return ZKUtil.createEphemeralNodeAndWatch(zkw, znode, toByteArray(master));
134 }
135
136 /**
137 * Check if there is a master available.
138 * @return true if there is a master set, false if not.
139 */
140 public boolean hasMaster() {
141 return super.getData(false) != null;
142 }
143
144 /**
145 * @param sn
146 * @return Content of the master znode as a serialized pb with the pb
147 * magic as prefix.
148 */
149 static byte [] toByteArray(final ServerName sn) {
150 ZooKeeperProtos.Master.Builder mbuilder = ZooKeeperProtos.Master.newBuilder();
151 HBaseProtos.ServerName.Builder snbuilder = HBaseProtos.ServerName.newBuilder();
152 snbuilder.setHostName(sn.getHostname());
153 snbuilder.setPort(sn.getPort());
154 snbuilder.setStartCode(sn.getStartcode());
155 mbuilder.setMaster(snbuilder.build());
156 mbuilder.setRpcVersion(HConstants.RPC_CURRENT_VERSION);
157 return ProtobufUtil.prependPBMagic(mbuilder.build().toByteArray());
158 }
159
160 /**
161 * delete the master znode if its content is same as the parameter
162 */
163 public static boolean deleteIfEquals(ZooKeeperWatcher zkw, final String content) {
164 if (content == null){
165 throw new IllegalArgumentException("Content must not be null");
166 }
167
168 try {
169 Stat stat = new Stat();
170 byte[] data = ZKUtil.getDataNoWatch(zkw, zkw.getMasterAddressZNode(), stat);
171 ServerName sn = ServerName.parseFrom(data);
172 if (sn != null && content.equals(sn.toString())) {
173 return (ZKUtil.deleteNode(zkw, zkw.getMasterAddressZNode(), stat.getVersion()));
174 }
175 } catch (KeeperException e) {
176 LOG.warn("Can't get or delete the master znode", e);
177 } catch (DeserializationException e) {
178 LOG.warn("Can't get or delete the master znode", e);
179 }
180
181 return false;
182 }
183 }