From 059377b5d7e4b0b9c98350893ef5cb90caaf8de6 Mon Sep 17 00:00:00 2001 From: SerLiunx-ctrl <17689543@qq.com> Date: Tue, 21 May 2024 01:53:45 +0800 Subject: [PATCH] feat: support aliyun instance and more. --- .../core/context/AbstractInstanceContext.java | 15 ++-- .../core/factory/AbstractInstanceFactory.java | 9 ++- .../ddns/core/instance/AbstractInstance.java | 48 +++++++++---- .../ddns/core/instance/AliyunInstance.java | 72 +++++++++++++------ .../ddns/core/instance/HuaweiInstance.java | 29 ++++++++ .../ddns/core/instance/TencentInstance.java | 5 +- .../ddns/support/NetworkContextHolder.java | 2 - .../serliunx/ddns/support/Refreshable.java | 4 +- .../ddns/support/SystemInitializer.java | 2 + .../com/serliunx/ddns/test/FactoryTest.java | 19 +++++ .../ddns/test/support/ClientTest.java | 17 +++++ 11 files changed, 173 insertions(+), 49 deletions(-) create mode 100644 src/main/java/com/serliunx/ddns/core/instance/HuaweiInstance.java create mode 100644 src/test/java/com/serliunx/ddns/test/FactoryTest.java create mode 100644 src/test/java/com/serliunx/ddns/test/support/ClientTest.java 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 e067044..a3fc85f 100644 --- a/src/main/java/com/serliunx/ddns/core/context/AbstractInstanceContext.java +++ b/src/main/java/com/serliunx/ddns/core/context/AbstractInstanceContext.java @@ -48,9 +48,11 @@ public abstract class AbstractInstanceContext implements InstanceContext, Multip .forEach(ListableInstanceFactory::refresh); // 加载、过滤所有实例 Set instances = new HashSet<>(); - listableInstanceFactories.forEach(f -> instances.addAll(f.getInstances())); - // TODO 加载实例, 按照实例工厂的优先级从低到高优先级排, 高优先级的实例会覆盖低优先级的实例信息(如果存在重复的实例信息) + // 高优先级的实例工厂会覆盖低优先级实例工厂所加载的实例 + listableInstanceFactories.stream() + .sorted() + .forEach(f -> instances.addAll(f.getInstances())); // 初次载入 cacheInstanceMap = new HashMap<>(instances.stream().collect(Collectors.toMap(Instance::getName, i -> i))); @@ -119,9 +121,12 @@ public abstract class AbstractInstanceContext implements InstanceContext, Multip * 缓存清理 */ protected void clearCache(){ - int size = cacheInstanceMap.size(); - cacheInstanceMap.clear(); - log.debug("缓存信息清理 => {} 条", size); + if(cacheInstanceMap != null + && !cacheInstanceMap.isEmpty()){ + int size = cacheInstanceMap.size(); + cacheInstanceMap.clear(); + log.debug("缓存信息清理 => {} 条", size); + } // 清理实例工厂的缓存信息 listableInstanceFactories.forEach(Refreshable::afterRefresh); } 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 c1417dc..079f02e 100644 --- a/src/main/java/com/serliunx/ddns/core/factory/AbstractInstanceFactory.java +++ b/src/main/java/com/serliunx/ddns/core/factory/AbstractInstanceFactory.java @@ -77,9 +77,12 @@ public abstract class AbstractInstanceFactory implements InstanceFactory, Listab @Override public void afterRefresh() { - int size = instanceMap.size(); - instanceMap.clear(); - log.debug("缓存信息清理 => {} 条", size); + if(instanceMap != null + && !instanceMap.isEmpty()){ + int size = instanceMap.size(); + instanceMap.clear(); + log.debug("缓存信息清理 => {} 条", size); + } } /** diff --git a/src/main/java/com/serliunx/ddns/core/instance/AbstractInstance.java b/src/main/java/com/serliunx/ddns/core/instance/AbstractInstance.java index 54aeb93..aa579ff 100644 --- a/src/main/java/com/serliunx/ddns/core/instance/AbstractInstance.java +++ b/src/main/java/com/serliunx/ddns/core/instance/AbstractInstance.java @@ -5,10 +5,14 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; import com.serliunx.ddns.constant.InstanceSource; import com.serliunx.ddns.constant.InstanceType; +import com.serliunx.ddns.support.NetworkContextHolder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import static com.serliunx.ddns.constant.SystemConstants.XML_ROOT_INSTANCE_NAME; /** + * 实例抽象实现 * @author SerLiunx * @since 1.0 */ @@ -17,6 +21,7 @@ import static com.serliunx.ddns.constant.SystemConstants.XML_ROOT_INSTANCE_NAME; @JacksonXmlRootElement(localName = XML_ROOT_INSTANCE_NAME) public abstract class AbstractInstance implements Instance { + private static final Logger log = LoggerFactory.getLogger(AbstractInstance.class); /** * 实例名称 *
  • 全局唯一 @@ -56,8 +61,30 @@ public abstract class AbstractInstance implements Instance { @Override public void run() { - if(query()) + value = query(); + final String ipAddress = NetworkContextHolder.getIpAddress(); + try { + if (value != null && !value.isEmpty() + && ipAddress != null && !ipAddress.isEmpty()) { + if (value.equals(ipAddress)) + return; + } + value = ipAddress; run0(); + }catch (Exception e){ + log.error(e.getMessage()); + }finally { + this.value = null; + } + } + + @Override + public boolean validate() { + // 校验通用参数, 具体子类的参数交由子类校验 + if(name == null || name.isEmpty() || interval <= 0 || type == null){ + return false; + } + return validate0(); } @Override @@ -105,15 +132,6 @@ public abstract class AbstractInstance implements Instance { return source; } - @Override - public boolean validate() { - // 校验通用参数, 具体子类的参数交由子类校验 - if(name == null || name.isEmpty() || interval <= 0 || type == null){ - return false; - } - return validate0(); - } - /** * 具体的初始化逻辑 */ @@ -125,10 +143,14 @@ public abstract class AbstractInstance implements Instance { protected abstract boolean validate0(); /** - * 更新前检查是否需要更新 - * @return 无需更新返回假, 否则返回真 + * 获取解析当前的ip地址 + *
  • 由子类完成具体逻辑 + * @return 返回当前解析记录的ip地址, 由子类决定. + * @see AliyunInstance + * @see TencentInstance + * @see HuaweiInstance */ - protected abstract boolean query(); + protected abstract String query(); /** * 具体执行逻辑 diff --git a/src/main/java/com/serliunx/ddns/core/instance/AliyunInstance.java b/src/main/java/com/serliunx/ddns/core/instance/AliyunInstance.java index 9d99b23..f28cb38 100644 --- a/src/main/java/com/serliunx/ddns/core/instance/AliyunInstance.java +++ b/src/main/java/com/serliunx/ddns/core/instance/AliyunInstance.java @@ -3,13 +3,11 @@ package com.serliunx.ddns.core.instance; import com.aliyun.auth.credentials.Credential; import com.aliyun.auth.credentials.provider.StaticCredentialProvider; import com.aliyun.sdk.service.alidns20150109.AsyncClient; -import com.aliyun.sdk.service.alidns20150109.models.DescribeDomainRecordInfoRequest; -import com.aliyun.sdk.service.alidns20150109.models.DescribeDomainRecordInfoResponse; -import com.aliyun.sdk.service.alidns20150109.models.DescribeDomainRecordInfoResponseBody; +import com.aliyun.sdk.service.alidns20150109.models.*; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; -import com.serliunx.ddns.support.NetworkContextHolder; import darabonba.core.client.ClientOverrideConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -107,12 +105,34 @@ public class AliyunInstance extends AbstractInstance { @Override protected void run0() { - log("test"); + UpdateDomainRecordRequest request = UpdateDomainRecordRequest.builder() + .recordId(recordId) + .rr(rr) + .type(recordType) + .value(value) + .build(); + debug("正在更新解析记录."); + CompletableFuture requestResponse = client.updateDomainRecord(request); + try { + requestResponse.whenComplete((v, t) -> { + if(t != null){ //出现异常 + handleThrowable(t); + }else{ + String result = null; + try { + result = jsonMapper.writeValueAsString(v.getBody()); + } catch (JsonProcessingException ignored) {} finally { + debug("操作结束, 结果: {}", result == null ? v : result); + } + } + }); + } catch (Exception e) { + throw new RuntimeException(e); + } } @Override - protected boolean query() { - debug("正在校验是否需要更新记录."); + protected String query() { DescribeDomainRecordInfoRequest describeDomainRecordInfoRequest = DescribeDomainRecordInfoRequest.builder() .recordId(recordId) .build(); @@ -122,24 +142,15 @@ public class AliyunInstance extends AbstractInstance { DescribeDomainRecordInfoResponse response = responseCompletableFuture.get(5, TimeUnit.SECONDS); DescribeDomainRecordInfoResponseBody body = response.getBody(); if(body != null){ - String recordValue = body.getValue(); - String ipAddress = NetworkContextHolder.getIpAddress(); - debug("当前记录值 => {}", recordValue); - boolean result = !(recordValue != null && !recordValue.isEmpty() - && recordValue.equals(ipAddress)); - if(result) - debug("需要更新IP地址: {} => {}", recordValue, ipAddress); - else - debug("无需更新."); - return result; + return body.getValue(); } - return false; + return null; } catch (InterruptedException | ExecutionException e) { error("出现了不应该出现的异常 => {}", e); - return false; + return null; } catch (TimeoutException e) { - error("记录查询超时! 将跳过查询直接执行更新操作."); - return true; + error("记录查询超时!"); + return null; } } @@ -206,6 +217,25 @@ public class AliyunInstance extends AbstractInstance { this.jsonMapper = jsonMapper; } + @Override + public String toString() { + return "AliyunInstance{" + + "accessKeyId='" + accessKeyId + '\'' + + ", accessKeySecret='" + accessKeySecret + '\'' + + ", recordId='" + recordId + '\'' + + ", rr='" + rr + '\'' + + ", recordType='" + recordType + '\'' + + ", client=" + client + + ", jsonMapper=" + jsonMapper + + ", name='" + name + '\'' + + ", fatherName='" + fatherName + '\'' + + ", interval=" + interval + + ", type=" + type + + ", source=" + source + + ", value='" + value + '\'' + + '}'; + } + private void handleThrowable(Throwable t){ error("出现异常 {}:", t.getCause(), t.getMessage()); } diff --git a/src/main/java/com/serliunx/ddns/core/instance/HuaweiInstance.java b/src/main/java/com/serliunx/ddns/core/instance/HuaweiInstance.java new file mode 100644 index 0000000..69c09f7 --- /dev/null +++ b/src/main/java/com/serliunx/ddns/core/instance/HuaweiInstance.java @@ -0,0 +1,29 @@ +package com.serliunx.ddns.core.instance; + +/** + * 华为云实例定义 + * @author SerLiunx + * @since 1.0 + */ +public class HuaweiInstance extends AbstractInstance { + + @Override + protected void init() { + + } + + @Override + protected boolean validate0() { + return false; + } + + @Override + protected String query() { + return null; + } + + @Override + protected void run0() { + + } +} diff --git a/src/main/java/com/serliunx/ddns/core/instance/TencentInstance.java b/src/main/java/com/serliunx/ddns/core/instance/TencentInstance.java index eb2fdb3..54f7b30 100644 --- a/src/main/java/com/serliunx/ddns/core/instance/TencentInstance.java +++ b/src/main/java/com/serliunx/ddns/core/instance/TencentInstance.java @@ -1,6 +1,7 @@ package com.serliunx.ddns.core.instance; /** + * 腾讯云实例定义 * @author SerLiunx * @since 1.0 */ @@ -17,8 +18,8 @@ public class TencentInstance extends AbstractInstance { } @Override - protected boolean query() { - return false; + protected String query() { + return null; } @Override diff --git a/src/main/java/com/serliunx/ddns/support/NetworkContextHolder.java b/src/main/java/com/serliunx/ddns/support/NetworkContextHolder.java index 4125684..a964448 100644 --- a/src/main/java/com/serliunx/ddns/support/NetworkContextHolder.java +++ b/src/main/java/com/serliunx/ddns/support/NetworkContextHolder.java @@ -38,7 +38,6 @@ public final class NetworkContextHolder { } public static String getIpAddress(){ - log.debug("正在尝试获取最新的IP地址."); if(IP_ADDRESS != null) return IP_ADDRESS; try { @@ -46,7 +45,6 @@ public final class NetworkContextHolder { log.error("IP地址获取超时."); return null; } - log.debug("最新的IP地址获取成功."); return IP_ADDRESS; } catch (InterruptedException e) { log.error("IP地址获取出现异常 => {}", e.getMessage()); diff --git a/src/main/java/com/serliunx/ddns/support/Refreshable.java b/src/main/java/com/serliunx/ddns/support/Refreshable.java index c6f44a8..ce9cee7 100644 --- a/src/main/java/com/serliunx/ddns/support/Refreshable.java +++ b/src/main/java/com/serliunx/ddns/support/Refreshable.java @@ -16,7 +16,5 @@ public interface Refreshable { /** * 刷新后逻辑定义, 一般用于资源清理 */ - default void afterRefresh(){ - - } + default void afterRefresh(){} } diff --git a/src/main/java/com/serliunx/ddns/support/SystemInitializer.java b/src/main/java/com/serliunx/ddns/support/SystemInitializer.java index b27bf5d..9d1f331 100644 --- a/src/main/java/com/serliunx/ddns/support/SystemInitializer.java +++ b/src/main/java/com/serliunx/ddns/support/SystemInitializer.java @@ -17,6 +17,7 @@ import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Map; import java.util.Set; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -38,6 +39,7 @@ public final class SystemInitializer implements Refreshable{ private ScheduledThreadPoolExecutor scheduledThreadPoolExecutor; private Set instances; + private Map runningInstances; SystemInitializer(Configuration configuration, MultipleSourceInstanceContext instanceContext) { this.configuration = configuration; diff --git a/src/test/java/com/serliunx/ddns/test/FactoryTest.java b/src/test/java/com/serliunx/ddns/test/FactoryTest.java new file mode 100644 index 0000000..21baca8 --- /dev/null +++ b/src/test/java/com/serliunx/ddns/test/FactoryTest.java @@ -0,0 +1,19 @@ +package com.serliunx.ddns.test; + +import com.serliunx.ddns.constant.SystemConstants; +import com.serliunx.ddns.core.factory.YamlFileInstanceFactory; +import org.junit.Test; + +/** + * @author SerLiunx + * @since 1.0 + */ +public class FactoryTest { + + @Test + public void testYamlFileFactory(){ + YamlFileInstanceFactory factory = new YamlFileInstanceFactory(SystemConstants.USER_INSTANCE_DIR); + factory.refresh(); + factory.getInstances().forEach(System.out::println); + } +} diff --git a/src/test/java/com/serliunx/ddns/test/support/ClientTest.java b/src/test/java/com/serliunx/ddns/test/support/ClientTest.java new file mode 100644 index 0000000..136c888 --- /dev/null +++ b/src/test/java/com/serliunx/ddns/test/support/ClientTest.java @@ -0,0 +1,17 @@ +package com.serliunx.ddns.test.support; + +import com.serliunx.ddns.support.feign.client.IPAddressClient; +import org.junit.Test; + +/** + * @author SerLiunx + * @since 1.0 + */ +public class ClientTest { + + @Test + public void test(){ + IPAddressClient client = IPAddressClient.instance; + System.out.println(client.getIPAddress().getQuery()); + } +}