viernes jul 29, 2005

Simple SwingWorker example

As promised this is the first release of a SwingWorker example. Source code is available under the LGPL (see INSTALL.txt for installation instructions). This is a simple example of a two-tier application that queries a PointBase database and searchs customers by name.

Note: I can't remember if Pointbase comes bundled with NetBeans 4.1 or if you need the J2EE 1.4 JDK installed instead. If you don't have Pointbase with your NetBeans 4.1 installation then just install J2EE 1.4 JDK available for free here. For details on how to run PointBase use this link. As Geertjan suggests you may consider using MySQL instead although that'll require, of course, some SQL and setup modifications.

Purpose of this demo

On this very first release I have concentrated on all but SwingWorker, I'm afraid. I just wanted to build a sound base that I can improve in the future. Today I'll describe a little bit this simple example and in next releases I'll enhance the SwingWorker in different ways, including timeouts, single executions, cancellations and progress monitors.

The domain package

In the example I've included a "domain" package that contains some weird things, such as a Customer Transfer Object, a Customer DAO and a Customer Business Delegate. Let me explain:

  • CustomerTO is a transfer object I use to hold information about customers. This is a simple Serializable Java Bean (a POJO: Plain Old Java Object).
  • CustomerDAO is a Data Access Object. This concentrates all SQL of the examples. For this example this just has a simple "findByName" query.
  • CustomerBusinessDelegate is a Business Delegate. I use business delegates to delegate operations to DAOs, and to handle different DAOs in a single database transaction. On this simple example I just call CustomerDAO.findByName and I do that in a transaction. Although transactions are not required for query methods I've decided to include transaction handling, so you can see how to handle these.
Many of you will recognize these as J2EE design patterns. I like using these even on my Swing applications. Having all business methods concentrated on the BusinessDelegate allows me to move the examples from a two-tier to a three-tier architecture in the future (using EJBs, for instance) in case of need. That's why I've included this stuff in a standalone package.

TableModels

I've included a quick & dirty implementation of TableModel that uses generics to hold an ArrayList of transfer objects. I've called that AbstractTOTableModel. That allows me to quickly derive specific implementations quite fast, such as CustomerTOTableModel.

