Simple Messaging Scheme for Swing Applications

Nigel Thompson (nigel-t@msn.com)

April 27, 2004

I have found that working on Swing-based applications can be very frustrating when it comes to making synchronous or asynchronous updates of live data in a window. This article provides a simple solution based on messages that can be sent to any JPanel-derived window. The user is freed from dealing with the Swing thread context and can instead focus on the functionality of the application. Windows programmers will find the mechanism similar to the SendMessage and PostMessage functions.

The MessageSink Interface

The core of the solution is the MessageSink interface which is implemented by any class that wants to receive messages. MessageSink has three functions defined: onMessage, postMessage and sendMessage. 

The postMessage and sendMessage functions are called to deliver messages to the object. The postMessage function is called to deliver a message asynchronously to the object. The call returns immediately and the message is delivered to the object later on the dispatch thread. The sendMessage function attempts to deliver a message for synchronous execution. I.e. by the time the call returns to the caller, the message has been processed by the receiving object.

 

 

public interface MessageSink

{

  /**

   * An application class implements this to receive meaasges

   * from a MessageAgent on the dispatch thread

   * @param message Message

   */

  public void onMessage(Message message);

 

  /**

   * The application class implements this as a call to its local message agent

   * @param message Message

   */

  public void postMessage(Message message);

 

  /**

   * The application class implements this as a call to its local message agent

   * @param message Message

   */

  public void sendMessage(Message message);

}

 

 

The Message Class

Messages are objects derived from the Message class show below:

 

 

public class Message

{

  private long _timeStamp;

 

  public Message()

  {

    _timeStamp = System.currentTimeMillis();

  }

 

  public long getTimeStamp()

  {

    return _timeStamp;

  }

 

}

 

 

The MessageAgent Class

The MessageSink interface has no code to implement any of its functions. The MessageAgent class provides implementations of the postMessage and sendMessage functions in the MessageSink interface. Applications can use a MessageAgent object to implement the MessageSink interface.

 

 

public class MessageAgent

{

  private MessageSink _messageSink;

 

  public MessageAgent(MessageSink messageSink)

  {

    _messageSink = messageSink;

  }

 

  /**

   * Send a message to the object for execution later

   * @param message Message

   */

  public void postMessage(Message message)

  {

    // create a runnable object that will be exectued on the

    // dispatch thread later

 

    final Message m = message;

    final MessageSink ms = _messageSink;

    Runnable runnable = new Runnable()

    {

      public void run()

      {

        // Execute the local function

        ms.onMessage(m);

      }

    };

 

    // schedule our object to be run later

    EventQueue.invokeLater(runnable);

    try {

      Thread.currentThread().sleep(0); // yield

    } catch (InterruptedException ex) {

    }

  }

 

  /**

   * Send a message to the object for execution now. If the sender is running on

   * the dispatch thread, the call is changed to a postMessage call

   * @param message Message

   */

  public void sendMessage(Message message)

  {

    if (EventQueue.isDispatchThread()) {

      postMessage(message);

      return;

    }

 

    // now we create a runnable object that will be exectued on the

    // dispatch thread and wait here for it to be run

 

    final Message m = message;

    final MessageSink ms = _messageSink;

     Runnable runnable = new Runnable()

    {

      public void run()

      {

        // Execute the local function

        ms.onMessage(m);

      }

 

    };

 

    // Wait for our object to be run

    try {

      EventQueue.invokeAndWait(runnable);

    } catch (InvocationTargetException ex) {

      ex.printStackTrace();

    } catch (InterruptedException ex) {

      ex.printStackTrace();

    }

  }

 

}

 

Note that messages send through the postMessage call are always asynchronous but messages sent through sendMessage are only synchronous if the calling thread is not the dispatch thread. If the calling code is running on the dispatch thread, the call is converted to an asynchronous postMessage call.

A JPanel-Derived Class

The following code shows a class derived from JPanel which implements MessageSink using a local MessageAgent object to implement the bulk of the code.

 

 

public abstract class NPanel

    extends JPanel

    implements MessageSink

{

  // The agent that actually receives the messages and schedules them

  // on the dispatch thread for execution here later

  private MessageAgent _messageAgent = new MessageAgent(this);

 

  /**

   * A derived class implements this to receive messages

   * on the dispatch thread that were sent to the sendMessage

   * or postMessage functions.

   * @param message Message

   */

  public abstract void onMessage(Message message);

 

  /**

   * Overloaded from MessageSink

   * @param message Message

   */

  public void postMessage(Message message)

  {

    _messageAgent.postMessage(message);

  }

 

  /**

   * Overloaded from MessageSink

   * @param message Message

   */

  public void sendMessage(Message message)

  {

    _messageAgent.sendMessage(message);

  }

 

}

 

Applications can derive classes from the NPanel class above to receive messages simply by overriding the onMessage function.

InPractice

I have used the classes shown here to implement a RISC emulator that I wrote to teach my son Mark about assembly language programming. The emulator uses the messaging system described above to keep all the GUI components updated from the emulation engine as it executes the machine code.

Messages are used to update the register display, the output state and the memory state panels.