I have a bat script which runs a java application. If I press ctrl+c on it, it the application terminates gracefully, invoking all the shutdown hooks. However, if I just close the cmd window of the bat script, the shutdown hooks are never invoked.
Is there a way to solve this? Perhaps there's a way to tell the bat script how to terminate the invoked applications when its window is closed?
From addShutdownHook documentation:
In rare circumstances the virtual machine may abort, that is, stop running without shutting down cleanly. This occurs when the virtual machine is terminated externally, for example with the SIGKILL signal on Unix or the TerminateProcess call on Microsoft Windows.
So i think nothing to do here, unfortunately.
CTRL-CLOSE signal in Windows Console. Seems non-tweakable.
Quoting above link:
The system generates a CTRL+CLOSE signal when the user closes a console. All processes attached to the console receive the signal, giving each process an opportunity to clean up before termination. When a process receives this signal, the handler function can take one of the following actions after performing any cleanup operations:
Call ExitProcess to terminate the process.
Return FALSE. If none of the registered handler functions returns TRUE, the default handler terminates the process.
Return TRUE. In this case, no other handler functions are called, and a pop-up dialog box asks the user whether to terminate the process. If the user chooses not to terminate the process, the system does not close the console until the process finally terminates.
UPD. If native tweaks are acceptable for you, WinAPI SetConsoleCtrlHandler function opens way for suppressing of default behavior.
UPD2. Revelations on Java signal handling and termination relatively old article, but section Writing Java signal handlers really may contain what you need.
UPD3.
I've tried Java signal handlers from article above. It works with SIGINT nicely, but it not what we need, and i decided to carry it with SetConsoleCtrlHandler. The result is a bit complicated and may be not worth to implement in your project. Anyway, it could help someone else.
So, the idea was:
Keep reference to shutdown handler thread.
Set custom native console handler routine with JNI.
Call custom Java method on CTRL+CLOSE signal.
Call shutdown handler from that method.
Java code:
public class TestConsoleHandler {
private static Thread hook;
public static void main(String[] args) {
System.out.println("Start");
hook = new ShutdownHook();
Runtime.getRuntime().addShutdownHook(hook);
replaceConsoleHandler(); // actually not "replace" but "add"
try {
Thread.sleep(10000); // You have 10 seconds to close console
} catch (InterruptedException e) {}
}
public static void shutdown() {
hook.run();
}
private static native void replaceConsoleHandler();
static {
System.loadLibrary("TestConsoleHandler");
}
}
class ShutdownHook extends Thread {
public void run() {
try {
// do some visible work
new File("d:/shutdown.mark").createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Shutdown");
}
}
Native replaceConsoleHandler:
JNIEXPORT void JNICALL Java_TestConsoleHandler_replaceConsoleHandler(JNIEnv *env, jclass clazz) {
env->GetJavaVM(&jvm);
SetConsoleCtrlHandler(&HandlerRoutine, TRUE);
}
And handler itself:
BOOL WINAPI HandlerRoutine(__in DWORD dwCtrlType) {
if (dwCtrlType == CTRL_CLOSE_EVENT) {
JNIEnv *env;
jint res = jvm->AttachCurrentThread((void **)(&env), &env);
jclass cls = env->FindClass("TestConsoleHandler");
jmethodID mid = env->GetStaticMethodID(cls, "shutdown", "()V");
env->CallStaticVoidMethod(cls, mid);
jvm->DetachCurrentThread();
return TRUE;
}
return FALSE;
}
And it works. In JNI code all error checks are omitted for clearance. Shutdown handler creates empty file "d:\shutdown.mark" to indicate correct shutdown.
Complete sources with compiled test binaries here.
Further to the above answer of using SetConsoleCtrlHandler, you can also do this using JNA rather than writing your own native code.
You could create your own interface on kernel32 if you wanted, or use the one provided in this excellent framework: https://gitlab.com/axet/desktop
Some example code:
import com.github.axet.desktop.os.win.GetLastErrorException;
import com.github.axet.desktop.os.win.handle.HANDLER_ROUTINE;
import com.github.axet.desktop.os.win.libs.Kernel32Ex;
...
private static HANDLER_ROUTINE handler =
new HANDLER_ROUTINE()
{
#Override
public long callback(long dwCtrlType) {
if ((int)dwCtrlType == CTRL_CLOSE_EVENT) {
// *** do your shutdown code here ***
return 1;
}
return 0;
}
};
public static void assignShutdownHook() {
if (!Kernel32Ex.INSTANCE.SetConsoleCtrlHandler(handler, true))
throw new GetLastErrorException();
}
Note that I assigned the anonymous class to a field. I originally defined it in the call to SetConsoleCtrlHandler, but I think it was getting collected by the JVM.
Edit 4/9/17: Updated link from github to gitlab.
Though the batch file may be terminated, the console (window) the batch file has been running in may be left open, depending on the operating system, the command processor, and how batch file execution was started (from a command prompt or through a shortcut).
taskkill is a good command for ending a program which is distributed with Windows (I'm assuming you want to stop a DIFFERENT program, not the batch file itself).
It is possible to end programs by process id, executable name, window title, status (i.e. not responding), DLL name, or service name.
Here are some examples based on how you might use this in your batch file:
Force "program.exe" to stop (often the /f "force" flag is needed to force the program to stop, just check whether it is needed for your application by trial and error):
taskkill /f /im program.exe
Stop any non-responsive programs:
taskkill /fi "Status eq NOT RESPONDING"
Stop a program based on its window title (* wildcard is allowed here to match anything):
taskkill /fi "WindowTitle eq Please Login"
taskkill /fi "WindowTitle eq Microsoft*"
You can even use it to stop a program on another computer on your network (although writing the password of an account in a batch file is just not a good idea).
taskkill /s JimsPC /u Jim /p James_007 /im firefox.exe
Another alternative also distributed with Windows is tskill. Although it doesn't have nearly as many options, the commands are a bit simpler.
EDIT:
I am not sure there is. I would think that closing the cmd window is akin to force-closing the app i.e. immediate termination with no further notice. This has the behavior of many applications that, when they are asked to force-close, they actually take a long time to finally terminate. This is because in the OS's efforts to release all resources, some resources (especially certain I/O and/or file resources) don't let go immediately.
IMO, there's nothing Java could even do about it if it wanted to. A force-close happens at the OS level, at which point it releases the memory, file and I/O handles in use, etc. Java does not (nor does any other program) have control over a force-close. At this point, the OS is taking charge.
Someone please correct me if I'm wrong.
Related
I have JVM and all dependencies for my Java program ready. With Java I would run like:
javac HelloWorld.java
java HelloWorld
Now I want to, in Linux environment, control this Java program processes using Go's cmd package. In Go, when you run command you are given the PID. With this PID, I want to terminate the Java program whenever j want and restart using the same cmd package. Would this work correctly as long as I have JVM installed? I want to do:
cmd := exec.Command("bash", "-c", " "java HelloWorld")
cmd.Start()
syscall.Kill(cmd.Process.Pid)
Thanks!
In short, yes.
As a test, with added interrupt handling so your own Go process doesn't terminate this will work:
package main
import (
"os/exec"
"syscall"
"os"
"os/signal"
"fmt"
)
func main() {
cmd := exec.Command("bash", "-c", "java HelloWorld")
err := cmd.Start()
fmt.Printf("Starting java proccess with pid %d\n", cmd.Process.Pid)
if err != nil {
// do something about it
}
c := make(chan os.Signal, 1)
done := make(chan bool, 1)
signal.Notify(c, os.Interrupt)
signal.Notify(c, syscall.SIGTERM)
go func() {
<-c
fmt.Printf("Sending interrupt to pid: %d\n", cmd.Process.Pid)
syscall.Kill(cmd.Process.Pid, syscall.SIGHUP)
done <- true
}()
<-done
}
Companion Java class:
public class HelloWorld {
public static void main(String[] args) throws Exception {
System.out.println("Hello World from Go! But you cant see me :)");
while (true) {
System.out.println("you cant see this because I am outing to the STDOUT of a subshell!");
Thread.sleep(5000);
}
}
}
But it is full of gotchas. As long as your Go process exits normally, it will send the signal you specify (sighup would be natural choice, if I'd venture a guess) to the java pid. But you need to ensure that you wont let a zombie in case your own Go process crash or in case your java application hangs on after failing to shut down cleanly when you tell it to. Saving that pid to a /tmp/ file and doing all sorts of things with it in case of a restart could be interesting, but you know your needs.
Edit: controlling a JVM process from another program might get finicky quick. You should evaluate if you really want to do that. If you are in Linux, I'd take a look at the SysV init/systemd/upstart/start-stop-daemon system your distro uses if your companion java program acts as a daemon.
I am trying to create a console application which hangs up in a way that pressing CTRL + BREAK or sending a SIGTERM signal to the process doesn't terminate it [i.e. it keeps on hanging, without closing]. I want to test that it keeps on going with the following Java code:
public static void main(String[] args) throws IOException {
//Replace APPLICATION PATH HERE with path towards the executable file
Process process = Runtime.getRuntime().exec("APPLICATION PATH HERE");
// this should kill the process
process.destroy();
// if the process is alive, exitValue() will throw exception
try {
process.exitValue();
// the process is dead - hasn't survived kill
System.out.println("WRONG: process died");
} catch (IllegalThreadStateException e) {
// the process is still running
// the process is not dead and survived destroy()
System.out.println("OK: process hanged");
}
}
So far, I have managed to find the following information:
How Can I Make A Command Prompt Hang? , though it doesn't stop SIGTERM, just SIGINT.
I have also tried using Shutdown Hooks in a java -jar executable file, which also give me control over SIGINT,but not over SIGTERM. I want the program to keep running when given SIGTERM,so that I test the destroy function.
I have also written a program in Go which does a similar thing,with the exception that it registers CTRL+ BREAK as an interrupt,for some reason[I am not sure why, but it still doesn't handle SIGTERM signals from the java code]:
package main
import "fmt"
import "os"
import "os/signal"
import "syscall"
func main() {
sigs := make(chan os.Signal, 1)
done := make(chan bool, 1)
signal.Notify(sigs, syscall.SIGTERM, syscall.SIGINT)
go func() {
sig := <-sigs
fmt.Println("TEST!!")
fmt.Println(sig)
done <- true
}()
fmt.Println("awaiting signal")
<-done
sigs2 := make(chan os.Signal, 1)
done2 := make(chan bool, 1)
signal.Notify(sigs2, syscall.SIGTERM, syscall.SIGINT)
go func() {
sig2 := <-sigs2
fmt.Println("TEST!!")
fmt.Println(sig2)
done2 <- true
}()
fmt.Println("awaiting signal 2")
<-done2
}
NOTE: I still want to be able to close the application with a SIGKILL signal, or the red X in the window :)
Thank you for any ideas :)
From the Java API documentation (emphasis mine):
public abstract void destroy()
Kills the subprocess. The subprocess represented by this Process object is forcibly terminated.
In Windows terms, that means it terminates the process - the equivalent of SIGKILL. There is no way for a process to detect, prevent, or postpone termination.
Note that Windows does not have anything equivalent to SIGTERM. For GUI applications, the recommended procedure is documented in KB178893.
In an effort to make my app more OSX friendly, I am trying to set the dock name of my program to something like MyApp instead of a fully qualified class name (the default), such as myproject.mypackage.mysubpackage.myclass. Obviously, the first is much more appealing.
To do this, I use the OSX -Xdock:name command as a command line option when executing my .jar file. So to execute it, the command might look something like java -Xdock:name=MyApp -jar /mypath/myjar.jar. This works perfectly and sets the .jar's dock name to MyApp. But the issue is that this .jar will never be executed via command line and will be a double-clicked runnable .jar with a GUI display.
The only way I have thought of to set this command line option programmatically is to have a second class execute the class that actually starts the program. So something like this:
public class AppStarter {
public static void main(String[] args) {
String cmd = "java -Xdock:name=MyApp -cp myproject/mypackage/AppBuilder";
try {
Runtime runtime = Runtime.getRuntime();
runtime.exec(cmd);
} catch(IOException ex) {
//Display error message
}
}
}
public class AppBuilder {
public static void main(String[] args) {
//Start actual program and build GUI display
}
}
So here, AppStarter sets the command line options for AppBuilder, which when executed, has the dock name MyApp. The problem I see with this is that it is very tightly coupled. If for some reason the command line is inaccessible on the device or some IOException keeps getting thrown, literally nothing will happen with the program and it will be dead. There would be no way for the average computer user to recover from this.
So I'm wondering if it is possible to perhaps set these command line options after the .jar has already started executing. The old way to programmatically set the app's name has been ineffective for several OSX updates, so I'm stuck with only this command line option. Thanks for any advice.
Once the java command is executed, the command line arguments are parsed and set for the running JVM. You cannot change it any more.
This is usually handled by execution scripts (bash, etc.). If you cannot use them, you can use your approach, but the biggest disadvantage is that it will be running in a separate process.
I am working on a program written in Java which, for some actions, launches external programs using user-configured command lines. Currently it uses Runtime.exec() and does not retain the Process reference (the launched programs are either a text editor or archive utility, so no need for the system in/out/err streams).
There is a minor problem with this though, in that when the Java program exits, it doesn't really quit until all the launched programs are exited.
I would greatly prefer it if the launched programs were completely independent of the JVM which launched them.
The target operating system is multiple, with Windows, Linux and Mac being the minimum, but any GUI system with a JVM is really what is desired (hence the user configurability of the actual command lines).
Does anyone know how to make the launched program execute completely independently of the JVM?
Edit in response to a comment
The launch code is as follows. The code may launch an editor positioned at a specific line and column, or it may launch an archive viewer. Quoted values in the configured command line are treated as ECMA-262 encoded, and are decoded and the quotes stripped to form the desired exec parameter.
The launch occurs on the EDT.
static Throwable launch(String cmd, File fil, int lin, int col) throws Throwable {
String frs[][]={
{ "$FILE$" ,fil.getAbsolutePath().replace('\\','/') },
{ "$LINE$" ,(lin>0 ? Integer.toString(lin) : "") },
{ "$COLUMN$",(col>0 ? Integer.toString(col) : "") },
};
String[] arr; // array of parsed tokens (exec(cmd) does not handle quoted values)
cmd=TextUtil.replace(cmd,frs,true,"$$","$");
arr=(String[])ArrayUtil.removeNulls(TextUtil.stringComponents(cmd,' ',-1,true,true,true));
for(int xa=0; xa<arr.length; xa++) {
if(TextUtil.isQuoted(arr[xa],true)) {
arr[xa]=TextDecode.ecma262(TextUtil.stripQuotes(arr[xa]));
}
}
log.println("Launching: "+cmd);
Runtime.getRuntime().exec(arr);
return null;
}
This appears to be happening only when the program is launched from my IDE. I am closing this question since the problem exists only in my development environment; it is not a problem in production. From the test program in one of the answers, and further testing I have conducted I am satisfied that it is not a problem that will be seen by any user of the program on any platform.
There is a parent child relation between your processes and you have to break that.
For Windows you can try:
Runtime.getRuntime().exec("cmd /c start editor.exe");
For Linux the process seem to run detached anyway, no nohup necessary.
I tried it with gvim, midori and acroread.
import java.io.IOException;
public class Exec {
public static void main(String[] args) {
try {
Runtime.getRuntime().exec("/usr/bin/acroread");
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Finished");
}
}
I think it is not possible to to it with Runtime.exec in a platform independent way.
for POSIX-Compatible system:
Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", "your command"}).waitFor();
I have some observations that may help other people facing similar issue.
When you use Runtime.getRuntime().exec() and then you ignore the java.lang.Process handle you get back (like in the code from original poster), there is a chance that the launched process may hang.
I have faced this issue in Windows environment and traced the problem to the stdout and stderr streams. If the launched application is writing to these streams, and the buffer for these stream fills up then the launched application may appear to hang when it tries to write to the streams. The solutions are:
Capture the Process handle and empty out the streams continually - but if you want to terminate the java application right after launching the process then this is not a feasible solution
Execute the process call as cmd /c <<process>> (this is only for Windows environment).
Suffix the process command and redirect the stdout and stderr streams to nul using 'command > nul 2>&1'
It may help if you post a test section of minimal code needed to reproduce the problem. I tested the following code on Windows and a Linux system.
public class Main {
/**
* #param args the command line arguments
*/
public static void main(String[] args) throws Exception {
Runtime.getRuntime().exec(args[0]);
}
}
And tested with the following on Linux:
java -jar JustForTesting.jar /home/monceaux/Desktop/__TMP/test.sh
where test.sh looks like:
#!/bin/bash
ping -i 20 localhost
as well as this on Linux:
java -jar JustForTesting.jar gedit
And tested this on Windows:
java -jar JustForTesting.jar notepad.exe
All of these launched their intended programs, but the Java application had no problems exiting. I have the following versions of Sun's JVM as reported by java -version :
Windows: 1.6.0_13-b03
Linux: 1.6.0_10-b33
I have not had a chance to test on my Mac yet. Perhaps there is some interaction occuring with other code in your project that may not be clear. You may want to try this test app and see what the results are.
You want to launch the program in the background, and separate it from the parent. I'd consider nohup(1).
I suspect this would require a actual process fork. Basically, the C equivalent of what you want is:
pid_t id = fork();
if(id == 0)
system(command_line);
The problem is you can't do a fork() in pure Java. What I would do is:
Thread t = new Thread(new Runnable()
{
public void run()
{
try
{
Runtime.getRuntime().exec(command);
}
catch(IOException e)
{
// Handle error.
e.printStackTrace();
}
}
});
t.start();
That way the JVM still won't exit, but no GUI and only a limited memory footprint will remain.
I tried everything mentioned here but without success. Main parent Java process can't quit until the quit of subthread even with cmd /c start and redirecting streams tu nul.
Only one reliable solution for me is this:
try {
Runtime.getRuntime().exec("psexec -i cmd /c start cmd.cmd");
}
catch (Exception e) {
// handle it
}
I know that this is not clear, but this small utility from SysInternals is very helpful and proven. Here is the link.
One way I can think of is to use Runtime.addShutdownHook to register a thread that kills off all the processes (you'd need to retain the process objects somewhere of course).
The shutdown hook is only called when the JVM exits so it should work fine.
A little bit of a hack but effective.
In Linux when I run the destroy function on java.lang.Process object (Which is true typed java.lang.UNIXProcess ) it sends a SIGTERM signal to process, is there a way to kill it with SIGKILL?
Not using pure Java.
Your simplest alternative is to use Runtime.exec() to run a kill -9 <pid> command as an external process.
Unfortunately, it is not that simple to get hold of the PID. You will either need to use reflection black-magic to access the private int pid field, or mess around with the output from the ps command.
UPDATE - actually, there is another way. Create a little utility (C program, shell script, whatever) that will run the real external application. Code the utility so that it remembers the PID of the child process, and sets up a signal handler for SIGTERM that will SIGKILL the child process.
Stephen his answer is correct. I wrote what he said:
public static int getUnixPID(Process process) throws Exception
{
System.out.println(process.getClass().getName());
if (process.getClass().getName().equals("java.lang.UNIXProcess"))
{
Class cl = process.getClass();
Field field = cl.getDeclaredField("pid");
field.setAccessible(true);
Object pidObject = field.get(process);
return (Integer) pidObject;
} else
{
throw new IllegalArgumentException("Needs to be a UNIXProcess");
}
}
public static int killUnixProcess(Process process) throws Exception
{
int pid = getUnixPID(process);
return Runtime.getRuntime().exec("kill " + pid).waitFor();
}
You can also get the pid this way:
public static int getPID() {
String tmp = java.lang.management.ManagementFactory.getRuntimeMXBean().getName();
tmp = tmp.split("#")[0];
return Integer.valueOf(tmp);
}
If you know process name you can use pkill
Runtime.getRuntime().exec("pkill firefox").waitFor();
Since Java 1.8
you can call the method destroyForcibly(), which calls the destroy() method by default, but according to the Java docs, all sub-processes returned by ProcessBuilder or Runtime.exec() implement this method.