View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.zookeeper;
20  
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  import org.apache.hadoop.classification.InterfaceAudience;
24  import org.apache.hadoop.classification.InterfaceStability;
25  import org.apache.hadoop.conf.Configuration;
26  import org.apache.hadoop.hbase.HConstants;
27  import org.apache.hadoop.util.StringUtils;
28  
29  import java.io.IOException;
30  import java.io.InputStream;
31  import java.net.InetAddress;
32  import java.net.UnknownHostException;
33  import java.util.ArrayList;
34  import java.util.List;
35  import java.util.Map.Entry;
36  import java.util.Properties;
37  
38  /**
39   * Utility methods for reading, and building the ZooKeeper configuration.
40   */
41  @InterfaceAudience.Public
42  @InterfaceStability.Evolving
43  public class ZKConfig {
44    private static final Log LOG = LogFactory.getLog(ZKConfig.class);
45  
46    private static final String VARIABLE_START = "${";
47    private static final int VARIABLE_START_LENGTH = VARIABLE_START.length();
48    private static final String VARIABLE_END = "}";
49    private static final int VARIABLE_END_LENGTH = VARIABLE_END.length();
50  
51    /**
52     * Make a Properties object holding ZooKeeper config.
53     * Parses the corresponding config options from the HBase XML configs
54     * and generates the appropriate ZooKeeper properties.
55     * @param conf Configuration to read from.
56     * @return Properties holding mappings representing ZooKeeper config file.
57     */
58    public static Properties makeZKProps(Configuration conf) {
59      if (conf.getBoolean(HConstants.HBASE_CONFIG_READ_ZOOKEEPER_CONFIG, false)) {
60        LOG.warn(
61            "Parsing ZooKeeper's " + HConstants.ZOOKEEPER_CONFIG_NAME +
62            " file for ZK properties " +
63            "has been deprecated. Please instead place all ZK related HBase " +
64            "configuration under the hbase-site.xml, using prefixes " +
65            "of the form '" + HConstants.ZK_CFG_PROPERTY_PREFIX + "', and " +
66            "set property '" + HConstants.HBASE_CONFIG_READ_ZOOKEEPER_CONFIG +
67            "' to false");
68        // First check if there is a zoo.cfg in the CLASSPATH. If so, simply read
69        // it and grab its configuration properties.
70        ClassLoader cl = HQuorumPeer.class.getClassLoader();
71        final InputStream inputStream =
72          cl.getResourceAsStream(HConstants.ZOOKEEPER_CONFIG_NAME);
73        if (inputStream != null) {
74          try {
75            return parseZooCfg(conf, inputStream);
76          } catch (IOException e) {
77            LOG.warn("Cannot read " + HConstants.ZOOKEEPER_CONFIG_NAME +
78                     ", loading from XML files", e);
79          }
80        }
81      } else {
82        if (LOG.isTraceEnabled()) {
83          LOG.trace("Skipped reading ZK properties file '" + HConstants.ZOOKEEPER_CONFIG_NAME +
84            "' since '" + HConstants.HBASE_CONFIG_READ_ZOOKEEPER_CONFIG + "' was not set to true");
85        }
86      }
87  
88      // Otherwise, use the configuration options from HBase's XML files.
89      Properties zkProperties = new Properties();
90  
91      // Directly map all of the hbase.zookeeper.property.KEY properties.
92      for (Entry<String, String> entry : new Configuration(conf)) { // copy for mt safety
93        String key = entry.getKey();
94        if (key.startsWith(HConstants.ZK_CFG_PROPERTY_PREFIX)) {
95          String zkKey = key.substring(HConstants.ZK_CFG_PROPERTY_PREFIX_LEN);
96          String value = entry.getValue();
97          // If the value has variables substitutions, need to do a get.
98          if (value.contains(VARIABLE_START)) {
99            value = conf.get(key);
100         }
101         zkProperties.put(zkKey, value);
102       }
103     }
104 
105     // If clientPort is not set, assign the default.
106     if (zkProperties.getProperty(HConstants.CLIENT_PORT_STR) == null) {
107       zkProperties.put(HConstants.CLIENT_PORT_STR,
108           HConstants.DEFAULT_ZOOKEPER_CLIENT_PORT);
109     }
110 
111     // Create the server.X properties.
112     int peerPort = conf.getInt("hbase.zookeeper.peerport", 2888);
113     int leaderPort = conf.getInt("hbase.zookeeper.leaderport", 3888);
114 
115     final String[] serverHosts = conf.getStrings(HConstants.ZOOKEEPER_QUORUM,
116                                                  HConstants.LOCALHOST);
117     for (int i = 0; i < serverHosts.length; ++i) {
118       String serverHost = serverHosts[i];
119       String address = serverHost + ":" + peerPort + ":" + leaderPort;
120       String key = "server." + i;
121       zkProperties.put(key, address);
122     }
123 
124     return zkProperties;
125   }
126 
127   /**
128    * Parse ZooKeeper's zoo.cfg, injecting HBase Configuration variables in.
129    * This method is used for testing so we can pass our own InputStream.
130    * @param conf HBaseConfiguration to use for injecting variables.
131    * @param inputStream InputStream to read from.
132    * @return Properties parsed from config stream with variables substituted.
133    * @throws IOException if anything goes wrong parsing config
134    * @deprecated in 0.96 onwards. HBase will no longer rely on zoo.cfg
135    * availability.
136    */
137   @Deprecated
138   public static Properties parseZooCfg(Configuration conf,
139       InputStream inputStream) throws IOException {
140     Properties properties = new Properties();
141     try {
142       properties.load(inputStream);
143     } catch (IOException e) {
144       final String msg = "fail to read properties from "
145         + HConstants.ZOOKEEPER_CONFIG_NAME;
146       LOG.fatal(msg);
147       throw new IOException(msg, e);
148     }
149     for (Entry<Object, Object> entry : properties.entrySet()) {
150       String value = entry.getValue().toString().trim();
151       String key = entry.getKey().toString().trim();
152       StringBuilder newValue = new StringBuilder();
153       int varStart = value.indexOf(VARIABLE_START);
154       int varEnd = 0;
155       while (varStart != -1) {
156         varEnd = value.indexOf(VARIABLE_END, varStart);
157         if (varEnd == -1) {
158           String msg = "variable at " + varStart + " has no end marker";
159           LOG.fatal(msg);
160           throw new IOException(msg);
161         }
162         String variable = value.substring(varStart + VARIABLE_START_LENGTH, varEnd);
163 
164         String substituteValue = System.getProperty(variable);
165         if (substituteValue == null) {
166           substituteValue = conf.get(variable);
167         }
168         if (substituteValue == null) {
169           String msg = "variable " + variable + " not set in system property "
170                      + "or hbase configs";
171           LOG.fatal(msg);
172           throw new IOException(msg);
173         }
174 
175         newValue.append(substituteValue);
176 
177         varEnd += VARIABLE_END_LENGTH;
178         varStart = value.indexOf(VARIABLE_START, varEnd);
179       }
180       // Special case for 'hbase.cluster.distributed' property being 'true'
181       if (key.startsWith("server.")) {
182         boolean mode = conf.getBoolean(HConstants.CLUSTER_DISTRIBUTED, HConstants.DEFAULT_CLUSTER_DISTRIBUTED);
183         if (mode == HConstants.CLUSTER_IS_DISTRIBUTED && value.startsWith(HConstants.LOCALHOST)) {
184           String msg = "The server in zoo.cfg cannot be set to localhost " +
185               "in a fully-distributed setup because it won't be reachable. " +
186               "See \"Getting Started\" for more information.";
187           LOG.fatal(msg);
188           throw new IOException(msg);
189         }
190       }
191       newValue.append(value.substring(varEnd));
192       properties.setProperty(key, newValue.toString());
193     }
194     return properties;
195   }
196 
197   /**
198    * Return the ZK Quorum servers string given zk properties returned by
199    * makeZKProps
200    * @param properties
201    * @return Quorum servers String
202    */
203   public static String getZKQuorumServersString(Properties properties) {
204     String clientPort = null;
205     List<String> servers = new ArrayList<String>();
206 
207     // The clientPort option may come after the server.X hosts, so we need to
208     // grab everything and then create the final host:port comma separated list.
209     boolean anyValid = false;
210     for (Entry<Object,Object> property : properties.entrySet()) {
211       String key = property.getKey().toString().trim();
212       String value = property.getValue().toString().trim();
213       if (key.equals("clientPort")) {
214         clientPort = value;
215       }
216       else if (key.startsWith("server.")) {
217         String host = value.substring(0, value.indexOf(':'));
218         servers.add(host);
219         try {
220           //noinspection ResultOfMethodCallIgnored
221           InetAddress.getByName(host);
222           anyValid = true;
223         } catch (UnknownHostException e) {
224           LOG.warn(StringUtils.stringifyException(e));
225         }
226       }
227     }
228 
229     if (!anyValid) {
230       LOG.error("no valid quorum servers found in " + HConstants.ZOOKEEPER_CONFIG_NAME);
231       return null;
232     }
233 
234     if (clientPort == null) {
235       LOG.error("no clientPort found in " + HConstants.ZOOKEEPER_CONFIG_NAME);
236       return null;
237     }
238 
239     if (servers.isEmpty()) {
240       LOG.fatal("No servers were found in provided ZooKeeper configuration. " +
241           "HBase must have a ZooKeeper cluster configured for its " +
242           "operation. Ensure that you've configured '" +
243           HConstants.ZOOKEEPER_QUORUM + "' properly.");
244       return null;
245     }
246 
247     StringBuilder hostPortBuilder = new StringBuilder();
248     for (int i = 0; i < servers.size(); ++i) {
249       String host = servers.get(i);
250       if (i > 0) {
251         hostPortBuilder.append(',');
252       }
253       hostPortBuilder.append(host);
254       hostPortBuilder.append(':');
255       hostPortBuilder.append(clientPort);
256     }
257 
258     return hostPortBuilder.toString();
259   }
260 
261   /**
262    * Return the ZK Quorum servers string given the specified configuration.
263    * @param conf
264    * @return Quorum servers
265    */
266   public static String getZKQuorumServersString(Configuration conf) {
267     return getZKQuorumServersString(makeZKProps(conf));
268   }
269 }