Taming Swing Threads Part 4: Simplifying Threading in ActionListeners

Less is more.

In the previous blog I discussed the merits of the SwingWorker and how it solves the problems of offloading processing to a background task, as well as allowing your application to react once the task is complete (all in a thread safe way).

 

During the course of my career as a swing developer I found myself on numerous occasions writing the same code to create and execute a SwingWorker from an ActionEvent. To be honest, nothing annoys me more than rewriting the same code again and again, especially if it contains a lot of conditional logic!

The code snippet below that should give you a rough idea of what was driving me crazy:

 

 

While there is nothing wrong with the code and it definitely works, however I just find it a bit on the heavy side (it has alot of conditional logic and heavy indentation). Especially when you seem to be coding extremely similar Action Listeners for every button on the screen that needs to make a server call.

 

The solution for my environment.

The solution I came up with is a pretty good solution for the environment I was working in. That specific code base was for a swing thick client, whose long running tasks were communicating with a remote server. As a result, I did not need to give feedback while the task was executing. I only needed to provide feedback to the gui once the task was complete. (In case you were wondering, while the long running process was executing a continuous progress bar was displayed, and a glasspane was activated to prevent users from interacting with other screen elements).

 

The solution I propose caters for this scenario rather nicely, however it does not allow for the background thread to provide feedback during the thread’s execution. The reason I don’t provide that functionality is two fold. Firstly, it was not necessary for me to provide feedback in that particular swing framework. Secondly, the SwingWorker’s process() and publish() methods are declared final, thereby preventing me from overriding them to provide the desired functionality.

 

The solution the the code above was to create a new class called a ThreadedActionListener that implements ActionListener as well as wrapping a SwingWorker. Before I show you the main code implemented in the actionPerformed method, I think its best to start with an explanation of the methods. Don't be surprised if it seems very SwingWorker'ish.

 

 

Now that you have seen the methods, its time to show you how they are all linked together in the performAction method. 

 

 

The code above is actually just a little bit scary, but the main goal is to show you how the process of attaching a well behaved thread safe ActionListener to a button. The example below is to illustrate the process of creating a new customer on a remove service.

The steps are as follows:

  • We create a customer object from the information on the screen (if we are reading swing components this must be done on the EDT).
  • we then check to see if the customer captured from the screen is valid. If the customer is invalid we provide feedback to the user and abort the remainder of the process.
  • If the customer is valid we attempt to save the customer using some persistence mechanism. If the attempt is successful we will receive the customer's primary key. If the attempt fails, and exception will be thrown.
  • Appropriate feedback should be displayed to the user once the operation is complete.

The code belows inplements the above steps in a very simple manner which has minimized the amount of conditional logic as well as the subsequent indentation.

 

 

Some would argue that the above solution looks just as large, however there is a key difference, all but the performBackgroundTask() method or optional methods. If you do not need to perform prior validation or you know your method will never throw an exception you may choose to omit some of the above methods.

 

Well I hope you find this solution as useful as I did, if you have any advice or criticism I would be glad to hear from you. The source code for the class can be found below, and feel free to use it in any application you wish, on condition that you leave in the licencing header and aknowledge its source.

Very nicely done!

Very nicely done!

Thanks!

I just would like to thank you for putting up this tutorial/guide. I have been working recently with SWING and I didn't understand much of how it worked.

It all makes sense now!

The LATEST chatter

User login

Syndicate

Syndicate content