Skip to content

Commit ce7e0a5

Browse files
committed
feat(Restart): support restart
1 parent e49ebfd commit ce7e0a5

File tree

2 files changed

+138
-2
lines changed

2 files changed

+138
-2
lines changed

src/main/java/chiloven/xamlsorter/Main.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
import chiloven.xamlsorter.modules.DataOperationHelper;
66
import chiloven.xamlsorter.modules.I18n;
77
import chiloven.xamlsorter.modules.PreferencesManager;
8-
import chiloven.xamlsorter.utils.TaskExecutorService;
98
import chiloven.xamlsorter.modules.preferences.Language;
109
import chiloven.xamlsorter.ui.MainPage;
10+
import chiloven.xamlsorter.utils.RestartHelper;
1111
import chiloven.xamlsorter.utils.ShowAlert;
12+
import chiloven.xamlsorter.utils.TaskExecutorService;
1213
import javafx.application.Application;
1314
import javafx.scene.Scene;
1415
import javafx.scene.image.Image;
@@ -25,6 +26,8 @@ public class Main extends Application {
2526
public static Stage primaryStage;
2627
public static final String version = "Beta.0.3.0";
2728

29+
private static volatile boolean restartRequested = false;
30+
2831
public static void main(String[] args) {
2932
launch(args);
3033
}
@@ -35,6 +38,12 @@ public static void safeClose() {
3538
}
3639
}
3740

41+
public static void safeRestart() {
42+
logger.info("Safe restart requested.");
43+
restartRequested = true;
44+
safeClose();
45+
}
46+
3847
public static void applyTheme() {
3948
boolean isDark = PreferencesManager.isDarkMode();
4049
logger.info("Applying {} theme", isDark ? "dark" : "light");
@@ -83,6 +92,7 @@ public void start(Stage primaryStage) {
8392
boolean canExit = mainPage.promptSaveIfNeeded();
8493
if (!canExit) {
8594
logger.info("Exit cancelled by user.");
95+
restartRequested = false;
8696
event.consume();
8797
} else {
8898
PreferencesManager.removeThemeChangeListener(Main::applyTheme);
@@ -101,7 +111,6 @@ public void start(Stage primaryStage) {
101111

102112
primaryStage.setScene(scene);
103113
primaryStage.show();
104-
105114
logger.info("Application UI loaded successfully");
106115
} catch (Exception e) {
107116
logger.fatal("Error loading UI", e);
@@ -118,6 +127,17 @@ public void start(Stage primaryStage) {
118127
public void stop() {
119128
logger.info("Stopping xamlSorter.Java application");
120129
TaskExecutorService.shutdown();
130+
131+
if (restartRequested) {
132+
logger.info("Relaunching application...");
133+
try {
134+
RestartHelper.relaunchCurrentApp();
135+
logger.info("Relaunch command issued successfully.");
136+
} catch (Exception ex) {
137+
logger.error("Failed to relaunch application.", ex);
138+
}
139+
}
140+
121141
logger.info("Application stopped successfully");
122142
}
123143

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package chiloven.xamlsorter.utils;
2+
3+
import chiloven.xamlsorter.Main;
4+
import org.apache.logging.log4j.LogManager;
5+
import org.apache.logging.log4j.Logger;
6+
7+
import java.io.File;
8+
import java.net.URISyntaxException;
9+
import java.nio.file.Paths;
10+
import java.util.ArrayList;
11+
import java.util.List;
12+
13+
public final class RestartHelper {
14+
15+
private static final Logger logger = LogManager.getLogger(RestartHelper.class);
16+
17+
private RestartHelper() {
18+
}
19+
20+
/**
21+
* Relaunches the current application.
22+
* This method first attempts to use the jpackage launcher if available.
23+
* If the jpackage launcher is not available or not executable,
24+
* it falls back to relaunching the application using the Java runtime.
25+
*
26+
* @throws Exception if an error occurs during the relaunch process
27+
*/
28+
public static void relaunchCurrentApp() throws Exception {
29+
// jpackage
30+
String launcher = System.getProperty("jpackage.app-path");
31+
if (launcher != null && !launcher.isBlank()) {
32+
File launcherFile = new File(launcher);
33+
if (launcherFile.exists() && launcherFile.canExecute()) {
34+
ProcessBuilder pb = new ProcessBuilder(launcherFile.getAbsolutePath());
35+
pb.directory(launcherFile.getParentFile());
36+
pb.inheritIO();
37+
logger.debug("Relaunch via jpackage launcher: {}", launcherFile.getAbsolutePath());
38+
pb.start();
39+
return;
40+
} else {
41+
logger.warn("jpackage launcher not executable or missing: {}", launcher);
42+
}
43+
} else {
44+
logger.debug("System property 'jpackage.app-path' not present; fallback to Java relaunch.");
45+
}
46+
47+
// IDE or java -jar
48+
List<String> cmd = new ArrayList<>();
49+
cmd.add(getJavaBin());
50+
51+
if (isRunningFromJar()) {
52+
String jarPath = getRunningJarPath();
53+
cmd.add("-jar");
54+
cmd.add(jarPath);
55+
} else {
56+
String classpath = System.getProperty("java.class.path");
57+
cmd.add("-cp");
58+
cmd.add(classpath);
59+
cmd.add(Main.class.getName());
60+
}
61+
62+
ProcessBuilder pb = new ProcessBuilder(cmd);
63+
pb.directory(new File(System.getProperty("user.dir")));
64+
pb.inheritIO();
65+
logger.debug("Relaunch via Java fallback: {}", String.join(" ", cmd));
66+
pb.start();
67+
}
68+
69+
/**
70+
* Returns the path to the Java binary executable.
71+
*
72+
* @return the absolute path to the Java binary
73+
*/
74+
private static String getJavaBin() {
75+
String javaHome = System.getProperty("java.home");
76+
String exe = isWindows() ? "java.exe" : "java";
77+
File bin = Paths.get(javaHome, "bin", exe).toFile();
78+
return bin.getAbsolutePath();
79+
}
80+
81+
/**
82+
* Checks if the application is running from a JAR file.
83+
*
84+
* @return true if running from a JAR, false otherwise
85+
*/
86+
private static boolean isRunningFromJar() {
87+
try {
88+
String path = Main.class.getProtectionDomain()
89+
.getCodeSource().getLocation().toURI().getPath();
90+
return path != null && path.endsWith(".jar");
91+
} catch (URISyntaxException e) {
92+
return false;
93+
}
94+
}
95+
96+
/**
97+
* Gets the absolute path of the currently running JAR file.
98+
*
99+
* @return the absolute path of the running JAR
100+
* @throws URISyntaxException if the URI syntax is incorrect
101+
*/
102+
private static String getRunningJarPath() throws URISyntaxException {
103+
return new File(Main.class.getProtectionDomain()
104+
.getCodeSource().getLocation().toURI()).getAbsolutePath();
105+
}
106+
107+
/**
108+
* Checks if the current operating system is Windows.
109+
*
110+
* @return true if the OS is Windows, false otherwise
111+
*/
112+
private static boolean isWindows() {
113+
String os = System.getProperty("os.name", "").toLowerCase();
114+
return os.contains("win");
115+
}
116+
}

0 commit comments

Comments
 (0)