This is the riskier, but more powerful method of catching your program's exit. There are several ways that you could intentionally break this method, and there's some wiggle room where the OS could accidentally break it as well. That's why I said 'riskier'.
You create one class, the Bodyguard, who figures out its own process id and then runs the RealProgram with the processid as input. The Bodyguard then waits until the RealProgram shuts down, then terminates. Meanwhile, the RealProgram watches and checks to see that the Bodyguard's process is still running. If it sees that the Bodyguard has died, then it quits its main loop and calls quit() before terminating.
I have tested this on Windows 10, running Java 8, and killing the Bodyguard from the eclipse debugger. When the Bodyguard is killed, the RealProgram shuts down after calling quit(). When the RealProgram is killed, the Bodyguard terminates as well.
Since I figure you'll want to play around with the code, I've left some of my debugging elements, such as the writer, in there.
I credit:
Bodyguard Class:
package so_38154756;
import java.lang.reflect.InvocationTargetException;
public class Bodyguard {
public static void main(String[] args){
Bodyguard lamb = new Bodyguard();
lamb.initiateSacrifice();
}
private void initiateSacrifice() {
try{
int ownPid = getPID();
runRealProgram(ownPid);
} catch(Exception e){
System.out.println("Sorry, this isn't working.");
}
}
private int getPID() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
java.lang.management.RuntimeMXBean runtime = java.lang.management.ManagementFactory.getRuntimeMXBean();
java.lang.reflect.Field jvm = runtime.getClass().getDeclaredField("jvm");
jvm.setAccessible(true);
sun.management.VMManagement mgmt = (sun.management.VMManagement) jvm.get(runtime);
java.lang.reflect.Method pid_method = mgmt.getClass().getDeclaredMethod("getProcessId");
pid_method.setAccessible(true);
int pid = (Integer) pid_method.invoke(mgmt);
return pid;
}
private void runRealProgram(int ownPid) throws Exception {
String separator = System.getProperty("file.separator");
String classpath = System.getProperty("java.class.path");
String path = System.getProperty("java.home") + separator + "bin" + separator + "java";
ProcessBuilder processBuilder = new ProcessBuilder(path, "-cp", classpath, RealProgram.class.getName(), Integer.valueOf(ownPid).toString());
Process process = processBuilder.start();
process.waitFor();//will wait forever, but will close when the RealProgram closes
}
}
and the RealProgram class:
package so_38154756;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.regex.Pattern;
public class RealProgram {
private Integer bodyguardPID;
private PrintWriter writer;
public RealProgram(Integer bodyguardPID, PrintWriter writer) {
this.bodyguardPID = bodyguardPID;
this.writer = writer;
}
public static void main(String[] args){
Integer sacrificialPID = Integer.parseInt(args[0]);
try {
PrintWriter writer = new PrintWriter(new File("output.txt"));
RealProgram rp = new RealProgram(sacrificialPID, writer);
writer.println("Past Constructor");
rp.checkToSeeIfBodyguardLives();
writer.println("Bodyguard had died");
writer.close();//yes, yes, this could be slightly prettier with Java 8's try-with-resources
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
private void checkToSeeIfBodyguardLives() {
while(true){
try { Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}//I wait because I was debugging - you may find it wise to not wait
//do whatever it is you're doing
if(!isBodyguardAlive()){
quit();
break;
}
}
}
private boolean isBodyguardAlive() {
try {
String process;
Pattern pattern = Pattern.compile(".*\\\""+bodyguardPID+"\\\".*");//Windows - have tested
//you'll have to figure out your own different pattern for linux - possible source for editing
writer.println("Searching for PID of "+bodyguardPID);
//Process p = Runtime.getRuntime().exec("ps -few"); - Linux - haven't tested - again, possible source for editing
Process p = Runtime.getRuntime().exec(System.getenv("windir") +"\\system32\\"+"tasklist.exe /fo csv /nh");//Windows - have tested
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((process = input.readLine()) != null) {
if(pattern.matcher(process).matches()){
return true;
}
}
input.close();
} catch (Exception err) {
err.printStackTrace();
}
return false;
}
private void quit(){
writer.println("Detected Bodyguard has died");
}
}