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); 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();
}
/** /**
* 载入配置信息请加锁 * 载入配置信息请加锁
*/ */

View File

@@ -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,23 +25,32 @@ 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() {
try {
instanceLock.lock();
if (listableInstanceFactories.isEmpty()) if (listableInstanceFactories.isEmpty())
return; return;
@@ -60,22 +71,29 @@ public abstract class AbstractInstanceContext implements InstanceContext, Multip
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);
}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 @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

View File

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

View File

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

View File

@@ -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 实例名称

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

@@ -121,10 +121,9 @@ public final class SystemInitializer implements Refreshable, Clearable {
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) {
log.error("文件 {} 解压失败!, 原因: {}", resourceName, e.getMessage()); log.error("文件 {} 解压失败!, 原因: {}", resourceName, e.getMessage());

View File

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

View File

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