001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase; 019 020import java.io.IOException; 021import java.lang.reflect.InvocationTargetException; 022import java.lang.reflect.Method; 023import java.util.Map; 024import org.apache.hadoop.conf.Configuration; 025import org.apache.hadoop.hbase.util.VersionInfo; 026import org.apache.hadoop.hbase.zookeeper.ZKConfig; 027import org.apache.yetus.audience.InterfaceAudience; 028import org.slf4j.Logger; 029import org.slf4j.LoggerFactory; 030 031/** 032 * Adds HBase configuration files to a Configuration 033 */ 034@InterfaceAudience.Public 035public class HBaseConfiguration extends Configuration { 036 private static final Logger LOG = LoggerFactory.getLogger(HBaseConfiguration.class); 037 038 /** 039 * Instantiating HBaseConfiguration() is deprecated. Please use HBaseConfiguration#create() to 040 * construct a plain Configuration 041 * @deprecated since 0.90.0. Please use {@link #create()} instead. 042 * @see #create() 043 * @see <a href="https://issues.apache.org/jira/browse/HBASE-2036">HBASE-2036</a> 044 */ 045 @Deprecated 046 public HBaseConfiguration() { 047 // TODO:replace with private constructor, HBaseConfiguration should not extend Configuration 048 super(); 049 addHbaseResources(this); 050 LOG.warn("instantiating HBaseConfiguration() is deprecated. Please use" 051 + " HBaseConfiguration#create() to construct a plain Configuration"); 052 } 053 054 /** 055 * Instantiating HBaseConfiguration() is deprecated. Please use HBaseConfiguration#create(conf) to 056 * construct a plain Configuration 057 * @deprecated since 0.90.0. Please use {@link #create(Configuration)} instead. 058 * @see #create(Configuration) 059 * @see <a href="https://issues.apache.org/jira/browse/HBASE-2036">HBASE-2036</a> 060 */ 061 @Deprecated 062 public HBaseConfiguration(final Configuration c) { 063 // TODO:replace with private constructor 064 this(); 065 merge(this, c); 066 } 067 068 private static void checkDefaultsVersion(Configuration conf) { 069 if (conf.getBoolean("hbase.defaults.for.version.skip", Boolean.FALSE)) return; 070 String defaultsVersion = conf.get("hbase.defaults.for.version"); 071 String thisVersion = VersionInfo.getVersion(); 072 if (!thisVersion.equals(defaultsVersion)) { 073 throw new RuntimeException( 074 "hbase-default.xml file seems to be for an older version of HBase (" + defaultsVersion 075 + "), this version is " + thisVersion); 076 } 077 } 078 079 public static Configuration addHbaseResources(Configuration conf) { 080 conf.addResource("hbase-default.xml"); 081 conf.addResource("hbase-site.xml"); 082 083 checkDefaultsVersion(conf); 084 return conf; 085 } 086 087 /** 088 * Creates a Configuration with HBase resources 089 * @return a Configuration with HBase resources 090 */ 091 public static Configuration create() { 092 Configuration conf = new Configuration(); 093 // In case HBaseConfiguration is loaded from a different classloader than 094 // Configuration, conf needs to be set with appropriate class loader to resolve 095 // HBase resources. 096 conf.setClassLoader(HBaseConfiguration.class.getClassLoader()); 097 return addHbaseResources(conf); 098 } 099 100 /** 101 * @param that Configuration to clone. 102 * @return a Configuration created with the hbase-*.xml files plus the given configuration. 103 */ 104 public static Configuration create(final Configuration that) { 105 Configuration conf = create(); 106 merge(conf, that); 107 return conf; 108 } 109 110 /** 111 * Merge two configurations. 112 * @param destConf the configuration that will be overwritten with items from the srcConf 113 * @param srcConf the source configuration 114 **/ 115 public static void merge(Configuration destConf, Configuration srcConf) { 116 for (Map.Entry<String, String> e : srcConf) { 117 destConf.set(e.getKey(), e.getValue()); 118 } 119 } 120 121 /** 122 * Returns a subset of the configuration properties, matching the given key prefix. The prefix is 123 * stripped from the return keys, ie. when calling with a prefix of "myprefix", the entry 124 * "myprefix.key1 = value1" would be returned as "key1 = value1". If an entry's key matches the 125 * prefix exactly ("myprefix = value2"), it will <strong>not</strong> be included in the results, 126 * since it would show up as an entry with an empty key. 127 */ 128 public static Configuration subset(Configuration srcConf, String prefix) { 129 Configuration newConf = new Configuration(false); 130 for (Map.Entry<String, String> entry : srcConf) { 131 if (entry.getKey().startsWith(prefix)) { 132 String newKey = entry.getKey().substring(prefix.length()); 133 // avoid entries that would produce an empty key 134 if (!newKey.isEmpty()) { 135 newConf.set(newKey, entry.getValue()); 136 } 137 } 138 } 139 return newConf; 140 } 141 142 /** 143 * Sets all the entries in the provided {@code Map<String, String>} as properties in the given 144 * {@code Configuration}. Each property will have the specified prefix prepended, so that the 145 * configuration entries are keyed by {@code prefix + entry.getKey()}. 146 */ 147 public static void setWithPrefix(Configuration conf, String prefix, 148 Iterable<Map.Entry<String, String>> properties) { 149 for (Map.Entry<String, String> entry : properties) { 150 conf.set(prefix + entry.getKey(), entry.getValue()); 151 } 152 } 153 154 /** Returns whether to show HBase Configuration in servlet */ 155 public static boolean isShowConfInServlet() { 156 boolean isShowConf = false; 157 try { 158 if (Class.forName("org.apache.hadoop.conf.ConfServlet") != null) { 159 isShowConf = true; 160 } 161 } catch (LinkageError e) { 162 // should we handle it more aggressively in addition to log the error? 163 LOG.warn("Error thrown: ", e); 164 } catch (ClassNotFoundException ce) { 165 LOG.debug("ClassNotFound: ConfServlet"); 166 // ignore 167 } 168 return isShowConf; 169 } 170 171 /** 172 * Get the value of the <code>name</code> property as an <code>int</code>, possibly referring to 173 * the deprecated name of the configuration property. If no such property exists, the provided 174 * default value is returned, or if the specified value is not a valid <code>int</code>, then an 175 * error is thrown. 176 * @param name property name. 177 * @param deprecatedName a deprecatedName for the property to use if non-deprecated name is not 178 * used 179 * @param defaultValue default value. 180 * @throws NumberFormatException when the value is invalid 181 * @return property value as an <code>int</code>, or <code>defaultValue</code>. 182 * @deprecated it will be removed in 3.0.0. Use 183 * {@link Configuration#addDeprecation(String, String)} instead. 184 */ 185 @Deprecated 186 public static int getInt(Configuration conf, String name, String deprecatedName, 187 int defaultValue) { 188 if (conf.get(deprecatedName) != null) { 189 LOG.warn(String.format("Config option \"%s\" is deprecated. Instead, use \"%s\"", 190 deprecatedName, name)); 191 return conf.getInt(deprecatedName, defaultValue); 192 } else { 193 return conf.getInt(name, defaultValue); 194 } 195 } 196 197 /** 198 * Get the password from the Configuration instance using the getPassword method if it exists. If 199 * not, then fall back to the general get method for configuration elements. 200 * @param conf configuration instance for accessing the passwords 201 * @param alias the name of the password element 202 * @param defPass the default password 203 * @return String password or default password 204 */ 205 public static String getPassword(Configuration conf, String alias, String defPass) 206 throws IOException { 207 String passwd = null; 208 try { 209 Method m = Configuration.class.getMethod("getPassword", String.class); 210 char[] p = (char[]) m.invoke(conf, alias); 211 if (p != null) { 212 LOG.debug(String.format( 213 "Config option \"%s\" was found through" + " the Configuration getPassword method.", 214 alias)); 215 passwd = new String(p); 216 } else { 217 LOG.debug( 218 String.format("Config option \"%s\" was not found. Using provided default value", alias)); 219 passwd = defPass; 220 } 221 } catch (NoSuchMethodException e) { 222 // this is a version of Hadoop where the credential 223 // provider API doesn't exist yet 224 LOG.debug(String.format( 225 "Credential.getPassword method is not available." + " Falling back to configuration.")); 226 passwd = conf.get(alias, defPass); 227 } catch (SecurityException e) { 228 throw new IOException(e.getMessage(), e); 229 } catch (IllegalAccessException e) { 230 throw new IOException(e.getMessage(), e); 231 } catch (IllegalArgumentException e) { 232 throw new IOException(e.getMessage(), e); 233 } catch (InvocationTargetException e) { 234 throw new IOException(e.getMessage(), e); 235 } 236 return passwd; 237 } 238 239 /** 240 * Generates a {@link Configuration} instance by applying the ZooKeeper cluster key to the base 241 * Configuration. Note that additional configuration properties may be needed for a remote 242 * cluster, so it is preferable to use {@link #createClusterConf(Configuration, String, String)}. 243 * @param baseConf the base configuration to use, containing prefixed override properties 244 * @param clusterKey the ZooKeeper quorum cluster key to apply, or {@code null} if none 245 * @return the merged configuration with override properties and cluster key applied 246 * @see #createClusterConf(Configuration, String, String) 247 */ 248 public static Configuration createClusterConf(Configuration baseConf, String clusterKey) 249 throws IOException { 250 return createClusterConf(baseConf, clusterKey, null); 251 } 252 253 /** 254 * Generates a {@link Configuration} instance by applying property overrides prefixed by a cluster 255 * profile key to the base Configuration. Override properties are extracted by the 256 * {@link #subset(Configuration, String)} method, then the merged on top of the base Configuration 257 * and returned. 258 * @param baseConf the base configuration to use, containing prefixed override properties 259 * @param clusterKey the ZooKeeper quorum cluster key to apply, or {@code null} if none 260 * @param overridePrefix the property key prefix to match for override properties, or {@code null} 261 * if none 262 * @return the merged configuration with override properties and cluster key applied 263 */ 264 public static Configuration createClusterConf(Configuration baseConf, String clusterKey, 265 String overridePrefix) throws IOException { 266 Configuration clusterConf = HBaseConfiguration.create(baseConf); 267 if (clusterKey != null && !clusterKey.isEmpty()) { 268 applyClusterKeyToConf(clusterConf, clusterKey); 269 } 270 271 if (overridePrefix != null && !overridePrefix.isEmpty()) { 272 Configuration clusterSubset = HBaseConfiguration.subset(clusterConf, overridePrefix); 273 HBaseConfiguration.merge(clusterConf, clusterSubset); 274 } 275 return clusterConf; 276 } 277 278 /** 279 * Apply the settings in the given key to the given configuration, this is used to communicate 280 * with distant clusters 281 * @param conf configuration object to configure 282 * @param key string that contains the 3 required configuratins 283 */ 284 private static void applyClusterKeyToConf(Configuration conf, String key) throws IOException { 285 ZKConfig.ZKClusterKey zkClusterKey = ZKConfig.transformClusterKey(key); 286 conf.set(HConstants.ZOOKEEPER_QUORUM, zkClusterKey.getQuorumString()); 287 conf.setInt(HConstants.ZOOKEEPER_CLIENT_PORT, zkClusterKey.getClientPort()); 288 conf.set(HConstants.ZOOKEEPER_ZNODE_PARENT, zkClusterKey.getZnodeParent()); 289 // Without the right registry, the above configs are useless. Also, we don't use setClass() 290 // here because the ConnectionRegistry* classes are not resolvable from this module. 291 // This will be broken if ZkConnectionRegistry class gets renamed or moved. Is there a better 292 // way? 293 LOG.info("Overriding client registry implementation to {}", 294 HConstants.ZK_CONNECTION_REGISTRY_CLASS); 295 conf.set(HConstants.CLIENT_CONNECTION_REGISTRY_IMPL_CONF_KEY, 296 HConstants.ZK_CONNECTION_REGISTRY_CLASS); 297 } 298 299 /** 300 * For debugging. Dump configurations to system output as xml format. Master and RS configurations 301 * can also be dumped using http services. e.g. "curl http://master:16010/dump" 302 */ 303 public static void main(String[] args) throws Exception { 304 HBaseConfiguration.create().writeXml(System.out); 305 } 306}