My application basically uses apache’s httpclient to connect to a server and do something for the user.
I am a java beginner (though not a development beginner), so many java specific approaches may have been calmly ignored.
DESCRIPTION
- the app uses a TrayIcon with a popup menu as the only user interface and reacts with either tray messages or popup [confirm] dialogs.
- it uses threads because the httpclient was effectively blocking the main thread when waiting for a response
- employs a client-server to assure a single instance only and reactions to second instance attempts
- employs observer/observable pattern to react to the httpclient’s and the single instance watchdog server’s messages
- the main behavior is controlled by a Controller class, which is the only Observer; everything related to the httpclient is in a separater WebClient class, which implements Runnable and extends Observable; then there’s the AttServer which is again Runnable and Observable and controls everything around the server listener (there’s not much actually).
This is my first application in Java, so many obscurities are possible..:(
PROBLEM
The desired behavior is that whenever a user attempts to run a second instance of this app, then it tries to bind a socket listener on port X and, failing, sends a string message to that port. The ‘main’ app accepts the message and reacts with a confirm dialog being shown asking the user for some input.
Everything was working just fine up to the point when I’ve added the client-server; the second instance successfully triggers the update() method of the observer, the observer correctly distinguishes what’s going on and fires the corresponding method; and then at the point of the first new GUI component (a confirm dialog) should show up, NOTHING happens:
- the app doesn’t do anything
- no errors or exceptions thrown in System.out
- the tray icon CONTINUES to work just fine (right click shows a menu etc)
..it’s just that all the instructions coming after the dialog doesn’t get executed and the dialog doesn’t show up.
WEIRDNESS #1
When the user uses the tray icon menu to fire some other command that shows a dialog, the GUI somehow coughs up and shows BOTH (or more when appropriate) dialogs in the correct order (first the stuck hidden dialog shows up and then the second fresh one) and everything just seems that this is the correct state and all commands get executed correctly (albeit some of them ‘a bit’ later).
WEIRDNESS #2
I guess this is caused by the swing being not really compatible with threads; however, when the update() method gets fired up from the httpclient (which is a separate thread as well), the GUI works just fine. The only difference in the way the Observer gets notified is that the WebClient calls the notifyObservers() after some user action (albeit in a separate thread and after (at worst) 30s timeout and the AttServer calls notifyObservers() in a catch() block, because IOException is thrown when the specified port has been already bound.
WEIRDNESS #3
This problem is irregular to say the least. It USUALLY occurs in the development environment (netbeans), although sometimes it doesn’t — at first I’ve got the impression it was solved by calling another (dummy) JDialog, which remained hidden intentionally, before the actual problematic dialog — it seemed that swing needed a little ‘nudge outta the door’; though after building the project into a jar or an exe (via launch4j), the problem was back. Then I’ve tried to use a single JFrame component for ALL the swing dialogs and it helped somehow — sometimes when the output jar is run it works as it should and the next day it hides dialogs again.
I am totally lost, scoured the Earth for a solution, found nothing really. I hope that somebody else might bump into some similar problem and, being a more experienced Javaman, found a solution. I’ve also tried some doLayout(), validate(), revalidate(), repaint(), update() calls on the main JFrame instance, but no go.
There’s hope 🙂 Thanks for reading and thanks even more for any ideas.
CODE EXAMPLES
This is the run() function from AttServer — the source of the non-working update()s.. (it’s a bit hard coded as the main functionality is there and I got stuck elsewhere..)
public void run() {
this.addObserver( Controller.getInstance() );
setChanged();
// this.command == INIT set and new thread with AttServer called in the beginning of the main()
if( null != this.command ) {
switch( this.command ) {
case INIT:
try {
init();
} catch( IOException ex ) {
notifyObservers( new ErrorEvent( 100 , "Could not bind the specified port." ) );
}
break;
default:
} //switch
// no command, looks like an incoming connection
} else {
try {
DataInputStream in = new DataInputStream( socket.getInputStream() );
String inputLine;
while( ( inputLine = in.readLine() ) != null ) {
if( inputLine.trim().equals( EventType.CLOCKIN.toString() ) ) {
notifyObservers( EventType.CLOCKIN );
return;
}
}
} catch( IOException ex ) {
//todo
}
}
}
..this is the opposite side of the fence, the update() method:
public void update( Observable obj, Object arg) {
// received a remote request *******************************************
if( obj instanceof AttServer
&& arg instanceof EventType ) {
EventType command = (EventType) arg;
Attendance.debugMsg( "Received " + command.toString() + " request from AttServer" );
if( false == isOnClock ) {
doClock( true , true );
}
} // ..........
.. and this is the essence of the doClock() method called from update() — there’s nothing really, and even when the dialog is shown right after the call, or event when it’s shown right in the update() method, it doesn’t work.
(overloaded with default data)
public static void doClock( Boolean clockIn , Boolean confirm , String message , String title ) {
Event lastEvent = Log.getLastClock();
if( confirm ) {
if( JOptionPane.NO_OPTION == JOptionPane.showConfirmDialog( mainFrame , message , title , JOptionPane.YES_NO_OPTION ) ) {
return;
}
}
Without reading through this whole document (!!!) I would guess that you are not updating the GUI via the Event Dispatch Thread (EDT).
The GUI is supposed to be updated only by a single thread, the EDT.
You can have background threads in your applications, but the update of the GUI must be passed to be done to the EDT only.
Do not touch any Swing control in a thread of your own i.e. other than EDT.
Otherwise you will have tons of problems.
It is plainly wrong not to go through the EDT