如何重新启动 Java 应用程序?

发布于 2024-10-01 16:34:59 字数 142 浏览 2 评论 0原文

如何重新启动 Java AWT 应用程序?我有一个按钮,我已将事件处理程序附加到该按钮。我应该使用什么代码来重新启动应用程序?

我想要执行与 Application.Restart() 在 C# 应用程序中执行的操作相同的操作。

How can I restart a Java AWT application? I have a button to which I have attached an event handler. What code should I use to restart the application?

I want to do the same thing that Application.Restart() do in a C# application.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

萌辣 2024-10-08 16:34:59

当然,可以重新启动 Java 应用程序。

以下方法显示了重新启动 Java 应用程序的方法:

public void restartApplication()
{
  final String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
  final File currentJar = new File(MyClassInTheJar.class.getProtectionDomain().getCodeSource().getLocation().toURI());

  /* is it a jar file? */
  if(!currentJar.getName().endsWith(".jar"))
    return;

  /* Build command: java -jar application.jar */
  final ArrayList<String> command = new ArrayList<String>();
  command.add(javaBin);
  command.add("-jar");
  command.add(currentJar.getPath());

  final ProcessBuilder builder = new ProcessBuilder(command);
  builder.start();
  System.exit(0);
}

基本上它执行以下操作:

  1. 查找 java 可执行文件(我在这里使用了 java 二进制文件,但这取决于您的要求)
  2. 查找应用程序(在我的例子中是一个 jar,使用 < code>MyClassInTheJar 类来查找 jar 位置本身)
  3. 构建一个命令来重新启动 jar(在本例中使用 java 二进制文件)
  4. 执行它! (从而终止当前应用程序并再次启动)

Of course it is possible to restart a Java application.

The following method shows a way to restart a Java application:

public void restartApplication()
{
  final String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
  final File currentJar = new File(MyClassInTheJar.class.getProtectionDomain().getCodeSource().getLocation().toURI());

  /* is it a jar file? */
  if(!currentJar.getName().endsWith(".jar"))
    return;

  /* Build command: java -jar application.jar */
  final ArrayList<String> command = new ArrayList<String>();
  command.add(javaBin);
  command.add("-jar");
  command.add(currentJar.getPath());

  final ProcessBuilder builder = new ProcessBuilder(command);
  builder.start();
  System.exit(0);
}

Basically it does the following:

  1. Find the java executable (I used the java binary here, but that depends on your requirements)
  2. Find the application (a jar in my case, using the MyClassInTheJar class to find the jar location itself)
  3. Build a command to restart the jar (using the java binary in this case)
  4. Execute it! (and thus terminating the current application and starting it again)
把梦留给海 2024-10-08 16:34:59
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;

public class Main {
    public static void main(String[] args) throws IOException, InterruptedException {
        StringBuilder cmd = new StringBuilder();
        cmd.append(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java ");
        for (String jvmArg : ManagementFactory.getRuntimeMXBean().getInputArguments()) {
            cmd.append(jvmArg + " ");
        }
        cmd.append("-cp ").append(ManagementFactory.getRuntimeMXBean().getClassPath()).append(" ");
        cmd.append(Main.class.getName()).append(" ");
        for (String arg : args) {
            cmd.append(arg).append(" ");
        }
        Runtime.getRuntime().exec(cmd.toString());
        System.exit(0);
    }
}

献给所有说不可能的人。

该程序收集所有可用于重建原始命令行的信息。然后,它启动它,并且由于它是完全相同的命令,因此您的应用程序会第二次启动。然后我们退出原始程序,子程序仍然运行(即使在 Linux 下)并执行相同的操作。

警告:如果您运行此命令,请注意它永远不会结束创建新进程,类似于

import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;

public class Main {
    public static void main(String[] args) throws IOException, InterruptedException {
        StringBuilder cmd = new StringBuilder();
        cmd.append(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java ");
        for (String jvmArg : ManagementFactory.getRuntimeMXBean().getInputArguments()) {
            cmd.append(jvmArg + " ");
        }
        cmd.append("-cp ").append(ManagementFactory.getRuntimeMXBean().getClassPath()).append(" ");
        cmd.append(Main.class.getName()).append(" ");
        for (String arg : args) {
            cmd.append(arg).append(" ");
        }
        Runtime.getRuntime().exec(cmd.toString());
        System.exit(0);
    }
}

Dedicated to all those who say it is impossible.

This program collects all information available to reconstruct the original commandline. Then, it launches it and since it is the very same command, your application starts a second time. Then we exit the original program, the child program remains running (even under Linux) and does the very same thing.

WARNING: If you run this, be aware that it never ends creating new processes, similar to a fork bomb.

淑女气质 2024-10-08 16:34:59

基本上,你不能。至少不是以可靠的方式。但是,您不需要这样做。

cant 部分

要重新启动 Java 程序,您需要重新启动 JVM。要重新启动 JVM,您需要

