feat: 新增实例相关异常、现在实例容器是线程安全的

This commit is contained in:
2024-05-25 11:54:22 +08:00
parent 2211a6edc0
commit 6f5050269b
10 changed files with 142 additions and 66 deletions

View File

@@ -76,15 +76,6 @@ public abstract class AbstractConfiguration implements Configuration {
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
@SuppressWarnings({"rawtypes", "unchecked"})
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;
}
@Override
public void refresh() {
// 刷新配置信息
refresh0();
final Boolean needPrint = getBoolean(ConfigurationKeys.KEY_CFG_LOG_ONSTART);
if (needPrint)
printDetails();
}
/**
* 载入配置信息请加锁
*/

View File

@@ -4,13 +4,15 @@ import com.serliunx.ddns.constant.InstanceType;
import com.serliunx.ddns.core.Clearable;
import com.serliunx.ddns.core.factory.ListableInstanceFactory;
import com.serliunx.ddns.core.instance.Instance;
import com.serliunx.ddns.exception.InstanceExistsException;
import com.serliunx.ddns.support.Assert;
import com.serliunx.ddns.core.Refreshable;
import com.serliunx.ddns.util.ReflectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
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 {
private static final Logger log = LoggerFactory.getLogger(AbstractInstanceContext.class);
private final Set<ListableInstanceFactory> listableInstanceFactories = new HashSet<>();
/**
* 实例操作锁
* <li> 为保证数据的一致性, 实例新增、删除等相关操作尽量加锁
*/
private final Lock instanceLock = new ReentrantLock();
/**
* 完整的实例信息
* <li> 作为主要操作对象
*/
private Map<String, Instance> instanceMap;
private Map<String, Instance> instanceMap = new HashMap<>(16);
/**
* 实例信息缓存, 此时的实例继承关系并不完整
* <li> 不能作为主要的操作对象
* <li> 容器一般会在刷新完毕后清空该Map, 具体取决于容器本身
*/
private Map<String, Instance> cacheInstanceMap;
private Map<String, Instance> cacheInstanceMap = new HashMap<>(16);
@Override
public void refresh() {
if (listableInstanceFactories.isEmpty())
return;
try {
instanceLock.lock();
if (listableInstanceFactories.isEmpty())
return;
// 初始化所有实例工厂
listableInstanceFactories.stream()
.filter(f -> f != this)
.forEach(ListableInstanceFactory::refresh);
// 加载、过滤所有实例
Set<Instance> instances = new HashSet<>();
// 初始化所有实例工厂
listableInstanceFactories.stream()
.filter(f -> f != this)
.forEach(ListableInstanceFactory::refresh);
// 加载、过滤所有实例
Set<Instance> instances = new HashSet<>();
// 高优先级的实例工厂会覆盖低优先级实例工厂所加载的实例
listableInstanceFactories.stream()
.sorted()
.forEach(f -> instances.addAll(f.getInstances()));
// 高优先级的实例工厂会覆盖低优先级实例工厂所加载的实例
listableInstanceFactories.stream()
.sorted()
.forEach(f -> instances.addAll(f.getInstances()));
// 初次载入
cacheInstanceMap = new HashMap<>(instances.stream().collect(Collectors.toMap(Instance::getName, i -> i)));
Set<Instance> builtInstances = buildInstances(instances);
// 初次载入
cacheInstanceMap = new HashMap<>(instances.stream().collect(Collectors.toMap(Instance::getName, i -> i)));
Set<Instance> builtInstances = buildInstances(instances);
instanceMap = builtInstances.stream().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 = builtInstances.stream().collect(Collectors.toMap(Instance::getName, i -> i));
}catch (Exception e){
throw new RuntimeException(e);
}finally {
instanceLock.unlock();
}
instanceMap.put(instance.getName(), instance);
return true;
}
@Override
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

View File

@@ -7,6 +7,16 @@ package com.serliunx.ddns.core.context;
*/
public class GenericInstanceContext extends AbstractInstanceContext {
private final boolean clearable;
public GenericInstanceContext(boolean clearable) {
this.clearable = clearable;
}
public GenericInstanceContext() {
this(false);
}
@Override
protected void clear0() {
clearCache();
@@ -14,6 +24,6 @@ public class GenericInstanceContext extends AbstractInstanceContext {
@Override
public boolean isClearable() {
return false;
return clearable;
}
}

View File

@@ -46,20 +46,10 @@ public abstract class AbstractInstanceFactory implements InstanceFactory, Listab
.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
public void addInstance(Instance instance) {
addInstance(instance, false);
validateInstance(instance);
instanceMap.put(instance.getName(), instance);
}
@Override

View File

@@ -2,8 +2,8 @@ package com.serliunx.ddns.core.factory;
import com.serliunx.ddns.core.Clearable;
import com.serliunx.ddns.core.Priority;
import com.serliunx.ddns.core.instance.Instance;
import com.serliunx.ddns.core.Refreshable;
import com.serliunx.ddns.core.instance.Instance;
/**
* @author SerLiunx
@@ -18,14 +18,6 @@ public interface InstanceFactory extends Priority, Comparable<InstanceFactory>,
*/
void addInstance(Instance instance);
/**
* 添加实例
* @param instance 实例信息
* @param override 是否覆盖原有的同名实例
* @return 成功添加返回真, 否则返回假
*/
boolean addInstance(Instance instance, boolean override);
/**
* 根据实例名称获取实例
* @param instanceName 实例名称

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -110,7 +110,7 @@ public final class SystemInitializer implements Refreshable, Clearable {
ClassLoader classLoader = SystemConstants.class.getClassLoader();
Path path = Paths.get(SystemConstants.USER_DIR + File.separator + resourceName);
// 检查文件是否已存在
if(Files.exists(path)){
if (Files.exists(path)) {
log.debug("文件 {} 已存在, 无需解压.", resourceName);
return;
}
@@ -120,10 +120,9 @@ public final class SystemInitializer implements Refreshable, Clearable {
OutputStream outputStream = Files.newOutputStream(path);
byte[] buffer = new byte[1024];
int bytesRead;
if(inputStream != null) {
while ((bytesRead = inputStream.read(buffer)) != -1) {
if (inputStream != null) {
while ((bytesRead = inputStream.read(buffer)) != -1)
outputStream.write(buffer, 0, bytesRead);
}
}
outputStream.close();
} catch (Exception e) {

View File

@@ -1,5 +1,6 @@
package com.serliunx.ddns.test;
import com.serliunx.ddns.constant.InstanceType;
import com.serliunx.ddns.constant.SystemConstants;
import com.serliunx.ddns.core.context.FileInstanceContext;
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.XmlFileInstanceFactory;
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;
/**
@@ -17,7 +20,7 @@ public class ContextTest {
@Test
public void testGenericContext() {
GenericInstanceContext genericInstanceContext = new GenericInstanceContext();
GenericInstanceContext genericInstanceContext = new GenericInstanceContext(true);
genericInstanceContext.addListableInstanceFactory(new XmlFileInstanceFactory(SystemConstants.USER_INSTANCE_DIR));
genericInstanceContext.addListableInstanceFactory(new YamlFileInstanceFactory(SystemConstants.USER_INSTANCE_DIR));
@@ -25,6 +28,8 @@ public class ContextTest {
genericInstanceContext.refresh();
genericInstanceContext.getInstances().forEach(System.out::println);
genericInstanceContext.clear();
}
@Test
@@ -33,4 +38,19 @@ public class ContextTest {
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);
}
}

View File

@@ -15,5 +15,6 @@ public class FactoryTest {
YamlFileInstanceFactory factory = new YamlFileInstanceFactory(SystemConstants.USER_INSTANCE_DIR);
factory.refresh();
factory.getInstances().forEach(System.out::println);
factory.clear();
}
}