使Systemd服务安装卸载逻辑在整体程序启动之前运行,以解决使用--install更新服务而现有服务正在运行导致Tomcat绑定端口失败而整体程序退出,未运行处理服务文件的逻辑的故障

This commit is contained in:
2025-09-25 15:57:35 +08:00
parent 386aa84d35
commit 74a4b9a62c
2 changed files with 24 additions and 25 deletions
@@ -1,12 +1,8 @@
package xyz.thewhitedog9487.WebAPI.Startup; package xyz.thewhitedog9487.WebAPI.Startup;
import discord4j.core.GatewayDiscordClient; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.system.ApplicationHome; import org.springframework.boot.system.ApplicationHome;
import org.springframework.stereotype.Component;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@@ -16,8 +12,7 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
@Slf4j @Slf4j
@Component public class SystemdInstallerManager{
class SystemdInstallerManager implements CommandLineRunner {
static String TemplateContent = """ static String TemplateContent = """
[Unit] [Unit]
Description=WebAPI Description=WebAPI
@@ -35,23 +30,22 @@ class SystemdInstallerManager implements CommandLineRunner {
WantedBy=multi-user.target WantedBy=multi-user.target
"""; """;
@Autowired ObjectProvider<GatewayDiscordClient> DiscordClientProvider; static ApplicationHome AppHome = new ApplicationHome( SystemdInstallerManager.class );
ApplicationHome AppHome = new ApplicationHome( SystemdInstallerManager.class ); static String CurrentOS = System.getProperty("os.name");
String CurrentOS = System.getProperty("os.name"); static String CurrentUser = System.getProperty("user.name");
String CurrentUser = System.getProperty("user.name"); static Path CurrentExecutableFilePath = GetExecutblePath();
Path CurrentExecutableFilePath = GetExecutblePath(); static Path WorkingDirectory = AppHome
Path WorkingDirectory = AppHome
.getDir() .getDir()
.toPath(); .toPath();
Path ServiceFileDirectory = Path.of("/etc/systemd/system/"); static Path ServiceFileDirectory = Path.of("/etc/systemd/system/");
Path ServiceFileName = Path.of("WebAPI.service"); static Path ServiceFileName = Path.of("WebAPI.service");
String SoftLinkFileName = "Current"; static String SoftLinkFileName = "Current";
Path SymbolicLinkPath = WorkingDirectory.resolve(SoftLinkFileName); static Path SymbolicLinkPath = WorkingDirectory.resolve(SoftLinkFileName);
/** /**
* 检测当前运行环境是否为GraalVM生成的Native Image * 检测当前运行环境是否为GraalVM生成的Native Image
*/ */
boolean IsImageCode(){ static boolean IsImageCode(){
try { try {
return (boolean) Class.forName("org.graalvm.nativeimage.ImageInfo") return (boolean) Class.forName("org.graalvm.nativeimage.ImageInfo")
.getMethod("inImageCode") .getMethod("inImageCode")
@@ -66,7 +60,7 @@ class SystemdInstallerManager implements CommandLineRunner {
* <br> * <br>
* 如果是直接用JRE运行的JAR包,则返回JAR文件的路径 * 如果是直接用JRE运行的JAR包,则返回JAR文件的路径
*/ */
Path GetExecutblePath(){ static Path GetExecutblePath(){
try { try {
var IsImageCode = IsImageCode(); var IsImageCode = IsImageCode();
log.debug("IsImageCode: {}", IsImageCode); log.debug("IsImageCode: {}", IsImageCode);
@@ -90,15 +84,16 @@ class SystemdInstallerManager implements CommandLineRunner {
} catch (IOException ex) { } catch (IOException ex) {
throw new RuntimeException(ex); } } } throw new RuntimeException(ex); } } }
@Override @SneakyThrows
public void run(String... args) throws Exception { public static void ProcessArguments(String[] CommandLineArguments) {
for (String Argument : args) { log.debug("CommandLineArguments: {}", Arrays.toString(CommandLineArguments));
for (String Argument : CommandLineArguments) {
if ( List.of("install", "--install").contains(Argument) == true ) { if ( List.of("install", "--install").contains(Argument) == true ) {
/* /*
在Native Image / Jar文件旁边生成指向自身的,名为Current的软连接 在Native Image / Jar文件旁边生成指向自身的,名为Current的软连接
然后在软连接旁生成systemd服务文件,执行目标指向软连接 然后在软连接旁生成systemd服务文件,执行目标指向软连接
*/ */
List<String> DiscordBotToken = Arrays.stream(args) List<String> DiscordBotToken = Arrays.stream(CommandLineArguments)
.filter(s -> { .filter(s -> {
for (String o : List.of("--Discord_Bot_Token=")) { for (String o : List.of("--Discord_Bot_Token=")) {
if (s.startsWith(o) == true) { return true; } } if (s.startsWith(o) == true) { return true; } }
@@ -117,6 +112,8 @@ class SystemdInstallerManager implements CommandLineRunner {
log.debug("CurrentExecutableFilePath: {}", CurrentExecutableFilePath); log.debug("CurrentExecutableFilePath: {}", CurrentExecutableFilePath);
log.debug("WorkingDirectory: {}", WorkingDirectory); log.debug("WorkingDirectory: {}", WorkingDirectory);
log.debug("SymbolicLinkPath: {}", SymbolicLinkPath); log.debug("SymbolicLinkPath: {}", SymbolicLinkPath);
new ProcessBuilder("systemctl", "stop", ServiceFileName.toString()).start().waitFor();
log.info("已停止systemd服务: {}", ServiceFileName);
Files.deleteIfExists(SymbolicLinkPath); Files.deleteIfExists(SymbolicLinkPath);
Files.createSymbolicLink(SymbolicLinkPath, CurrentExecutableFilePath); Files.createSymbolicLink(SymbolicLinkPath, CurrentExecutableFilePath);
log.info("成功创建指向当前程序的软链接: {} -> {}", SymbolicLinkPath, CurrentExecutableFilePath); log.info("成功创建指向当前程序的软链接: {} -> {}", SymbolicLinkPath, CurrentExecutableFilePath);
@@ -2,13 +2,15 @@ package xyz.thewhitedog9487.WebAPI;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import xyz.thewhitedog9487.WebAPI.Startup.SystemdInstallerManager;
@SpringBootApplication @SpringBootApplication
public class WebApiApplication { public class WebApiApplication {
public static void main(String[] args) { public static void main(String[] args) {
//TODO: 从配置文件或环境变量读取并设置HTTP端口号 //TODO: 从配置文件或环境变量读取并设置HTTP端口号
SpringApplication.run(WebApiApplication.class, args); var SpringApp = new SpringApplication(WebApiApplication.class);
SystemdInstallerManager.ProcessArguments(args);
SpringApp.run(args);
} }
} }