Files
yjearth/src/main/java/com/yj/earth/common/util/SdkUtil.java
2025-11-25 14:27:10 +08:00

183 lines
7.3 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
import java.io.FileInputStream;
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 {
// 读取SDK端口
Integer serverPort = getServerPortFromSdkConfig();
// 未配置则不启动
if (serverPort == null) {
log.info("未配置SDK端口");
return;
}
// 配置存在时、正常启动SDK
startSdkJar(serverPort);
}
// 接收已确认的端口、启动SDK
private static void startSdkJar(int serverPort) throws IOException {
// 获取项目根目录(当前工作目录)
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);
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;
}
log.info("准备启动SDK: {}", sdkJarPath);
log.info("使用JDK路径: {}", javaExecutablePath);
log.info("使用SDK端口: {}", serverPort);
// 构建启动命令、添加 -Dserver.port 参数
List<String> command = new ArrayList<>();
command.add(javaExecutablePath); // 使用指定路径的java
command.add("-Dserver.port=" + serverPort);
command.add("-jar");
command.add(sdkJarPath);
// 构建进程启动器
ProcessBuilder processBuilder = new ProcessBuilder(command);
// 打印执行的命令
String commandStr = command.stream().collect(Collectors.joining(" "));
log.info("执行命令: {}", commandStr);
// 输出SDK的控制台日志到当前应用的日志中
processBuilder.redirectErrorStream(true);
// 日志文件路径建议优化: 避免与项目根目录混淆
File sdkLogFile = new File(projectRoot, GlobalConstant.SDKLOG);
// 确保目录存在(避免日志写入失败)
if (!sdkLogFile.getParentFile().exists()) {
sdkLogFile.getParentFile().mkdirs();
}
processBuilder.redirectOutput(sdkLogFile);
// 启动进程(非阻塞)
sdkProcess = processBuilder.start();
log.info("SDK已在后台启动、进程ID: {}", sdkProcess.pid());
// 注册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 {
log.warn("SDK进程未能正常停止、尝试通过端口{}强制终止...", serverPort);
// 通过端口强制终止
boolean killSuccess = PortKillUtil.killProcessByPort(serverPort);
if (killSuccess) {
log.info("已通过端口{}强制终止SDK进程", serverPort);
} else {
log.error("通过端口{}强制终止SDK进程失败", serverPort);
}
}
} catch (InterruptedException e) {
log.error("停止SDK进程时发生中断", e);
Thread.currentThread().interrupt();
}
}
}, "SDK-Process-Shutdown-Hook"));
}
/**
* 根据新目录结构获取JDK的java可执行文件路径
*/
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() {
Yaml yaml = new Yaml();
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)) {
// 解析YAML文件为Map
Map<String, Object> yamlMap = yaml.load(inputStream);
// 逐级获取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();
}
}
}
log.error("sdk 配置文件中未配置 server.port");
} catch (IOException e) {
log.error("读取 sdk 配置文件失败", e);
} catch (ClassCastException e) {
log.error("sdk 配置文件中 server.port 格式错误、应为数字类型", e);
}
return null;
}
}