Restarting a program completely in Java?

TheKill_TnT Posted messages 172 Status Member -  
KX Posted messages 19031 Status Moderator -
Hello,

My question is as follows: How to completely restart a Java program?
I have already asked a similar question, but it didn't go this far. Here, I want to restart a program in order to update it (it has been re-downloaded and the idea is to apply the update).
Theoretically, it would be possible using a second Java program (which would then serve as the launcher), but I don't know how to do it.
The idea would be to have something like this:
 public void restart(){ Something.OtherThing(new File("updater.jar")) //Instantiate a JAR file } 

And so our updater.jar would be made of:
 public static void main(String[] args){ //let's imagine all this is behind try catch okay File f = new File("programme_update.jar") Something.OtherThing(f, "programme.jar") //Rename the downloaded file to "programme.jar" Something.OtherThing(f) //Instantiate a JAR file again } 

And in this way finalize the update.
Thank you in advance!

Configuration: Windows / Chrome 71.0.3578.98, Java Dev Kit 8

1 answer

KX Posted messages 19031 Status Moderator 3 020
 
Hello,

To go further, you could use different ClassLoaders, one for each version of the program.
The only constraint is that the loader must be in the system ClassLoader; it cannot be updated.

import java.net.URL; import java.net.URLClassLoader; import java.util.Arrays; public class Loader { // anywhere except in E:/Workspace/CCM/bin/ public static void main(String[] args) throws Exception { for (int i = 0; i < 10; i++) { URL[] classUrls = { new URL("file:///E:/Workspace/CCM/bin/") }; System.out.println(Arrays.toString(classUrls)); try (URLClassLoader ucl = new URLClassLoader(classUrls)) { System.out.println(ucl); ucl.loadClass("Main").getMethod("main", String[].class).invoke(null, new Object[] { args }); Thread.sleep(2_000); } } } }

public class Main { // compiled in E:/Workspace/CCM/bin/ public static void main(String[] args) { System.out.println("Hello 1"); } } 

If I recompile Main with "Hello 2" for example, the Loader will load the new class without having to stop the Loader program.
--
Trust does not exclude control.
0
TheKill_TnT Posted messages 172 Status Member 40
 
Certainly, I can use it to start a class, but if I want to directly start a JAR file (Given that I don't plan to use any external installer if people have to do I don't know what manipulation to launch the program...), does this method work?
I would have thought of a method that executes a command line or directly a file, but I don't see where that could be found.
0
KX Posted messages 19031 Status Moderator 3 020 > TheKill_TnT Posted messages 172 Status Member
 
According to the same principle, you have the JarClassLoader.

The idea would be: you have a Jar that the user starts and its operation is as follows.
  • We look for the latest version of the program, we copy the Jar into a folder
  • With a JarClassLoader we load the Jar and execute its main
  • When we want to update, we repeat (latest version, copy, loading, execution)

Then it would be important to know what limits you impose for "restarting" a program.
If you go for command lines, you will not have a running program, but several.
With the ClassLoaders, you will always have the same Java process running, even after "restart".

Note: this operates on the same principle as application servers (Tomcat & co) which will start/stop wars, but the server itself does not stop, allowing it to stop certain applications while keeping others running.
0
KX Posted messages 19031 Status Moderator 3 020
 
A complete example:

I have a project C:/Workspace/ccm-Thread that creates a thread-1.0-SNAPSHOT.jar with the following content:

package ccm; public class MainThread extends Thread { @Override public void run() { for (int i = 0; !isInterrupted(); i++) { System.out.println(i); try { Thread.sleep(1_000); } catch (InterruptedException e) { return; } } } }

I have a project C:/Workspace/ccm-Loader that creates a loader-1.0-SNAPSHOT.jar with the following content:

package ccm; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.Scanner; import java.util.logging.Level; import java.util.logging.Logger; public class Loader { private static final Path HOME_LATEST = Paths.get("C:/Workspace/ccm-Loader/target/thread-local.jar"); private static final Path CLOUD_LATEST = Paths.get("C:/Workspace/ccm-Thread/target/thread-1.0-SNAPSHOT.jar"); public static void main(String[] args) { downloadLatestThreadJar(); Thread thread = loadLocalThreadJar("ccm.MainThread", args); thread.start(); try (Scanner sc = new Scanner(System.in)) { while (true) { sc.nextLine(); // Press Enter to update and restart try { if (downloadLatestThreadJar()) { thread.interrupt(); thread.join(); thread = loadLocalThreadJar("ccm.MainThread", args); thread.start(); } } catch (Exception e) { Logger.getGlobal().log(Level.WARNING, "Can't update or restart", e); } } } } private static boolean downloadLatestThreadJar() { Logger.getGlobal().info("downloadLatestThreadJar: " + CLOUD_LATEST + " -> " + HOME_LATEST); try { HOME_LATEST.getParent().toFile().mkdirs(); Files.copy(CLOUD_LATEST, HOME_LATEST, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES); Logger.getGlobal().info("downloadLatestThreadJar: success!"); return true; } catch (Exception e) { Logger.getGlobal().log(Level.WARNING, "Can't downloadLatestThreadJar", e); return false; } } private static Thread loadLocalThreadJar(String className, String[] args) { Logger.getGlobal().info("loadThreadJar: " + HOME_LATEST); try (URLClassLoader jcl = new URLClassLoader(new URL[] { HOME_LATEST.toUri().toURL() })) { @SuppressWarnings("unchecked") Class<Thread> threadClass = (Class<Thread>) jcl.loadClass(className); Thread thread = threadClass.newInstance(); thread.setName("Embedded " + className); thread.setContextClassLoader(jcl); Logger.getGlobal().info("loadLocalThreadJar: success!"); return thread; } catch (Exception e) { Logger.getGlobal().log(Level.WARNING, "Can't loadLocalThreadJar", e); return null; } } }

Operation: you launch the command
java -cp loader-1.0-SNAPSHOT.jar ccm.Loader

(You can optionally configure ccm.Loader as Main-Class in the Manifest)

The Loader will take the thread-1.0-SNAPSHOT.jar file (we could implement downloading the file from the web, for example) to copy it into thread-local.jar, load it, and execute ccm.MainThread

In the Loader console, we can press Enter, which will trigger the update and restart of the thread, except in the case of an update error, in which case we let the previous version continue running.
0