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 java.util.Collections; 021import java.util.Set; 022import java.util.WeakHashMap; 023import org.apache.hadoop.conf.Configuration; 024import org.apache.yetus.audience.InterfaceAudience; 025import org.apache.yetus.audience.InterfaceStability; 026import org.slf4j.Logger; 027import org.slf4j.LoggerFactory; 028 029/** 030 * Maintains the set of all the classes which would like to get notified when the Configuration is 031 * reloaded from the disk in the Online Configuration Change mechanism, which lets you update 032 * certain configuration properties on-the-fly, without having to restart the cluster. 033 * <p> 034 * If a class has configuration properties which you would like to be able to change on-the-fly, do 035 * the following: 036 * <ol> 037 * <li>Implement the {@link ConfigurationObserver} interface. This would require you to implement 038 * the {@link ConfigurationObserver#onConfigurationChange(Configuration)} method. This is a callback 039 * that is used to notify your class' instance that the configuration has changed. In this method, 040 * you need to check if the new values for the properties that are of interest to your class are 041 * different from the cached values. If yes, update them. <br /> 042 * However, be careful with this. Certain properties might be trivially mutable online, but others 043 * might not. Two properties might be trivially mutable by themselves, but not when changed 044 * together. For example, if a method uses properties "a" and "b" to make some decision, and is 045 * running in parallel when the notifyOnChange() method updates "a", but hasn't yet updated "b", it 046 * might make a decision on the basis of a new value of "a", and an old value of "b". This might 047 * introduce subtle bugs. This needs to be dealt on a case-by-case basis, and this class does not 048 * provide any protection from such cases.</li> 049 * <li>Register the appropriate instance of the class with the {@link ConfigurationManager} 050 * instance, using the {@link ConfigurationManager#registerObserver(ConfigurationObserver)} method. 051 * Be careful not to do this in the constructor, as you might cause the 'this' reference to escape. 052 * Use a factory method, or an initialize() method which is called after the construction of the 053 * object.</li> 054 * <li>Deregister the instance using the 055 * {@link ConfigurationManager#deregisterObserver(ConfigurationObserver)} method when it is going 056 * out of scope. In case you are not able to do that for any reason, it is still okay, since entries 057 * for dead observers are automatically collected during GC. But nonetheless, it is still a good 058 * practice to deregister your observer, whenever possible.</li> 059 * </ol> 060 * </p> 061 */ 062@InterfaceAudience.Private 063@InterfaceStability.Evolving 064public class ConfigurationManager { 065 private static final Logger LOG = LoggerFactory.getLogger(ConfigurationManager.class); 066 067 // The set of Configuration Observers. These classes would like to get 068 // notified when the configuration is reloaded from disk. This is a set 069 // constructed from a WeakHashMap, whose entries would be removed if the 070 // observer classes go out of scope. 071 private final Set<ConfigurationObserver> configurationObservers = 072 Collections.newSetFromMap(new WeakHashMap<>()); 073 074 /** 075 * Register an observer class 076 * @param observer observer to be registered. 077 */ 078 public void registerObserver(ConfigurationObserver observer) { 079 synchronized (configurationObservers) { 080 configurationObservers.add(observer); 081 if (observer instanceof PropagatingConfigurationObserver) { 082 ((PropagatingConfigurationObserver) observer).registerChildren(this); 083 } 084 } 085 } 086 087 /** 088 * Deregister an observer class 089 * @param observer to be deregistered. 090 */ 091 public void deregisterObserver(ConfigurationObserver observer) { 092 synchronized (configurationObservers) { 093 configurationObservers.remove(observer); 094 if (observer instanceof PropagatingConfigurationObserver) { 095 ((PropagatingConfigurationObserver) observer).deregisterChildren(this); 096 } 097 } 098 } 099 100 /** 101 * The conf object has been repopulated from disk, and we have to notify all the observers that 102 * are expressed interest to do that. 103 */ 104 public void notifyAllObservers(Configuration conf) { 105 LOG.info("Starting to notify all observers that config changed."); 106 synchronized (configurationObservers) { 107 for (ConfigurationObserver observer : configurationObservers) { 108 try { 109 if (observer != null) { 110 observer.onConfigurationChange(conf); 111 } 112 } catch (Throwable t) { 113 LOG.error("Encountered a throwable while notifying observers: of type : {}({})", 114 observer.getClass().getCanonicalName(), observer, t); 115 } 116 } 117 } 118 } 119 120 /** Returns the number of observers. */ 121 public int getNumObservers() { 122 synchronized (configurationObservers) { 123 return configurationObservers.size(); 124 } 125 } 126}