Solution

Spin

Now let's take a look at the Spin solution. Here's the code:

GUI.java

public void actionPerformed(ActionEvent e)

{


  label.setText("...");

  label.setText
(bean.getValue());


}





public void propertyChange(PropertyChangeEvent ev)

{


  label.setText((String)ev.getNewValue());

}

 

BeanImpl.java

public String getValue()

{


  String value;

 
// extensive calculation

 
return value;


}



public void setValue(String value)

{


  this.value = value;

  firePropertyChange
(value);


}

Hey wait a minute! It's the same code as shown in the previous section Standard Swing. But the colors have changed - GUI.java is completely green and BeanImpl.java is completely red - how can this be?

Spin makes this solution possible - as you can see with no impact on the old code. An invisible layer between these two classes handles all threading issues transparently.

All we have to do is to spin-off the bean from the EDT. For this we wrap the bean in an instance of type Spin. The result can safely be casted to any interface the bean (or one of its superclasses) implements. The best place to do this is before a reference to this bean is passed to a GUI component (why bother the programmer of the GUI with this little detail):

bean = (Bean)Spin.off(bean);

The only restriction here is that the Bean has to be broken into interface and implementation. The GUI components will only use the interface! The following picture shows how Spin connects the GUI and the bean. Calls on the EDT from the GUI to the bean are brokered to other threads invocating the beans functionality: Spin off

The need for an interface isn't really a restriction:

  • It is generally recommended to separate an application in different layers which communicate through well defined interfaces. The GUI of an application is certainly part of another layer than extensive calculations or I/O operations.
  • If you don't want to or are unable to use an interface you can utilize CGLib instead of JDK proxies.
For the notification of changes to the bean we use an inverse technique.

We must spin-over any invocation of a GUI callback-method on another thread to the EDT. This time we wrap the GUI in a Spin instance assuming that the bean allows for an PropertyChangeListener to be added as a callback (this could be any interface like foo.DataChangedListener):
bean.addPropertyChangeListener((PropertyChangeListener)Spin.over(gui);
Spin over

This is all you have to know to get Spin to work in your project. If you're interested in the internals of Spin go on to the next section.

Internals

Spin is built on top of virtual proxies and a technique borrowed from the java.awt.Dialog component. While a modal dialog has to wait for user input, the EDT is rerouted to the swing event processing to handle further events.

The following diagram shows how this is used in Spin. Each invocation of a bean's method is intercepted and handled by a SpinOffEvaluator:

getValue() is evaluated asynchronously on another thread (customizable with a Starter) while Swing events are dispatched through a Dispatcher. Once the call to the bean returns the dispatching of events is stopped and the EDT is free to return to the standard event processing: Spin off sequence

For asynchronous notifications from the bean to the GUI we reuse the technique introduced in the previous sections. But this time the call to invokeAndWait() is encapsulated by Spin with a SpinOverEvaluator: Spin over sequence

Please take a look at the full API for details on how to customize Spin.

Caveats

Although Spin handles threading transparently there are caveats with spin-off that you should be aware of:

Security
For dispatching Spin needs access to AWT internals that are not available in applets or untrusted JavaWebStart applications due to security restrictions. This will hopefully change once AWT offers an official way to dispatch events.

Meanwhile Spin offers alternative solutions which are less performant but also work in a secured environment. Please take a look at DialogDispatcherFactory and InternalOptionPaneDispatcherFactory.
Reference backdoor
If your GUI hands over references to parts of its swing models (e.g. TreeModel, TableModel) in method calls to your bean, these could possibly be altered on another thread than the EDT thus VIOLATING THE SWING SINGLE THREADING RULE.
Bean threadsafety
If your GUI doesn't disable all further actions while an invocation on your bean is being processed, the event dispatching may cause a second concurrent call to the bean. In cases where this is desired the BEAN MUST BE THREADSAFE.
Asynchronous
Whenever your GUI calls a beans method through Spin, further actions should be allowed only if they are related to the current Spin invocation. This includes Cancel functionality and the retrieval of the current state of invocation or intermediate results (e.g. for updating a progress bar or incremental filling of a table).

You're running into problems if you're using Spin for real asynchronous executions. Let me give an example:

File tranfers of an Explorer-like application wich can be arbitrarily started and stopped while others are independently continuing are NOT A RECOMMENDED USAGE for Spin. Nevertheless Spin can be used to spin-over events from the transfers (e.g. completion notification) to the EDT.
Incomplete Event Handling
An event that triggers Spin will not be completely precessed until the return of the Spin invocation.

This might lead to minor visual annoyances, e.g. a JComboBox that does not close its popup or a JButton that stays depressed while Spin is running. But this behaviour could also result in other unexpected behaviours that you should be aware of.

Because of this Swing developers have expressed their concern about Spin and similar techniques, stating that 'Swing is not completely reentrant'.

While this may be true, the same objection could be brought forward against any modal dialog or modal internal frame. If you're using these in your application there is no reason to be afraid of Spin.

Conclusion

Spin is a small library that concentrates on offering a powerful solution to build non-freezing Swing applications. Spin enforces good application design by separating the GUI and non-visual components through interfaces. If it is used wisely in an application framework, the GUI programmers will never have to think about threads again.

Spin comes with several demonstration classes that show how to solve formerly challenging Swing programming problems with ease:

  • spin.demo.SpinOffGUI - shows how to execute extensive calculations without "freeze"
  • spin.demo.SpinOverGUI - demonstrates asynchronous event notification without pain
  • spin.demo.exception.ExceptionGUI - proves the transparent exception handling offered by Spin
  • spin.demo.pogress.PullGUI - shows how to handle visual progress while extensive calculations are executed
  • spin.demo.pogress.PushGUI - uses asynchronous event notification to update a progressbar
  • spin.demo.prompt.CallGUI - explains how to prompt the user between multiple extensive calculations
  • spin.demo.prompt.CallbackGUI - prompts the user for input which is triggered by callbacks from an extensive calculation
  • spin.demo.async.AsyncGUI - starts asynchronous calculations transparently through Spin
  • spin.demo.dispatcher.DispatcherGUI - test different dispatchers
We have successfully used Spin successfully in several projects to wrap all remote communication (RMI) between rich-clients and the application server.