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