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