Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.github.javaclub.toolbox.conf.CompositeAppConfigProperties Maven / Gradle / Ivy
/*
* @(#)CompositeAppConfigProperties.java 2021-8-31
*
* Copyright (c) 2021. All Rights Reserved.
*
*/
package com.github.javaclub.toolbox.conf;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.javaclub.toolbox.ToolBox.Collections;
import com.github.javaclub.toolbox.ToolBox.Environments;
import com.github.javaclub.toolbox.ToolBox.Files;
import com.github.javaclub.toolbox.ToolBox.IO;
import com.github.javaclub.toolbox.ToolBox.Maps;
import com.github.javaclub.toolbox.ToolBox.Numbers;
import com.github.javaclub.toolbox.ToolBox.Objects;
import com.github.javaclub.toolbox.ToolBox.Strings;
import com.github.javaclub.toolbox.conf.yml.YamlParseException;
import com.github.javaclub.toolbox.conf.yml.YmlConfigFile;
import com.github.javaclub.toolbox.file.FileChangeListener;
import com.github.javaclub.toolbox.file.FileMonitor;
import com.github.javaclub.toolbox.thread.ExecutorServiceInstance;
import com.google.common.collect.Lists;
/**
* CompositeAppConfigProperties
*
* @author Gerald Chen
* @version $Id: CompositeAppConfigProperties.java 2021-8-31 15:38:17 Exp $
*/
public class CompositeAppConfigProperties {
private static final Logger log = LoggerFactory.getLogger(CompositeAppConfigProperties.class);
private volatile Properties cachedProperties;
private final FileChangeListener changeListener = new ConfChangedListener(this);
private static class SingletonHolder {
private static final CompositeAppConfigProperties INSTANCE = new CompositeAppConfigProperties();
}
private CompositeAppConfigProperties() {
String appJarPath = ConfUtils.getAppJarPath();
log.info("Current Application Jar path: {}", appJarPath);
this.cachedProperties = new Properties();
if (Strings.isBlank(appJarPath)) {
Set pathUrls = ConfUtils.getClasspathArray();
log.info("Loading config from classpath");
parseClasspathConfig(pathUrls);
loadAndSetExtraConfigs();
loadExtraConfigsDelayed();
return;
}
FileMonitor.getInstance().addFileChangeListener(changeListener, appJarPath, 6000L);
log.info("Current Application Jar is Added to FileMonitor");
loadFromCurrentJar(appJarPath); // 1. 先从当前应用jar包中获取配置
overrideByOuterConfig(appJarPath); // 2. 当前应用jar包同级目录及子目录有配置项,则加载并覆盖jar包内的配置
loadAndSetExtraConfigs(); // 3. 加载其他配置并监听变更
loadExtraConfigsDelayed(); // 4. 延迟加载一些额外的配置
}
public static final CompositeAppConfigProperties getInstance() {
return SingletonHolder.INSTANCE;
}
public Properties getAppConfigProperties() {
return cachedProperties;
}
public Properties selectConfigProperties(String ... keys) {
Properties properties = new Properties();
if(null == keys) {
return properties;
}
for (String key : keys) {
if ( hasKey(key) ) {
properties.put(key, getValue(key));
}
}
return properties;
}
public Properties selectPrefixKeyProperties(String ... prefixKeys) {
Properties properties = new Properties();
if(null == prefixKeys) {
return properties;
}
for (String prefix : prefixKeys) {
Properties group = this.selectPrefixKeyGroup(prefix);
if ( null != group) {
properties.putAll(group);
}
}
return properties;
}
public Properties selectPrefixKeyGroup(String prefix) {
Properties properties = new Properties();
if(null == cachedProperties || Strings.isBlank(prefix)) {
return properties;
}
for (Object key : cachedProperties.keySet()) {
String keyVal = Objects.toString(key, "");
if (Strings.isBlank(keyVal) || !keyVal.startsWith(prefix)) {
continue;
}
properties.put(keyVal, getValue(keyVal));
}
return properties;
}
public boolean hasKey(String configKey) {
if(null == cachedProperties) {
return false;
}
return cachedProperties.containsKey(configKey);
}
public String getEnv() {
return getValue("env", "application.env", "spring.application.env", "system.configs.env",
"spring.profiles.active", "app.env", "project.env", "configserver.env");
}
public String getAppName() {
return getValue("spring.application.name", "dubbo.application.name", "application.name",
"app.name", "project.name", "system.configs.appName");
}
public String getValue(String key) {
return cachedProperties.getProperty(key);
}
public String getValueOrDefault(String key, String defaultValueIfNull) {
String value = cachedProperties.getProperty(key);
return value == null ? defaultValueIfNull : value;
}
public String getValueOrDefault(String[] keys, String defaultValueIfNull) {
String value = getValue(keys);
return value == null ? defaultValueIfNull : value;
}
public String getValue(String key, String anotherKey) {
String val = cachedProperties.getProperty(key);
return Objects.toString(val, cachedProperties.getProperty(anotherKey));
}
public String getValue(String... keys) {
if (Objects.isEmpty(keys)) {
return null;
}
for (String key : keys) {
String val = cachedProperties.getProperty(key);
if (null != val) {
return Strings.cleanQuotes(val);
}
}
return null;
}
public String getValue(String[] keys, String defaultValueIfNull) {
return getValueOrDefault(keys, defaultValueIfNull);
}
public int intValue(String[] keys, int defaultValueIfNull) {
String val = getValue(keys);
if (null == val) {
return defaultValueIfNull;
}
Integer integer = Numbers.parseInt(val);
return null == integer ? defaultValueIfNull : integer.intValue();
}
public long longValue(String[] keys, long defaultValueIfNull) {
String val = getValue(keys);
if (null == val) {
return defaultValueIfNull;
}
Long longVal = Numbers.parseLong(val);
return null == longVal ? defaultValueIfNull : longVal.longValue();
}
public boolean boolValue(String[] keys, boolean defaultValueIfNull) {
for (String key : keys) {
String val = cachedProperties.getProperty(key);
if (null != val) {
Boolean flag = Objects.parseBoolean(val);
if (null != flag) {
return flag.booleanValue();
}
}
}
return defaultValueIfNull;
}
public byte byteValue(String key) {
try {
return Byte.parseByte(getValue(key));
} catch (NumberFormatException e) {
}
return 0;
}
public byte byteValue(String key, byte defaultVal) {
try {
return Byte.parseByte(getValue(key));
} catch (NumberFormatException e) {
}
return defaultVal;
}
public short shortValue(String key) {
try {
return Short.parseShort(getValue(key));
} catch (NumberFormatException e) {
}
return 0;
}
public short shortValue(String key, short defaultVal) {
try {
return Short.parseShort(getValue(key));
} catch (NumberFormatException e) {
}
return defaultVal;
}
public int intValue(String key) {
try {
return Integer.parseInt(getValue(key));
} catch (NumberFormatException e) {
}
return 0;
}
public int intValue(String key, int defaultVal) {
try {
return Integer.parseInt(getValue(key));
} catch (NumberFormatException e) {
}
return defaultVal;
}
public long longValue(String key) {
try {
return Long.parseLong(getValue(key));
} catch (NumberFormatException e) {
}
return 0L;
}
public long longValue(String key, long defaultVal) {
try {
return Long.parseLong(getValue(key));
} catch (NumberFormatException e) {
}
return defaultVal;
}
public float floatValue(String key) {
try {
return Float.parseFloat(getValue(key));
} catch (NumberFormatException e) {
}
return 0.0f;
}
public float floatValue(String key, float defaultVal) {
try {
return Float.parseFloat(getValue(key));
} catch (NumberFormatException e) {
}
return defaultVal;
}
public double doubleValue(String key) {
try {
return Double.parseDouble(getValue(key));
} catch (NumberFormatException e) {
}
return 0D;
}
public double doubleValue(String key, double defaultVal) {
try {
return Double.parseDouble(getValue(key));
} catch (NumberFormatException e) {
}
return defaultVal;
}
public boolean boolValue(String key, boolean defaultVal) {
String val = getValue(key);
if (null == val) {
return defaultVal;
}
Boolean parseVal = Objects.parseBoolean(val);
boolean boolVal = (null == parseVal ? defaultVal : parseVal.booleanValue());
return boolVal;
}
public boolean boolValue(String key) {
String[] focusArray = new String[] {
"true", "yes", "enabled", "opened",
"ok", "on", "enable", "open",
"T", "Y", "O", "1"
};
String val = null == getValue(key) ? null : getValue(key).trim();
return Strings.equalsAnyIgnoreCase(val, focusArray);
}
public String[] arrayValue(String key) {
String configValue = getValue(key);
if (null == configValue) {
return new String[0];
}
return Strings.splitArrayString(configValue);
}
public String[] arrayValue(String... keys) {
if (null == keys || 0 >= keys.length) {
return new String[0];
}
for (String key : keys) {
if (Strings.isBlank(key)) {
continue;
}
String configValue = getValue(key);
if (Strings.isBlank(configValue)) {
continue;
}
String[] array = Strings.splitArrayString(configValue);
if (null != array && 0 < array.length) {
return array;
}
}
return new String[0];
}
public long bytesAmount(String key, long defaultVal) {
String configValue = getValue(key);
if (Strings.isBlank(configValue)) {
return defaultVal;
}
String val = configValue.trim();
try {
return Strings.parseDataSize(val);
} catch (Exception e) {
}
return defaultVal;
}
public synchronized void destroy() {
if (null == cachedProperties) {
return;
}
cachedProperties.clear();
log.warn("AppConfigProperties was destroying!");
}
public synchronized boolean merge(Map map) {
try {
if (null == cachedProperties) {
cachedProperties = new Properties();
}
if (Maps.isEmpty(map)) {
return true;
}
cachedProperties.putAll(map);
this.initAlarmsConfig(false);
} catch (Exception e) {
return false;
}
return true;
}
public synchronized boolean remove(String key) {
try {
if (null == cachedProperties) {
cachedProperties = new Properties();
return true;
}
if (!cachedProperties.containsKey(key)) {
return true;
}
cachedProperties.remove(key);
} catch (Exception e) {
return false;
}
return true;
}
protected synchronized boolean merge(Properties properties) {
try {
if (null == cachedProperties) {
cachedProperties = new Properties();
}
if(null == properties || properties.isEmpty()) {
return true;
}
cachedProperties.putAll(properties);
this.initAlarmsConfig(false);
} catch (Exception e) {
return false;
}
return true;
}
protected synchronized boolean change(Properties properties, Properties removeProperties) {
try {
if (null == cachedProperties) {
cachedProperties = new Properties();
}
if(null != properties && !properties.isEmpty()) {
cachedProperties.putAll(properties);
}
if (null != removeProperties && !removeProperties.isEmpty()) {
for (Object key : removeProperties.keySet()) {
String keyVal = Objects.toString(key, "");
if (Strings.isBlank(keyVal)) {
cachedProperties.remove(keyVal);
}
}
}
this.initAlarmsConfig(false);
} catch (Exception e) {
return false;
}
return true;
}
protected synchronized boolean reset(Properties properties) {
try {
if (null == cachedProperties) {
cachedProperties = new Properties();
} else {
cachedProperties.clear();
}
if(null != properties && !properties.isEmpty()) {
cachedProperties.putAll(properties);
}
this.loadAndSetExtraConfigs();
} catch (Exception e) {
return false;
}
return true;
}
protected void loadExtraConfigsDelayed() {
ExecutorServiceInstance.get().scheduleAtFixedRate(() -> {
Properties properties = ExtraConfigsDelayLoader.getInstance().loadConfigs();
if (null != properties && !properties.isEmpty()) {
this.merge(properties);
}
}, 5L, 90L, TimeUnit.SECONDS);
}
protected void loadAndSetExtraConfigs() {
// Apollo配置(如果有的话)
if (Environments.support(ConfUtils.APOLLO_CONFIG_SERVICE_CLASS)) {
String configNamespaces = getValue(ConfUtils.APP_APOLLO_BOOTSTRAP_NAMESPACES);
String[] namespaces = Strings.splitArrayString(configNamespaces);
log.info("Apollo bootstrap namespaces = {}", configNamespaces);
Properties properties = ApolloConfigLoader.getInstance().loadApolloConfigs(namespaces);
if (null != properties && !properties.isEmpty()) {
this.merge(properties);
}
}
// Configcenter配置 (延迟加载)
if (Environments.support(ConfUtils.CONFIGCENTER_SERVICE_CLASS)) {
ExecutorServiceInstance.get().schedule(() -> {
Properties properties = ConfigcenterLoader.getInstance().loadConfigs();
if (null != properties && !properties.isEmpty()) {
this.merge(properties);
log.info("Configcenter loaded configKeyCount={}", properties.size());
}
}, Numbers.random(10L, 200L), TimeUnit.MILLISECONDS);
}
// Nacos 配置
if (Environments.support(ConfUtils.NACOS_SERVICE_CLASS)) {
log.info("support => {}", ConfUtils.NACOS_SERVICE_CLASS);
ExecutorServiceInstance.get().schedule(() -> {
Properties properties = NacosConfigLoader.getInstance().loadConfigs();
if (null != properties && !properties.isEmpty()) {
this.merge(properties);
log.info("NacosConfig loaded configKeyCount={}", properties.size());
}
}, Numbers.random(10L, 200L), TimeUnit.MILLISECONDS);
}
// 设置一些额外的配置
if (Environments.support(ConfUtils.FIRE_MONITORS_CLASS)) {
initAlarmsConfig(true);
}
}
private void initAlarmsConfig(boolean stdoutLog) {
if (hasKey(ConfUtils.CONFIG_KEY_MASTER_AKS) || hasKey(ConfUtils.CONFIG_KEY_SLAVE_AKS)) {
String masterAks = getValue(ConfUtils.CONFIG_KEY_MASTER_AKS);
String slaveAks = getValue(ConfUtils.CONFIG_KEY_SLAVE_AKS);
String[] aks1 = Strings.splitAndTrim(masterAks, ",");
String[] aks2 = Strings.splitAndTrim(slaveAks, ",");
List list = new ArrayList();
if(null != aks1) {
list.addAll(Arrays.asList(aks1));
}
if(null != aks2) {
list.addAll(Arrays.asList(aks2));
}
String[] tokens = list.toArray(new String[0]);
boolean setDingTalkAks = Objects.invoke(ConfUtils.FIRE_MONITORS_CLASS, "setDingTalkAks",
new Class>[] { String[].class }, new Object[] { tokens });
// configcenter => DingTalkUtils
boolean setWebAks = Objects.invoke(ConfUtils.CONFIGCNTER_DINGTALK_CLASS, "setWebAks",
new Class>[] { String[].class }, new Object[] { tokens });
if (stdoutLog) {
log.info("AlarmsConfig init setDingTalkAks = {}, setWebAks = {}", setDingTalkAks, setWebAks);
}
}
if (hasKey(ConfUtils.MC_SMS_API_URL)) {
boolean setSmsInvokeURL = Objects.invoke(ConfUtils.FIRE_MONITORS_CLASS, "setSmsInvokeURL",
new Class>[] { String.class }, new Object[] { getValue(ConfUtils.MC_SMS_API_URL) });
if (stdoutLog) {
log.info("AlarmsConfig init setSmsInvokeURL = {}", setSmsInvokeURL);
}
}
}
private void parseClasspathConfig(Collection urls) {
final FileFilter configFileFilter = new FileFilter() {
public boolean accept(File file) {
String filename = file.getAbsolutePath().toLowerCase();
boolean matched = Strings.endsWith(filename, new String[] { ".properties", ".yml", ".yaml" });
return file.isFile() && matched;
}
};
List filelist = Collections.newArrayList();
for (String url : urls) {
if (url.endsWith(".jar")) {
continue;
}
File file = new File(url);
if (file.exists() && file.isDirectory()) {
File[] configFiles = Files.listTree(file, configFileFilter);
if (null != configFiles) {
filelist.addAll(Arrays.asList(configFiles));
}
}
}
loadFromDirectory(filelist);
}
private void overrideByOuterConfig(String appJarPath) {
File appJarFile = new File(appJarPath);
File appJarDir = appJarFile.getParentFile();
final FileFilter configFileFilter = new FileFilter() {
public boolean accept(File file) {
String filename = file.getAbsolutePath().toLowerCase();
boolean matched = Strings.endsWith(filename, new String[] { ".properties", ".yml", ".yaml" });
return file.isFile() && matched;
}
};
// 防止遍历根目录,导致内存溢出
File[] configFiles = (null == appJarDir.getParentFile()) ?
appJarDir.listFiles(configFileFilter) : Files.listTree(appJarDir, configFileFilter);
loadFromDirectory(null == configFiles ? Lists.newArrayList() : Arrays.asList(configFiles));
}
private void loadFromDirectory(Collection configFiles) {
if (null == configFiles || 0 >= configFiles.size()) {
return;
}
try {
for (File file : configFiles) {
String filename = file.getAbsolutePath().toLowerCase();
if (Strings.endsWith(filename, new String[] {
"target/maven-archiver/pom.properties", "target\\maven-archiver\\pom.properties"
})) {
continue;
}
if (Strings.containsAny(filename, new String[] {
"/meta-inf/maven/", "\\meta-inf\\maven", "/META-INF/maven/", "\\META-INF\\maven"
}) && Strings.endsWithIgnoreCase(filename, "pom.properties")) {
continue;
}
if (Strings.containsAny(filename, new String[] {
"/eclipse/configuration/org.eclipse.osgi/", "\\eclipse\\configuration\\org.eclipse.osgi"
})) {
continue;
}
InputStream input = new FileInputStream(file);
if (Strings.endsWithIgnoreCase(filename, ".properties")) {
this.processPropertiesFile(input);
} else if (Strings.endsWith(filename, new String[] { ".yml", ".yaml" })) {
this.processYmlFile(input);
}
log.info("Parse config file: {}", filename);
FileMonitor.getInstance().addFileChangeListener(changeListener, filename, 6000L);
}
} catch (Exception e) {
log.error("overrideByOuterConfig error", e);
throw new YamlParseException("load Properties from Jar outerConfigFile failed ", e);
}
}
private void loadFromCurrentJar(String appJarPath) {
JarFile jarFile = null;
try {
jarFile = new JarFile(appJarPath);
Enumeration enums = jarFile.entries();
while (enums.hasMoreElements()) {
JarEntry jarEntry = (JarEntry) enums.nextElement();
String name = jarEntry.getName().toLowerCase();
if (jarEntry.isDirectory() || name.indexOf("classes/") <= -1) {
continue;
}
if (!Strings.endsWith(name, new String[] { ".properties", ".yml", ".yaml" })) {
continue;
}
InputStream input = jarFile.getInputStream(jarEntry);
if (Strings.endsWithIgnoreCase(name, ".properties")) {
this.processPropertiesFile(input);
} else if (Strings.endsWith(name, new String[] { ".yml", ".yaml" })) {
this.processYmlFile(input);
}
log.info("Parse JarEntry config: {}", jarEntry.getName());
}
} catch (IOException e) {
log.error("loadFromCurrentJar error", e);
throw new YamlParseException("load Properties from current Jar failed ", e);
} finally {
if(null != jarFile) {
try {
jarFile.close();
} catch (IOException e) {
}
}
}
}
private void processYmlFile(InputStream input) throws IOException {
try {
String content = Files.readAsString(input, "UTF-8");
YmlConfigFile ymlConfigFile = new YmlConfigFile(content);
this.merge(ymlConfigFile.asProperties());
} finally {
IO.closeQuietly(input);
}
}
private void processPropertiesFile(InputStream input) throws IOException {
try {
Properties p = new Properties();
p.load(input);
this.merge(p);
} finally {
IO.closeQuietly(input);
}
}
}