diff --git a/pom.xml b/pom.xml
index 112db16..8955965 100644
--- a/pom.xml
+++ b/pom.xml
@@ -82,6 +82,19 @@
+
+ org.snmp4j
+ snmp4j
+ 2.8.18
+
+
+
+
+ com.google.code.gson
+ gson
+ 2.13.1
+
+
@@ -124,8 +137,8 @@
- 7
- 7
+ 8
+ 8
groovy-eclipse-compiler
true
true
diff --git a/src/main/java/com/sunyard/SydApi.java b/src/main/java/com/sunyard/SydApi.java
index c3901c0..7153498 100644
--- a/src/main/java/com/sunyard/SydApi.java
+++ b/src/main/java/com/sunyard/SydApi.java
@@ -8,6 +8,8 @@ import com.sunyard.inf.customize.SydQNUCApi;
import com.sunyard.inf.customize.SydUnionPayApi;
import racal.sunyard.main.SydApiBuilder;
+import java.util.List;
+
/**
* Created by Cheney on 2017/11/11.
* 程序调用接口
@@ -117,4 +119,6 @@ public interface SydApi extends
public Object getBindObject();
public void setBindObject(Object bindObject);
+
+ void importCertToServers(List addressList, int timeout, String cert);
}
diff --git a/src/main/java/com/sunyard/entity/ServerConfig.java b/src/main/java/com/sunyard/entity/ServerConfig.java
new file mode 100644
index 0000000..bcef442
--- /dev/null
+++ b/src/main/java/com/sunyard/entity/ServerConfig.java
@@ -0,0 +1,86 @@
+package com.sunyard.entity;
+
+import java.util.List;
+
+public class ServerConfig {
+
+ /**
+ * 主机节点信息
+ */
+ private List masterNodeList;
+
+ /**
+ * 备机节点信息
+ */
+ private List slaveNodeList;
+
+
+ /**
+ * 连接超时时间
+ */
+ private Integer timeout;
+
+ public ServerConfig(List masterNodeList, List slaveNodeList, Integer timeout) {
+ this.masterNodeList = masterNodeList;
+ this.slaveNodeList = slaveNodeList;
+ this.timeout = timeout;
+ }
+
+ public List getMasterNodeList() {
+ return masterNodeList;
+ }
+
+ public void setMasterNodeList(List masterNodeList) {
+ this.masterNodeList = masterNodeList;
+ }
+
+ public Integer getTimeout() {
+ return timeout;
+ }
+
+ public void setTimeout(Integer timeout) {
+ this.timeout = timeout;
+ }
+
+ public List getSlaveNodeList() {
+ return slaveNodeList;
+ }
+
+ public void setSlaveNodeList(List slaveNodeList) {
+ this.slaveNodeList = slaveNodeList;
+ }
+
+
+ public static class ServerAddress {
+
+
+ private String ip;
+
+ private Integer port;
+
+ public ServerAddress(String ip, Integer port) {
+ this.ip = ip;
+ this.port = port;
+ }
+
+
+ public String getIp() {
+ return ip;
+ }
+
+ public void setIp(String ip) {
+ this.ip = ip;
+ }
+
+ public Integer getPort() {
+ return port;
+ }
+
+ public void setPort(Integer port) {
+ this.port = port;
+ }
+
+ }
+
+
+}
diff --git a/src/main/java/com/sunyard/snmp/SnmpTrapSender.java b/src/main/java/com/sunyard/snmp/SnmpTrapSender.java
new file mode 100644
index 0000000..81f1bc9
--- /dev/null
+++ b/src/main/java/com/sunyard/snmp/SnmpTrapSender.java
@@ -0,0 +1,134 @@
+package com.sunyard.snmp;
+
+import org.snmp4j.*;
+import org.snmp4j.mp.*;
+import org.snmp4j.security.*;
+import org.snmp4j.smi.*;
+import org.snmp4j.transport.DefaultUdpTransportMapping;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Vector;
+
+public class SnmpTrapSender {
+ private static volatile SnmpTrapSender instance;
+ private Snmp snmp;
+ private Properties config;
+
+ // 配置参数
+ private String ip;
+ private int port;
+ private String enterpriseOid;
+ private String community;
+ private int version;
+
+
+ private SnmpTrapSender() throws IOException {
+// loadConfig();
+ ip = "172.16.18.113";
+ port = 162;
+ enterpriseOid = "1.3.6.1.4.1.2021.251.1";
+ community = "public";
+ version = 1;
+ initializeSNMP();
+ }
+
+ public static SnmpTrapSender getInstance() throws IOException {
+ if (instance == null) {
+ synchronized (SnmpTrapSender.class) {
+ if (instance == null) {
+ instance = new SnmpTrapSender();
+ }
+ }
+ }
+ return instance;
+ }
+
+ private void loadConfig() {
+ try (InputStream input = getClass().getClassLoader().getResourceAsStream("test.properties")) {
+ config = new Properties();
+ config.load(input);
+
+ ip = config.getProperty("snmp.target.ip");
+ port = Integer.parseInt(config.getProperty("snmp.target.port"));
+ enterpriseOid = config.getProperty("enterprise.oid");
+ community = config.getProperty("snmp.community");
+ version = Integer.parseInt(config.getProperty("snmp.version"));
+
+ } catch (IOException | NumberFormatException e) {
+ throw new RuntimeException("加载配置文件失败", e);
+ }
+ }
+
+ public void loadConfig(Map map) {
+ ip = map.getOrDefault("snmp.target.ip", null);
+// port = Integer.parseInt(map.getOrDefault("snmp.target.port", "0"));
+ enterpriseOid = map.getOrDefault("enterprise.oid", null);
+ community = map.getOrDefault("snmp.community", null);
+// version = Integer.parseInt(map.getOrDefault("snmp.version", "1"));
+ }
+
+ private void initializeSNMP() throws IOException {
+ TransportMapping> transport = new DefaultUdpTransportMapping();
+ snmp = new Snmp(transport);
+ snmp.listen();
+ }
+
+ public void sendTrap(String message, int severity) throws IOException {
+ switch (version) {
+ case SnmpConstants.version1:
+ sendV1Trap(message, severity);
+ break;
+ case SnmpConstants.version2c:
+ sendV2Trap(message, severity);
+ break;
+ default:
+ throw new IllegalArgumentException("不支持的SNMP版本");
+ }
+ }
+
+ private void sendV1Trap(String message, int severity) throws IOException {
+ CommunityTarget target = createCommunityTarget(SnmpConstants.version1);
+ PDUv1 pdu = new PDUv1();
+ configureCommonTrap(pdu, message, severity);
+ pdu.setGenericTrap(PDUv1.ENTERPRISE_SPECIFIC);
+ snmp.send(pdu, target);
+ }
+
+ private void sendV2Trap(String message, int severity) throws IOException {
+ CommunityTarget target = createCommunityTarget(SnmpConstants.version2c);
+ PDU pdu = new PDU();
+ configureCommonTrap(pdu, message, severity);
+ pdu.setType(PDU.TRAP);
+ snmp.send(pdu, target);
+ }
+
+
+ private void configureCommonTrap(PDU pdu, String message, int severity) {
+ pdu.add(new VariableBinding(SnmpConstants.sysUpTime,
+ new TimeTicks(System.currentTimeMillis()/1000)));
+ pdu.add(new VariableBinding(SnmpConstants.snmpTrapOID,
+ new OID(enterpriseOid + ".0.1")));
+ pdu.add(new VariableBinding(new OID(enterpriseOid + ".1.1"),
+ new OctetString(message)));
+ pdu.add(new VariableBinding(new OID(enterpriseOid + ".1.2"),
+ new Integer32(severity)));
+ }
+
+ private CommunityTarget createCommunityTarget(int version) {
+ CommunityTarget target = new CommunityTarget();
+ target.setCommunity(new OctetString(community));
+ target.setAddress(GenericAddress.parse("udp:"+ip+"/"+port));
+ target.setVersion(version);
+ target.setTimeout(5000);
+ target.setRetries(3);
+ return target;
+ }
+
+ public void close() throws IOException {
+ if (snmp != null) {
+ snmp.close();
+ }
+ }
+}
diff --git a/src/main/java/com/sunyard/task/AsyncTaskExecutor.java b/src/main/java/com/sunyard/task/AsyncTaskExecutor.java
new file mode 100644
index 0000000..615c878
--- /dev/null
+++ b/src/main/java/com/sunyard/task/AsyncTaskExecutor.java
@@ -0,0 +1,141 @@
+package com.sunyard.task;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.*;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+public class AsyncTaskExecutor {
+
+ private final ExecutorService taskExecutor;
+ private final ScheduledExecutorService retryScheduler;
+
+ public AsyncTaskExecutor(int taskThreads, int retryThreads) {
+ this.taskExecutor = Executors.newFixedThreadPool(taskThreads);
+ this.retryScheduler = Executors.newScheduledThreadPool(retryThreads);
+ }
+
+ // 默认配置构造函数
+ public AsyncTaskExecutor() {
+ this(4, 2);
+ }
+
+ public List> execTasks(List> taskContexts) {
+ List> futures =
+ taskContexts.stream().map(this::retryWithDelay).collect(Collectors.toList());
+ List> taskOutcomes = new ArrayList<>();
+ taskContexts.forEach(task ->
+ retryWithDelay(task).whenCompleteAsync((result, ex) -> {
+ if (ex != null) {
+ // 提取原始异常
+ Throwable rootCause = ex instanceof CompletionException ? ex.getCause() : ex;
+ taskOutcomes.add(new TaskOutcome<>(task, rootCause));
+ } else {
+ taskOutcomes.add(new TaskOutcome<>(task, result));
+ }
+ }));
+ return taskOutcomes;
+ }
+
+ public List> execBatchTasks(List> taskContexts) {
+ // 转换每个Future为明确的TaskOutcome类型
+ List>> futures = taskContexts.stream()
+ .map(task ->
+ retryWithDelay(task)
+ .>handle((result, ex) -> {
+ if (ex != null) {
+ // 提取原始异常
+ Throwable cause = ex instanceof CompletionException ? ex.getCause() : ex;
+ return new TaskOutcome<>(task, cause);
+ } else {
+ return new TaskOutcome<>(task, result);
+ }
+ })
+ )
+ .collect(Collectors.toList());
+
+ // 等待所有任务完成
+ CompletableFuture allFutures = CompletableFuture.allOf(
+ futures.toArray(new CompletableFuture[0])
+ );
+
+ // 收集处理结果
+ return allFutures.thenApply(v ->
+ futures.stream()
+ .map(CompletableFuture::join)
+ .collect(Collectors.toList())
+ ).join();
+ }
+
+
+ /**
+ * 带延迟的重试逻辑
+ */
+ private CompletableFuture retryWithDelay(TaskContext context) {
+ return CompletableFuture.supplyAsync(context.getTask(), taskExecutor)
+ .handleAsync((result, ex) -> {
+ if (ex == null) {
+ return CompletableFuture.completedFuture(result);
+ } else {
+ return handleRetry(context, ex);
+ }
+ }, taskExecutor)
+ .thenCompose(f -> f);
+ }
+
+
+
+ /**
+ * 处理重试逻辑
+ */
+ private CompletableFuture handleRetry(TaskContext context, Throwable ex) {
+ if (context.canRetry()) {
+ context.decreaseRetry();
+ System.out.printf("[重试调度] ID: %s | 延迟: %ss | 剩余重试: %d | 错误: %s%n",
+ context.getTaskId(),
+ context.getRetryDelay().getSeconds(),
+ context.getRetriesLeft(),
+ ex.getCause().getMessage());
+
+ // 创建延迟重试的Future
+ CompletableFuture delayedRetry = new CompletableFuture<>();
+ retryScheduler.schedule(() -> {
+ retryWithDelay(context).whenComplete((retryResult, retryEx) -> {
+ if (retryEx != null) {
+ delayedRetry.completeExceptionally(retryEx);
+ } else {
+ delayedRetry.complete(retryResult);
+ }
+ });
+ }, context.getRetryDelay().toMillis(), TimeUnit.MILLISECONDS);
+
+ return delayedRetry;
+ } else {
+ // 重试耗尽,传递原始异常
+ CompletableFuture failed = new CompletableFuture<>();
+ failed.completeExceptionally(ex);
+ return failed;
+ }
+ }
+
+ /**
+ * 关闭线程池
+ */
+ public void shutdown() {
+ taskExecutor.shutdown();
+ retryScheduler.shutdown();
+ try {
+ if (!taskExecutor.awaitTermination(1, TimeUnit.SECONDS)) {
+ taskExecutor.shutdownNow();
+ }
+ if (!retryScheduler.awaitTermination(1, TimeUnit.SECONDS)) {
+ retryScheduler.shutdownNow();
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+}
diff --git a/src/main/java/com/sunyard/task/TaskContext.java b/src/main/java/com/sunyard/task/TaskContext.java
new file mode 100644
index 0000000..6d41eb4
--- /dev/null
+++ b/src/main/java/com/sunyard/task/TaskContext.java
@@ -0,0 +1,50 @@
+package com.sunyard.task;
+
+import java.time.Duration;
+import java.util.UUID;
+import java.util.function.Supplier;
+
+public class TaskContext {
+ private final String taskId; // 唯一任务ID
+ private final Supplier task; // 原始任务逻辑
+ private int retriesLeft; // 剩余重试次数
+ private final Duration retryDelay; // 重试延迟
+
+ private final String address;
+
+ public TaskContext(Supplier task, int maxRetries, Duration retryDelay, String address) {
+ this.taskId = UUID.randomUUID().toString();
+ this.task = task;
+ this.retriesLeft = maxRetries;
+ this.retryDelay = retryDelay;
+ this.address = address;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public String getTaskId() {
+ return taskId;
+ }
+
+ public Supplier getTask() {
+ return task;
+ }
+
+ public int getRetriesLeft() {
+ return retriesLeft;
+ }
+
+ public boolean canRetry() {
+ return retriesLeft > 0;
+ }
+
+ public void decreaseRetry() {
+ retriesLeft--;
+ }
+
+ public Duration getRetryDelay() {
+ return retryDelay;
+ }
+}
diff --git a/src/main/java/com/sunyard/task/TaskOutcome.java b/src/main/java/com/sunyard/task/TaskOutcome.java
new file mode 100644
index 0000000..66f7d6b
--- /dev/null
+++ b/src/main/java/com/sunyard/task/TaskOutcome.java
@@ -0,0 +1,35 @@
+package com.sunyard.task;
+
+public class TaskOutcome {
+ private final TaskContext context;
+ private final T result;
+ private final Throwable error;
+
+ public TaskOutcome(TaskContext context, T result) {
+ this.context = context;
+ this.result = result;
+ this.error = null;
+ }
+
+ public TaskOutcome(TaskContext context, Throwable error) {
+ this.context = context;
+ this.result = null;
+ this.error = error;
+ }
+
+ public boolean isSuccess() {
+ return error == null;
+ }
+
+ public TaskContext getContext() {
+ return context;
+ }
+
+ public T getResult() {
+ return result;
+ }
+
+ public Throwable getError() {
+ return error;
+ }
+}
diff --git a/src/main/java/racal/sunyard/main/SydApi4j.java b/src/main/java/racal/sunyard/main/SydApi4j.java
index 5b9de24..f9d80f8 100644
--- a/src/main/java/racal/sunyard/main/SydApi4j.java
+++ b/src/main/java/racal/sunyard/main/SydApi4j.java
@@ -1,11 +1,12 @@
package racal.sunyard.main;
import cn.hutool.core.bean.BeanUtil;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
import com.sunyard.RetWrap;
import com.sunyard.SYMEnDeLongData;
import com.sunyard.SydApi;
import com.sunyard.SydApiException;
-import com.sunyard.cert.Cert;
import com.sunyard.cert.X509;
import com.sunyard.constant.CertUsage;
import com.sunyard.entity.Struct;
@@ -15,11 +16,16 @@ import com.sunyard.proto.Packet;
import com.sunyard.proto.PacketSection;
import com.sunyard.proto.Util;
import com.sunyard.proto.section.SectionValue;
+import com.sunyard.snmp.SnmpTrapSender;
+import com.sunyard.task.TaskContext;
+import com.sunyard.task.AsyncTaskExecutor;
+import com.sunyard.task.TaskOutcome;
import com.sunyard.trans.Alg;
import com.sunyard.trans.FullMode;
import com.sunyard.trans.PacketSN;
import com.sunyard.trans.RoundMode;
import com.sunyard.util.*;
+import org.apache.commons.collections4.CollectionUtils;
import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.cms.SydCmsUtil;
@@ -32,7 +38,6 @@ import racal.sunyard.main.dto.DTO7A;
import racal.sunyard.main.proto.*;
import javax.security.auth.x500.X500Principal;
-import java.beans.FeatureDescriptor;
import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;
@@ -46,7 +51,10 @@ import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
+import java.time.Duration;
import java.util.*;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
import static com.sunyard.util.DnUtil.verifyDn;
import static org.bouncycastle.asn1.ASN1Encoding.DL;
@@ -1296,6 +1304,76 @@ public class SydApi4j implements SydApi {
return dn;
}
+ public void initSnmpTrapConfig(String config) {
+ Gson gson = new Gson();
+ Map map =
+ gson.fromJson(config, new TypeToken