/* | |
* Licensed to the Apache Software Foundation (ASF) under one or more | |
* contributor license agreements. See the NOTICE file distributed with | |
* this work for additional information regarding copyright ownership. | |
* The ASF licenses this file to You 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 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; | |
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; | |
public class ConfigUtils { | |
private static final Logger logger = LoggerFactory.getLogger(ConfigUtils.class); | |
private static Pattern VARIABLE_PATTERN = Pattern.compile( | |
"\\$\\s*\\{?\\s*([\\._0-9a-zA-Z]+)\\s*\\}?"); | |
private static volatile Properties PROPERTIES; | |
private static int PID = -1; | |
private ConfigUtils() { | |
} | |
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); | |
} | |
/** | |
* Insert default extension into extension list. | |
* <p> | |
* Extension list support<ul> | |
* <li>Special value <code><strong>default</strong></code>, means the location for default extensions. | |
* <li>Special symbol<code><strong>-</strong></code>, means remove. <code>-foo1</code> will remove default extension 'foo'; <code>-default</code> will remove all default extensions. | |
* </ul> | |
* | |
* @param type Extension type | |
* @param cfg Extension name list | |
* @param def Default extension list | |
* @return result extension list | |
*/ | |
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>(); | |
// add initial values | |
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 is not included | |
if (!names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) { | |
// add default extension | |
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); | |
} | |
// merge - configuration | |
for (String name : new ArrayList<String>(names)) { | |
if (name.startsWith(Constants.REMOVE_VALUE_PREFIX)) { | |
names.remove(name); | |
names.remove(name.substring(1)); | |
} | |
} | |
return names; | |
} | |
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(); | |
} | |
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 setProperties(Properties properties) { | |
PROPERTIES = properties; | |
} | |
public static void addProperties(Properties properties) { | |
if (properties != null) { | |
getProperties().putAll(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); | |
} | |
/** | |
* System environment -> System properties | |
* | |
* @param key key | |
* @return value | |
*/ | |
public static String getSystemProperty(String key) { | |
String value = System.getenv(key); | |
if (value == null || value.length() == 0) { | |
value = System.getProperty(key); | |
} | |
return value; | |
} | |
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. | |
* @param optional is optional. if <code>false</code>, log warn when properties config file not found!s | |
* @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 optional) { | |
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 + "(ignore 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.isEmpty()) { | |
if (!optional) { | |
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 | |
} | |
// fall back to use method getResourceAsStream | |
try { | |
properties.load(ClassHelper.getClassLoader().getResourceAsStream(fileName)); | |
} catch (Throwable e) { | |
logger.warn("Failed to load " + fileName + " file from " + fileName + "(ignore 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 + "(ignore this file): " + e.getMessage(), e); | |
} | |
} | |
return properties; | |
} | |
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; | |
} | |
@SuppressWarnings("deprecation") | |
public static int getServerShutdownTimeout() { | |
int timeout = Constants.DEFAULT_SERVER_SHUTDOWN_TIMEOUT; | |
String value = ConfigUtils.getProperty(Constants.SHUTDOWN_WAIT_KEY); | |
if (value != null && value.length() > 0) { | |
try { | |
timeout = Integer.parseInt(value); | |
} catch (Exception e) { | |
// ignore | |
} | |
} else { | |
value = ConfigUtils.getProperty(Constants.SHUTDOWN_WAIT_SECONDS_KEY); | |
if (value != null && value.length() > 0) { | |
try { | |
timeout = Integer.parseInt(value) * 1000; | |
} catch (Exception e) { | |
// ignore | |
} | |
} | |
} | |
return timeout; | |
} | |
} |