2 * Copyright The Apache Software Foundation
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
20 package org
.apache
.hadoop
.hbase
;
22 import java
.io
.DataOutput
;
23 import java
.io
.IOException
;
24 import java
.io
.OutputStream
;
25 import java
.util
.ArrayList
;
26 import java
.util
.HashMap
;
27 import java
.util
.Iterator
;
28 import java
.util
.List
;
31 import org
.apache
.hadoop
.conf
.Configuration
;
32 import org
.apache
.hadoop
.hbase
.util
.Bytes
;
33 import org
.apache
.yetus
.audience
.InterfaceAudience
;
35 import org
.apache
.hbase
.thirdparty
.org
.apache
.commons
.collections4
.iterators
.UnmodifiableIterator
;
38 * Do a shallow merge of multiple KV configuration pools. This is a very useful
39 * utility class to easily add per-object configurations in addition to wider
40 * scope settings. This is different from Configuration.addResource()
41 * functionality, which performs a deep merge and mutates the common data
44 * The iterator on CompoundConfiguration is unmodifiable. Obtaining iterator is an expensive
47 * For clarity: the shallow merge allows the user to mutate either of the
48 * configuration objects and have changes reflected everywhere. In contrast to a
49 * deep merge, that requires you to explicitly know all applicable copies to
52 * WARNING: The values set in the CompoundConfiguration are do not handle Property variable
53 * substitution. However, if they are set in the underlying configuration substitutions are
56 @InterfaceAudience.Private
57 public class CompoundConfiguration
extends Configuration
{
59 private Configuration mutableConf
= null;
62 * Default Constructor. Initializes empty configuration
64 public CompoundConfiguration() {
67 // Devs: these APIs are the same contract as their counterparts in
69 private interface ImmutableConfigMap
extends Iterable
<Map
.Entry
<String
,String
>> {
70 String
get(String key
);
71 String
getRaw(String key
);
72 Class
<?
> getClassByName(String name
) throws ClassNotFoundException
;
76 private final List
<ImmutableConfigMap
> configs
= new ArrayList
<>();
78 static class ImmutableConfWrapper
implements ImmutableConfigMap
{
79 private final Configuration c
;
81 ImmutableConfWrapper(Configuration conf
) {
86 public Iterator
<Map
.Entry
<String
,String
>> iterator() {
91 public String
get(String key
) {
96 public String
getRaw(String key
) {
101 public Class
<?
> getClassByName(String name
)
102 throws ClassNotFoundException
{
103 return c
.getClassByName(name
);
112 public String
toString() {
118 * If set has been called, it will create a mutableConf. This converts the mutableConf to an
119 * immutable one and resets it to allow a new mutable conf. This is used when a new map or
120 * conf is added to the compound configuration to preserve proper override semantics.
122 void freezeMutableConf() {
123 if (mutableConf
== null) {
124 // do nothing if there is no current mutableConf
128 this.configs
.add(0, new ImmutableConfWrapper(mutableConf
));
133 * Add Hadoop Configuration object to config list.
134 * The added configuration overrides the previous ones if there are name collisions.
135 * @param conf configuration object
136 * @return this, for builder pattern
138 public CompoundConfiguration
add(final Configuration conf
) {
141 if (conf
instanceof CompoundConfiguration
) {
142 this.configs
.addAll(0, ((CompoundConfiguration
) conf
).configs
);
145 // put new config at the front of the list (top priority)
146 this.configs
.add(0, new ImmutableConfWrapper(conf
));
151 * Add Bytes map to config list. This map is generally
152 * created by HTableDescriptor or HColumnDescriptor, but can be abstractly
153 * used. The added configuration overrides the previous ones if there are
158 * @return this, for builder pattern
160 public CompoundConfiguration
addBytesMap(
161 final Map
<Bytes
, Bytes
> map
) {
164 // put new map at the front of the list (top priority)
165 this.configs
.add(0, new ImmutableConfigMap() {
166 private final Map
<Bytes
, Bytes
> m
= map
;
169 public Iterator
<Map
.Entry
<String
,String
>> iterator() {
170 Map
<String
, String
> ret
= new HashMap
<>();
171 for (Map
.Entry
<Bytes
, Bytes
> entry
: map
.entrySet()) {
172 String key
= Bytes
.toString(entry
.getKey().get());
173 String val
= entry
.getValue() == null ?
null : Bytes
.toString(entry
.getValue().get());
176 return ret
.entrySet().iterator();
180 public String
get(String key
) {
181 Bytes ibw
= new Bytes(Bytes
183 if (!m
.containsKey(ibw
))
185 Bytes value
= m
.get(ibw
);
186 if (value
== null || value
.get() == null)
188 return Bytes
.toString(value
.get());
192 public String
getRaw(String key
) {
197 public Class
<?
> getClassByName(String name
)
198 throws ClassNotFoundException
{
208 public String
toString() {
216 * Add String map to config list. This map is generally created by HTableDescriptor
217 * or HColumnDescriptor, but can be abstractly used. The added configuration
218 * overrides the previous ones if there are name collisions.
220 * @return this, for builder pattern
222 public CompoundConfiguration
addStringMap(final Map
<String
, String
> map
) {
225 // put new map at the front of the list (top priority)
226 this.configs
.add(0, new ImmutableConfigMap() {
227 private final Map
<String
, String
> m
= map
;
230 public Iterator
<Map
.Entry
<String
,String
>> iterator() {
231 return map
.entrySet().iterator();
235 public String
get(String key
) {
240 public String
getRaw(String key
) {
245 public Class
<?
> getClassByName(String name
)
246 throws ClassNotFoundException
{
256 public String
toString() {
264 public String
toString() {
265 StringBuilder sb
= new StringBuilder();
266 sb
.append("CompoundConfiguration: " + this.configs
.size() + " configs");
267 for (ImmutableConfigMap m
: this.configs
) {
270 return sb
.toString();
274 public String
get(String key
) {
275 if (mutableConf
!= null) {
276 String value
= mutableConf
.get(key
);
282 for (ImmutableConfigMap m
: this.configs
) {
283 String value
= m
.get(key
);
292 public String
getRaw(String key
) {
293 if (mutableConf
!= null) {
294 String value
= mutableConf
.getRaw(key
);
300 for (ImmutableConfigMap m
: this.configs
) {
301 String value
= m
.getRaw(key
);
310 public Class
<?
> getClassByName(String name
) throws ClassNotFoundException
{
311 if (mutableConf
!= null) {
312 Class
<?
> value
= mutableConf
.getClassByName(name
);
318 for (ImmutableConfigMap m
: this.configs
) {
319 Class
<?
> value
= m
.getClassByName(name
);
324 throw new ClassNotFoundException();
327 // TODO: This method overestimates the number of configuration settings -- if a value is masked
328 // by an overriding config or map, it will be counted multiple times.
333 if (mutableConf
!= null) {
334 ret
+= mutableConf
.size();
337 for (ImmutableConfigMap m
: this.configs
) {
344 * Get the value of the <code>name</code>. If the key is deprecated,
345 * it returns the value of the first key which replaces the deprecated key
347 * If no such property exists,
348 * then <code>defaultValue</code> is returned.
350 * The CompooundConfiguration does not do property substitution. To do so we need
351 * Configuration.getProps to be protected or package visible. Though in hadoop2 it is
352 * protected, in hadoop1 the method is private and not accessible.
354 * All of the get* methods call this overridden get method.
356 * @param name property name.
357 * @param defaultValue default value.
358 * @return property value, or <code>defaultValue</code> if the property
362 public String
get(String name
, String defaultValue
) {
363 String ret
= get(name
);
364 return ret
== null ? defaultValue
: ret
;
368 public Iterator
<Map
.Entry
<String
, String
>> iterator() {
369 Map
<String
, String
> ret
= new HashMap
<>();
371 // add in reverse order so that oldest get overridden.
372 if (!configs
.isEmpty()) {
373 for (int i
= configs
.size() - 1; i
>= 0; i
--) {
374 ImmutableConfigMap map
= configs
.get(i
);
375 Iterator
<Map
.Entry
<String
, String
>> iter
= map
.iterator();
376 while (iter
.hasNext()) {
377 Map
.Entry
<String
, String
> entry
= iter
.next();
378 ret
.put(entry
.getKey(), entry
.getValue());
383 // add mutations to this CompoundConfiguration last.
384 if (mutableConf
!= null) {
385 Iterator
<Map
.Entry
<String
, String
>> miter
= mutableConf
.iterator();
386 while (miter
.hasNext()) {
387 Map
.Entry
<String
, String
> entry
= miter
.next();
388 ret
.put(entry
.getKey(), entry
.getValue());
392 return UnmodifiableIterator
.unmodifiableIterator(ret
.entrySet().iterator());
396 public void set(String name
, String value
) {
397 if (mutableConf
== null) {
399 mutableConf
= new Configuration(false); // an empty configuration
401 mutableConf
.set(name
, value
);
404 /***********************************************************************************************
405 * These methods are unsupported, and no code using CompoundConfiguration depend upon them.
406 * Quickly abort upon any attempts to use them.
407 **********************************************************************************************/
410 public void clear() {
411 throw new UnsupportedOperationException("Immutable Configuration");
415 public void write(DataOutput out
) throws IOException
{
416 throw new UnsupportedOperationException("Immutable Configuration");
420 public void writeXml(OutputStream out
) throws IOException
{
421 throw new UnsupportedOperationException("Immutable Configuration");