Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Windows shutdown hook on java application run from a bat script

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?

like image 302
Zoltán Avatar asked Feb 14 '12 13:02

Zoltán


1 Answers

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:

  1. Keep reference to shutdown handler thread.
  2. Set custom native console handler routine with JNI.
  3. Call custom Java method on CTRL+CLOSE signal.
  4. 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.

like image 157
Mersenne Avatar answered Oct 12 '22 23:10

Mersenne