apollo.internals.DefaultConfig Maven / Gradle / Ivy
The newest version!
package apollo.internals;
import apollo.enums.ConfigSourceType;
import apollo.enums.PropertyChangeType;
import apollo.model.ConfigChange;
import apollo.model.ConfigChangeEvent;
import apollo.util.ExceptionUtil;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.RateLimiter;
import framework.apollo.core.utils.ClassLoaderUtil;
import framework.apollo.tracer.Tracer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
/**
* @author Jason Song([email protected])
*/
public class DefaultConfig extends AbstractConfig implements RepositoryChangeListener {
private static final Logger logger = LoggerFactory.getLogger(DefaultConfig.class);
private final String m_namespace;
private final Properties m_resourceProperties;
private final AtomicReference m_configProperties;
private final ConfigRepository m_configRepository;
private final RateLimiter m_warnLogRateLimiter;
private volatile ConfigSourceType m_sourceType = ConfigSourceType.NONE;
/**
* Constructor.
*
* @param namespace the appNamespace of this config instance
* @param configRepository the config repository for this config instance
*/
public DefaultConfig(String namespace, ConfigRepository configRepository) {
m_namespace = namespace;
m_resourceProperties = loadFromResource(m_namespace);
m_configRepository = configRepository;
m_configProperties = new AtomicReference<>();
m_warnLogRateLimiter = RateLimiter.create(0.017); // 1 warning log output per minute
initialize();
}
private void initialize() {
try {
updateConfig(m_configRepository.getConfig(), m_configRepository.getSourceType());
} catch (Throwable ex) {
Tracer.logError(ex);
logger.warn("Init Apollo Local Config failed - appNamespace: {}, reason: {}.",
m_namespace, ExceptionUtil.getDetailMessage(ex));
} finally {
//register the change listener no matter config repository is working or not
//so that whenever config repository is recovered, config could get changed
m_configRepository.addChangeListener(this);
}
}
@Override
public String getProperty(String key, String defaultValue) {
// step 1: check system properties, i.e. -Dkey=value
String value = System.getProperty(key);
// step 2: check local cached properties file
if (value == null && m_configProperties.get() != null) {
value = m_configProperties.get().getProperty(key);
}
/**
* step 3: check env variable, i.e. PATH=...
* normally system environment variables are in UPPERCASE, however there might be exceptions.
* so the caller should provide the key in the right case
*/
if (value == null) {
value = System.getenv(key);
}
// step 4: check properties file from classpath
if (value == null && m_resourceProperties != null) {
value = (String) m_resourceProperties.get(key);
}
if (value == null && m_configProperties.get() == null && m_warnLogRateLimiter.tryAcquire()) {
logger.warn("Could not load config for appNamespace {} from Apollo, please check whether the configs are released in Apollo! Return default value now!", m_namespace);
}
return value == null ? defaultValue : value;
}
@Override
public Set getPropertyNames() {
Properties properties = m_configProperties.get();
if (properties == null) {
return Collections.emptySet();
}
return stringPropertyNames(properties);
}
@Override
public ConfigSourceType getSourceType() {
return m_sourceType;
}
private Set stringPropertyNames(Properties properties) {
//jdk9以下版本Properties#enumerateStringProperties方法存在性能问题,keys() + get(k) 重复迭代, jdk9之后改为entrySet遍历.
Map h = new HashMap<>();
for (Map.Entry