完善NvMaps工具类、测试代码

This commit is contained in:
aoshiguchen
2023-03-06 17:45:48 +08:00
parent 4efd86751a
commit 294e5efdae
10 changed files with 461 additions and 115 deletions
@@ -22,57 +22,66 @@ import java.util.*;
* @author: aoshiguchen
* @date: 2023/3/1
*/
public class Cfg<K,V> implements Map<K,V> , Serializable, Cloneable {
public class NvMap<K,V> implements Map<K,V> , Serializable, Cloneable {
private HashMap<K,K> aliasMap;
private HashMap<K,K> reverseAliasMap;
private LinkedHashMap<K, V> _m;
private HashMap<K, K> _k;
private Locale locale;
private String SEPARATOR = "_|-";
private Cfg<K,V> parent;
private NvMap<K,V> parent;
private Cfg() {
private NvMap() {
this(null, 16, null);
}
private Cfg(Cfg<K,V> parent) {
private NvMap(NvMap<K,V> parent) {
this(parent, 16, null);
}
public static <K,V> Cfg<K,V> of() {
return new Cfg<>();
public static <K,V> NvMap<K,V> of() {
return new NvMap<>();
}
public static <K,V> Cfg<K,V> of(Cfg<K,V> parent) {
return new Cfg(parent);
public static <K,V> NvMap<K,V> of(NvMap<K,V> parent) {
NvMap<K,V> nvMap = new NvMap(parent);
nvMap.aliasMap.putAll(parent.aliasMap);
nvMap.reverseAliasMap.putAll(parent.reverseAliasMap);
return nvMap;
}
public Cfg<K,V> set(K k, V v) {
this.put(k, v);
public NvMap<K,V> set(K k, V v) {
if (this.aliasMap.containsKey(convertKey(k))) {
this.put(this.aliasMap.get(convertKey(k)), v);
} else {
this.put(k, v);
}
return this;
}
public Cfg<K,V> set(K k, K alias, V v) {
public NvMap<K,V> set(K k, K alias, V v) {
this.set(k, v);
this.setAlias(k, alias);
return this;
}
public Cfg<K,V> setAlias(K k, K alias) {
public NvMap<K,V> setAlias(K k, K alias) {
this.aliasMap.put(convertKey(alias), convertKey(k));
this.reverseAliasMap.put(convertKey(k), convertKey(alias));
return this;
}
public Cfg(Cfg<K,V> parent, int initialCapacity, Locale locale) {
public NvMap(NvMap<K,V> parent, int initialCapacity, Locale locale) {
this.parent = parent;
this._m = new LinkedHashMap<K, V>(initialCapacity) {
@Override
public boolean containsKey(Object key) {
return Cfg.this.containsKey(key);
return NvMap.this.containsKey(key);
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
boolean doRemove = Cfg.this.removeEldestEntry(eldest);
boolean doRemove = NvMap.this.removeEldestEntry(eldest);
if (doRemove) {
_k.remove(convertKey(eldest.getKey()));
}
@@ -81,6 +90,7 @@ public class Cfg<K,V> implements Map<K,V> , Serializable, Cloneable {
};
this._k = new HashMap<>(initialCapacity);
this.aliasMap = new HashMap<>(initialCapacity);
this.reverseAliasMap = new HashMap<>(initialCapacity);
this.locale = (locale != null ? locale : Locale.getDefault());
}
@@ -226,14 +236,15 @@ public class Cfg<K,V> implements Map<K,V> , Serializable, Cloneable {
}
@Override
protected Cfg<K,V> clone() {
Cfg<K,V> res = new Cfg<>();
protected NvMap<K,V> clone() {
NvMap<K,V> res = new NvMap<>();
if (null != parent) {
res.parent = parent.clone();
}
res._m = (LinkedHashMap<K, V>) _m.clone();
res._k = (HashMap<K, K>) _k.clone();
res.aliasMap = (HashMap<K, K>) aliasMap.clone();
res.reverseAliasMap = (HashMap<K, K>) reverseAliasMap.clone();
res.locale = locale;
return res;
}
@@ -241,11 +252,15 @@ public class Cfg<K,V> implements Map<K,V> , Serializable, Cloneable {
// ====== 为了方便取值操作get方法保持Map接口原有语义不支持栈式取值 =======
@Override
public V get(Object key) {
key = convertKey(key);
if (this.aliasMap.containsKey(key)) {
key = this.aliasMap.get(key);
V res = null;
if (this.aliasMap.containsKey(convertKey(key))) {
Object aliasKey = this.aliasMap.get(convertKey(key));
res = this._m.get(this._k.get(convertKey(aliasKey)));
}
return this._m.get(this._k.get(key));
if (null == res) {
res = this._m.get(this._k.get(convertKey(key)));
}
return res;
}
public V get(Object key, V defaultValue) {
@@ -351,12 +366,12 @@ public class Cfg<K,V> implements Map<K,V> , Serializable, Cloneable {
// ====== 为了方便取值操作take开头的方法全部基于栈式取值 =======
public V take(K k) {
V res = null;
K aliasK = this.stackGetAlias(k);
K aliasK = this.getAlias(k);
if (null != aliasK) {
res = this.stackGet(aliasK);
res = stackGet(aliasK);
}
if (null == res) {
res = this.stackGet(k);
res = stackGet(k);
}
return res;
}
@@ -0,0 +1,204 @@
package fun.asgc.neutrino.core.base;
import com.alibaba.fastjson.JSONObject;
import fun.asgc.neutrino.core.constant.FileTypeEnum;
import fun.asgc.neutrino.core.constant.MetaDataConstant;
import fun.asgc.neutrino.core.util.*;
import org.yaml.snakeyaml.Yaml;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* 配置管理器
*
* <p>
* 支持以下五级配置:
* 1、别名、默认值
* 2、内部配置文件
* 3、环境变量
* 4、外部配置文件
* 5、启动参数
* </p>
*
* <p>
* 支持以下3种配置文件格式:
* 1、yml
* 2、properties
* 3、json
* </p>
*
* <p>
* 支持指定以下配置:
* 1、内部配置文件路径、格式 (可根据文件后缀自动识别格式)
* 2、环境变量前缀 (若指定了前缀,则只加载包含该前缀的环境变量)
* 3、外部配置文件路径、格式(可根据文件后缀自动识别格式)
* 4、启动参数前缀(若指定了前缀,则只加载包含该前缀的启动参数)
* </p>
* @author: aoshiguchen
* @date: 2023/3/5
*/
public final class NvMaps {
private NvMap nvMap;
private NvMaps() {
this.nvMap = NvMap.of();
}
public static NvMaps of() {
return new NvMaps();
}
public NvMaps stageDone() {
this.nvMap = NvMap.of(this.nvMap);
return this;
}
public NvMap getNvMap() {
return this.nvMap;
}
public NvMaps setKv(Object k, Object v) {
this.nvMap.set(k, v);
return this;
}
public NvMaps setKv(Object k, Object alias, Object v) {
this.nvMap.set(k, alias, v);
return this;
}
public NvMaps setAlias(Object k, Object alias) {
this.nvMap.setAlias(k, alias);
return this;
}
public NvMaps loadFile(String path) throws IOException {
if (StringUtil.isEmpty(path)) {
return this;
}
String suffix = FileUtil.getFileSuffix(path);
if (StringUtil.isEmpty(suffix)) {
return this;
}
suffix = suffix.toLowerCase();
if (FileTypeEnum.YML.getSuffixSet().contains(suffix)) {
return loadFile(FileTypeEnum.YML, path);
} else if (FileTypeEnum.PROPERTIES.getSuffixSet().contains(suffix)) {
return loadFile(FileTypeEnum.PROPERTIES, path);
} else if (FileTypeEnum.JSON.getSuffixSet().contains(suffix)) {
return loadFile(FileTypeEnum.JSON, path);
}
return this;
}
public NvMaps loadYmlFile(String path) throws IOException {
return loadFile(FileTypeEnum.YML, path);
}
public NvMaps loadPropertiesFile(String path) throws IOException {
return loadFile(FileTypeEnum.PROPERTIES, path);
}
public NvMaps loadJsonFile(String path) throws IOException {
return loadFile(FileTypeEnum.JSON, path);
}
public NvMaps loadFile(FileTypeEnum fileType, String path) throws IOException {
try (InputStream in = FileUtil.getInputStream(path)){
return loadFile(fileType, in);
}
}
public NvMaps loadFile(FileTypeEnum fileType, InputStream in) throws IOException {
if (null == fileType || null == in) {
return this;
}
if (fileType == FileTypeEnum.YML) {
Map<String, Object> map = new Yaml().load(in);
load(map);
} else if (fileType == FileTypeEnum.PROPERTIES) {
Properties properties = new Properties();
properties.load(in);
} else if (fileType == FileTypeEnum.JSON) {
String content = FileUtil.readContentAsString(in);
JSONObject jsonObject = JSONObject.parseObject(content);
load(jsonObject);
}
return null;
}
public NvMaps load(Properties properties) {
if (null == properties) {
return this;
}
for (String k : properties.stringPropertyNames()) {
this.setKv(k, properties.getProperty(k));
}
return this;
}
public NvMaps load(Map<String, Object> config) {
if (CollectionUtil.isEmpty(config)) {
return this;
}
for (String key : config.keySet()) {
Object value = config.get(key);
if (null != value && Map.class.isAssignableFrom(value.getClass())) {
load(key, (Map)config.get(key));
} else {
this.setKv(key, value);
}
}
return this;
}
private void load(String prefix, Map<Object, Object> config) {
for (Object key : config.keySet()) {
String k = TypeUtil.conversion(key, String.class);
if (null == k) {
continue;
}
Object value = config.get(key);
if (null != value && Map.class.isAssignableFrom(value.getClass())) {
load(prefix.concat("." + k), (Map)config.get(key));
} else {
this.setKv(prefix.concat("." + k), value);
}
}
}
public NvMaps loadEnvironmentVariable() {
String env = System.getenv(MetaDataConstant.ENVIRONMENT_VARIABLE_KEY);
if (StringUtil.isEmpty(env)) {
return this;
}
String[] tmp = env.split(File.pathSeparator);
if (ArrayUtil.isEmpty(tmp) || tmp.length < 2) {
return this;
}
load(kvListToMap(tmp));
return this;
}
public NvMaps loadMainArgs(String[] mainArgs) {
load(kvListToMap(mainArgs));
return this;
}
private Map<String, Object> kvListToMap(String[] args) {
Map<String, Object> map = new HashMap<>();
if (ArrayUtil.isEmpty(args)) {
return map;
}
for (String kv : args) {
if (!StringUtil.isEmpty(kv)) {
int index = kv.indexOf("=");
if (index > 0 && index < kv.length() - 1) {
String key = kv.substring(0, index);
String val = kv.substring(index + 1);
map.put(key, val);
}
}
}
return map;
}
}
@@ -0,0 +1,33 @@
package fun.asgc.neutrino.core.constant;
import com.google.common.collect.Sets;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author: aoshiguchen
* @date: 2023/3/6
*/
@AllArgsConstructor
@Getter
public enum FileTypeEnum {
YML(1, "yml", Sets.newHashSet(".yml", ".yaml")),
PROPERTIES(2, "properties", Sets.newHashSet(".properties")),
JSON(3, "json", Sets.newHashSet(".json")),
;
private Integer type;
private String desc;
private Set<String> suffixSet;
private static final Map<Integer, FileTypeEnum> typeMap = Stream.of(FileTypeEnum.values()).collect(Collectors.toMap(FileTypeEnum::getType, Function.identity()));
public static FileTypeEnum ofType(Integer type) {
return typeMap.get(type);
}
}
@@ -84,4 +84,8 @@ public interface MetaDataConstant {
* app生命周期主题
*/
String TOPIC_APP_LIFE_CYCLE = "TP_APP_LIFE_CYCLE";
/**
* 环境变量key
*/
String ENVIRONMENT_VARIABLE_KEY = "NEUTRINO_PROXY";
}
@@ -28,10 +28,6 @@ import org.apache.commons.io.FileDeleteStrategy;
import org.apache.commons.lang3.StringUtils;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
@@ -90,6 +86,20 @@ public class FileUtil {
}
}
/**
* 读取文件内容
* @param in
* @return
* @throws IOException
*/
public static String readContentAsString(InputStream in) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(in))){
return br.lines().collect(Collectors.joining("\n"));
} catch (Exception e) {
return null;
}
}
/**
* 读取文件内容
* @param path
@@ -220,4 +230,15 @@ public class FileUtil {
}
}
}
public static String getFileSuffix(String path) {
if (StringUtils.isBlank(path)) {
return "";
}
int index = path.lastIndexOf(".");
if (index >= 0) {
return path.substring(index);
}
return "";
}
}
@@ -1,85 +0,0 @@
package fun.asgc.neutrino.core.base;
import org.junit.Assert;
import org.junit.Test;
/**
* @author: aoshiguchen
* @date: 2023/3/1
*/
public class CfgTest {
@Test
public void test1() {
Object key1 = new Object();
Cfg cfg0 = Cfg.of()
.setAlias("app.server.http.http-port", "aaa")
;
Cfg cfg1 = Cfg.of(cfg0)
.set("app.server.http.http-port", 8080)
.set("app.server.http.jks_Path", "/123/456")
.set("app.server.websocket.port", "ws-port", 8888) // 设值的同时设置别名
.set(key1, 1)
.setAlias("app.server.http.http-port", "http-port")
;
Cfg cfg2 = Cfg.of(cfg1)
.set("app.server.http.httpPort", 8081)
.set("app.server.http.context-path", "/")
;
Cfg cfg3 = Cfg.of(cfg2)
.setAlias("app.server.http.http-port", "port")
.setAlias("http-port", "p");
Assert.assertTrue(!cfg1.isEmpty());
Assert.assertTrue(cfg1.size() == 4);
Assert.assertTrue(cfg1.stackSize() == 4);
Assert.assertTrue(cfg1.get("app.server.http.http-port").equals(8080));
Assert.assertTrue(cfg1.get("app.server.http.httpPort").equals(8080));
Assert.assertTrue(cfg1.idx(0).equals(8080));
Assert.assertTrue(cfg1.get("app.server.http.jks_Path").equals("/123/456"));
Assert.assertTrue(cfg1.get("app.server.http.jksPath").equals("/123/456"));
Assert.assertTrue(cfg1.idx(1).equals("/123/456"));
Assert.assertTrue(cfg1.containsKey("app.server.http.http-port"));
Assert.assertTrue(cfg1.containsKey(key1));
Assert.assertFalse(cfg1.containsKey(new Object()));
Assert.assertTrue(!cfg2.isEmpty());
Assert.assertTrue(cfg2.size() == 2);
Assert.assertTrue(cfg2.stackSize() == 6);
Assert.assertTrue(cfg2.get("app.server.http.http-port").equals(8081));
Assert.assertTrue(cfg2.get("app.server.http.httpPort").equals(8081));
Assert.assertTrue(cfg2.get("app.server.http.jks-path") == null);
Assert.assertTrue(cfg2.get("app.server.http.jksPath") == null);
Assert.assertTrue(cfg2.get("app.server.http.context-path").equals("/"));
Assert.assertTrue(cfg2.stackGet("app.server.http.jks-path").equals("/123/456"));
Assert.assertTrue(cfg2.stackGet("app.server.http.jksPath").equals("/123/456"));
Assert.assertTrue(cfg2.stackGet("app.server.http.context-path").equals("/"));
Assert.assertTrue(cfg3.isEmpty());
Assert.assertTrue(!cfg3.isStackEmpty());
Assert.assertTrue(cfg3.size() == 0);
Assert.assertTrue(cfg3.stackSize() == 6);
Assert.assertTrue(cfg3.get("app.server.http.jks-path") == null);
Assert.assertTrue(cfg3.get("app.server.http.jksPath") == null);
Assert.assertTrue(cfg3.get("app.server.http.context-path") == null);
Assert.assertTrue(cfg3.stackGet("app.server.http.jks-path").equals("/123/456"));
Assert.assertTrue(cfg3.stackGet("app.server.http.jksPath").equals("/123/456"));
Assert.assertTrue(cfg3.stackGet("app.server.http.context-path").equals("/"));
Assert.assertTrue(cfg3.stackGetOrDefault("app.server.http.timeout", 60000).equals(60000));
Assert.assertTrue(cfg3.takeStr("app.server.http.http-port").equals("8081"));
Assert.assertNull(cfg3.take(null));
Assert.assertTrue(cfg3.takeStr("httpPort").equals("8081"));
Assert.assertTrue(cfg3.takeInt("http-port").equals(8081));
Assert.assertTrue(cfg3.takeInt("port").equals(8081));
// 注意:第3层的别名p,指向了第1层的别名http-port
Assert.assertTrue(cfg3.takeInt("p").equals(8080));
Assert.assertTrue(cfg3.takeStr("aaa").equals("8081"));
Assert.assertTrue(cfg3.takeInt("wsPort").equals(8888));
}
}
@@ -0,0 +1,85 @@
package fun.asgc.neutrino.core.base;
import org.junit.Assert;
import org.junit.Test;
/**
* @author: aoshiguchen
* @date: 2023/3/1
*/
public class NvMapTest {
@Test
public void test1() {
Object key1 = new Object();
NvMap nvMap0 = NvMap.of()
.setAlias("app.server.http.http-port", "aaa")
;
NvMap nvMap1 = NvMap.of(nvMap0)
.set("app.server.http.http-port", 8080)
.set("app.server.http.jks_Path", "/123/456")
.set("app.server.websocket.port", "ws-port", 8888) // 设值的同时设置别名
.set(key1, 1)
.setAlias("app.server.http.http-port", "http-port")
;
NvMap nvMap2 = NvMap.of(nvMap1)
.set("app.server.http.httpPort", 8081)
.set("app.server.http.context-path", "/")
;
NvMap nvMap3 = NvMap.of(nvMap2)
.setAlias("app.server.http.http-port", "port")
.setAlias("http-port", "p");
Assert.assertTrue(!nvMap1.isEmpty());
Assert.assertTrue(nvMap1.size() == 4);
Assert.assertTrue(nvMap1.stackSize() == 4);
Assert.assertTrue(nvMap1.get("app.server.http.http-port").equals(8080));
Assert.assertTrue(nvMap1.get("app.server.http.httpPort").equals(8080));
Assert.assertTrue(nvMap1.idx(0).equals(8080));
Assert.assertTrue(nvMap1.get("app.server.http.jks_Path").equals("/123/456"));
Assert.assertTrue(nvMap1.get("app.server.http.jksPath").equals("/123/456"));
Assert.assertTrue(nvMap1.idx(1).equals("/123/456"));
Assert.assertTrue(nvMap1.containsKey("app.server.http.http-port"));
Assert.assertTrue(nvMap1.containsKey(key1));
Assert.assertFalse(nvMap1.containsKey(new Object()));
Assert.assertTrue(!nvMap2.isEmpty());
Assert.assertTrue(nvMap2.size() == 2);
Assert.assertTrue(nvMap2.stackSize() == 6);
Assert.assertTrue(nvMap2.get("app.server.http.http-port").equals(8081));
Assert.assertTrue(nvMap2.get("app.server.http.httpPort").equals(8081));
Assert.assertTrue(nvMap2.get("app.server.http.jks-path") == null);
Assert.assertTrue(nvMap2.get("app.server.http.jksPath") == null);
Assert.assertTrue(nvMap2.get("app.server.http.context-path").equals("/"));
Assert.assertTrue(nvMap2.stackGet("app.server.http.jks-path").equals("/123/456"));
Assert.assertTrue(nvMap2.stackGet("app.server.http.jksPath").equals("/123/456"));
Assert.assertTrue(nvMap2.stackGet("app.server.http.context-path").equals("/"));
Assert.assertTrue(nvMap3.isEmpty());
Assert.assertTrue(!nvMap3.isStackEmpty());
Assert.assertTrue(nvMap3.size() == 0);
Assert.assertTrue(nvMap3.stackSize() == 6);
Assert.assertTrue(nvMap3.get("app.server.http.jks-path") == null);
Assert.assertTrue(nvMap3.get("app.server.http.jksPath") == null);
Assert.assertTrue(nvMap3.get("app.server.http.context-path") == null);
Assert.assertTrue(nvMap3.stackGet("app.server.http.jks-path").equals("/123/456"));
Assert.assertTrue(nvMap3.stackGet("app.server.http.jksPath").equals("/123/456"));
Assert.assertTrue(nvMap3.stackGet("app.server.http.context-path").equals("/"));
Assert.assertTrue(nvMap3.stackGetOrDefault("app.server.http.timeout", 60000).equals(60000));
Assert.assertTrue(nvMap3.takeStr("app.server.http.http-port").equals("8081"));
Assert.assertNull(nvMap3.take(null));
Assert.assertTrue(nvMap3.takeStr("httpPort").equals("8081"));
Assert.assertTrue(nvMap3.takeInt("http-port").equals(8081));
Assert.assertTrue(nvMap3.takeInt("port").equals(8081));
// 注意:第3层的别名p,指向了第1层的别名http-port
Assert.assertTrue(nvMap3.takeInt("p").equals(8081));
Assert.assertTrue(nvMap3.takeStr("aaa").equals("8081"));
Assert.assertTrue(nvMap3.takeInt("wsPort").equals(8888));
}
}
@@ -0,0 +1,59 @@
package fun.asgc.neutrino.core.base;
import org.junit.Test;
import java.io.IOException;
/**
* @author: aoshiguchen
* @date: 2023/3/6
*/
public class NvMapsTest {
/**
* 先给电脑设置环境变量,然后重启idea
* EUTRINO_PROXY=http-port=6000:app-name=neutrino-proxy-client
* @throws IOException
*/
@Test
public void test1() throws IOException {
// 创建NvMaps实例
NvMaps nvMaps = NvMaps.of();
// 阶段1:设置内部默认值
nvMaps.setKv("neutrino.application.name", "app-name",null);
nvMaps.setKv("neutrino.http.enable", true);
nvMaps.setKv("neutrino.http.port", "httpPort",null);
nvMaps.setKv("neutrino.http.context-path", "context-path", "/");
nvMaps.stageDone();
// 阶段2:内部配置文件
nvMaps.loadFile(NvMapsTest.class.getResource("/application.yml").getPath());
nvMaps.stageDone();
// 阶段3:环境变量
nvMaps.loadEnvironmentVariable();
nvMaps.stageDone();
// 结算4:外部配置文件
nvMaps.loadFile(NvMapsTest.class.getResource("/aaa.json").getPath());
nvMaps.stageDone();
// 阶段5:启动参数
String[] mainArgs = new String[]{
"httpPort=8100"
};
nvMaps.loadMainArgs(mainArgs);
nvMaps.stageDone();
// 获取NvMap实例
NvMap nvMap = nvMaps.getNvMap();
System.out.println(nvMap.takeInt("httpPort"));
System.out.println(nvMap.takeInt("neutrino.http.port"));
System.out.println(nvMap.takeStr("appName"));
System.out.println(nvMap.takeStr("neutrino.application.name"));
}
}
+10
View File
@@ -0,0 +1,10 @@
{
"neutrino": {
"application": {
"name": "test-app"
},
"http": {
}
}
}
@@ -1,9 +1,9 @@
neutrino:
application:
name: neutrino-proxy-server
name:
http:
enable: false
port: 8080
port:
context-path: /test
max-content-length-desc: 128K
static-resource: