Reject a OS X system shutdown - java

We have an SWT application which causes to hang when it is still running and the user triggers a system shutdown on OS X. Closing the application from the application menu works fine.
I've tried to register a SWT.Close listener to the Display instance:
display.addListener(SWT.Close, new Listener() {
#Override
public void handleEvent(Event event) {
if (!handleExitRequest()) {
event.doit = false;
event.type = SWT.None;
}
}
});
This solves magically the hang, but unfortunately, it is not reliable called when exiting. :(

I have had mixed results listening for shutdown hooks on the main thread. Sometimes it works and sometimes it doesn't. What has worked for me in the past is to create a separate thread and register the shutdown hook to that.
public class MyGuiApplication
{
public static void main( String[] args )
{
Runtime runtime = Runtime.getRuntime();
Thread shutdownThread = new Thread(new Runnable()
{
#Override
public void run()
{
// Put graceful shutdown code of the main application/thread here.
}
});
runtime.addShutdownHook( shutdownThread );
startMyApp();
}
}
Hopefully that might work for you.

Look at this class and this class.
The Application class allows you to integrate your Java application with the native OS X environment.
...
Cancel shutdown/logout if the user has unsaved changes in your application.
You can take a look at following questions too:
How to cancel shutdown in Mac OS X when application is an agent?

Related

How can I do some stuff in my java app, after it has been killed?

I would like to make some critical (saving) operations in my java app, which should be executed, when the java process is killed by the task manager or by the IDE. Is it possible to do it anyhow?
EDIT:
Tried with the shutdown hook, but it doesn't work, with process kills, just with normal JVM shutdowns, or exceptions.
What I need is to print the SHUTDOWN word to the console when I kill my program using IntelliJ, but it doesn't work:
public static void main(String[] args) throws InterruptedException {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
public void run() {
System.out.println("SHUTDOWN");
}
}));
while(true){
System.out.println("NOTHING");
Thread.sleep(500);
}
}
Depending on how you choose to 'kill' your java process, this may be of interest to you:
How to handle a SIGTERM

How to silently termintate a Java program with threads and locks

In C programs using system threads for example, I can pass a SIGINT with Ctrl+C and the process will be killed silently. But when I do the same thing to a Java program with threads, locks, semaphores et cetera, the JVM just stops there and I have to kill the process "outside", by closing the terminal or rebooting the system. How can a make a Java program silently exit as it should without closing the terminal when I see some wrong behaviors in runtime?
You can add a shutdown hook to the JVM that gets triggered when a SIGINT is received and then in there call Runtime.getRuntime().halt(0). That will kill the process. You can even use the Shutdown Hook to clean your running Threads.
[EDIT] My initial answer was to use System.exit() in the hook. But that will not work because System.exit will trigger the already running hook.
You can try this example with the hook and not registering the hook.
public class Exit {
public static void main(String[] args) {
Runtime.getRuntime().addShutdownHook(new ExitHok());
Thread t = new Thread(new Printer());
t.start();
}
private static class ExitHok extends Thread {
#Override
public void run() {
System.out.println("Received shutdown");
Runtime.getRuntime().halt(0);
}
}
private static class Printer implements Runnable {
#Override
public void run() {
int counter = 0;
while (true) {
System.out.println(++counter);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
}
}

Java Swing application terminates unexpectedly

I'm trying to write a Swing application in Java that also runs the Google AppEngine Dev-Server (see Developing a Java Application that uses an AppEngine database) and am running into a strange problem with the Swing Eventloop.
I have the following two classes:
A debug-window, which will eventually receive log messages, etc:
public class DebugWindow {
private static JFrame debugWindow = null;
private static JTextArea debugContent = null;
public static void show() {
debugWindow = new JFrame("Debug");
debugWindow.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
debugContent = new JTextArea("Debug messages go here!");
debugWindow.add(debugContent, BorderLayout.CENTER);
debugWindow.pack();
debugWindow.setVisible(true);
}
}
A helper-class that loads the Google AppEngine Dev-Server:
// other imports
import com.google.appengine.tools.development.DevAppServerMain;
public class DevServer {
public static void launch(final String[] args, boolean waitFor) {
Logger logger = Logger.getLogger("");
logger.info("Launching AppEngine server...");
Thread server = new Thread() {
#Override
public void run() {
try {
DevAppServerMain.main(args); // run DevAppServer
} catch (Exception e) { e.printStackTrace(); }
}
};
server.setDaemon(true); // shut down server when rest of app completes
server.start(); // run server in separate thread
if (!waitFor) return; // done if we don't want to wait for server
URLConnection cxn;
try {
cxn = new URL("http://localhost:8888").openConnection();
} catch (IOException e) { return; } // should never happen
boolean running = false;
while (!running) {
try {
cxn.connect(); // try to connect to server
running = true;
} catch (Exception e) {}
}
logger.info("Server running.");
}
}
My main(...) method looks like this:
public static void main(final String[] args) throws Exception {
DevServer.launch(args, true); // launch and wait for AppEngine dev server
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
DebugWindow.show(); // create and show debug window
}
});
}
With this I'm getting some very strange behavior regarding the Swing Eventloop:
First, the way Swing should work: If I comment out the line DevServer.launch(...) in main(...), the application launches, shows the debug window, keeps running, and when I close the debug window, it shuts down.
If I add DevServer.launch(...) back in, it launches the server as expected, and then quits immediately (it probably also showed the debug window briefly, but it's too quick to see).
If I move DevServer.launch(...) line after SwingUtilities.invokeLater(...), it shows the debug window, then launches the server, and when the server is up, it quits immediately.
Now it get's really weird: If I change the line to DevServer.launch(args, false), i.e. I don't wait for the server to actually launch, but simply let my main(...) method complete immediately, the debug window shows, the server loads correctly, the application keeps running, but doesn't quit if I close the debug window?!
If I then also change JFrame.DISPOSE_ON_CLOSE to JFrame.EXIT_ON_CLOSE, the debug window shows, the server loads correctly, the application keeps running, and it quits correctly if I close the debug window.
Any idea what is going on with the Swing event loop here? I'm stumped... Are there things that will cause the Swing event loop to terminate early (scenario 2 and 3)? Do multi-threaded applications prevent Swing from detecting the last disposed window (scenario 4)?
For reference, here is the source of the Google AppEngine Dev Server.
Items #4 and #5 are actually expected behavior. A Java/Swing application doesn't stop when the last Swing window is disposed, but when the last thread stops executing. These two conditions are equivalent for single-threaded applications, but not for multi-threaded ones.
As for #1, #2 and #3: looking through the AppEngine Dev Server code, I noticed a fair amount of System.exit(int) calls in there. One of these is probably the culprit. If the code you're showing is all that's relevant, then the offending System.exit is likely called in response to the connection established after if (!waitFor) return; (due to #4)

Java equivalent of .NET's Environment.HasShutdownStarted

In .NET, you can check the Environment.HasShutdownStarted property to see whether your service is being unloaded for whatever reason, and perform graceful unloading/cleanup.
So instead of:
while (true) { }
...you can use...
while (!Environment.HasShutdownStarted) { }
Is there an equivalent thing in Java?
Perhaps you're looking for a shutdown hook? This allows you to specify a thread to be run when the application is closed (as long as it's not brutally forced closed with kill -9 or similar, but in that case no environment can guarantee to do anything on shutdown.)
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
//Code here.
}
});
From a practical perspective, you should also make these threads quick to execute - since otherwise the application will appear to hang upon exiting, and no-one likes that (plus, the OS or user may choose to kill off the application, aborting the hook at an arbitrary point.)
You can add multiple shutdown hooks, and they will be executed concurrently (and in an arbitrary order.)
Removal of shutdown hooks can be down in a similar way by calling removeShutdownHook().
You could add a shutdown hook. Basically registers an unstarted thread that will run when the application terminates.
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
// logic for making a clean shutdown...
}
})
The link above has a very good description of what happens during shutdown.
You can look at the shutdown hook API, and instead of doing
while(true) {
}
You can declare a member in your thread/runnable implementation to signify shutdown:
class MyRunnable implements Runnable {
private running= false;
public void setRunning(boolean running) {
running= running;
}
public void run() {
setRunning(true);
while(running) {
// do task
}
}
}
public static void main(String[] args) {
final MyRunnable myRunnable= new MyRunnable();
final Thread runThread= new Thread(myRunnable);
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
myRunnable.setRunning(false);
}
});
runThread.start();
runThread.join();
}
This is not a perfect solution, but should get you at least in the right direction.

Stop ALL Awt/Swing threads and monitors and stuff so that just the main thread is left

I have the following
public static void main(String[] args) {
boolean running = true;
boolean foo= false;
while(running)
{
doSomeTask(); // might set foo true
if(foo) {
//This call waits/blocks until gui is done working.
fireUpSwingGui(); //does work...
foo=false;
godModeReleaseGUIandALLResourcesOnlyWantMainThreadLeft();
}
}
}
Hope that godModeReleaseGUIandALLResourcesOnlyWantMainThreadLeft() says it all.
Bear in mind that we might fire up the gui again at a later stage when foo becomes true again somewhere inside doSomeTask().
Take a look at AWT Threading Issues which explains the criteria for an AWT application to exit. The part you want to focus on is the following:
Therefore, a stand-alone AWT application that wishes to exit cleanly
without calling System.exit must:
Make sure that all AWT or Swing components are made undisplayable when the application finishes. This can be done by calling Window.dispose on all top-level Windows. See Frame.getFrames.
Make sure that no method of AWT event listeners registered by the application with any AWT or Swing component can run into an infinite loop or hang indefinitely. For example, an AWT listener method triggered by some AWT event can post a new AWT event of the same type to the EventQueue. The argument is that methods of AWT event listeners are typically executed on helper threads.
A quick sample app to demonstrate...
import java.awt.Frame;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
public class CloseAWT
{
private static boolean running = true;
private static int response = -1;
public static void main(String[] args)
{
boolean showSwing = true;
boolean checkFrames = true;
while (running)
{
if (showSwing)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
response = JOptionPane.showConfirmDialog(null, "Hello World?");
}
});
showSwing = false;
}
else
{
if (response >= 0 && checkFrames)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
// topFrame.dispose();
Frame[] frames = Frame.getFrames();
System.out.printf("frames.length=%d\n", frames.length);
}
});
checkFrames = false;
}
}
}
}
}
To confirm the behavior was as expected, I ran this in JProfiler. After clicking 'yes' to dismiss the confirmation dialog, the 'AWT-EventQueue-0' thread was marked as dead. The only threads alive after this were the 'main' and the thread which listens for Ctrl-Break.
I highly recommend using something like JProfiler, YourKit, JProbe or one of the free profilers to make sure you've properly released all the components and removed all the listeners.
One final thought... You might want to consider spawning your GUI as a separate process and using some sort of IPC to pass information between your daemon process and GUI. Although this incurs the additional overhead of an extra process and the IPC, it would give you a greater assurance that your GUI is completely cleaned up when it is no longer needed.
Assuming you're opening JFrame isntances, why don't you just store them in a collection and inside godModeReleaseGUIandALLResourcesOnlyWantMainThreadLeft() you iterate over them ans call setVisible(false);
I'm not sure whether you actually can stop the AWT event queue that drives the gui.

Categories

Resources