feat: 新增实例相关异常、现在实例容器是线程安全的
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 载入配置信息请加锁
|
||||
*/
|
||||
|
||||
@@ -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,23 +25,32 @@ 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() {
|
||||
try {
|
||||
instanceLock.lock();
|
||||
if (listableInstanceFactories.isEmpty())
|
||||
return;
|
||||
|
||||
@@ -60,22 +71,29 @@ public abstract class AbstractInstanceContext implements InstanceContext, Multip
|
||||
Set<Instance> builtInstances = buildInstances(instances);
|
||||
|
||||
instanceMap = builtInstances.stream().collect(Collectors.toMap(Instance::getName, i -> i));
|
||||
}catch (Exception e){
|
||||
throw new RuntimeException(e);
|
||||
}finally {
|
||||
instanceLock.unlock();
|
||||
}
|
||||
|
||||
@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);
|
||||
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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 实例名称
|
||||
|
||||
@@ -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();
|
||||
Path path = Paths.get(SystemConstants.USER_DIR + File.separator + resourceName);
|
||||
// 检查文件是否已存在
|
||||
if(Files.exists(path)){
|
||||
if (Files.exists(path)) {
|
||||
log.debug("文件 {} 已存在, 无需解压.", resourceName);
|
||||
return;
|
||||
}
|
||||
@@ -120,11 +120,10 @@ 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) {
|
||||
log.error("文件 {} 解压失败!, 原因: {}", resourceName, e.getMessage());
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user