AbstractTOTableModel is not very well designed (it fires "tableChanged" events to listeners although a single row is changed) but is good enough for this example. The important part for me is that building CustomerTO table models is very fast with this AbstractTOTableModel background. Furtheremore, I can play with generics (and that's something I still need ;-) )

You'll notice that AbstractTOTableModel<TransferObject> uses internally an ArrayList<TransferObject> to hold transfer objects. And, of course, you know that ArrayList is not thread safe. The fact is that this is not important in the example, because I'll follow this convention:

Convention I All models (in the MVC paradigm) will be modified in the Swing thread.

This is a convention I usually follow when building Swing applications. I consider that handling models with different threads is just asking for trouble. After all there's no need to do that, as we'll see.

FindCustomerByNameSwingWorker

Well, this is the interesting part. I'll describe it in detail below, method by method, (sorry if this is getting too long!).

Class declaration

public class FindCustomerByNameSwingWorker
extends SwingWorker<CustomerTOTableModel,CustomerTO>
{
We're declaring a SwingWorker that will return a CustomerTOTableModel and that will generate intermediate results in the form of CustomerTO's (we'll see that in detail below).

Constructor

public FindCustomerByNameSwingWorker( String aName,
 CustomerTOTableModel aCustomerTOTableModel )
We pass as arguments the name of the customer we want to search (or null if we want to search all customers) and a table model. We'll be incrementally inserting CustomerTO's into the TableModel as the search progresses. And, as stated earlier, we'll do those model modifications in the Swing thread.

doInBackground

@Override
protected CustomerTOTableModel doInBackground()
  throws Exception
{
As you know this is the method that is invoked in the worker thread. This is \*not\* executed in the Swing thread. What we basically do in this method is this:
  • We invoke the business delegate to fetch all customers. Then, for each customer:
    • We update the progress of the computation (automagically generating events into the Swing thread).
    • We publish intermediate results (automagically generating events into the Swing thread).
    • We sleep things a bit, so as to simulate a slow computation.
  • Finally we return the results and perform any post processing.
Invoking the business delegate
CustomerBusinessDelegate cbd = new CustomerBusinessDelegate();
ArrayList customers = cbd.findByName( customerName );
Nothing very interesting here, right? ;-) We just fectch a collection of customers from the database. Then, for each customer in the collection:

We update progress

setProgress( (int) progress );
As you probably know, SwingWorkers generate propertyChangedEvent's to registered PropertyChangeListeners (you can add and remove PropertyChangeListeners to any SwingWorker by using the "addPropertyChangeListener" and "removePropertyChangeListener" methods). In our example we don't use this feature, but we update progress anyway (we'll use that in future enhancements). At the moment just remember that we can update progress. That's good enough at the moment.

We publish intermediate results

For each CustomerTO in the collection we "publish()" it (an intermediate result, after all) to the event dispatch thread. Remember that we declared FindCustomersByNameSwingWorker as extending SwingWorker<CustomerTOTableModel,CustomerTO>? Well, the second argument to that generic declaration states that our intermediate results are CustomerTOs.

publish( customers.get(i) );
Invoking the "publish()" method will in turn invoke the "process" method in the Swing thread. This is, if you "publish" an intermediate result then you may "process" it in the Swing thread. In our case we process each intermediate CustomerTO by updating our table model, like this:
@Override
protected void process(CustomerTO... chunks)
{
  for( CustomerTO customer: chunks )
  {
    customerTableModel.add( customer );
  }
}
This is, all intermediate CustomerTO's are "publish()'ed" in the worker thread and then "process()'ed" in the Swing thread. In our case we just update our table model (so we modify the table model in the Swing thread).

Sleeping

We simulate slow processing by Thread.currentThread().sleep(100L).

Finishing and post processing.

We may process any results in the "done()" method. In our case this is not neccessary because in the example the modifications to CustomerTOTableModel are shown automagically in a JTable (a modification to a TableModel makes the corresponding JTable to be automagically updated).

Executing the SwingWorker

Finally the example includes a simple CustomerFinderPanel that exercises the whole thing by invoking the "execute()" method of the SwingWorker.

Enough for today

Well, I think this is too long for today. I don't want to overflow you on a friday ;-). To summarize these are the important things I'd like to transmit today:

  • SwingWorker<A,B> returns a result of type A and may publish one or more intermediate results of type B.
  • SwingWorkers have a "doInBackground()" method that is processed in a worker thread.
  • SwingWorkers have a "publish()" method that publishes intermediate values in the worker thread, that in turn are "process()" ed on the Swing thread.
  • SwingWorkers have a "setProgress()" method that generates events on the Swing thread.
  • SwingWorkers may be executed by using the "execute()" method.

On future releases about SwingWorkers I'll talk about exception handling, SwingWorker state change events, SwingWorker progress events, cancelling SwingWorkers and allowing a SwingWorker to gracefully die after a timeout period.

As always all questions/suggestions are welcome.

Have a good weekend and keep on Swinging,
Antonio

sábado jul 02, 2005

On GUI Multithreading

So everybody seems nervous about doing multithreading on GUIs. It seems some people thinks Swing should be thread safe.

That's nonsense! There's not a single GUI in the market (AFAIK) that is thread safe!. And there's a simple reason for that: it's not possible or desireable to build one!

In fact Microsoft's MFC, GTK, wxWidgets (aka wxWindows), SWT and QT are not threadsafe. (and you could include Motif 2.0, Athena, Xt and some other older toolkits here)

All these toolkits use a thread to dispatch events to widgets (the event-loop thread) and, maybe, some others for paiting. Same thing in Swing.

So if you perform heavy-duty, time-consuming, stuff in the event loop thread you pause sending events (and/or repainting) and resume later after the time-consuming stuff ends. Meanwhile your GUI seems to hang, does not respond (because no events are being processed) and, depending on the toolkit, may even stop painting the widgets.

It's that simple. And it has to be. There's no workaround. You have to live with it.

Now, if you investigate a little bit on how MFC, GTK, wxWidgets, SWT or any other ease this task you'll probably find out that the new SwingWorker is just a piece of cake.

And, with the addition ( JSR 166) of the excellent Doug Lea's concurrent utilities to JDK 5.0, the rebranded SwingWorker offers new possibilities to boost your GUI performance and your productivity.

I'm for holidays today, too weeks around Spain, but I promise posting some sample code of SwingWorker by the end of the month (and maybe some photos too).

Meanwhile I'll accept suggestions for examples. Please include a comment below about what sort of thing you would like me to work on the example. Should be simple enough for a short example. Hints: invoke an EJB on the background? invoke a web service? retrieve content from a RSS feed? a JTree for a filesystem that seeks the nodes (files) in the background?

Happy Swinging!!

martes jun 21, 2005

new SwingWorker !!

A few months ago I discontinued the SwingWorkers project. The fact is that someone at Sun told me they were working on a rearchitecture of SwingWorker in order to include it in Mustang, so I decided to allow them to finish the stuff and give them the URL at Java.net.

Well, the whole think is done and it looks extremely well.

These guys have been smart enough so as to provide a SwingWorker with PropertyChange support, so you can fire PropertyChangeEvent whenever you want in your "doInBackground()" method, in order to notify the GUI (Swing thread) about any advances on computation. Some other nice features include cancelling the background task (public final boolean cancel(boolean mayInterruptIfRunning)) and including the possibility to schedule SwingWorkers using the Executor of your choice.

I'm sort of busy now but I promise posting some examples in the future.

Enjoy it!!

sábado ene 01, 2005

Working out Swing with SwingWorkers

First of all: Happy New Year, everybody!

So say you have to do some heavy-duty tasks in your Swing application and that you need to span a worker thread to do that. Then you have to interact again with Swing components, using the Swing thread, of course. Of course using a thread pool would be nice, to speed up task creation and to reduce/control resource consumption.

I have been playing a little bit with the new concurrent utilities included in JDK 5.0 and, in fact, all this stuff is very easy to do. You can find a draft implementation here.

It's basically a rewrite of the famous SwingWorker with some clean-up (you just need an interface now) and with an ExecutorService used to pool threads.

So please if you use SwingWorkers give it a try and let me know how well it fits your needs.

Have fun in 2005!

About

swinger

Search

Archives
« abril 2014
lunmarmiéjueviesábdom
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    
       
Hoy