I would like to exit a console application while a javax.swing.JFrame still exists.
I have a console program that displays a javax.swing.JFrame. Sometimes I want to redirect the standard output to a file.
java -cp path com.domain.package > output.log
One plausible JFrame config value is WindowConstants.DISPOSE_ON_CLOSE and it is my understanding that the JFrame becomes disposed of when it is clicked to close, but the console program blocks at the closing brace of main. I do not know what it is waiting for. At that point I have to Control-C to terminate main.
Another plausible JFrame config value is WindowConstants.EXIT_ON_CLOSE and it is my understanding that when the JFrame is clicked to close, not only does the JFrame close but it also causes main to terminate. Note that even if main has reached the closing brace it does not exit until the click that closes the JFrame. This behaviour is better than the previous case because there is no need to Control-C to terminate main.
I want the console program to terminate so that the output.log file is released even if the JFrame is still alive. I tried to do this by having the JFrame owned by a Runnable in a Thread. That did not work; the console program does not terminate until the JFrame is closed.
// main program...
static void main(String[] args)
{
PlotWorker worker = new PlotWorker(data);
Thread thread = new Thread(worker);
thread.start();
// do not use thread.join
// simply exit at the closing brace of main
}
// owner of the JFrame...
class PlotWorker implements Runnable
{
JFrame jFrame;
#Override
public void run()
{
jFrame = new JFrame();
jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
}
Realistically I know I can look at the output.log file with a text editor (or just Linux cat the file from a second console) even if the standard output in the first console still has a "handle" on the output.log. I would prefer a clean solution that exits while the JFrame still exists.
EDIT: This question is no longer important to me. I decided to screen capture and save to disk the JFrame image, thereby obviating the need to keep the JFrame open, then proceed to close the JFrame, thereby closing all files.
EXIT_ON_CLOSE and DISPOSE_ON_CLOSE to my knowledge only govern the application behavior in regards to what happens with the JFrame, I.E. if it's set to exit_on_close, you get a System.exit(0) when the JFrame is closed.
In general, I think System.exit(status) is what you want when terminating your application, this should dispose of any object created by it, release file-locks and close any windows. Might be a little messy if you're using a stream to write to your logfile because that will be terminated too, possibly without calling it's .close() function.
Related
Java newbie here. Here's the deal:
I've got the following, simple shutdown-hook:
class SHook implements Runnable {
public void run() {
System.out.println("SH is done.");
}
}
And I've got a GUI application with a TextArea were the output is redirected, so that the message shows up there. Its window listener is again really simple:
public class MyAdapter extends WindowAdapter {
public void windowClosing(WindowEvent w){
System.exit(0);
}
}
As I close the GUI application with the [X] button, the message shows up for a split second before the whole thing shuts down. Nothing wrong here.
What I would like to do is: after the message has been printed, wait for a couple seconds, enough to clearly see that the message has been printed.
Adding a sleep or wait or whatever to the run method doesn't seem to work as the application still closes immediately.
I'm guessing the shutdown-hook is actually doing its job properly, it's just that the application doesn't care and closes the GUI immediately, while my hook prints its message somewhere in the Java limbo.
Is that correct? If yes, is there any simple way to make sure the GUI application only closes after the shutdown-hook is done, without making the shutdown-hook a non-shutdown-hook?
On your JFrame call method:
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
Then make sure that your WindowAdapter is added to JFrame with method adWindowListener.
Then in windowClosing put some println to see when it's called and of course use System.exit(0) in windowClosing if you want frame to be closed.
I have an application that uses a JTabbedPane to show several different tabs. One of these tabs has a thread running to display it's contents. I have already implemented a ComponentListener to stop the thread when the tab is not visible. I can see the thread becoming active when the tab is selected and stopping when becoming invisible, quite as expected.
If I close my application when the tab with the thread is not selected, everything goes well and the application closes. If I close my application when the tab with the thread is visible, the tab does not receive a ComponentEvent, so the thread stays active and I need to kill the application by hand (using the Terminate button on the console in Eclipse).
I prefer not to use the System.exit() method to close my application, but rather stop all threads and dispose all windows. This is working like a charm, except for this one tab with it's thread.
I have already tried to set the JTabbedPane to invisible before disposing my window, or removeAll(). Neither had the expected result. The removeAll() even had the opposite result. If the tab was not active, it would receive a ComponentEvent to indicate that is had become visible (componentShown) (actually, all tabs would receive the event in turn, but none would get a componentHidden).
Obviously, the thread should be stopped both when I close the window via the file menu (over which I have some control and where I have tested the removeAll and setVisible(false) methods) as well as when the window is disposed because the user clicks on the cross in the window corner.
Update
I have found a way to make the thread that seems to cause the problem run as daemon thread as suggested. This has however led to an unexpected problem. The class that started the problematic thread was the VisRunner class from the JUNG software package I am using. It contains a method "relax" in which the thread is started.
#Override
public void relax() {
// in case its running
stop();
stop = false;
thread = new Thread(this);
thread.setPriority(Thread.MIN_PRIORITY);
thread.start();
}
I have created the MyVisRunner class:
import edu.uci.ics.jung.algorithms.layout.util.VisRunner;
import edu.uci.ics.jung.algorithms.util.IterativeContext;
public class MyVisRunner extends VisRunner {
public MyVisRunner(final IterativeContext process) {
super(process);
}
#Override
public void relax() {
// in case it's running
Log.d("Relaxing");
stop();
stop = false;
thread = new Thread(this);
thread.setPriority(Thread.MIN_PRIORITY);
thread.setDaemon(true);
thread.start();
}
}
I load the relaxer as such:
visModel = new DefaultVisualizationModel<>(layout);
visModel.setRelaxer(new MyVisRunner(layout));
I would have expected this to solve the problem, but it only increases the problem. When I now start my software, it will not stop, even when the problematic tab is not even visible (the tab is constructed, but is not visible). The relax method from MyVisRunner has not even been called in that case; the thread is not initialized anywhere else in the VisRunner class. Commenting out the setRelaxer line will solve this additional problem (obviously keeping the original problem).
Update 2
I have solved the problem finally. I didn't realize that when I set my own relaxer, there was already a relaxer running. I have adjusted the code to:
visModel = new DefaultVisualizationModel<>(layout);
visModel.getRelaxer().stop();
visModel.setRelaxer(new MyVisRunner(layout));
This has solved both the additional problem as well as my original one.
You should set the thread as a daemon thread:
myThread.setDaemon(true);
A Virtual Machine will terminate if there are no more running non-daemon threads.
BTW you can add a WindowListener on your JFrame for windowClosing events.
I'm just getting to grips with GUI programming in java. Here is a trivial program (from O'Reilly's "Head First Java") which on the face of it looks easy to understand, but there's an aspect of it which I don't follow.
import javax.swing.*;
public class Test {
public static void main(String[] args) {
JFrame frame=new JFrame();
JButton button = new JButton("click me");
frame.getContentPane().add(button);
frame.setSize(300,300);
frame.setVisible(true);
}
}
This simple program, when compiled and run, will open a window with a button on it.
What I don't understand is what is happening with the flow of execution. When I run this program, the static main method of the Test class runs, all the commands in main() are executed -- so why doesn't the process terminate after the window appears? Why am I still sitting on what looks like an infinite loop? What is looping?
If I add the line
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
then I find the result even more imcomprehensible. Now, of course, the program terminates
once I've closed the window. But again I don't see why. The frame will be on the stack but I don't see where the program flow is and just the existence of something on the stack is not enough to keep the program alive, surely? I'm missing something fundamental which as far as I can see is not covered in the book I'm reading. I am slightly surprised by this -- "Head first Java" has been very good up until now at pointing out subtleties and explaining what is really going on, but doesn't seem to address this point (at least not that I've spotted).
why doesn't the process terminate after the window appears?
Because the Java Virtual Machine exits only after all non-daemon threads have finished. While not apparent, there's in fact two threads in your program: the main thread, and the event dispatching thread, which does everything related to the Swing GUI components. The event dispatching thread keeps going as long as any GUI components are visible.
Actually the program, while it may work, is wrong, because you're creating and accessing Swing components from the main thread. You ought to be doing all GUI work in the event dispatching thread. That is, it should be something like:
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
JFrame frame=new JFrame();
JButton button = new JButton("click me");
frame.getContentPane().add(button);
frame.setSize(300,300);
frame.setVisible(true);
});
}
The Java process terminates when the last non-demon thread dies. Normally there is just one, the main thread. When you display Swing components additional non-demon threads for event dispatching and GUI shotdown are started. Those terminate when the last top-level component gets disposed. In your sample the main thread dies after leaving the main method. You can have a look into the threads with a debugger or jvisualvm from the JDK tools.
The rest of the GUI flow is event driven. When you e.g. click on the frame's close button an event is generated and sent to the appropriate listeners within the event dispatching thread.
Setting JFrame.EXIT_ON_CLOSE as default close operation is like adding a default event listener to the frame. A quite harsh one, it just shuts down the JVM without respect to the rest of the application's state.
My app is a JApplet which opens a JFrame. the applet has a listener and a button, so that if the frame goes behind another window (looses focus), the user can simply click the button to get it to come to the front. frame.toFront(); This works fine.
But initially (in the applet's public void init() {}),
after calling frame.setVisible(true); I call frame.toFront(); to to make sure it starts in front. However, the frame then immediately goes behind the browser. Pressing the button calls it back, though.
I have tried a running a separate thread which repeatedly calls frame.toFront(); But as soon as this stops, the frame goes behind the browser anyways. Only when the button is pressed does it come to the front, and stay in front. Also, having a loop or time continually holding it in front is not a good option, because the user may need or want to have it go behind on purpose.
This "bug" is not present on the Mac (which runs Java 1.5), but on Windows (running 1.6) - including IE, FF, Chrome, Safari, but not Opera (strangely).
Possible cause and fix?
Have you tried setAlwaysOnTop(true) on the frame? I'm not sure however, if this is allowed on frames or windows created from an applet.
The setAlwaysOnTop(true) solves one problem, but create another, namely that now there is no way for the user to actually send the window to the back.
My sollution is a hack:
In the WindowListener attached to the JFrame, place this code:
#Override
public void windowDeactivated(WindowEvent e)
{
if(firstToBack) //firstToBack is an bloolean instance variable initialized to true
{
final JFrame f = frame;
new Thread() { public void run() {
try { Thread.sleep(300); } catch(InterruptedException ie) {}
f.toFront();
}}.start();
firstToBack = false;
}
}
This basically starts a new Thread first time, which waits a little and then calls the JFrame to the front. It only executes once, so the frame doesn't keep coming to the front every time the user sends it to the back. The 300 milliseconds is an arbitrary amount of time and perhaps not even necessary.
Perhaps someone can tell me why this works, but when the same kind of thread was started fron the applet's init() method, the window went to the back anyways, after the thread ended?
If I make a JFrame like this
public static void main(String[] args) {
new JFrame().setVisible(true);
}
then after closing the window the appication doesn't stop (I need to kill it).
What is the proper way of showing application's main windows ?
I'd also like to know a reason of a proposed solution.
Thanks in advance.
You should call the setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); in your JFrame.
Example code:
public static void main(String[] args) {
Runnable guiCreator = new Runnable() {
public void run() {
JFrame fenster = new JFrame("Hallo Welt mit Swing");
fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
fenster.setVisible(true);
}
};
SwingUtilities.invokeLater(guiCreator);
}
There's a difference between the application window and the application itself... The window runs in its own thread, and finishing main() will not end the application if other threads are still active. When closing the window you should also make sure to close the application, possibly by calling System.exit(0);
Yuval =8-)
You must dispose the frame, invoking the dispose method in your window listener or using setDefaultCloseOperation. For the argument of the last one, you can use two options:
DISPOSE_ON_CLOSE or EXIT_ON_CLOSE.
DISPOSE_ON_CLOSE only dispose the frame resources.
EXIT_ON_CLOSE disposes the frame resources and then invokes System.exit.
There is no real difference between the two unless you have non daemon threads.
I prefer to use DISPOSE_ON_CLOSE because this way I'm able to notice if I forgot to terminate a thread, because the JVM will stop if there are no more threads running. That's also the reason closing a Frame without disposing will not terminate the application, since Swing creates a thread to handle events that is terminated only when dispose is invoked.
The correct way to do this (unless you're writing a very trivial single-window app, i.e. no other windows or threads etc..) is to catch the windowClosing() event, and then call the dispose(); method of the form.
If your program doesn't completely exit after this, it means you have other non-deamon threads running, and you must stop these as best you see fit depending on your program.
Calling System.exit() or setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); will force stop any other threads (and the whole program), meaning your code is less portable, and force-stopping threads is obviously dangerous (in a programming kind of way).
You can set a window listener to the frame so the program terminates after you close it.
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
}