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.conf;
019
020import static org.junit.jupiter.api.Assertions.assertFalse;
021import static org.junit.jupiter.api.Assertions.assertTrue;
022
023import org.apache.hadoop.conf.Configuration;
024import org.apache.hadoop.hbase.testclassification.ClientTests;
025import org.apache.hadoop.hbase.testclassification.SmallTests;
026import org.junit.jupiter.api.Tag;
027import org.junit.jupiter.api.Test;
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030
031@Tag(SmallTests.TAG)
032@Tag(ClientTests.TAG)
033public class TestConfigurationManager {
034
035  private static final Logger LOG = LoggerFactory.getLogger(TestConfigurationManager.class);
036
037  static class DummyConfigurationObserver implements ConfigurationObserver {
038    private boolean notifiedOnChange = false;
039    private final ConfigurationManager cm;
040
041    public DummyConfigurationObserver(ConfigurationManager cm) {
042      this.cm = cm;
043      register();
044    }
045
046    @Override
047    public void onConfigurationChange(Configuration conf) {
048      notifiedOnChange = true;
049    }
050
051    // Was the observer notified on Configuration change?
052    public boolean wasNotifiedOnChange() {
053      return notifiedOnChange;
054    }
055
056    public void resetNotifiedOnChange() {
057      notifiedOnChange = false;
058    }
059
060    public void register() {
061      cm.registerObserver(this);
062    }
063
064    public void deregister() {
065      cm.deregisterObserver(this);
066    }
067  }
068
069  /**
070   * Test if observers get notified by the <code>ConfigurationManager</code> when the Configuration
071   * is reloaded.
072   */
073  @Test
074  public void testCheckIfObserversNotified() {
075    Configuration conf = new Configuration();
076    ConfigurationManager cm = new ConfigurationManager();
077    DummyConfigurationObserver d1 = new DummyConfigurationObserver(cm);
078
079    // Check if we get notified.
080    cm.notifyAllObservers(conf);
081    assertTrue(d1.wasNotifiedOnChange());
082    d1.resetNotifiedOnChange();
083
084    // Now check if we get notified on change with more than one observers.
085    DummyConfigurationObserver d2 = new DummyConfigurationObserver(cm);
086    cm.notifyAllObservers(conf);
087    assertTrue(d1.wasNotifiedOnChange());
088    d1.resetNotifiedOnChange();
089    assertTrue(d2.wasNotifiedOnChange());
090    d2.resetNotifiedOnChange();
091
092    // Now try deregistering an observer and verify that it was not notified
093    d2.deregister();
094    cm.notifyAllObservers(conf);
095    assertTrue(d1.wasNotifiedOnChange());
096    d1.resetNotifiedOnChange();
097    assertFalse(d2.wasNotifiedOnChange());
098  }
099
100  // Register an observer that will go out of scope immediately, allowing
101  // us to test that out of scope observers are deregistered.
102  private void registerLocalObserver(ConfigurationManager cm) {
103    new DummyConfigurationObserver(cm);
104  }
105
106  /**
107   * Test if out-of-scope observers are deregistered on GC.
108   */
109  @Test
110  public void testDeregisterOnOutOfScope() {
111    Configuration conf = new Configuration();
112    ConfigurationManager cm = new ConfigurationManager();
113
114    boolean outOfScopeObserversDeregistered = false;
115
116    // On my machine, I was able to cause a GC after around 5 iterations.
117    // If we do not cause a GC in 100k iterations, which is very unlikely,
118    // there might be something wrong with the GC.
119    for (int i = 0; i < 100000; i++) {
120      registerLocalObserver(cm);
121      cm.notifyAllObservers(conf);
122
123      // 'Suggest' the system to do a GC. We should be able to cause GC
124      // atleast once in the 2000 iterations.
125      System.gc();
126
127      // If GC indeed happened, all the observers (which are all out of scope),
128      // should have been deregistered.
129      if (cm.getNumObservers() <= i) {
130        outOfScopeObserversDeregistered = true;
131        break;
132      }
133    }
134    if (!outOfScopeObserversDeregistered) {
135      LOG.warn("Observers were not GC-ed! Something seems to be wrong.");
136    }
137    assertTrue(outOfScopeObserversDeregistered);
138  }
139}