Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

增加对properties文件的支持: ali-sentinel.properties #72

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -15,25 +15,26 @@
*/
package com.alibaba.csp.sentinel.config;

import java.io.File;
import java.io.FileInputStream;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

import com.alibaba.csp.sentinel.log.LogBase;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.util.AppNameUtil;
import com.alibaba.csp.sentinel.util.ClassHelper;
import com.alibaba.csp.sentinel.util.StringUtil;

import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
* The universal config of Courier. The config is retrieved from
* {@code ${user.home}/logs/csp/${appName}.properties} by default.
* {@code classpath: sentinel.properties} by default.
*
* @author leyou
*/
public class SentinelConfig {

private static final String DEFAULT_PROPERTIES_NAME = "sentinel";
private static final Map<String, String> props = new ConcurrentHashMap<String, String>();

public static final String CHARSET = "csp.sentinel.charset";
Expand All @@ -58,25 +59,29 @@ private static void initialize() {
}

private static void loadProps() {
// Resolve app name.
AppNameUtil.resolveAppName();
String propertiesName = DEFAULT_PROPERTIES_NAME;
try {
String appName = AppNameUtil.getAppName();
if (appName == null) {
appName = "";
if (!StringUtil.isEmpty(appName)) {
propertiesName = appName;
}
// We first retrieve the properties from the property file.
String fileName = LogBase.getLogBaseDir() + appName + ".properties";
File file = new File(fileName);
if (file.exists()) {
RecordLog.info("[SentinelConfig] Reading config from " + fileName);
FileInputStream fis = new FileInputStream(fileName);
Properties fileProps = new Properties();
fileProps.load(fis);
fis.close();

String fileName = propertiesName + ".properties";
Properties fileProps = loadProperties(fileName);
if (fileProps.size() > 0) {
RecordLog.info("read SentinelConfig from " + fileName);
for (Object key : fileProps.keySet()) {
SentinelConfig.setConfig((String)key, (String)fileProps.get(key));
try {
String systemValue = System.getProperty((String)key);
if (!StringUtil.isEmpty(systemValue)) {
SentinelConfig.setConfig((String)key, systemValue);
}
} catch (Exception e) {
RecordLog.info(e.getMessage(), e);
}
RecordLog.info(key + " value: " + SentinelConfig.getConfig((String)key));
}
}
} catch (Throwable ioe) {
Expand All @@ -93,6 +98,7 @@ private static void loadProps() {
RecordLog.info("[SentinelConfig] JVM parameter overrides {0}: {1} -> {2}", configKey, configValueOld, configValue);
}
}
AppNameUtil.setAppName(SentinelConfig.getConfig(AppNameUtil.APP_NAME));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to consider what if the appName from SentinelConfig is invalid (null or empty)?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

您的两个问题都是关于appName不合法, 在dubbo里dubbo.application.name是必填项, 不配置启动会异常.
如果我们这里需要一个默认值的话, 那或许我可以在resolveAppName()中改为解析不到project.name就设置成"sentinel".
综合你们的考虑看需要怎么改呢, 因为这里主要目的是为了加载配置文件.

}

/**
Expand Down Expand Up @@ -143,4 +149,57 @@ public static int totalMetricFileCount() {
return DEFAULT_TOTAL_METRIC_FILE_COUNT;
}
}

private static Properties loadProperties(String fileName) {
Properties properties = new Properties();
if (fileName.startsWith("/")) {
try {
FileInputStream input = new FileInputStream(fileName);
try {
properties.load(input);
} finally {
input.close();
}
} catch (Throwable e) {
RecordLog.warn("Failed to load " + fileName + " file from " + fileName + "(ignore this file): " + e.getMessage(), e);
}
return properties;
}

List<URL> list = new ArrayList<URL>();
try {
Enumeration<URL> urls = ClassHelper.getClassLoader().getResources(fileName);
list = new ArrayList<java.net.URL>();
while (urls.hasMoreElements()) {
list.add(urls.nextElement());
}
} catch (Throwable t) {
RecordLog.warn("Fail to load " + fileName + " file: " + t.getMessage(), t);
}

if (list.isEmpty()) {
RecordLog.warn("No " + fileName + " found on the class path.");
return properties;
}
for (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) {
RecordLog.warn("Fail to load " + fileName + " file from " + url + "(ignore this file): " + e.getMessage(), e);
}
}
return properties;
}
}
Expand Up @@ -60,34 +60,15 @@ private AppNameUtil() {
RecordLog.info("App name resolved: " + appName);
}

/**
* resolve app name from jvm options
*/
public static void resolveAppName() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to consider the case when project.name is absent both in system property and configuration file, so we have to provide a default parsed appName rather than empty.

String app = System.getProperty(APP_NAME);
// use -Dproject.name first
if (!isEmpty(app)) {
appName = app;
return;
}
appName = System.getProperty(APP_NAME);
}

// parse sun.java.command property
String command = System.getProperty(SUN_JAVA_COMMAND);
if (isEmpty(command)) {
return;
}
command = command.split("\\s")[0];
String separator = File.separator;
if (command.contains(separator)) {
String[] strs;
if ("\\".equals(separator)) {
strs = command.split("\\\\");
} else {
strs = command.split(separator);
}
command = strs[strs.length - 1];
}
if (command.endsWith(JAR_SUFFIX_LOWER) || command.endsWith(JAR_SUFFIX_UPPER)) {
command = command.substring(0, command.length() - 4);
}
appName = command;
public static void setAppName(String appName) {
AppNameUtil.appName = appName;
}

public static String getAppName() {
Expand Down
@@ -0,0 +1,181 @@
/*
* 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.csp.sentinel.util;

import java.lang.reflect.Array;
import java.util.*;

public class ClassHelper {
Ep0019 marked this conversation as resolved.
Show resolved Hide resolved

/**
* Suffix for array class names: "[]"
*/
public static final String ARRAY_SUFFIX = "[]";
/**
* Prefix for internal array class names: "[L"
*/
private static final String INTERNAL_ARRAY_PREFIX = "[L";
/**
* Map with primitive type name as key and corresponding primitive type as
* value, for example: "int" -> "int.class".
*/
private static final Map<String, Class<?>> primitiveTypeNameMap = new HashMap<String, Class<?>>(16);
/**
* Map with primitive wrapper type as key and corresponding primitive type
* as value, for example: Integer.class -> int.class.
*/
private static final Map<Class<?>, Class<?>> primitiveWrapperTypeMap = new HashMap<Class<?>, Class<?>>(8);

static {
primitiveWrapperTypeMap.put(Boolean.class, boolean.class);
primitiveWrapperTypeMap.put(Byte.class, byte.class);
primitiveWrapperTypeMap.put(Character.class, char.class);
primitiveWrapperTypeMap.put(Double.class, double.class);
primitiveWrapperTypeMap.put(Float.class, float.class);
primitiveWrapperTypeMap.put(Integer.class, int.class);
primitiveWrapperTypeMap.put(Long.class, long.class);
primitiveWrapperTypeMap.put(Short.class, short.class);

Set<Class<?>> primitiveTypeNames = new HashSet<Class<?>>(16);
primitiveTypeNames.addAll(primitiveWrapperTypeMap.values());
primitiveTypeNames.addAll(Arrays
.asList(new Class<?>[]{boolean[].class, byte[].class, char[].class, double[].class,
float[].class, int[].class, long[].class, short[].class}));
for (Iterator<Class<?>> it = primitiveTypeNames.iterator(); it.hasNext(); ) {
Class<?> primitiveClass = (Class<?>) it.next();
primitiveTypeNameMap.put(primitiveClass.getName(), primitiveClass);
}
}

/**
* get class loader
*
* @param cls
* @return class loader
*/
public static ClassLoader getClassLoader(Class<?> cls) {
ClassLoader cl = null;
try {
cl = Thread.currentThread().getContextClassLoader();
} catch (Throwable ex) {
// Cannot access thread context ClassLoader - falling back to system class loader...
}
if (cl == null) {
// No thread context class loader -> use class loader of this class.
cl = cls.getClassLoader();
}
return cl;
}

/**
* Return the default ClassLoader to use: typically the thread context
* ClassLoader, if available; the ClassLoader that loaded the ClassUtils
* class will be used as fallback.
* <p>
* Call this method if you intend to use the thread context ClassLoader in a
* scenario where you absolutely need a non-null ClassLoader reference: for
* example, for class path resource loading (but not necessarily for
* <code>Class.forName</code>, which accepts a <code>null</code> ClassLoader
* reference as well).
*
* @return the default ClassLoader (never <code>null</code>)
* @see java.lang.Thread#getContextClassLoader()
*/
public static ClassLoader getClassLoader() {
return getClassLoader(ClassHelper.class);
}

/**
* Same as <code>Class.forName()</code>, except that it works for primitive
* types.
*/
public static Class<?> forName(String name) throws ClassNotFoundException {
return forName(name, getClassLoader());
}

/**
* Replacement for <code>Class.forName()</code> that also returns Class
* instances for primitives (like "int") and array class names (like
* "String[]").
*
* @param name the name of the Class
* @param classLoader the class loader to use (may be <code>null</code>,
* which indicates the default class loader)
* @return Class instance for the supplied name
* @throws ClassNotFoundException if the class was not found
* @throws LinkageError if the class file could not be loaded
* @see Class#forName(String, boolean, ClassLoader)
*/
public static Class<?> forName(String name, ClassLoader classLoader)
throws ClassNotFoundException, LinkageError {

Class<?> clazz = resolvePrimitiveClassName(name);
if (clazz != null) {
return clazz;
}

// "java.lang.String[]" style arrays
if (name.endsWith(ARRAY_SUFFIX)) {
String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length());
Class<?> elementClass = forName(elementClassName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
}

// "[Ljava.lang.String;" style arrays
int internalArrayMarker = name.indexOf(INTERNAL_ARRAY_PREFIX);
if (internalArrayMarker != -1 && name.endsWith(";")) {
String elementClassName = null;
if (internalArrayMarker == 0) {
elementClassName = name
.substring(INTERNAL_ARRAY_PREFIX.length(), name.length() - 1);
} else if (name.startsWith("[")) {
elementClassName = name.substring(1);
}
Class<?> elementClass = forName(elementClassName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
}

ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = getClassLoader();
}
return classLoaderToUse.loadClass(name);
}

/**
* Resolve the given class name as primitive class, if appropriate,
* according to the JVM's naming rules for primitive classes.
* <p>
* Also supports the JVM's internal class names for primitive arrays. Does
* <i>not</i> support the "[]" suffix notation for primitive arrays; this is
* only supported by {@link #forName}.
*
* @param name the name of the potentially primitive class
* @return the primitive class, or <code>null</code> if the name does not
* denote a primitive class or primitive array class
*/
public static Class<?> resolvePrimitiveClassName(String name) {
Class<?> result = null;
// Most class names will be quite long, considering that they
// SHOULD sit in a package, so a length check is worthwhile.
if (name != null && name.length() <= 8) {
// Could be a primitive - likely.
result = (Class<?>) primitiveTypeNameMap.get(name);
}
return result;
}
}
@@ -0,0 +1,14 @@
package com.alibaba.csp.sentinel.config;

import org.junit.Test;

import static org.junit.Assert.assertEquals;

public class SentinelConfigTest {
@Test
public void test() {
SentinelConfig sentinelConfig = new SentinelConfig();
assertEquals(SentinelConfig.getAppName(), "sentinel-config-test");
assertEquals(SentinelConfig.getConfig("csp.sentinel.dashboard.server"), "localhost:8080");
}
}
2 changes: 2 additions & 0 deletions sentinel-core/src/test/resources/sentinel.properties
@@ -0,0 +1,2 @@
project.name=sentinel-config-test
csp.sentinel.dashboard.server=localhost:8080
2 changes: 2 additions & 0 deletions sentinel-dashboard/src/main/resources/sentinel.properties
@@ -0,0 +1,2 @@
project.name=sentinel-dashboard
csp.sentinel.dashboard.server=localhost:8080