/* | |
* 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.lang.management.ManagementFactory; | |
import java.lang.management.RuntimeMXBean; | |
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 static int PID = -1; | |
public static int getPid() { | |
if (PID < 0) { | |
try { | |
RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean(); | |
String name = runtime.getName(); // format: "pid@hostname" | |
PID = Integer.parseInt(name.substring(0, name.indexOf('@'))); | |
} catch (Throwable e) { | |
PID = 0; | |
} | |
} | |
return PID; | |
} | |
private ConfigUtils() {} | |
} |