Week 33 — What would be a possible way to restart a java application?

Question of the Week #33
What would be a possible way to restart a java application?
5 Replies
Eric McIntyre
Eric McIntyre17mo ago
If you are running from jar Windows: 1. Go to Task Manager. 2. Find java and click "End Task". 3. Double click on the jar. Linux: 1.
pkill java
pkill java
. 2.
java -jar jarname.jar
java -jar jarname.jar
. If you are running from IDE (IntelliJ IDEA, Eclipse, e.t.c.) 1. Click "Debug" or "Run" while the project is running. 2. A notification saying "Are you sure you want to restart?" will appear, Click the positive answer. if it is not a Java Swing project with close operation set to nothing, just click X on the top panel of the window
Submission from geuxy#0000
Eric McIntyre
Eric McIntyre17mo ago
Spring boot provides the actuator enpoint that can be called to do shutdown POST .../actuator/shutdown (you need to configure it to true as per default this is disabled. For vanilla Java System.exit(0) - when you want to go though the shutdown hooks. or Runtime.getRuntime().halt(1); when you want to skip said hooks.
Submission from cazacu23#0000
Eric McIntyre
Eric McIntyre17mo ago
Restarting an application after it exits, errors or does other things usually involves the application host restarting it itself, so using something like a shell script to wrap the java command would most likely be used:
while [[ True ]]; do
java -jar theApp.jar
echo "Process exited with exit code $?, waiting 10 seconds and restarting... (press ctrl + c to cancel)"
sleep 10s
echo "Restarting..."
done
while [[ True ]]; do
java -jar theApp.jar
echo "Process exited with exit code $?, waiting 10 seconds and restarting... (press ctrl + c to cancel)"
sleep 10s
echo "Restarting..."
done
Java itself has a feature to run code when the jvm shuts down, called shutdown hooks. You can register one yourself using Runtime#addShutdownHook(Thread), but using hooks to restart the application would be a misuse, especially since the jvm fully expects to shut down after all threads have executed. Another way to do it directly in java would be to rewrite the main method like so:
public static void main(String[] args) throws InterruptedException {
while (true) {
try {
// Original main code
} catch (Throwable t) {
t.printStackTrace();
}
System.out.println("Main method exited, waiting 10 seconds and restarting...");
Thread.sleep(10_000);
System.out.println("Restarting...");
}
}
public static void main(String[] args) throws InterruptedException {
while (true) {
try {
// Original main code
} catch (Throwable t) {
t.printStackTrace();
}
System.out.println("Main method exited, waiting 10 seconds and restarting...");
Thread.sleep(10_000);
System.out.println("Restarting...");
}
}
, although this wouldn't have some of the benefits the shell script version has. If the jvm itself breaks somehow, this won't restart it entirely, it'll just restart the main method. This also wouldn't work if System.exit() would be called, since that just ends the vm (almost) instantly. Shutdown hooks are still executed tho.
⭐ Submission from 0x150#0000
Eric McIntyre
Eric McIntyre17mo ago
Java does not natively support restarting the current application. However, there are many ways to restart it. The simplest way to do that would be to create a script that automatically restarts the Java application once it is stopped. This can also include a check for the return value so that the application only restarts on specific return values (e.g. 0):
# stop on nonzero exit codes
set -e
# run the application until script stops
while true; do
java -jar yourjar.jar
done
# stop on nonzero exit codes
set -e
# run the application until script stops
while true; do
java -jar yourjar.jar
done
The application can then restart using System.exit(0); and stop using System.exit(1); (or any other non-zero exit code). Alternatively, Java applications can just run themselves before exiting:
String jarLocation = "yourjar.jar";
ProcessBuilder pb = new ProcessBuilder("java", "-jar", jarLocation);
pb.start();
System.exit(0);
String jarLocation = "yourjar.jar";
ProcessBuilder pb = new ProcessBuilder("java", "-jar", jarLocation);
pb.start();
System.exit(0);
However, this approach will not inherit the console (System.in/System.out) as the restarted application will run in the background and requires the application to know where it is located and assumes that the correct java executable is on the PATH. The latter issue can be prevented by dynamically discovering the location of the JAR file:
String javaHome = System.getProperty("java.home");
String javaPath = javaHome+"/bin/java";
if (System.getProperty("os.name").toLowerCase().contains("windows")) {
javaPath += ".exe";
}

CodeSource mainCodeSource = YourMainClass.class.getProtectionDomain().getCodeSource();

if (mainCodeSource != null) {
URL location = mainCodeSource.getLocation();
if(location != null) {
String codeSourceFileName = location.getFile();
codeSourceFileName = new File(codeSourceFileName).getCanonicalPath();
if (codeSourceFileName.endsWith(".jar")) {
ProcessBuilder pb = new ProcessBuilder(javaPath, "-jar", codeSourceFileName);
pb.start();
System.exit(0);
} else {
System.err.println("Cannot restart: Main class does not origin from a JAR file");
}
} else {
System.err.println("Cannot restart: Cannot get location from code source");
}
} else {
System.err.println("Cannot restart: Cannot locate code source of main class");
}
String javaHome = System.getProperty("java.home");
String javaPath = javaHome+"/bin/java";
if (System.getProperty("os.name").toLowerCase().contains("windows")) {
javaPath += ".exe";
}

CodeSource mainCodeSource = YourMainClass.class.getProtectionDomain().getCodeSource();

if (mainCodeSource != null) {
URL location = mainCodeSource.getLocation();
if(location != null) {
String codeSourceFileName = location.getFile();
codeSourceFileName = new File(codeSourceFileName).getCanonicalPath();
if (codeSourceFileName.endsWith(".jar")) {
ProcessBuilder pb = new ProcessBuilder(javaPath, "-jar", codeSourceFileName);
pb.start();
System.exit(0);
} else {
System.err.println("Cannot restart: Main class does not origin from a JAR file");
}
} else {
System.err.println("Cannot restart: Cannot get location from code source");
}
} else {
System.err.println("Cannot restart: Cannot locate code source of main class");
}
Note that this approach does not include the classpath/modulepath when restarting the application. In many cases, restarting the application is actually not required. but it is sufficient to reset all public state, stop everything happening in the background and run the main method again. Some libraries/frameworks provide functionality for stopping background activities of the said library/framework.
//example of how this could look like
ExecutorService someThreadPoolDoingStuffInTheBackground = getCurrentExecutorService();
someThreadPoolDoingStuffInTheBackground.shutdown();
boolean poolStopped = false;
try{
poolStopped = someThreadPoolDoingStuffInTheBackground.awaitTermination(30, TimeUnit.MILLISECONDS);
} catch(InterruptedException e) {
//we will interrupt/stop anywys
}
if (!poolStopped){
someThreadPoolDoingStuffInTheBackground.shutdownNow();//shutdown thread pool and also interrupt threads of that thread pool
}
Thread.currentThread().interrupt();//also interrupt current thread
new Thread(() -> {YourMainClass.main(new String[0])}).start();
throw new ApplicationStoppedException();//maybe even use ThreadDeath or InterruptedException
//example of how this could look like
ExecutorService someThreadPoolDoingStuffInTheBackground = getCurrentExecutorService();
someThreadPoolDoingStuffInTheBackground.shutdown();
boolean poolStopped = false;
try{
poolStopped = someThreadPoolDoingStuffInTheBackground.awaitTermination(30, TimeUnit.MILLISECONDS);
} catch(InterruptedException e) {
//we will interrupt/stop anywys
}
if (!poolStopped){
someThreadPoolDoingStuffInTheBackground.shutdownNow();//shutdown thread pool and also interrupt threads of that thread pool
}
Thread.currentThread().interrupt();//also interrupt current thread
new Thread(() -> {YourMainClass.main(new String[0])}).start();
throw new ApplicationStoppedException();//maybe even use ThreadDeath or InterruptedException
A fundamentally different approach is using a ClassLoader. This works by splitting the application in two: One JAR that contains the application code and one program (can also be a JAR) that is responsible for loading and restarting the other program. The loading program loads the JAR with the application code and runs its main method using a URLClassLoader. It also needs to provide a public method that can be invoked from the application code in order to restart it.
package io.github.danthe1st.jar_test.run;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

public class Restarter {

private static URLClassLoader restartLoader;

public static void main(String[] args) throws Throwable {
restart(args);
}

public static void restart(String[] args) throws MalformedURLException, ClassNotFoundException,
NoSuchMethodException, IllegalAccessException, Throwable {
if (restartLoader != null) {
restartLoader.close();
}
restartLoader = new URLClassLoader(new URL[] { new File("yourjar.jar").toURI().toURL() },
Restarter.class.getClassLoader());
Class<?> cl = restartLoader.loadClass("io.github.danthe1st.jar_test.Main");
Method method = cl.getDeclaredMethod("main", String[].class);
new Thread(() -> {
try {
method.invoke(null, (Object)args);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
}).start();
}
}
package io.github.danthe1st.jar_test.run;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

public class Restarter {

private static URLClassLoader restartLoader;

public static void main(String[] args) throws Throwable {
restart(args);
}

public static void restart(String[] args) throws MalformedURLException, ClassNotFoundException,
NoSuchMethodException, IllegalAccessException, Throwable {
if (restartLoader != null) {
restartLoader.close();
}
restartLoader = new URLClassLoader(new URL[] { new File("yourjar.jar").toURI().toURL() },
Restarter.class.getClassLoader());
Class<?> cl = restartLoader.loadClass("io.github.danthe1st.jar_test.Main");
Method method = cl.getDeclaredMethod("main", String[].class);
new Thread(() -> {
try {
method.invoke(null, (Object)args);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
}).start();
}
}
Eric McIntyre
Eric McIntyre17mo ago
From the application code, the program can be restarted using
Class<?> restartClass = Main.class.getClassLoader().loadClass("io.github.danthe1st.jar_test.run.Restarter");
restartClass.getDeclaredMethod("restart", String[].class).invoke(null, (Object) args);
Class<?> restartClass = Main.class.getClassLoader().loadClass("io.github.danthe1st.jar_test.run.Restarter");
restartClass.getDeclaredMethod("restart", String[].class).invoke(null, (Object) args);
This calls the restart method of the loader which first closes the loader which loaded the application code and loads it again. This allows restarting the application code and applying possibly changes in the JAR due to an update as well as clearing all static variables in the application code as these are initialized when the classes are loaded. However, the application code must make sure that all application code (threads started by it) are stopped when the application is restarted. It is important that the loader (the Restarter class) is not available inside the JAR. Both should be distinct programs. It is possible to compile the application code with Restarter in the classpath but Restarter must not be in the JAR of the application code. This approach keeps System.in/System.out/System.err as the restarted application runs in the same process but automatically clears all state (static variables). However, the application code must stop by itself without calling System.exit.
⭐ Submission from dan1st#0000
Want results from more Discord servers?
Add your server