  1. 找到所使用的 java 启动器。您可以尝试使用 System.getProperty("java.home") 但不能保证这实际上会指向用于启动您的应用程序的启动器。 (返回的值可能不会指向用于启动应用程序的 JRE,或者它可能已被 -Djava.home 覆盖。)

  2. 您可能希望遵循原始内存设置等(- Xmx-Xms、...),因此您需要弄清楚使用哪些设置来启动第一个 JVM。您可以尝试使用 ManagementFactory.getRuntimeMXBean().getInputArguments() 但不能保证这会反映所使用的设置。这甚至在 该方法的文档

    <块引用>

    通常,并非“java”命令的所有命令行选项都会传递到 Java 虚拟机。因此,返回的输入参数可能不包括所有命令行选项。

  3. 如果您的程序从 Standard.in 读取输入,则原始标准输入将在重新启动时丢失。

  4. 在存在 SecurityManager 的情况下,许多这些技巧和技巧都会失败。

不应该部分

我建议您设计您的应用程序,以便轻松清理所有内容,然后创建“主”类的新实例。

许多应用程序的设计目的只是在主方法中创建一个实例:

public class MainClass {
    ...
    public static void main(String[] args) {
        new MainClass().launch();
    }
    ...
}

通过使用此模式,应该很容易执行以下操作:

public class MainClass {
    ...
    public static void main(String[] args) {
        boolean restart;
        do {
            restart = new MainClass().launch();
        } while (restart);
    }
    ...
}

并让 launch() 返回 true 当且仅当应用程序被关闭,需要重新启动。

Basically, you can't. At least not in a reliable way. However, you shouldn't need to.

The can't part

To restart a Java program, you need to restart the JVM. To restart the JVM you need to

  1. Locate the java launcher that was used. You may try with System.getProperty("java.home") but there's no guarantee that this will actually point to the launcher that was used to launch your application. (The value returned may not point to the JRE used to launch the application or it could have been overridden by -Djava.home.)

  2. You would presumably want to honor the original memory settings etc (-Xmx, -Xms, …) so you need to figure out which settings where used to start the first JVM. You could try using ManagementFactory.getRuntimeMXBean().getInputArguments() but there's no guarantee that this will reflect the settings used. This is even spelled out in the documentation of that method:

    Typically, not all command-line options to the 'java' command are passed to the Java virtual machine. Thus, the returned input arguments may not include all command-line options.

  3. If your program reads input from Standard.in the original stdin will be lost in the restart.

