The Source for Java Technology Collaboration
User: Password:
Register | Login help    

Search

Online Books:
java.net on MarkMail:


Thread-Safe Swing Application (Part 3)

Posted by rexyoung on October 11, 2009 at 3:53 PM PDT

In order to handle user input to Swing components, you need to implement event listeners and make them listen to Swing components. Your listener will be invoked when the Swing component fires an event. This is from front-end to back-end. On the other hand, from back-end to front-end, it is still true if you do not employ some other work flow frameworks. For example, your back-end receives some data from remote server, and fires events. Listeners in the different layers of code will update data models, cache, Swing models, and eventually update the Swing component. This way the screen is able to show new data to user.

Usually Observer pattern is implemented by immediate method invocation, which mean when firing an event, the thread of the event producer immediately enter the listener and execute event handling code there. This process continues if the handling code fires its own events.

Obviously this kind of implementation of Observer pattern can not achieve our goal outlined in the last part to limit threads in designated scopes.

Immediate method invocation will cause some common coding issues often found in real life applications. Producer firing an event starts execution of handling in listeners immediately. The producer must make sure its state is stable as listeners usually will access the producer back for more data during event handling. It is not a big issuer when the producer is small and original. Problems happen on big compound event producers. These compound producers usually listen to smaller producers for raw events and then fire high level events. It is a programming challenge to maintain its state stable to listeners. I ran into this kind of problems from time to time. You may change some code that do not seem would trigger events but actually they do. If you are lucky, you discover it before check in. Otherwise, it shows up in the bug list.

Here I am going to introduce you several helper classes that will ease you implementing advanced Observer pattern that will: 1) Switch threads between producer and listener; 2) Ease coding issues above by allowing producer to complete its own handling before its listeners start event handling. For the latter, it actually avoid all this kind of issues for Swing components as event-dispatching thread is the only thread to run tasks in the queue. (There may be an additional queue for each modal dialog. However it does not bother us in this aspect.)

abstract public class ListenerList {

protected Executor notifierExecutor;
protected ArrayList listenerList;

public ListenerList(Executor notifierExecutor) {
this.notifierExecutor = notifierExecutor;
}

public synchronized void addListener (L listener) {
if (listener == null)
return;

if (listenerList == null)
listenerList = new ArrayList ();
else if (indexOf (listenerList, listener) > -1)
return;

listenerList.add (listener);
}

public synchronized void removeListener (L listener) {
if (listener == null || listenerList == null)
return;

int index = indexOf (listenerList, listener);
if (index < 0)
return;

listenerList.remove (index);

if (listenerList.isEmpty ())
listenerList = null;
}

public synchronized void notifyListeners (Object... parameters) {
if (listenerList == null)
return;

for (L listener : listenerList) {
ListenerRunnable messaging = new ListenerRunnable (listener, parameters);

if (listener instanceof SwingListener) {
SwingUtilities.invokeLater (messaging);
} else if (listener instanceof ThreadAwareListener) {
((ThreadAwareListener) listener).getListenerExecutor ().execute (messaging);
} else {
notifierExecutor.execute (messaging);
}
}
}

protected int indexOf (List itemList, Object item) {
for (int i = 0; i < itemList.size (); i++) {
if (itemList.get (i) == item)
return i;
}

return -1;
}

protected class ListenerRunnable implements Runnable {
private L listener;
private Object[] parameters;

public ListenerRunnable(L listener, Object... parameters) {
this.listener = listener;
this.parameters = parameters;
}

public void run () {
try {
invokeListener (listener, parameters);
} catch (Exception e) {
// log the error
}
}
}

abstract protected void invokeListener (L listener, Object... parameters);

}

  

public interface SwingListener {
    // marker interface
}

 

public interface ThreadAwareListener {
    Executor getListenerExecutor ();
}

The core idea is pretty straight forward that producer asks for each listener what thread it wants to execute event handling, and all events will be stored in a queue (via Executor) thus producer can continue its code right after firing. Of course, helper classes can not provide all of these without corporations from other designs in the application.

Class ListenerList is designed to be used by an event producer to manage listeners and fire events. The producer needs to implement the abstract invokeListener() method as this class is not aware of particular listener interfaces.

This code mainly demonstrate the idea. You may customize it to fit the nature of your Swing application. For example, my application has only two thread domains: Swing domain and the rest. All of my listeners do not provide their own listener Executors. I use a global notifierExecutor which uses a single thread to deliver messages but execute them as each remote server is backed by its own Executor.

I just realized the title of the post does not mention non-hanging Swing application. While many goals I listed in the part 1 is about building a smooth Swing application. I may change it later when I start talking about non-hanging next post.

 

Related Topics >> Blogs      Java Desktop      Programming      Swing      
Comments
Comments are listed in date ascending order (oldest first)

Rex, You may want to check

Rex,

You may want to check out https://eventbus.dev.java.net/

EventBus is designed to handle multi-threaded notification, ensure that Swing components are notified on the EDT, and provides easy configuration mechanisms using annotations. I was heavily relying on it in my previous project and was very pleased with it.

Karl

Event Producer

>>Class ListenerList is designed to be used by an event producer to manage listeners and fire events. Consider using java.beans.EventHandler for this problem. Good articles and thanks for supporting Swing.