blob: dc5035c9d3aea6af40f61d2350bbac5e31f462c6 [file] [log] [blame]
/*
* Copyright 1999-2011 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.dubbo.common.utils;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
/**
* @author ding.lid
* @author william.liangf
*/
public class ConfigUtils {
private static final Logger logger = LoggerFactory.getLogger(ConfigUtils.class);
public static boolean isNotEmpty(String value) {
return ! isEmpty(value);
}
public static boolean isEmpty(String value) {
return value == null || value.length() == 0
|| "false".equalsIgnoreCase(value)
|| "0".equalsIgnoreCase(value)
|| "null".equalsIgnoreCase(value)
|| "N/A".equalsIgnoreCase(value);
}
public static boolean isDefault(String value) {
return "true".equalsIgnoreCase(value)
|| "default".equalsIgnoreCase(value);
}
/**
* 扩展点列表中插入缺省扩展点。
* <p>
* 扩展点列表支持<ul>
* <li>特殊值<code><strong>default</strong></code>,表示缺省扩展点插入的位置
* <li>特殊符号<code><strong>-</strong></code>,表示剔除。 <code>-foo1</code>,剔除添加缺省扩展点foo1。<code>-default</code>,剔除添加所有缺省扩展点。
* </ul>
*
* @param type 扩展点类型
* @param cfg 扩展点名列表
* @param def 缺省的扩展点的列表
* @return 完成缺省的扩展点列表插入后的列表
*/
public static List<String> mergeValues(Class<?> type, String cfg, List<String> def) {
List<String> defaults = new ArrayList<String>();
if (def != null) {
for (String name : def) {
if (ExtensionLoader.getExtensionLoader(type).hasExtension(name)) {
defaults.add(name);
}
}
}
List<String> names = new ArrayList<String>();
// 加入初始值
String[] configs = (cfg == null || cfg.trim().length() == 0) ? new String[0] : Constants.COMMA_SPLIT_PATTERN.split(cfg);
for (String config : configs) {
if(config != null && config.trim().length() > 0) {
names.add(config);
}
}
// 不包含 -default
if (! names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
// 加入 插入缺省扩展点
int i = names.indexOf(Constants.DEFAULT_KEY);
if (i > 0) {
names.addAll(i, defaults);
} else {
names.addAll(0, defaults);
}
names.remove(Constants.DEFAULT_KEY);
}
else {
names.remove(Constants.DEFAULT_KEY);
}
// 合并-的配置项
for (String name : new ArrayList<String>(names)) {
if (name.startsWith(Constants.REMOVE_VALUE_PREFIX)) {
names.remove(name);
names.remove(name.substring(1));
}
}
return names;
}
private static Pattern VARIABLE_PATTERN = Pattern.compile(
"\\$\\s*\\{?\\s*([\\._0-9a-zA-Z]+)\\s*\\}?");
public static String replaceProperty(String expression, Map<String, String> params) {
if (expression == null || expression.length() == 0 || expression.indexOf('$') < 0) {
return expression;
}
Matcher matcher = VARIABLE_PATTERN.matcher(expression);
StringBuffer sb = new StringBuffer();
while (matcher.find()) { // 逐个匹配
String key = matcher.group(1);
String value = System.getProperty(key);
if (value == null && params != null) {
value = params.get(key);
}
if (value == null) {
value = "";
}
matcher.appendReplacement(sb, Matcher.quoteReplacement(value));
}
matcher.appendTail(sb);
return sb.toString();
}
private static volatile Properties PROPERTIES;
public static Properties getProperties() {
if (PROPERTIES == null) {
synchronized (ConfigUtils.class) {
if (PROPERTIES == null) {
String path = System.getProperty(Constants.DUBBO_PROPERTIES_KEY);
if (path == null || path.length() == 0) {
path = System.getenv(Constants.DUBBO_PROPERTIES_KEY);
if (path == null || path.length() == 0) {
path = Constants.DEFAULT_DUBBO_PROPERTIES;
}
}
PROPERTIES = ConfigUtils.loadProperties(path, false, true);
}
}
}
return PROPERTIES;
}
public static void addProperties(Properties properties) {
if (properties != null) {
getProperties().putAll(properties);
}
}
public static void setProperties(Properties properties) {
if (properties != null) {
PROPERTIES = properties;
}
}
public static String getProperty(String key) {
return getProperty(key, null);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public static String getProperty(String key, String defaultValue) {
String value = System.getProperty(key);
if (value != null && value.length() > 0) {
return value;
}
Properties properties = getProperties();
return replaceProperty(properties.getProperty(key, defaultValue), (Map)properties);
}
public static Properties loadProperties(String fileName) {
return loadProperties(fileName, false, false);
}
public static Properties loadProperties(String fileName, boolean allowMultiFile) {
return loadProperties(fileName, allowMultiFile, false);
}
/**
* Load properties file to {@link Properties} from class path.
*
* @param fileName properties file name. for example: <code>dubbo.properties</code>, <code>METE-INF/conf/foo.properties</code>
* @param allowMultiFile if <code>false</code>, throw {@link IllegalStateException} when found multi file on the class path.
* @return loaded {@link Properties} content. <ul>
* <li>return empty Properties if no file found.
* <li>merge multi properties file if found multi file
* </ul>
* @throws IllegalStateException not allow multi-file, but multi-file exsit on class path.
*/
public static Properties loadProperties(String fileName, boolean allowMultiFile, boolean allowEmptyFile) {
Properties properties = new Properties();
if (fileName.startsWith("/")) {
try {
FileInputStream input = new FileInputStream(fileName);
try {
properties.load(input);
} finally {
input.close();
}
} catch (Throwable e) {
logger.warn("Failed to load " + fileName + " file from " + fileName + "(ingore this file): " + e.getMessage(), e);
}
return properties;
}
List<java.net.URL> list = new ArrayList<java.net.URL>();
try {
Enumeration<java.net.URL> urls = ClassHelper.getClassLoader().getResources(fileName);
list = new ArrayList<java.net.URL>();
while (urls.hasMoreElements()) {
list.add(urls.nextElement());
}
} catch (Throwable t) {
logger.warn("Fail to load " + fileName + " file: " + t.getMessage(), t);
}
if(list.size() == 0) {
if (allowEmptyFile) {
logger.warn("No " + fileName + " found on the class path.");
}
return properties;
}
if(! allowMultiFile) {
if (list.size() > 1) {
String errMsg = String.format("only 1 %s file is expected, but %d dubbo.properties files found on class path: %s",
fileName, list.size(), list.toString());
logger.warn(errMsg);
// throw new IllegalStateException(errMsg); // see http://code.alibabatech.com/jira/browse/DUBBO-133
}
try {
properties.load(ClassHelper.getClassLoader().getResourceAsStream(fileName));
} catch (Throwable e) {
logger.warn("Failed to load " + fileName + " file from " + fileName + "(ingore this file): " + e.getMessage(), e);
}
return properties;
}
logger.info("load " + fileName + " properties file from " + list);
for(java.net.URL url : list) {
try {
Properties p = new Properties();
InputStream input = url.openStream();
if (input != null) {
try {
p.load(input);
properties.putAll(p);
} finally {
try {
input.close();
} catch (Throwable t) {}
}
}
} catch (Throwable e) {
logger.warn("Fail to load " + fileName + " file from " + url + "(ingore this file): " + e.getMessage(), e);
}
}
return properties;
}
private ConfigUtils() {}
}