In summary, what I want to do seems deceptively simple. There are 3 classes: Config, Account, and Mail. I do something like this:
Config c = new Config(); Account acc = new Account(c); Mail mail = new Mail(acc); int num = mail.checkMail();
Now I have a swing application that does this in Main(). I want it to run mail.checkMail(); repeatedly at specified intervals (in a thread), and I want the swing class to update a label with the new int upon every check. I can't figure out how to do that - there seem to be so many ways to do threads, and they're all a bit confusing.
Thoughts are welcome - I'm still gaining experience and have no "best practices" guide
One simple and effective way would be to use javax.swing.Timer. You create a Timer, and register an ActionListener with it (you've already written many of those, I'm sure.) The actionPerformed method of the listeners will be called at an interval that you can specify. In your actionPerformed method, you'd call checkMail() and then use SwingUtilities.invokeLater() to set the label text.
To implement this, you'd create the Timer in the same place where you actually assemble the GUI, and call start() on the timer after the GUI is all put together. The ActionListener would likely be a separate class. The Runnable you have to write for invokeLater() could be an anonymous class.
. is potentially a long-running task . needs Config and Account instance data, . may require error/exception interpretation, and . returns something ...
you may want to constrain Timer actionlistener to _only_ start a _checkMail thread (as appropriate) ... and let another listener be responsible for GUI update
the 'other' action/event listener (GUI updater) would be fired at _checkMail completion.
I left out a few things:-) It's more code, but can save some later hair-pulling if you plan to continue to use/distribute the app.
As an example, you could implement a MailMgr Class containing
- num (last available mail msg count, or -1 at init) - setters/getters for Config & Acct info - state variable (OK, POLLING, ERROR, etc) - listener overrides (_addMailListener(listener) etc) - checkMail() - to start MailThread, if appropriate - internal MailThread Class
the MailThread Class might contain
- 'listener' overrides (same set as above, invoked by MailMgr, keeps a list of listeners that we notify) - event notifier(s) (_fireMailCounted, _fireMailError ..) - run method! (retrieve mailcount, notify listeners of outcome)
Finally, you'd need to write the Event obj and Listener interface for event(s) you want to catch ...
In this scenario, you could do something like:
when you start the app.
When you receive a Timer event, you could then do something like
Likewise, upon 'hearing' completion events, the MailEventListener can trigger the appropriate GUI updates.
[You'll need to write a class to do this...:-]
SOOOOOooo what would these few peanuts buy you?
1) a little less pain when things go wrong
. won't tie-up a listener event q waiting for checkMail completion
. allows you to trigger visual cues (GUI) about poll state
. app can maintain info about data/connection issues without interrupting other gui tasks
. facilitates implementation of mail-status-widget to dynamically review poll state
Clio is right, although I would use only a slightly different approach than what I originally described. I wasn't really thinking when I recommended javax.swing.Timer -- I should have recommended java.util.Timer instead. If you use javax.swing.Timer, the mail checking will be done on the GUI thread -- not what you want, as it will block the GUI. I don't normally use this class, but I recommended it to you as it sounded like you'd be more confortable with the idea of an ActionListener than a Runnable. Mea Culpa.
Anyway, instead, use java.util.Timer (which wants a Runnable instead of an ActionListener, but otherwise, it's basically the same idea as the Swing timer) and checkMail will run on an independent thread, leaving the GUI to run in peace. Again, use SwingUtilities.invokeLater() to update the GUI when you're done.
Joined: Feb 16, 2004
Definitely lots to think about - thank you both for your advice. This level of Java coding seems to be quite a bit more advanced than the level I'm at, but without a challenge how can you learn....? I'll do my best at implementing something like that, since it's definitely a better idea to have the timer run in its own thread (separate from the GUI), and the extensibility it offers is tempting.
In other words, expect more questions on this stuff as I attempt it
Can either of you recommend any particularly good literature that might help me improve my GUI coding skills?
Joined: Apr 30, 2004
I agree with Ernest's approach, and it's certainly much clearer than the approach I suggested. I was trying to provide some 'food for thought' based on some snags i have run into in the past... kind of 'defensive programming':-)
As you may have seen in some earlier posts, I'm still struggling with 'best practices' for dealing w the Swing event Q myself ...
I can't get used to a single Q for GUI events, although I *think* I understand why it may have been architected this way [my "naive" theory is: because swing must be portable and operate the same across (theoretically 'any') gui windowing system, java must abstract a 'protected mode'-ish graphics kernel. i can only guess that swing's single asynch q simplifies 'kernel' programming/maint and awt backsupport ... as i say, it's a naive theory ...].
SwingWorker appears to acknowledge the limitations of Swing. The fact that it's not included in the SDK keeps some of us from shooting ourselves in the foot before we have learned enough about the particulars of Swing. Probably in the typical case, a programmer may never need a SwingWorker thread.
The bottom line is that we all wind up taking Swing design into account (eg _invokeLater) once the 'event-based' part of our need comes into play...
But to answer your question, I'm afraid I don't know of a 'best practices' type book. There was an author of a Swing book on this forum maybe a few months ago, and Gregg (moderator) asked him about this very topic. As I recall, the book didn't take a position on best practices, but offered various approaches. The book may have been reviewed elsewhere on this site. I don't believe the samples were online back then when I looked... but it's probably worth a look-see at the javaranch book reviews.
- I got (and get) most help from downloaded tutorials, examples, and open source:
1. The SwingSet2 demo that comes with the SDK is a tour-de-force of designs... I really like their initialization code, and the init infrastructure for launching the components. it's kind of a bean-starter lesson - a component-ized function orientation instead of the traditional top-down sequential design.
2. For open source, jEdit really impresses me, even though the code itself is way 'advanced java'. it may take a long while to understand the detail, but it's always instructive to study it. there are lots of neat tricks and thoughtful methods there...
3. Again on the 'advanced' side: if you want a case-study of a java e-mail filtering application using very advanced concepts (model-view-controller, thread pools, etc), you can download BlackMamba. In this article the author explains how and why he chose to design the app the way he did.
4. Finally, if you check out a handful of Sun examples, you'll see a pattern of their "_createAndShowGUI()" to get the main window displayed. The Sun coders are particularly mindful of the Q limitations, and by reviewing some of their examples, you'll begin to get a feel for when a deceptively simple need can trigger the need for a slew of defensive programming:^-)
Joined: Feb 16, 2004
I have to admit - I know enough to see that Clio's post is probably very good advice but it's so far beyond my understanding that I'm not sure what to do with it. I'll study some more examples (like the ones you spoke of), and see if it makes a bit more sense in a few days.
Joined: Feb 16, 2004
Need some help with threads...
In my GUI class main method I have a timer. I have a TimerTask now that, every few minutes, should launch a new instance of Mail, which is a Runnable object, in a new thread.
The run() method of Mail will do all the necessary operations to check my mail. I don't understand how to use invokeLater() in my Mail class, at the end of the run() method, to update the Gui class that has the timer so that the GUI will change to reflect the mail operation's outcome (i.e. # of new messages). The Mail class doesn't know much about the GUI class, so it can't very well call methods in that class that will update the GUI, can it? Can someone explain, by any chance?
[ August 21, 2004: Message edited by: Tom McAmmond ] [ August 21, 2004: Message edited by: Tom McAmmond ]
Joined: Apr 30, 2004
Hi Tom, I know my previous posts have been kind of dense, but I'll try to cut that out:-)
Basically, you will be able to design the mail thread in the way that best suits your needs. you can design it as
(1) a reusable component (operates same way each time, regardless), or
(2) a specialized class, with access to the caller's resources (i.e. it can directly or indirectly 'know' about and manipulate GUI view resources)
My first response was a strategy for case #1. This is considered a java/oo 'best practice', and it is the basis for the "bean" concept. a bean is just basically a reusable component.
in case #1, you would write an event class to represent the real-world "you've got mail!" event. (i called this the "MailCounted" event) Then your Mail thread would do "fireMailCounted()" when he's done. somewhere else you would have an eventlistener ready to 'do the right thing' when the event is fired..
... but there's nothing wrong w opting for strategy #2!
w strategy #2, your Mail thread will be designed to be a member/inner class of the invoking class. in this scenario, the Mail instance can get access to the caller's resources (such as class-accessible methods/variables).
For example, if the caller has a method
the Mail thread can invoke it...
that's just an example - the design options are open. but i'll stop right here before i get to blabbing on-n-on
hth:-) p.s. thanks for your feedback! it's valuable to know if responses are helpful or not
Joined: Feb 16, 2004
I really appreciate your help on this clio & Ernest. I realize these are pretty basic newbie questions.
I like option number 1 - make the Mail class a reusable component. The service I'm accessing has more than just Mail to offer, and eventually I would like to incorporate other reusable components into this program.
Here's what I think has to be done - please correct me if I'm wrong:
- So I'm going to learn how to create my own event classes (something I haven't done before).
- Then my TimerTask inner class in my GUI instantiates a new Mail class (Mail implements runnable()). Every time the timer goes off, it creates a new thread and passes the Mail class to the thread.
- The Mail class, upon t.start(), does a bunch of stuff to check the mail. It fires a new MailCounted (an Event class internal to the Mail class?) event upon completion.
- The GUI has a listener for this MailCounted event, and it updates the display with current information when it sees this event being fired.
Questions (some of them, I realize, extremely basic. I'm still researching and reading docs/tutorials constantly - might figure these out on my own soon):
- How and where, exactly, does the GUI listen for MailCounted events?
- How does the GUI get info from the Mail class when it's finished doing what it does? The thread is dead at that point...
- I take it I have to synchronize the methods in the Mail class that deal with updating, say, _numUnread - Should I attempt to stop my timer from starting new Mail threads if one already exists? Probably....
So most of my confusion relates to "How does the Mail thread update the GUI? Does the GUI access the Mail class when it's finished doing stuff, or does the Mail class send info to the GUI along with the Event?"
Once again, thanks for the help
Joined: Apr 30, 2004
Hi Tom, I think you're just nanoseconds away from working your way through - what i think of as - a pretty advanced programming task!
Don't be turned off by the event stuff - it's pretty quick to write once you assemble the pieces: event class, listener interface, listener.
you don't really need to think about using an event adapter until you have more than one event (an event adapter is just a programming convenience, so your eventlistener instances can pick and choose to override methods in the interface...)
on the design side, either Timer or Mail can start the checkMail thread. since you'll be firing an event at completion, you won't need to synchronize/join threads. when the task finishes, your listener will be notified of the event, and java implicitly zombie-fies the thread.
my pref would be to use one thread per distinct mail account. your Mail class can be the thread 'controller'. timer event just needs to trigger a Mail fetch ... Mail class can ignore the call if a thread is already running for the given mail acct
however you will want to use more than _isAlive() for checking thread 'state'...
why? java threads are a little dodgey when it comes to querying or managing state - i'm sure you've read about this. you will need to set and check your own state var(s) so that you can manage execution (stop, interrupt, 'isExecuting', etc). Thread.stop() is a no-no ... you basically have to write code to give the thread a poison pill (as needed)
on to your central question: your event listener uses the same mechanisms as the other java listeners (actionListener, windowListener etc). once you register the listener (eg _addActionListener), it's like registering a callback: java will load and run your listener-associated code whenever the so-named event (actionPerformed, windowClosing etc) is fired.
to try to clarify 'flow', i'll sketch out a skeletal design:
code fragments (see above code for MailUpdateEvent class, MailEventListener interface)
i know i'm forgetting things (like inner-workings of Mail class!), but i hope this helps to get you started ... let us know! [ August 21, 2004: Message edited by: clio katz ]
Joined: Feb 16, 2004
Thanks for not making it too easy - I learned lots Still some finishing touches and a little reorganizing to do, but it works! It's amazing how as you gain experience you start looking at your old code and saying "What was I thinking?". Plus, rereading your earlier posts now they become a lot clearer, clio.
This project has the potential to suck up plenty more of my time and I'll probably be back to ask more questions at some point, but the basic structure is laid our pretty much like you suggested, and I can't thank you enough. I've got events, listeners, interfaces, threads... all sorts of good stuff... I check my mail in a thread, it fires events based on errors or new mail, the GUI gets the messages and updates itself, the timer works....
Now to clean it all up and document it so I remember what I did
One more thing - Would you recommend putting the MailEvent and MailEventListener classes inside the MailMgr class, or can they go in their own class files? I might find out soon that you can only do it one way...
One again, thanks so much for your help.
Joined: Apr 30, 2004
great work! accept my praise for your persistence and follow-through - rare qualities. you pulled together many complex/confusing bits into a working whole - you have yourself to thank.
you probably already solved your packaging issue, but since you asked my opinion: having them in separate pkg/class probably fits best w the reusability goal
at the rate you're going, next time i expect to be thanking _you_ for help:-)