diff --git a/src/main/java/com/serliunx/ddns/config/AbstractConfiguration.java b/src/main/java/com/serliunx/ddns/config/AbstractConfiguration.java index bcbd971..e8c6ea4 100644 --- a/src/main/java/com/serliunx/ddns/config/AbstractConfiguration.java +++ b/src/main/java/com/serliunx/ddns/config/AbstractConfiguration.java @@ -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 getEnum(Class 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(); + } + /** * 载入配置信息请加锁 */ diff --git a/src/main/java/com/serliunx/ddns/core/context/AbstractInstanceContext.java b/src/main/java/com/serliunx/ddns/core/context/AbstractInstanceContext.java index 378b09a..bd25d93 100644 --- a/src/main/java/com/serliunx/ddns/core/context/AbstractInstanceContext.java +++ b/src/main/java/com/serliunx/ddns/core/context/AbstractInstanceContext.java @@ -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 listableInstanceFactories = new HashSet<>(); + /** + * 实例操作锁 + *
  • 为保证数据的一致性, 实例新增、删除等相关操作尽量加锁 + */ + private final Lock instanceLock = new ReentrantLock(); + /** * 完整的实例信息 *
  • 作为主要操作对象 */ - private Map instanceMap; + private Map instanceMap = new HashMap<>(16); /** * 实例信息缓存, 此时的实例继承关系并不完整 *
  • 不能作为主要的操作对象 *
  • 容器一般会在刷新完毕后清空该Map, 具体取决于容器本身 */ - private Map cacheInstanceMap; + private Map 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 instances = new HashSet<>(); + // 初始化所有实例工厂 + listableInstanceFactories.stream() + .filter(f -> f != this) + .forEach(ListableInstanceFactory::refresh); + // 加载、过滤所有实例 + Set 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 builtInstances = buildInstances(instances); + // 初次载入 + cacheInstanceMap = new HashMap<>(instances.stream().collect(Collectors.toMap(Instance::getName, i -> i))); + Set 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 diff --git a/src/main/java/com/serliunx/ddns/core/context/GenericInstanceContext.java b/src/main/java/com/serliunx/ddns/core/context/GenericInstanceContext.java index b0454ba..5efa4ac 100644 --- a/src/main/java/com/serliunx/ddns/core/context/GenericInstanceContext.java +++ b/src/main/java/com/serliunx/ddns/core/context/GenericInstanceContext.java @@ -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; } } diff --git a/src/main/java/com/serliunx/ddns/core/factory/AbstractInstanceFactory.java b/src/main/java/com/serliunx/ddns/core/factory/AbstractInstanceFactory.java index 56981d3..69c7b95 100644 --- a/src/main/java/com/serliunx/ddns/core/factory/AbstractInstanceFactory.java +++ b/src/main/java/com/serliunx/ddns/core/factory/AbstractInstanceFactory.java @@ -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 diff --git a/src/main/java/com/serliunx/ddns/core/factory/InstanceFactory.java b/src/main/java/com/serliunx/ddns/core/factory/InstanceFactory.java index a2c178a..152a286 100644 --- a/src/main/java/com/serliunx/ddns/core/factory/InstanceFactory.java +++ b/src/main/java/com/serliunx/ddns/core/factory/InstanceFactory.java @@ -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, */ void addInstance(Instance instance); - /** - * 添加实例 - * @param instance 实例信息 - * @param override 是否覆盖原有的同名实例 - * @return 成功添加返回真, 否则返回假 - */ - boolean addInstance(Instance instance, boolean override); - /** * 根据实例名称获取实例 * @param instanceName 实例名称 diff --git a/src/main/java/com/serliunx/ddns/exception/InstanceException.java b/src/main/java/com/serliunx/ddns/exception/InstanceException.java new file mode 100644 index 0000000..488e842 --- /dev/null +++ b/src/main/java/com/serliunx/ddns/exception/InstanceException.java @@ -0,0 +1,24 @@ +package com.serliunx.ddns.exception; + +/** + * 实例相关异常信息 + * @author SerLiunx + * @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; + } +} diff --git a/src/main/java/com/serliunx/ddns/exception/InstanceExistsException.java b/src/main/java/com/serliunx/ddns/exception/InstanceExistsException.java new file mode 100644 index 0000000..f9f0c74 --- /dev/null +++ b/src/main/java/com/serliunx/ddns/exception/InstanceExistsException.java @@ -0,0 +1,22 @@ +package com.serliunx.ddns.exception; + +import com.serliunx.ddns.core.instance.Instance; + +/** + * 异常信息, 实例已存在 + * @author SerLiunx + * @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; + } +} diff --git a/src/main/java/com/serliunx/ddns/support/SystemInitializer.java b/src/main/java/com/serliunx/ddns/support/SystemInitializer.java index ba3b995..f1e5590 100644 --- a/src/main/java/com/serliunx/ddns/support/SystemInitializer.java +++ b/src/main/java/com/serliunx/ddns/support/SystemInitializer.java @@ -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) { diff --git a/src/test/java/com/serliunx/ddns/test/ContextTest.java b/src/test/java/com/serliunx/ddns/test/ContextTest.java index d60fe3b..87d5f97 100644 --- a/src/test/java/com/serliunx/ddns/test/ContextTest.java +++ b/src/test/java/com/serliunx/ddns/test/ContextTest.java @@ -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); + } } diff --git a/src/test/java/com/serliunx/ddns/test/FactoryTest.java b/src/test/java/com/serliunx/ddns/test/FactoryTest.java index 293d077..f53bec9 100644 --- a/src/test/java/com/serliunx/ddns/test/FactoryTest.java +++ b/src/test/java/com/serliunx/ddns/test/FactoryTest.java @@ -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(); } }