feat: 新增实例相关异常、现在实例容器是线程安全的
This commit is contained in:
@@ -76,15 +76,6 @@ public abstract class AbstractConfiguration implements Configuration {
|
|||||||
return value == null ? defaultValue : Boolean.valueOf(value);
|
return value == null ? defaultValue : Boolean.valueOf(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void refresh() {
|
|
||||||
// 刷新配置信息
|
|
||||||
refresh0();
|
|
||||||
final Boolean needPrint = getBoolean(ConfigurationKeys.KEY_CFG_LOG_ONSTART);
|
|
||||||
if (needPrint)
|
|
||||||
printDetails();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
public <T extends Enum> T getEnum(Class<T> clazz, String key) {
|
public <T extends Enum> T getEnum(Class<T> clazz, String key) {
|
||||||
@@ -103,6 +94,15 @@ public abstract class AbstractConfiguration implements Configuration {
|
|||||||
return value == null ? defaultValue : value;
|
return value == null ? defaultValue : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void refresh() {
|
||||||
|
// 刷新配置信息
|
||||||
|
refresh0();
|
||||||
|
final Boolean needPrint = getBoolean(ConfigurationKeys.KEY_CFG_LOG_ONSTART);
|
||||||
|
if (needPrint)
|
||||||
|
printDetails();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 载入配置信息请加锁
|
* 载入配置信息请加锁
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -4,13 +4,15 @@ import com.serliunx.ddns.constant.InstanceType;
|
|||||||
import com.serliunx.ddns.core.Clearable;
|
import com.serliunx.ddns.core.Clearable;
|
||||||
import com.serliunx.ddns.core.factory.ListableInstanceFactory;
|
import com.serliunx.ddns.core.factory.ListableInstanceFactory;
|
||||||
import com.serliunx.ddns.core.instance.Instance;
|
import com.serliunx.ddns.core.instance.Instance;
|
||||||
|
import com.serliunx.ddns.exception.InstanceExistsException;
|
||||||
import com.serliunx.ddns.support.Assert;
|
import com.serliunx.ddns.support.Assert;
|
||||||
import com.serliunx.ddns.core.Refreshable;
|
|
||||||
import com.serliunx.ddns.util.ReflectionUtils;
|
import com.serliunx.ddns.util.ReflectionUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static com.serliunx.ddns.util.InstanceUtils.validateInstance;
|
import static com.serliunx.ddns.util.InstanceUtils.validateInstance;
|
||||||
@@ -23,59 +25,75 @@ import static com.serliunx.ddns.util.InstanceUtils.validateInstance;
|
|||||||
public abstract class AbstractInstanceContext implements InstanceContext, MultipleSourceInstanceContext {
|
public abstract class AbstractInstanceContext implements InstanceContext, MultipleSourceInstanceContext {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(AbstractInstanceContext.class);
|
private static final Logger log = LoggerFactory.getLogger(AbstractInstanceContext.class);
|
||||||
|
|
||||||
private final Set<ListableInstanceFactory> listableInstanceFactories = new HashSet<>();
|
private final Set<ListableInstanceFactory> listableInstanceFactories = new HashSet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实例操作锁
|
||||||
|
* <li> 为保证数据的一致性, 实例新增、删除等相关操作尽量加锁
|
||||||
|
*/
|
||||||
|
private final Lock instanceLock = new ReentrantLock();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 完整的实例信息
|
* 完整的实例信息
|
||||||
* <li> 作为主要操作对象
|
* <li> 作为主要操作对象
|
||||||
*/
|
*/
|
||||||
private Map<String, Instance> instanceMap;
|
private Map<String, Instance> instanceMap = new HashMap<>(16);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 实例信息缓存, 此时的实例继承关系并不完整
|
* 实例信息缓存, 此时的实例继承关系并不完整
|
||||||
* <li> 不能作为主要的操作对象
|
* <li> 不能作为主要的操作对象
|
||||||
* <li> 容器一般会在刷新完毕后清空该Map, 具体取决于容器本身
|
* <li> 容器一般会在刷新完毕后清空该Map, 具体取决于容器本身
|
||||||
*/
|
*/
|
||||||
private Map<String, Instance> cacheInstanceMap;
|
private Map<String, Instance> cacheInstanceMap = new HashMap<>(16);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void refresh() {
|
public void refresh() {
|
||||||
if (listableInstanceFactories.isEmpty())
|
try {
|
||||||
return;
|
instanceLock.lock();
|
||||||
|
if (listableInstanceFactories.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
// 初始化所有实例工厂
|
// 初始化所有实例工厂
|
||||||
listableInstanceFactories.stream()
|
listableInstanceFactories.stream()
|
||||||
.filter(f -> f != this)
|
.filter(f -> f != this)
|
||||||
.forEach(ListableInstanceFactory::refresh);
|
.forEach(ListableInstanceFactory::refresh);
|
||||||
// 加载、过滤所有实例
|
// 加载、过滤所有实例
|
||||||
Set<Instance> instances = new HashSet<>();
|
Set<Instance> instances = new HashSet<>();
|
||||||
|
|
||||||
// 高优先级的实例工厂会覆盖低优先级实例工厂所加载的实例
|
// 高优先级的实例工厂会覆盖低优先级实例工厂所加载的实例
|
||||||
listableInstanceFactories.stream()
|
listableInstanceFactories.stream()
|
||||||
.sorted()
|
.sorted()
|
||||||
.forEach(f -> instances.addAll(f.getInstances()));
|
.forEach(f -> instances.addAll(f.getInstances()));
|
||||||
|
|
||||||
// 初次载入
|
// 初次载入
|
||||||
cacheInstanceMap = new HashMap<>(instances.stream().collect(Collectors.toMap(Instance::getName, i -> i)));
|
cacheInstanceMap = new HashMap<>(instances.stream().collect(Collectors.toMap(Instance::getName, i -> i)));
|
||||||
Set<Instance> builtInstances = buildInstances(instances);
|
Set<Instance> builtInstances = buildInstances(instances);
|
||||||
|
|
||||||
instanceMap = builtInstances.stream().collect(Collectors.toMap(Instance::getName, i -> i));
|
instanceMap = builtInstances.stream().collect(Collectors.toMap(Instance::getName, i -> i));
|
||||||
}
|
}catch (Exception e){
|
||||||
|
throw new RuntimeException(e);
|
||||||
@Override
|
}finally {
|
||||||
public boolean addInstance(Instance instance, boolean override) {
|
instanceLock.unlock();
|
||||||
validateInstance(instance);
|
|
||||||
Instance i = instanceMap.get(instance.getName());
|
|
||||||
if (override && i != null) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
instanceMap.put(instance.getName(), instance);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addInstance(Instance instance) {
|
public void addInstance(Instance instance) {
|
||||||
addInstance(instance, false);
|
validateInstance(instance);
|
||||||
|
try {
|
||||||
|
instanceLock.lock();
|
||||||
|
String name = instance.getName();
|
||||||
|
Instance instanceExists = instanceMap.get(name);
|
||||||
|
if (instanceExists != null)
|
||||||
|
throw new InstanceExistsException("该实例已存在!", name, instanceExists);
|
||||||
|
else
|
||||||
|
instanceMap.put(name, instance);
|
||||||
|
}catch (Exception e){
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}finally {
|
||||||
|
instanceLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -7,6 +7,16 @@ package com.serliunx.ddns.core.context;
|
|||||||
*/
|
*/
|
||||||
public class GenericInstanceContext extends AbstractInstanceContext {
|
public class GenericInstanceContext extends AbstractInstanceContext {
|
||||||
|
|
||||||
|
private final boolean clearable;
|
||||||
|
|
||||||
|
public GenericInstanceContext(boolean clearable) {
|
||||||
|
this.clearable = clearable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GenericInstanceContext() {
|
||||||
|
this(false);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void clear0() {
|
protected void clear0() {
|
||||||
clearCache();
|
clearCache();
|
||||||
@@ -14,6 +24,6 @@ public class GenericInstanceContext extends AbstractInstanceContext {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isClearable() {
|
public boolean isClearable() {
|
||||||
return false;
|
return clearable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,20 +46,10 @@ public abstract class AbstractInstanceFactory implements InstanceFactory, Listab
|
|||||||
.collect(Collectors.toMap(Instance::getName, i -> i));
|
.collect(Collectors.toMap(Instance::getName, i -> i));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean addInstance(Instance instance, boolean override) {
|
|
||||||
validateInstance(instance);
|
|
||||||
Instance i = instanceMap.get(instance.getName());
|
|
||||||
if (override && i != null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
instanceMap.put(instance.getName(), instance);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addInstance(Instance instance) {
|
public void addInstance(Instance instance) {
|
||||||
addInstance(instance, false);
|
validateInstance(instance);
|
||||||
|
instanceMap.put(instance.getName(), instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ package com.serliunx.ddns.core.factory;
|
|||||||
|
|
||||||
import com.serliunx.ddns.core.Clearable;
|
import com.serliunx.ddns.core.Clearable;
|
||||||
import com.serliunx.ddns.core.Priority;
|
import com.serliunx.ddns.core.Priority;
|
||||||
import com.serliunx.ddns.core.instance.Instance;
|
|
||||||
import com.serliunx.ddns.core.Refreshable;
|
import com.serliunx.ddns.core.Refreshable;
|
||||||
|
import com.serliunx.ddns.core.instance.Instance;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author SerLiunx
|
* @author SerLiunx
|
||||||
@@ -18,14 +18,6 @@ public interface InstanceFactory extends Priority, Comparable<InstanceFactory>,
|
|||||||
*/
|
*/
|
||||||
void addInstance(Instance instance);
|
void addInstance(Instance instance);
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加实例
|
|
||||||
* @param instance 实例信息
|
|
||||||
* @param override 是否覆盖原有的同名实例
|
|
||||||
* @return 成功添加返回真, 否则返回假
|
|
||||||
*/
|
|
||||||
boolean addInstance(Instance instance, boolean override);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据实例名称获取实例
|
* 根据实例名称获取实例
|
||||||
* @param instanceName 实例名称
|
* @param instanceName 实例名称
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.serliunx.ddns.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实例相关异常信息
|
||||||
|
* @author <a href="mailto:serliunx@yeah.net">SerLiunx</a>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public abstract class InstanceException extends RuntimeException {
|
||||||
|
|
||||||
|
private final String instanceName;
|
||||||
|
|
||||||
|
public InstanceException() {
|
||||||
|
this.instanceName = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InstanceException(String message, String instanceName) {
|
||||||
|
super(message);
|
||||||
|
this.instanceName = instanceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getInstanceName() {
|
||||||
|
return instanceName;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.serliunx.ddns.exception;
|
||||||
|
|
||||||
|
import com.serliunx.ddns.core.instance.Instance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异常信息, 实例已存在
|
||||||
|
* @author <a href="mailto:serliunx@yeah.net">SerLiunx</a>
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
public class InstanceExistsException extends InstanceException {
|
||||||
|
|
||||||
|
private final Instance existsInstance;
|
||||||
|
|
||||||
|
public InstanceExistsException(String message, String instanceName, Instance existsInstance) {
|
||||||
|
super(message, instanceName);
|
||||||
|
this.existsInstance = existsInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instance getExistsInstance() {
|
||||||
|
return existsInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -110,7 +110,7 @@ public final class SystemInitializer implements Refreshable, Clearable {
|
|||||||
ClassLoader classLoader = SystemConstants.class.getClassLoader();
|
ClassLoader classLoader = SystemConstants.class.getClassLoader();
|
||||||
Path path = Paths.get(SystemConstants.USER_DIR + File.separator + resourceName);
|
Path path = Paths.get(SystemConstants.USER_DIR + File.separator + resourceName);
|
||||||
// 检查文件是否已存在
|
// 检查文件是否已存在
|
||||||
if(Files.exists(path)){
|
if (Files.exists(path)) {
|
||||||
log.debug("文件 {} 已存在, 无需解压.", resourceName);
|
log.debug("文件 {} 已存在, 无需解压.", resourceName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -120,10 +120,9 @@ public final class SystemInitializer implements Refreshable, Clearable {
|
|||||||
OutputStream outputStream = Files.newOutputStream(path);
|
OutputStream outputStream = Files.newOutputStream(path);
|
||||||
byte[] buffer = new byte[1024];
|
byte[] buffer = new byte[1024];
|
||||||
int bytesRead;
|
int bytesRead;
|
||||||
if(inputStream != null) {
|
if (inputStream != null) {
|
||||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
while ((bytesRead = inputStream.read(buffer)) != -1)
|
||||||
outputStream.write(buffer, 0, bytesRead);
|
outputStream.write(buffer, 0, bytesRead);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
outputStream.close();
|
outputStream.close();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.serliunx.ddns.test;
|
package com.serliunx.ddns.test;
|
||||||
|
|
||||||
|
import com.serliunx.ddns.constant.InstanceType;
|
||||||
import com.serliunx.ddns.constant.SystemConstants;
|
import com.serliunx.ddns.constant.SystemConstants;
|
||||||
import com.serliunx.ddns.core.context.FileInstanceContext;
|
import com.serliunx.ddns.core.context.FileInstanceContext;
|
||||||
import com.serliunx.ddns.core.context.GenericInstanceContext;
|
import com.serliunx.ddns.core.context.GenericInstanceContext;
|
||||||
@@ -7,6 +8,8 @@ import com.serliunx.ddns.core.context.MultipleSourceInstanceContext;
|
|||||||
import com.serliunx.ddns.core.factory.JsonFileInstanceFactory;
|
import com.serliunx.ddns.core.factory.JsonFileInstanceFactory;
|
||||||
import com.serliunx.ddns.core.factory.XmlFileInstanceFactory;
|
import com.serliunx.ddns.core.factory.XmlFileInstanceFactory;
|
||||||
import com.serliunx.ddns.core.factory.YamlFileInstanceFactory;
|
import com.serliunx.ddns.core.factory.YamlFileInstanceFactory;
|
||||||
|
import com.serliunx.ddns.core.instance.AliyunInstance;
|
||||||
|
import com.serliunx.ddns.core.instance.Instance;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -17,7 +20,7 @@ public class ContextTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGenericContext() {
|
public void testGenericContext() {
|
||||||
GenericInstanceContext genericInstanceContext = new GenericInstanceContext();
|
GenericInstanceContext genericInstanceContext = new GenericInstanceContext(true);
|
||||||
|
|
||||||
genericInstanceContext.addListableInstanceFactory(new XmlFileInstanceFactory(SystemConstants.USER_INSTANCE_DIR));
|
genericInstanceContext.addListableInstanceFactory(new XmlFileInstanceFactory(SystemConstants.USER_INSTANCE_DIR));
|
||||||
genericInstanceContext.addListableInstanceFactory(new YamlFileInstanceFactory(SystemConstants.USER_INSTANCE_DIR));
|
genericInstanceContext.addListableInstanceFactory(new YamlFileInstanceFactory(SystemConstants.USER_INSTANCE_DIR));
|
||||||
@@ -25,6 +28,8 @@ public class ContextTest {
|
|||||||
|
|
||||||
genericInstanceContext.refresh();
|
genericInstanceContext.refresh();
|
||||||
genericInstanceContext.getInstances().forEach(System.out::println);
|
genericInstanceContext.getInstances().forEach(System.out::println);
|
||||||
|
|
||||||
|
genericInstanceContext.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -33,4 +38,19 @@ public class ContextTest {
|
|||||||
|
|
||||||
context.getInstances().forEach(System.out::println);
|
context.getInstances().forEach(System.out::println);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEmptyContext(){
|
||||||
|
GenericInstanceContext instanceContext = new GenericInstanceContext(false);
|
||||||
|
instanceContext.addListableInstanceFactory(new YamlFileInstanceFactory(SystemConstants.USER_INSTANCE_DIR));
|
||||||
|
|
||||||
|
Instance instance = new AliyunInstance();
|
||||||
|
instance.setName("huawei2");
|
||||||
|
instance.setType(InstanceType.ALI_YUN);
|
||||||
|
|
||||||
|
instanceContext.refresh();
|
||||||
|
instanceContext.addInstance(instance);
|
||||||
|
|
||||||
|
instanceContext.getInstances().forEach(System.out::println);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,5 +15,6 @@ public class FactoryTest {
|
|||||||
YamlFileInstanceFactory factory = new YamlFileInstanceFactory(SystemConstants.USER_INSTANCE_DIR);
|
YamlFileInstanceFactory factory = new YamlFileInstanceFactory(SystemConstants.USER_INSTANCE_DIR);
|
||||||
factory.refresh();
|
factory.refresh();
|
||||||
factory.getInstances().forEach(System.out::println);
|
factory.getInstances().forEach(System.out::println);
|
||||||
|
factory.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user