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 static org.junit.jupiter.api.Assertions.assertEquals;
021import static org.junit.jupiter.api.Assertions.assertNull;
022import static org.junit.jupiter.api.Assertions.assertThrows;
023
024import java.util.HashMap;
025import java.util.Map;
026import org.apache.hadoop.conf.Configuration;
027import org.apache.hadoop.hbase.testclassification.MiscTests;
028import org.apache.hadoop.hbase.testclassification.SmallTests;
029import org.apache.hadoop.hbase.util.Bytes;
030import org.junit.jupiter.api.BeforeEach;
031import org.junit.jupiter.api.Tag;
032import org.junit.jupiter.api.Test;
033
034@Tag(MiscTests.TAG)
035@Tag(SmallTests.TAG)
036public class TestCompoundConfiguration {
037
038  private Configuration baseConf;
039  private int baseConfSize;
040
041  @BeforeEach
042  public void setUp() throws Exception {
043    baseConf = new Configuration();
044    baseConf.set("A", "1");
045    baseConf.setInt("B", 2);
046    baseConf.set("C", "3");
047    baseConfSize = baseConf.size();
048  }
049
050  @Test
051  public void testBasicFunctionality() throws ClassNotFoundException {
052    CompoundConfiguration compoundConf = new CompoundConfiguration().add(baseConf);
053    assertEquals("1", compoundConf.get("A"));
054    assertEquals(2, compoundConf.getInt("B", 0));
055    assertEquals(3, compoundConf.getInt("C", 0));
056    assertEquals(0, compoundConf.getInt("D", 0));
057
058    assertEquals(CompoundConfiguration.class,
059      compoundConf.getClassByName(CompoundConfiguration.class.getName()));
060    assertThrows(ClassNotFoundException.class, () -> compoundConf.getClassByName("bad_class_name"),
061      "Trying to load bad_class_name should throw an exception");
062  }
063
064  @Test
065  public void testPut() {
066    CompoundConfiguration compoundConf = new CompoundConfiguration().add(baseConf);
067    assertEquals("1", compoundConf.get("A"));
068    assertEquals(2, compoundConf.getInt("B", 0));
069    assertEquals(3, compoundConf.getInt("C", 0));
070    assertEquals(0, compoundConf.getInt("D", 0));
071
072    compoundConf.set("A", "1337");
073    compoundConf.set("string", "stringvalue");
074    assertEquals(1337, compoundConf.getInt("A", 0));
075    assertEquals("stringvalue", compoundConf.get("string"));
076
077    // we didn't modify the base conf
078    assertEquals("1", baseConf.get("A"));
079    assertNull(baseConf.get("string"));
080
081    // adding to the base shows up in the compound
082    baseConf.set("setInParent", "fromParent");
083    assertEquals("fromParent", compoundConf.get("setInParent"));
084  }
085
086  @Test
087  public void testWithConfig() {
088    Configuration conf = new Configuration();
089    conf.set("B", "2b");
090    conf.set("C", "33");
091    conf.set("D", "4");
092
093    CompoundConfiguration compoundConf = new CompoundConfiguration().add(baseConf).add(conf);
094    assertEquals("1", compoundConf.get("A"));
095    assertEquals("2b", compoundConf.get("B"));
096    assertEquals(33, compoundConf.getInt("C", 0));
097    assertEquals("4", compoundConf.get("D"));
098    assertEquals(4, compoundConf.getInt("D", 0));
099    assertNull(compoundConf.get("E"));
100    assertEquals(6, compoundConf.getInt("F", 6));
101
102    int cnt = 0;
103    for (Map.Entry<String, String> entry : compoundConf) {
104      cnt++;
105      if (entry.getKey().equals("B")) {
106        assertEquals("2b", entry.getValue());
107      } else if (entry.getKey().equals("G")) {
108        assertNull(entry.getValue());
109      }
110    }
111    // verify that entries from ImmutableConfigMap's are merged in the iterator's view
112    assertEquals(baseConfSize + 1, cnt);
113  }
114
115  private Bytes strToIb(String s) {
116    return new Bytes(Bytes.toBytes(s));
117  }
118
119  @Test
120  public void testWithIbwMap() {
121    Map<Bytes, Bytes> map = new HashMap<>();
122    map.put(strToIb("B"), strToIb("2b"));
123    map.put(strToIb("C"), strToIb("33"));
124    map.put(strToIb("D"), strToIb("4"));
125    // unlike config, note that IBW Maps can accept null values
126    map.put(strToIb("G"), null);
127
128    CompoundConfiguration compoundConf = new CompoundConfiguration().add(baseConf).addBytesMap(map);
129    assertEquals("1", compoundConf.get("A"));
130    assertEquals("2b", compoundConf.get("B"));
131    assertEquals(33, compoundConf.getInt("C", 0));
132    assertEquals("4", compoundConf.get("D"));
133    assertEquals(4, compoundConf.getInt("D", 0));
134    assertNull(compoundConf.get("E"));
135    assertEquals(6, compoundConf.getInt("F", 6));
136    assertNull(compoundConf.get("G"));
137
138    int cnt = 0;
139    for (Map.Entry<String, String> entry : compoundConf) {
140      cnt++;
141      if (entry.getKey().equals("B")) {
142        assertEquals("2b", entry.getValue());
143      } else if (entry.getKey().equals("G")) {
144        assertNull(entry.getValue());
145      }
146    }
147    // verify that entries from ImmutableConfigMap's are merged in the iterator's view
148    assertEquals(baseConfSize + 2, cnt);
149
150    // Verify that adding map after compound configuration is modified overrides properly
151    CompoundConfiguration conf2 = new CompoundConfiguration();
152    conf2.set("X", "modification");
153    conf2.set("D", "not4");
154    assertEquals("modification", conf2.get("X"));
155    assertEquals("not4", conf2.get("D"));
156    conf2.addBytesMap(map);
157    assertEquals("4", conf2.get("D")); // map overrides
158  }
159
160  @Test
161  public void testWithStringMap() {
162    Map<String, String> map = new HashMap<>();
163    map.put("B", "2b");
164    map.put("C", "33");
165    map.put("D", "4");
166    // unlike config, note that IBW Maps can accept null values
167    map.put("G", null);
168
169    CompoundConfiguration compoundConf = new CompoundConfiguration().addStringMap(map);
170    assertEquals("2b", compoundConf.get("B"));
171    assertEquals(33, compoundConf.getInt("C", 0));
172    assertEquals("4", compoundConf.get("D"));
173    assertEquals(4, compoundConf.getInt("D", 0));
174    assertNull(compoundConf.get("E"));
175    assertEquals(6, compoundConf.getInt("F", 6));
176    assertNull(compoundConf.get("G"));
177
178    int cnt = 0;
179    for (Map.Entry<String, String> entry : compoundConf) {
180      cnt++;
181      if (entry.getKey().equals("B")) {
182        assertEquals("2b", entry.getValue());
183      } else if (entry.getKey().equals("G")) {
184        assertNull(entry.getValue());
185      }
186    }
187    // verify that entries from ImmutableConfigMap's are merged in the iterator's view
188    assertEquals(4, cnt);
189
190    // Verify that adding map after compound configuration is modified overrides properly
191    CompoundConfiguration conf2 = new CompoundConfiguration();
192    conf2.set("X", "modification");
193    conf2.set("D", "not4");
194    assertEquals("modification", conf2.get("X"));
195    assertEquals("not4", conf2.get("D"));
196    conf2.addStringMap(map);
197    assertEquals("4", conf2.get("D")); // map overrides
198  }
199
200  @Test
201  public void testLaterConfigsOverrideEarlier() {
202    Map<String, String> map1 = new HashMap<>();
203    map1.put("A", "2");
204    map1.put("D", "5");
205    Map<String, String> map2 = new HashMap<>();
206    String newValueForA = "3", newValueForB = "4";
207    map2.put("A", newValueForA);
208    map2.put("B", newValueForB);
209
210    CompoundConfiguration compoundConf =
211      new CompoundConfiguration().addStringMap(map1).add(baseConf);
212    assertEquals("1", compoundConf.get("A"));
213    assertEquals("5", compoundConf.get("D"));
214    compoundConf.addStringMap(map2);
215    assertEquals(newValueForA, compoundConf.get("A"));
216    assertEquals(newValueForB, compoundConf.get("B"));
217    assertEquals("5", compoundConf.get("D"));
218
219    int cnt = 0;
220    for (Map.Entry<String, String> entry : compoundConf) {
221      cnt++;
222      if (entry.getKey().equals("A")) {
223        assertEquals(newValueForA, entry.getValue());
224      } else if (entry.getKey().equals("B")) {
225        assertEquals(newValueForB, entry.getValue());
226      }
227    }
228    // verify that entries from ImmutableConfigMap's are merged in the iterator's view
229    assertEquals(baseConfSize + 1, cnt);
230  }
231}