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)
Related
I am doing a project about chatting application. I'm using sockets. I'm creating this JFrame that gives me the option to connect as a server or a client. When I run my Options JFrame (choose to run as server or client) and I press server button some of my code gets skipped in the action listener. I already spend hours debugging and I don't know why some of my code gets skipped.
private void serverBtnMtd() {
try {
System.out.println("Running server Method");//Testing
MyServer myServer = new MyServer();//server gets Created and shows up
myServer.initializeServer();//Skipped
myServer.setVisible(true);//Skipped
System.out.println("End Running server Method");//Testing
} catch (Exception e) {
e.printStackTrace();
}
}
Action listener
btnServer.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
serverBtnMtd();
}
});
My Main Method
public class MyMain {
public static void main(String args[]) {
ServerOrClient mainApp = new ServerOrClient();
mainApp.setVisible(true);
mainApp.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
When I run my server class without the options JFrame everything runs. All of the skipping of code happens on the Action listener. I did some println's to test if all my code gets run and only the "Running server Method" shows up in my command line.
Server generally have infinite loops. So your code isn't skipped, just the new MyServer thing doesn't finish to resume your code. Put the code in a new thread.
You need to add:
setFocusable(true);
to your constructor
While using SwingWorker to sequentially update my front end, I think I may have found a bug.
I have create a server which listen for connections. A connection might only initiate in a few minutes, so I created a little connecting animation that plays while the server is waiting for a new incoming connection.
A SwingWorker<T,T> is used to update a JLabel every 0.5 seconds with a character, which in the end, creates a character that bounces from one end of the panel to next, upon connection, the animation is removed and the JLabel is updated with different text.
This has been working all fine and dandy, but I have recently realised that the animation sometimes freezes, but yet, every time I ran the animation, it stopped and the JLabel updated correctly.
I then realised that the JLabel text does not update when I am on a different workspace.
(Edit: Where workspace refers to the ability to switch between desktops (workspaces) in Linux (I'm on Linux Mint 15). See: http://linux.about.com/library/gnome/blgnome2n4a.htm
I tested and retested:
Test A:
Start listening for connections
Application Animation starts (powered by SwingWorker)
Keep app open on current workspace
Client connects and animation correctly disappears
Jlabel correctly updates to new text.
Test B:
Start listening for connections
Animation starts (powered by SwingWorker)
Move to a different workspace by pressing Ctrl+Alt, (arrow key) and leave app on previous workspace.
Wait until client connects and move back to workspace containing app.
Jlabel froze and DID NOT update to new text.
This is where it gets weird: If I select a JMenuItem, which will in turn overlap with the animation, the overlapping part of the text will then UPDATE partially to the correct text.
I have not tested this on another Linux distribution, but will do so after I get feedback that my code is not the cause of this problem.
CODE:
ConnectingAnimation will be running all the time, but only updating the animation when the server is not connected.
public class ConnectingAnimation extends SwingWorker<Void, String> {
#Override
protected Void doInBackground() throws Exception {
StringBuilder sb = new StringBuilder();
while (true) {
while (!connected) {
try {
...
//Append text to StringBuilder
...
//publish updated text to be displayed next
publish(sb.toString());
//Sleep
Thread.sleep(100);
} catch (InterruptedException e) {
return null;
}
}
//Waits until clientConnected() is called
try {
synchronized (clientConnection) {
clientConnected.wait();
}
} catch (InterruptedException e) {
logger.logException(e);
}
}
}
#Override
protected void process(final List<String> chunks) {
for (final String update : chunks) {
if (connected) {
connectionStatusLabel.setText("Client Connected");
break;
}
connectionStatusLabel.setText(update);
}
}
}
clientConnected() will be called by an external module notifying the animation to stop
public static void clientConnected() {
...
synchronized (clientConnection) {
connected = true;
clientConnection.notify();
}
}
So this is in reference to my previous questions here and here. Basically the problem I am having is the program goes into the Server class and stays there because there is a while look in the server code that essentially is continuously listening to the port for any response from the client; this makes the Main window completely unclickable since the program is not in the MainWindow class.
But really what I want it to do is setup the connection and then go back to MainWindow so the on-screen buttons are clickable. Or, as suggested by one poster, I could pass the MainWindow object by reference to the Server (which I have already done) and then use SwingUtilities.invokeLater() but I'm not really looking to update the GUI. I'm looking to give control back to the GUI so the user can click buttons and such.
When it goes back to MainWindow, I still need the connection to stay alive so the client can send things and I can receive it and display the received content on the screen for the user to control.
Basically, you could use a SwingWorker to perform actions in the background, off the Event Dispatching Thread, which will allow you application to continue working...
SwingWorker<Server , Object> worker = new SwingWorker<Server , Object>() {
#Override
protected ServerdoInBackground() throws Exception {
Server server = new Server();
//...
// Use publish(...) to send information
// back the EDT via the process method...
return server;
}
#Override
protected void done() {
try {
Server server = get();
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
}
}
#Override
protected void process(List<Object> chunks) {
// You can safly update the UI from here, maybe
// with some messages??
}
};
worker.execute();
Basically, this creates a new Server object in the doInBackground method, does what ever else it needs to do that might otherwise block the EDT and exits by returning the server instance.
You can use done if you want to be notified when the doInBackground method completes and either get the result of the method, or if it threw an exception, handle the exeption.
The publish/process methods can be used to send information back to the EDT, which could be used to update the state of the UI...or not...
Now, if you already have a reference to the server, may consider creating a custom SwingWorker to which you pass the reference of the Server to...
Server server = ...;
ConnectionWorker worker = new ConnectionWorker(server);
worker.execute();
//...
public class ConnectionWorker extends SwingWorker<Object, Object> {
private Server server;
public ConnectionWorker(Server server) {
this.server;
}
#Override
protected ServerdoInBackground() throws Exception {
server.doStuff(); //...
return ...;
}
}
Take a look at SwingWorker more details
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?
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.