2025-09-08 17:01:50 +08:00
|
|
|
|
package com.yj.earth.common.util;
|
|
|
|
|
|
|
|
|
|
|
|
import com.yj.earth.common.constant.GlobalConstant;
|
|
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
|
import org.yaml.snakeyaml.Yaml;
|
|
|
|
|
|
|
|
|
|
|
|
import java.io.File;
|
2025-09-28 15:10:29 +08:00
|
|
|
|
import java.io.FileInputStream;
|
2025-09-08 17:01:50 +08:00
|
|
|
|
import java.io.IOException;
|
|
|
|
|
|
import java.io.InputStream;
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
|
|
|
|
@Slf4j
|
|
|
|
|
|
public class SdkUtil {
|
|
|
|
|
|
// 保存SDK进程引用
|
|
|
|
|
|
private static Process sdkProcess;
|
|
|
|
|
|
|
|
|
|
|
|
// 对外提供的启动入口
|
|
|
|
|
|
public static void startSdkIfConfigured() throws IOException {
|
2025-09-28 15:10:29 +08:00
|
|
|
|
// 读取SDK端口
|
|
|
|
|
|
Integer serverPort = getServerPortFromSdkConfig();
|
2025-09-08 17:01:50 +08:00
|
|
|
|
// 未配置则不启动
|
2025-09-28 15:10:29 +08:00
|
|
|
|
if (serverPort == null) {
|
|
|
|
|
|
log.info("未配置SDK端口");
|
2025-09-08 17:01:50 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 配置存在时、正常启动SDK
|
2025-09-28 15:10:29 +08:00
|
|
|
|
startSdkJar(serverPort);
|
2025-09-08 17:01:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 接收已确认的端口、启动SDK
|
2025-09-28 15:10:29 +08:00
|
|
|
|
private static void startSdkJar(int serverPort) throws IOException {
|
2025-09-08 17:01:50 +08:00
|
|
|
|
// 获取项目根目录(当前工作目录)
|
|
|
|
|
|
String projectRoot = System.getProperty("user.dir");
|
|
|
|
|
|
// 获取SDK完整路径
|
|
|
|
|
|
String sdkJarPath = new File(projectRoot, GlobalConstant.SDKPATH).getAbsolutePath();
|
|
|
|
|
|
// 校验SDK
|
|
|
|
|
|
File sdkJarFile = new File(sdkJarPath);
|
|
|
|
|
|
if (!sdkJarFile.exists() || !sdkJarFile.isFile()) {
|
|
|
|
|
|
log.error("SDK不存在或不是有效文件:{}", sdkJarPath);
|
2025-09-28 15:10:29 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取SDK所在目录(sdk目录)
|
|
|
|
|
|
File sdkDir = sdkJarFile.getParentFile();
|
|
|
|
|
|
if (sdkDir == null || !sdkDir.exists()) {
|
|
|
|
|
|
log.error("无法获取SDK所在目录:{}", sdkJarPath);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 构建JDK的java可执行文件路径(适配新目录结构)
|
|
|
|
|
|
String javaExecutablePath = getJavaExecutablePath(sdkDir);
|
|
|
|
|
|
File javaExecutable = new File(javaExecutablePath);
|
|
|
|
|
|
if (!javaExecutable.exists() || !javaExecutable.canExecute()) {
|
|
|
|
|
|
log.error("JDK可执行文件不存在或不可执行:{}", javaExecutablePath);
|
|
|
|
|
|
return;
|
2025-09-08 17:01:50 +08:00
|
|
|
|
}
|
2025-09-28 15:10:29 +08:00
|
|
|
|
|
2025-09-08 17:01:50 +08:00
|
|
|
|
log.info("准备启动SDK: {}", sdkJarPath);
|
2025-09-28 15:10:29 +08:00
|
|
|
|
log.info("使用JDK路径: {}", javaExecutablePath);
|
|
|
|
|
|
log.info("使用SDK端口: {}", serverPort);
|
|
|
|
|
|
|
2025-09-08 17:01:50 +08:00
|
|
|
|
// 构建启动命令、添加 -Dserver.port 参数
|
|
|
|
|
|
List<String> command = new ArrayList<>();
|
2025-09-28 15:10:29 +08:00
|
|
|
|
command.add(javaExecutablePath); // 使用指定路径的java
|
|
|
|
|
|
command.add("-Dserver.port=" + serverPort);
|
2025-09-08 17:01:50 +08:00
|
|
|
|
command.add("-jar");
|
|
|
|
|
|
command.add(sdkJarPath);
|
2025-09-28 15:10:29 +08:00
|
|
|
|
|
2025-09-08 17:01:50 +08:00
|
|
|
|
// 构建进程启动器
|
|
|
|
|
|
ProcessBuilder processBuilder = new ProcessBuilder(command);
|
|
|
|
|
|
// 打印执行的命令
|
|
|
|
|
|
String commandStr = command.stream().collect(Collectors.joining(" "));
|
|
|
|
|
|
log.info("执行命令: {}", commandStr);
|
2025-09-28 15:10:29 +08:00
|
|
|
|
|
2025-09-08 17:01:50 +08:00
|
|
|
|
// 输出SDK的控制台日志到当前应用的日志中
|
|
|
|
|
|
processBuilder.redirectErrorStream(true);
|
|
|
|
|
|
// 日志文件路径建议优化: 避免与项目根目录混淆
|
|
|
|
|
|
File sdkLogFile = new File(projectRoot, GlobalConstant.SDKLOG);
|
|
|
|
|
|
// 确保目录存在(避免日志写入失败)
|
|
|
|
|
|
if (!sdkLogFile.getParentFile().exists()) {
|
|
|
|
|
|
sdkLogFile.getParentFile().mkdirs();
|
|
|
|
|
|
}
|
|
|
|
|
|
processBuilder.redirectOutput(sdkLogFile);
|
2025-09-28 15:10:29 +08:00
|
|
|
|
|
2025-09-08 17:01:50 +08:00
|
|
|
|
// 启动进程(非阻塞)
|
|
|
|
|
|
sdkProcess = processBuilder.start();
|
|
|
|
|
|
log.info("SDK已在后台启动、进程ID: {}", sdkProcess.pid());
|
2025-09-28 15:10:29 +08:00
|
|
|
|
|
2025-09-08 17:01:50 +08:00
|
|
|
|
// 注册JVM关闭钩子、在主程序退出时关闭SDK进程
|
|
|
|
|
|
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
|
|
|
|
|
if (sdkProcess != null && sdkProcess.isAlive()) {
|
|
|
|
|
|
log.info("主程序关闭、正在停止SDK进程(PID: {})...", sdkProcess.pid());
|
|
|
|
|
|
// 销毁子进程
|
|
|
|
|
|
sdkProcess.destroy();
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 等待进程终止(最多5秒)
|
|
|
|
|
|
boolean terminated = sdkProcess.waitFor(5, TimeUnit.SECONDS);
|
|
|
|
|
|
if (terminated) {
|
|
|
|
|
|
log.info("SDK进程已成功停止");
|
|
|
|
|
|
} else {
|
2025-09-28 15:10:29 +08:00
|
|
|
|
log.warn("SDK进程未能正常停止、尝试通过端口{}强制终止...", serverPort);
|
2025-09-08 17:01:50 +08:00
|
|
|
|
// 通过端口强制终止
|
2025-09-28 15:10:29 +08:00
|
|
|
|
boolean killSuccess = PortKillUtil.killProcessByPort(serverPort);
|
2025-09-08 17:01:50 +08:00
|
|
|
|
if (killSuccess) {
|
2025-09-28 15:10:29 +08:00
|
|
|
|
log.info("已通过端口{}强制终止SDK进程", serverPort);
|
2025-09-08 17:01:50 +08:00
|
|
|
|
} else {
|
2025-09-28 15:10:29 +08:00
|
|
|
|
log.error("通过端口{}强制终止SDK进程失败", serverPort);
|
2025-09-08 17:01:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
|
|
log.error("停止SDK进程时发生中断", e);
|
|
|
|
|
|
Thread.currentThread().interrupt();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}, "SDK-Process-Shutdown-Hook"));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2025-09-28 15:10:29 +08:00
|
|
|
|
* 根据新目录结构获取JDK的java可执行文件路径
|
2025-09-08 17:01:50 +08:00
|
|
|
|
*/
|
2025-09-28 15:10:29 +08:00
|
|
|
|
private static String getJavaExecutablePath(File sdkDir) {
|
|
|
|
|
|
// 从sdk目录向上两级找到根目录(sdk -> app -> 根目录)
|
|
|
|
|
|
File appDir = sdkDir.getParentFile();
|
|
|
|
|
|
if (appDir == null || !appDir.exists()) {
|
|
|
|
|
|
log.error("无法获取app目录(sdk的父目录)");
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
File rootDir = appDir.getParentFile();
|
|
|
|
|
|
if (rootDir == null || !rootDir.exists()) {
|
|
|
|
|
|
log.error("无法获取根目录(app的父目录)");
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 判断操作系统类型
|
|
|
|
|
|
boolean isWindows = System.getProperty("os.name").toLowerCase().contains("win");
|
|
|
|
|
|
// 构建JDK路径:根目录/jdk/bin/java(或java.exe)
|
|
|
|
|
|
String javaRelativePath = "jdk" + File.separator + "bin" + File.separator +
|
|
|
|
|
|
(isWindows ? "java.exe" : "java");
|
|
|
|
|
|
return new File(rootDir, javaRelativePath).getAbsolutePath();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 从sdk文件夹下的application.yml读取server.port配置
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static Integer getServerPortFromSdkConfig() {
|
2025-09-08 17:01:50 +08:00
|
|
|
|
Yaml yaml = new Yaml();
|
2025-09-28 15:10:29 +08:00
|
|
|
|
String projectRoot = System.getProperty("user.dir");
|
|
|
|
|
|
File sdkConfigFile = new File(projectRoot, "sdk/application.yml");
|
|
|
|
|
|
|
|
|
|
|
|
if (!sdkConfigFile.exists() || !sdkConfigFile.isFile()) {
|
|
|
|
|
|
log.error("配置文件不存在: {}", sdkConfigFile.getAbsolutePath());
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try (InputStream inputStream = new FileInputStream(sdkConfigFile)) {
|
2025-09-08 17:01:50 +08:00
|
|
|
|
// 解析YAML文件为Map
|
|
|
|
|
|
Map<String, Object> yamlMap = yaml.load(inputStream);
|
2025-09-28 15:10:29 +08:00
|
|
|
|
// 逐级获取server.port配置
|
|
|
|
|
|
if (yamlMap.containsKey("server")) {
|
|
|
|
|
|
Object serverObj = yamlMap.get("server");
|
|
|
|
|
|
if (serverObj instanceof Map) {
|
|
|
|
|
|
Map<?, ?> serverMap = (Map<?, ?>) serverObj;
|
|
|
|
|
|
if (serverMap.containsKey("port")) {
|
|
|
|
|
|
return ((Number) serverMap.get("port")).intValue();
|
2025-09-08 17:01:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-28 15:10:29 +08:00
|
|
|
|
log.error("sdk配置文件中未配置server.port");
|
2025-09-08 17:01:50 +08:00
|
|
|
|
} catch (IOException e) {
|
2025-09-28 15:10:29 +08:00
|
|
|
|
log.error("读取sdk配置文件失败", e);
|
|
|
|
|
|
} catch (ClassCastException e) {
|
2025-09-29 13:56:36 +08:00
|
|
|
|
log.error("sdk配置文件中server.port格式错误、应为数字类型", e);
|
2025-09-08 17:01:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|