Setup ShutdownHook and exit application

4k views Asked by At

I have the following code:

public static void main(String[] args) {

    // login event
    String event = "login";
    System.out.printf("Handling event: %s %s\n",event,getCurrentLogin());
    sendMessage(event, getCurrentLogin());

    // logout or shutdown event
    Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
        @Override
        public void run() {
                String event = "logout";
                System.out.printf("Handling event: %s %s\n",event,getCurrentLogin());
                sendMessage(event, getCurrentLogin());
        }
    }));
 }

It's a very simple program for logging user's login and logout. The problem is that the program exits when reaches the end of function main().

Am I using to the shutdown event hook correctly? I don't want to create a complex windows service, it must be a very simple application cause it will be used for remote connected windows sessions.

Do you have any suggesion for background waiting for the login termination ?

6

There are 6 answers

0
Germán On

The ShutdownHook is a thread that is executed when the JVM is exiting and the program is not doing anything after you execute the "sendMessage":

sendMessage(event, getCurrentLogin());

You should wait for a signal to exit, like a Ctrl+C on the console. Just wait for a lock or Semaphore to avoid the program finalization, and that lock should be released when you need to finish it.

0
JB Nizet On

If I understand correctly, you want to wait infinitely until the OS stops your program, is that right? If so, you need to have at least one daemon thread running:

public static void main(String[] args) throws InterruptedException {
    //...
    final Object o = new Object();
    synchronized(o) {
        while(true) {
            o.wait();
        }
    }
}

I'm not sure thet the OS will kill the JVM gently enough, and that the shutdown hook will execute, though. Test it.

0
Ernesto Campohermoso On

The shutdown hook is called when the application finalize. So if you want to let you application run forever you need to implement something as:

while (true) {
    //Your logic here
}

If you want to write a daemon or scheduler maybe you need to use some framework as Quartz http://quartz-scheduler.org

If you want to implement a daemon you can use a Service Wrapper like http://yajsw.sourceforge.net or http://wrapper.tanukisoftware.com/

1
dogbane On

Here is one approach using a CountDownLatch to keep the main-thread waiting:

public static void main(String[] args) {

    // login event
    String event = "login";
    System.out.printf("Handling event: %s %s\n",event,getCurrentLogin());
    sendMessage(event, getCurrentLogin());

    final CountDownLatch latch = new CountDownLatch(1);

    // logout or shutdown event
    Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
        @Override
        public void run() {
                String event = "logout";
                System.out.printf("Handling event: %s %s\n",event,getCurrentLogin());
                sendMessage(event, getCurrentLogin());
                latch.countDown();
        }
    }));

    latch.await();
 }
2
Scorpion On

I have added a latch to your example to try to make the example work. I unfortunately do not have an IDE to test, but this should be enough to give an idea.

EDIT : The example below would print both login and logout.

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

    public class Test {
        public static void main(String[] args) throws Exception {

            // login event
            String event = "login";
            System.out.printf("Handling event: %s %s\n", event, getCurrentLogin());
            sendMessage(event, getCurrentLogin());
            final CountDownLatch latch = new CountDownLatch(1);

            // logout or shutdown event
            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
                @Override
                public void run() {
                    String event = "logout";
                    System.out.printf("Handling event: %s %s\n", event, getCurrentLogin());
                    sendMessage(event, getCurrentLogin());
                    latch.countDown();
                }
            }));
            latch.await(2, TimeUnit.SECONDS);
        }

        private static void sendMessage(String event, Object currentLogin) {
        }

        private static Object getCurrentLogin() {
            return "X";
        }
    }
0
Gray On

There are some good answers here but I still see some missing information.

Your code registers a shutdown hook in main and then main reaches the end and returns. The shutdown hooks are called when the last non-daemon thread exits. I suspect that main is the only non-daemon thread so when main exits the hook is immediately called.

If you don't want your program to exit immediately then main will have to loop and wait for some sort of signal. From the context of your question, I guess it should be listening for some sort of response from sendMessage call? If you want to wait for control-c then I like @JB Nizet's answer for this:

public static void main(String[] args) throws InterruptedException {
    ...
    Runtime.getRuntime().addShutdownHook(...);
    // wait until control-c is called
    final Object o = new Object();
    synchronized(o) {
        while(true) {
            o.wait();
        }
    }
}

You want to start waiting after you register your shutdown hook so your shudown code gets executed.