  4. Lots of these tricks and hacks will fail in the presence of a SecurityManager.

The shouldn't need part

I recommend you to design your application so that it is easy to clean every thing up and after that create a new instance of your "main" class.

Many applications are designed to do nothing but create an instance in the main-method:

public class MainClass {
    ...
    public static void main(String[] args) {
        new MainClass().launch();
    }
    ...
}

By using this pattern, it should be easy enough to do something like:

public class MainClass {
    ...
    public static void main(String[] args) {
        boolean restart;
        do {
            restart = new MainClass().launch();
        } while (restart);
    }
    ...
}

and let launch() return true if and only if the application was shut down in a way that it needs to be restarted.

自我难过 2024-10-08 16:34:59

严格来说,Java 程序无法自行重新启动,因为为此它必须杀死正在运行的 JVM,然后重新启动它,但是一旦 JVM 不再运行(被杀死),则无法采取任何操作。

您可以使用自定义类加载器执行一些技巧来再次加载、打包和启动 AWT 组件,但这可能会导致 GUI 事件循环方面的许多麻烦。

根据应用程序的启动方式,您可以在包含 do/while 循环的包装器脚本中启动 JVM,该循环在 JVM 以特定代码退出时继续,然后 AWT 应用程序必须调用 System.exit (RESTART_CODE)。例如,在编写伪代码脚本中:

DO
  # Launch the awt program
  EXIT_CODE = # Get the exit code of the last process
WHILE (EXIT_CODE == RESTART_CODE)

AWT 应用程序应在不需要重新启动的“正常”终止时使用 RESTART_CODE 以外的其他内容退出 JVM。

Strictly speaking, a Java program cannot restart itself since to do so it must kill the JVM in which it is running and then start it again, but once the JVM is no longer running (killed) then no action can be taken.

You could do some tricks with custom classloaders to load, pack, and start the AWT components again but this will likely cause lots of headaches with regard to the GUI event loop.

Depending on how the application is launched, you could start the JVM in a wrapper script which contains a do/while loop, which continues while the JVM exits with a particular code, then the AWT app would have to call System.exit(RESTART_CODE). For example, in scripting pseudocode:

DO
  # Launch the awt program
  EXIT_CODE = # Get the exit code of the last process
WHILE (EXIT_CODE == RESTART_CODE)

The AWT app should exit the JVM with something other than the RESTART_CODE on "normal" termination which doesn't require restart.

我最亲爱的 2024-10-08 16:34:59

Eclipse 通常会在安装插件后重新启动。他们使用 Windows 的包装器 eclipse.exe(启动器应用程序)来完成此操作。此应用程序执行核心 eclipse runner jar,如果 eclipse java 应用程序因重新启动代码而终止,则 eclipse.exe 会重新启动工作台。您可以构建类似的本机代码、shell 脚本或另一个 java 代码包装器来实现重新启动。

Eclipse typically restarts after a plugin is installed. They do this using a wrapper eclipse.exe (launcher app) for windows. This application execs the core eclipse runner jar and if the eclipse java application terminates with a relaunch code, eclipse.exe restarts the workbench. You can build a similar bit of native code, shell script or another java code wrapper to achieve the restart.

追我者格杀勿论 2024-10-08 16:34:59

Windows

public void restartApp(){

    // This launches a new instance of application dirctly, 
    // remember to add some sleep to the start of the cmd file to make sure current instance is
    // completely terminated, otherwise 2 instances of the application can overlap causing strange
    // things:)

    new ProcessBuilder("cmd","/c start /min c:/path/to/script/that/launches/my/application.cmd ^& exit").start();
    System.exit(0);
}

/min 在最小化窗口中启动脚本

后退出以关闭 cmd 窗口

^& 完成示例 cmd 脚本

@echo off
rem add some sleep (e.g. 10 seconds) to allow the preceding application instance to release any open resources (like ports) and exit gracefully, otherwise the new instance could fail to start
sleep 10   
set path=C:\someFolder\application_lib\libs;%path%
java -jar application.jar

睡眠10 睡眠10秒

Windows

public void restartApp(){

    // This launches a new instance of application dirctly, 
    // remember to add some sleep to the start of the cmd file to make sure current instance is
    // completely terminated, otherwise 2 instances of the application can overlap causing strange
    // things:)

    new ProcessBuilder("cmd","/c start /min c:/path/to/script/that/launches/my/application.cmd ^& exit").start();
    System.exit(0);
}

/min to start script in minimized window

^& exit to close cmd window after finish

a sample cmd script could be

@echo off
rem add some sleep (e.g. 10 seconds) to allow the preceding application instance to release any open resources (like ports) and exit gracefully, otherwise the new instance could fail to start
sleep 10   
set path=C:\someFolder\application_lib\libs;%path%
java -jar application.jar

sleep 10 sleep for 10 seconds

最后的乘客 2024-10-08 16:34:59

只是添加其他答案中没有的信息。

如果 procfs /proc/self/cmdline 可用

如果您在提供 procfs ,因此具有可用的 /proc 文件系统(这意味着这不是一个可移植的解决方案),您可以让 Java 读取 /proc/self/cmdline 以便重新启动自身,如下所示:

public static void restart() throws IOException {
    new ProcessBuilder(getMyOwnCmdLine()).inheritIO().start();
}
public static String[] getMyOwnCmdLine() throws IOException {
    return readFirstLine("/proc/self/cmdline").split("\u0000");
}
public static String readFirstLine(final String filename) throws IOException {
    try (final BufferedReader in = new BufferedReader(new FileReader(filename))) {
        return in.readLine();
    }
}

在具有 /proc/self/cmdline 可用的系统上,这可能是最优雅的方式如何从 Java“重新启动”当前 Java 进程。不涉及 JNI,也无需猜测路径和其他内容。这还将处理传递给 java 二进制文件的所有 JVM 选项。该命令行将与当前 JVM 进程的命令行完全相同。

如今,包括 GNU/Linux(包括 Android)在内的许多 UNIX 系统都有 procfs 然而,在 FreeBSD 等一些系统上,它已被弃用并被逐步淘汰。 Mac OS X 是一个例外,因为它没有 procfsWindows也没有procfs。 Cygwin 有 procfs,但它对 Java 来说是不可见的,因为它只对使用 Cygwin DLL 而不是 Windows 系统调用的应用程序可见,而 Java 不知道 Cygwin。

不要忘记使用 ProcessBuilder.inheritIO()

默认是 stdin / stdout / stderr (在已启动进程的 Java 调用 System.in / System.out / System.err)被设置为管道它允许当前正在运行的进程与新启动的进程进行通信。如果您想重新启动当前进程,这很可能不是您想要的。相反,您希望 stdin / stdout / stderr 与当前虚拟机的相同。这称为继承。您可以通过调用 ProcessBuilder 实例的 inheritIO() 来完成此操作。

Windows 上的陷阱

restart() 函数的一个常见用例是在更新后重新启动应用程序。上次我在 Windows 上尝试此操作时出现了问题。当使用新版本覆盖应用程序的 .jar 文件时,应用程序开始出现行为异常并给出有关 .jar 文件的异常。我只是告诉你,以防这是你的用例。当时,我通过将应用程序包装在批处理文件中并使用我在批处理文件中查询的 System.exit() 的神奇返回值来解决这个问题,并让批处理文件重新启动应用程序。

Just adding information which is not present in other answers.

If procfs /proc/self/cmdline is available

If you are running in an environment which provides procfs and therefore has the /proc file system available (which means this is not a portable solution), you can have Java read /proc/self/cmdline in order to restart itself, like this:

public static void restart() throws IOException {
    new ProcessBuilder(getMyOwnCmdLine()).inheritIO().start();
}
public static String[] getMyOwnCmdLine() throws IOException {
    return readFirstLine("/proc/self/cmdline").split("\u0000");
}
public static String readFirstLine(final String filename) throws IOException {
    try (final BufferedReader in = new BufferedReader(new FileReader(filename))) {
        return in.readLine();
    }
}

On systems with /proc/self/cmdline available, this probably is the most elegant way of how to "restart" the current Java process from Java. No JNI involved, and no guessing of paths and stuff required. This will also take care of all JVM options passed to the java binary. The command line will be exactly identical to the one of the current JVM process.

Many UNIX systems including GNU/Linux (including Android) nowadays have procfs However on some like FreeBSD, it is deprecated and being phased out. Mac OS X is an exception in the sense that it does not have procfs. Windows also does not have procfs. Cygwin has procfs but it's invisible to Java because it's only visible to applications using the Cygwin DLLs instead of Windows system calls, and Java is unaware of Cygwin.

Don't forget to use ProcessBuilder.inheritIO()

The default is that stdin / stdout / stderr (in Java called System.in / System.out / System.err) of the started Process are set to pipes which allow the currently running process to communicate with the newly started process. If you want to restart the current process, this is most likely not what you want. Instead you would want that stdin / stdout / stderr are the same as those of the current VM. This is called inherited. You can do so by calling inheritIO() of your ProcessBuilder instance.

Pitfall on Windows

A frequent use case of a restart() function is to restart the application after an update. The last time I tried this on Windows this was problematic. When overwrote the application's .jar file with the new version, the application started to misbehave and giving exceptions about the .jar file. I'm just telling, in case this is your use case. Back then I solved the issue by wrapping the application in a batch file and using a magic return value from System.exit() that I queried in the batch file and had the batch file restart the application instead.

酸甜透明夹心 2024-10-08 16:34:59

尽管这个问题很老并且已经得到解答,但我偶然发现了一些解决方案的问题,并决定将我的建议添加到其中。

一些解决方案的问题在于它们构建了单个命令字符串。当某些参数包含空格时,尤其是java.home,这会产生问题。

例如,在 Windows 上,该行

final String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";

可能返回如下内容:C:\Program Files\Java\jre7\bin\java

由于 < 中的空格,该字符串必须用引号括起来或转义代码>程序文件。这不是一个大问题,但有点烦人和容易出错,特别是在跨平台应用程序中。

因此,我的解决方案将命令构建为命令的数组

public static void restart(String[] args) {

        ArrayList<String> commands = new ArrayList<String>(4 + jvmArgs.size() + args.length);
        List<String> jvmArgs = ManagementFactory.getRuntimeMXBean().getInputArguments();

        // Java
        commands.add(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java");

        // Jvm arguments
        for (String jvmArg : jvmArgs) {
            commands.add(jvmArg);
        }

        // Classpath
        commands.add("-cp");
        commands.add(ManagementFactory.getRuntimeMXBean().getClassPath());

        // Class to be executed
        commands.add(BGAgent.class.getName());

        // Command line arguments
        for (String arg : args) {
            commands.add(arg);
        }

        File workingDir = null; // Null working dir means that the child uses the same working directory

        String[] env = null; // Null env means that the child uses the same environment

        String[] commandArray = new String[commands.size()];
        commandArray = commands.toArray(commandArray);

        try {
            Runtime.getRuntime().exec(commandArray, env, workingDir);
            System.exit(0);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

Although this question is old and answered, I've stumbled across a problem with some of the solutions and decided to add my suggestion into the mix.

The problem with some of the solutions is that they build a single command string. This creates issues when some parameters contain spaces, especially java.home.

For example, on windows, the line

final String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";

Might return something like this:C:\Program Files\Java\jre7\bin\java

This string has to be wrapped in quotes or escaped due to the space in Program Files. Not a huge problem, but somewhat annoying and error prone, especially in cross platform applications.

Therefore my solution builds the command as an array of commands:

public static void restart(String[] args) {

        ArrayList<String> commands = new ArrayList<String>(4 + jvmArgs.size() + args.length);
        List<String> jvmArgs = ManagementFactory.getRuntimeMXBean().getInputArguments();

        // Java
        commands.add(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java");

        // Jvm arguments
        for (String jvmArg : jvmArgs) {
            commands.add(jvmArg);
        }

        // Classpath
        commands.add("-cp");
        commands.add(ManagementFactory.getRuntimeMXBean().getClassPath());

        // Class to be executed
        commands.add(BGAgent.class.getName());

        // Command line arguments
        for (String arg : args) {
            commands.add(arg);
        }

        File workingDir = null; // Null working dir means that the child uses the same working directory

        String[] env = null; // Null env means that the child uses the same environment

        String[] commandArray = new String[commands.size()];
        commandArray = commands.toArray(commandArray);

        try {
            Runtime.getRuntime().exec(commandArray, env, workingDir);
            System.exit(0);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
黑色毁心梦 2024-10-08 16:34:59

如果您确实需要重新启动您的应用程序,您可以编写一个单独的应用程序来启动它...

此页面为不同场景提供了许多不同的示例:

http://www.rgagnon.com/javadetails/java-0014.html

If you realy need to restart your app, you could write a separate app the start it...

This page provides many different examples for different scenarios:

http://www.rgagnon.com/javadetails/java-0014.html

一梦等七年七年为一梦 2024-10-08 16:34:59

与尤达的“改进”答案类似,但有进一步的改进(功能、可读性和可测试性)。现在可以安全运行,并且可以按照给定的程序参数数量重新启动多次。

  • 没有 JAVA_TOOL_OPTIONS 选项的累积。
  • 自动找到主类。
  • 继承当前的 stdout/stderr。

public static void main(String[] args) throws Exception {
    if (args.length == 0)
        return;
    else
        args = Arrays.copyOf(args, args.length - 1);

    List<String> command = new ArrayList<>(32);
    appendJavaExecutable(command);
    appendVMArgs(command);
    appendClassPath(command);
    appendEntryPoint(command);
    appendArgs(command, args);

    System.out.println(command);
    try {
        new ProcessBuilder(command).inheritIO().start();
    } catch (IOException ex) {
        ex.printStackTrace();
    }
}

private static void appendJavaExecutable(List<String> cmd) {
    cmd.add(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java");
}

private static void appendVMArgs(Collection<String> cmd) {
    Collection<String> vmArguments = ManagementFactory.getRuntimeMXBean().getInputArguments();

    String javaToolOptions = System.getenv("JAVA_TOOL_OPTIONS");
    if (javaToolOptions != null) {
        Collection<String> javaToolOptionsList = Arrays.asList(javaToolOptions.split(" "));
        vmArguments = new ArrayList<>(vmArguments);
        vmArguments.removeAll(javaToolOptionsList);
    }

    cmd.addAll(vmArguments);
}

private static void appendClassPath(List<String> cmd) {
    cmd.add("-cp");
    cmd.add(ManagementFactory.getRuntimeMXBean().getClassPath());
}

    private static void appendEntryPoint(List<String> cmd) {
    StackTraceElement[] stackTrace          = new Throwable().getStackTrace();
    StackTraceElement   stackTraceElement   = stackTrace[stackTrace.length - 1];
    String              fullyQualifiedClass = stackTraceElement.getClassName();
    String              entryMethod         = stackTraceElement.getMethodName();
    if (!entryMethod.equals("main"))
        throw new AssertionError("Entry point is not a 'main()': " + fullyQualifiedClass + '.' + entryMethod);

    cmd.add(fullyQualifiedClass);
}

private static void appendArgs(List<String> cmd, String[] args) {
    cmd.addAll(Arrays.asList(args));
}

V1.1 错误修正:如果未设置 JAVA_TOOL_OPTIONS 则为空指针


示例:

$ java -cp Temp.jar Temp a b c d e
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a, b, c, d]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a, b, c]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a, b]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp]
$

Similar to Yoda's 'improved' answer, but with further improvements (both functional, readability, and testability). It's now safe to run, and restarts for as as many times as the amount of program arguments given.

  • No accumulation of JAVA_TOOL_OPTIONS options.
  • Automatically finds main class.
  • Inherits current stdout/stderr.

public static void main(String[] args) throws Exception {
    if (args.length == 0)
        return;
    else
        args = Arrays.copyOf(args, args.length - 1);

    List<String> command = new ArrayList<>(32);
    appendJavaExecutable(command);
    appendVMArgs(command);
    appendClassPath(command);
    appendEntryPoint(command);
    appendArgs(command, args);

    System.out.println(command);
    try {
        new ProcessBuilder(command).inheritIO().start();
    } catch (IOException ex) {
        ex.printStackTrace();
    }
}

private static void appendJavaExecutable(List<String> cmd) {
    cmd.add(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java");
}

private static void appendVMArgs(Collection<String> cmd) {
    Collection<String> vmArguments = ManagementFactory.getRuntimeMXBean().getInputArguments();

    String javaToolOptions = System.getenv("JAVA_TOOL_OPTIONS");
    if (javaToolOptions != null) {
        Collection<String> javaToolOptionsList = Arrays.asList(javaToolOptions.split(" "));
        vmArguments = new ArrayList<>(vmArguments);
        vmArguments.removeAll(javaToolOptionsList);
    }

    cmd.addAll(vmArguments);
}

private static void appendClassPath(List<String> cmd) {
    cmd.add("-cp");
    cmd.add(ManagementFactory.getRuntimeMXBean().getClassPath());
}

    private static void appendEntryPoint(List<String> cmd) {
    StackTraceElement[] stackTrace          = new Throwable().getStackTrace();
    StackTraceElement   stackTraceElement   = stackTrace[stackTrace.length - 1];
    String              fullyQualifiedClass = stackTraceElement.getClassName();
    String              entryMethod         = stackTraceElement.getMethodName();
    if (!entryMethod.equals("main"))
        throw new AssertionError("Entry point is not a 'main()': " + fullyQualifiedClass + '.' + entryMethod);

    cmd.add(fullyQualifiedClass);
}

private static void appendArgs(List<String> cmd, String[] args) {
    cmd.addAll(Arrays.asList(args));
}

V1.1 Bugfix: null pointer if JAVA_TOOL_OPTIONS is not set


Example:

$ java -cp Temp.jar Temp a b c d e
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a, b, c, d]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a, b, c]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a, b]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp]
$
筑梦 2024-10-08 16:34:59

当我自己研究这个主题时,遇到了这个问题。

不管答案已经被接受,我仍然想提供一种替代方法以保证完整性。具体来说,Apache Ant 是一个非常灵活的解决方案。

基本上,一切都归结为具有单个 Java 执行任务的 Ant 脚本文件(请参阅此处此处)从 Java 代码调用(请参阅此处)。此 Java 代码可以是一个方法 launch,也可以是需要重新启动的应用程序的一部分。应用程序需要依赖 Apache Ant 库 (jar)。

每当应用程序需要重新启动时,它应该调用方法launch并退出VM。 Ant java 任务应该将选项 forkspawn 设置为 true。

下面是一个 Ant 脚本的示例:

<project name="applaucher" default="launch" basedir=".">
<target name="launch">
    <java classname="package.MasinClass" fork="true" spawn="true">
        <jvmarg value="-splash:splash.jpg"/>
        <jvmarg value="-D other VM params"/>
        <classpath>
            <pathelement location="lib-1.jar" />
            ...
            <pathelement location="lib-n.jar" />
        </classpath>
    </java>
</target>
</project>

launch 方法的代码可能看起来像这样:

public final void launch(final String antScriptFile) {
 /* configure Ant and execute the task */
   final File buildFile = new File(antScriptFile);
   final Project p = new Project();
   p.setUserProperty("ant.file", buildFile.getAbsolutePath());

   final DefaultLogger consoleLogger = new DefaultLogger();
   consoleLogger.setErrorPrintStream(System.err);
   consoleLogger.setOutputPrintStream(System.out);
   consoleLogger.setMessageOutputLevel(Project.MSG_INFO);
   p.addBuildListener(consoleLogger);

   try {
       p.fireBuildStarted();
       p.init();
       final ProjectHelper helper = ProjectHelper.getProjectHelper();
       p.addReference("ant.projectHelper", helper);
       helper.parse(p, buildFile);
       p.executeTarget(p.getDefaultTarget());
       p.fireBuildFinished(null);
   } catch (final BuildException e) {
       p.fireBuildFinished(e);
   }

   /* exit the current VM */
   System.exit(0);

}

这里一个非常方便的事情是,相同的脚本用于初始应用程序启动以及重新启动。

I was researching the subject myself when came across this question.

Regardless of the fact that the answer is already accepted, I would still like to offer an alternative approach for completeness. Specifically, Apache Ant served as a very flexible solution.

Basically, everything boils down to an Ant script file with a single Java execution task (refer here and here) invoked from a Java code (see here). This Java code, which can be a method launch, could be a part of the application that needs to be restarted. The application needs to have a dependency on the Apache Ant library (jar).

Whenever application needs to be restarted, it should call method launch and exit the VM. The Ant java task should have options fork and spawn set to true.

Here is an example of an Ant script:

<project name="applaucher" default="launch" basedir=".">
<target name="launch">
    <java classname="package.MasinClass" fork="true" spawn="true">
        <jvmarg value="-splash:splash.jpg"/>
        <jvmarg value="-D other VM params"/>
        <classpath>
            <pathelement location="lib-1.jar" />
            ...
            <pathelement location="lib-n.jar" />
        </classpath>
    </java>
</target>
</project>

The code for the launch method may look something like this:

public final void launch(final String antScriptFile) {
 /* configure Ant and execute the task */
   final File buildFile = new File(antScriptFile);
   final Project p = new Project();
   p.setUserProperty("ant.file", buildFile.getAbsolutePath());

   final DefaultLogger consoleLogger = new DefaultLogger();
   consoleLogger.setErrorPrintStream(System.err);
   consoleLogger.setOutputPrintStream(System.out);
   consoleLogger.setMessageOutputLevel(Project.MSG_INFO);
   p.addBuildListener(consoleLogger);

   try {
       p.fireBuildStarted();
       p.init();
       final ProjectHelper helper = ProjectHelper.getProjectHelper();
       p.addReference("ant.projectHelper", helper);
       helper.parse(p, buildFile);
       p.executeTarget(p.getDefaultTarget());
       p.fireBuildFinished(null);
   } catch (final BuildException e) {
       p.fireBuildFinished(e);
   }

   /* exit the current VM */
   System.exit(0);

}

A very convenient thing here is that the same script is used for initial application start up as well as for restarts.

琴流音 2024-10-08 16:34:59

老问题等等。但这是另一种提供一些优势的方式。

在 Windows 上,您可以要求任务计划程序再次为您启动您的应用程序。这样做的优点是在应用程序重新启动之前等待特定的时间。您可以转到任务管理器并删除该任务,它就会停止重复。

SimpleDateFormat hhmm = new SimpleDateFormat("kk:mm");    
Calendar aCal = Calendar.getInstance(); 
aCal.add(Calendar.SECOND, 65);
String nextMinute = hhmm.format(aCal.getTime()); //Task Scheduler Doesn't accept seconds and won't do current minute.
String[] create = {"c:\\windows\\system32\\schtasks.exe", "/CREATE", "/F", "/TN", "RestartMyProg", "/SC", "ONCE", "/ST", nextMinute, "/TR", "java -jar c:\\my\\dev\\RestartTest.jar"};  
Process proc = Runtime.getRuntime().exec(create, null, null);
System.out.println("Exit Now");
try {Thread.sleep(1000);} catch (Exception e){} // just so you can see it better
System.exit(0);

Old question and all of that. But this is yet another way that offers some advantages.

On Windows, you could ask the task scheduler to start your app again for you. This has the advantage of waiting a specific amount of time before the app is restarted. You can go to task manager and delete the task and it stops repeating.

SimpleDateFormat hhmm = new SimpleDateFormat("kk:mm");    
Calendar aCal = Calendar.getInstance(); 
aCal.add(Calendar.SECOND, 65);
String nextMinute = hhmm.format(aCal.getTime()); //Task Scheduler Doesn't accept seconds and won't do current minute.
String[] create = {"c:\\windows\\system32\\schtasks.exe", "/CREATE", "/F", "/TN", "RestartMyProg", "/SC", "ONCE", "/ST", nextMinute, "/TR", "java -jar c:\\my\\dev\\RestartTest.jar"};  
Process proc = Runtime.getRuntime().exec(create, null, null);
System.out.println("Exit Now");
try {Thread.sleep(1000);} catch (Exception e){} // just so you can see it better
System.exit(0);
写给空气的情书 2024-10-08 16:34:59
System.err.println("Someone is Restarting me...");
setVisible(false);
try {
    Thread.sleep(600);
} catch (InterruptedException e1) {
    e1.printStackTrace();
}
setVisible(true);

我想您并不是真的想停止该应用程序,而是想“重新启动”它。为此,您可以使用它并在睡眠之前和隐形窗口之后添加“重置”。

System.err.println("Someone is Restarting me...");
setVisible(false);
try {
    Thread.sleep(600);
} catch (InterruptedException e1) {
    e1.printStackTrace();
}
setVisible(true);

I guess you don't really want to stop the application, but to "Restart" it. For that, you could use this and add your "Reset" before the sleep and after the invisible window.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文