SwingCommand - Commands
![]() |
![]() |
SwingCommand targets the same problem area as SwingWorker - namely, how to handle background tasks and update the UI safely in Swing. Recall that you should generally only access Swing components and their models on the Swing Event Thread, due to Swing’s threading model. See the Swing tutorial if you are unfamiliar with this.
The main difference between SwingCommand and SwingWorker is that each SwingCommand instance can be executed multiple times. Each time you execute a SwingCommand, a Task instance is generated, which performs the processing and wraps the state and any parameters used for that execution. This means that SwingCommand instances can be created once and shared throughout your application. (There’s nothing to stop you creating a SwingCommand for one-off use, if this makes more sense in some situations).
This is how to create a simple SwingCommand
final JTextArea textArea = new JTextArea();
class ClearTextAreaCommand extends SwingCommand {
public Task createTask() {
return new Task() {
public void doInEventThread() {
textArea.setText("");
}
};
}
}
//Run the command
SwingCommand c = new ClearTextAreaCommand();
c.execute();
The task created by the SwingCommand above does not do any processing in a background thread - the doInEventThread() handler method will always be called in the SwingEvent thread, even if another Thread calls the execute() method. To create a task which does run in a background thread, we would need to extend BackgroundTask rather than extending Task directly.
Why would you want to create a SwingCommand which doesn’t run a background task?
Well, one of the main drivers for this, and for the Command pattern in general one might say, is reuse. A SwingCommand encapsulates a chunk of ui logic which can be shared throughout your app. Whenever you find yourself tempted to duplicate ui code, that’s a good time to consider creating a SwingCommand.
It’s also nice that both ‘Background Tasks’ and ‘Event Thread Only tasks’ extend the same Task abstraction, so that in many places in your client code, you need not descriminate between the two types.
If your command does need to perform processing on a background thread, it should create a task which extends BackgroundTask. The following example shows how you might write a command which queries the database to retreive some email messages for a user:
Creating a command which performs some work on a background thread:
class LoadMessagesCommand extends SwingCommand {
private Connection connection;
private String userName;
public LoadMessagesCommand(Connection connection, String userName) {
this.connection = connection;
this.userName = userName;
}
public Task createTask() {
return new BackgroundTask() {
StringBuilder text = new StringBuilder();
public void doInBackground() throws SQLException {
Statement s = connection.createStatement();
ResultSet r = s.executeQuery("select text from Message where user = " + userName);
while(r.next()) {
text.append(r.getString(1)).append("\n");
}
}
public void doInEventThread() {
textArea.setText(text.toString());
}
};
}
}
SwingCommand c = new LoadMessagesCommand(connection, "My User");
c.execute();
The SwingCommand above runs a database query in a background thread, and then updates a text area in the Swing Event thread. The command could be created and executed either from the Swing Event thread or from a background thread, and SwingCommand will ensure that the call to doInBackground() occurs in a background thread, and doInEventThread() occurs in the Event thread. In fact, the command could be created in one thread, and executed from another and this guarantee still applies.
One thing to note - in both the above examples, the Task type created by the SwingCommand is an inner class. This is purely for convenience, since it is often easiest to write SwingCommands this way. If you prefer not to use an inner class for this, it’s quite possible for a Task to be implemented as a top level class.
Passing parameters to Commands
Commands often take their parameters from Swing components - for example a value from a JSpinner or a JTextField. In this case, the simplest way to manage things is often to construct the SwingCommand with a reference to the inputs - then when createTask() is called you can read the values from the components and use them in the Task. You can either construct the Task with the parameter values, or if Task is an inner class pass them in as final reference, as below:
class ClearMessagesCommand extends SwingCommand {
private JTextField usernameField;
public ClearMessagesCommand(JTextField usernameField) {
this.usernameField = usernameField;
}
public Task createTask() {
final String user = usernameField.getText();
return new BackgroundTask() {
public void doInBackground() throws SQLException {
Statement s = connection.createStatement();
s.execute("delete from Message where User = " + user);
}
public void doInEventThread() {
}
};
}
}
JTextField userNameField = new JTextField();
SwingCommand c = new ClearMessagesCommand(userNameField);
c.execute();
Whenever the above ClearMessagesCommand is executed, it will read the current value from the supplied text field and use it to clear the messages for the named user.
The above pattern is nice because any of your other classes which want to execute the command don’t need to know about the text field. They can just call command.execute(). There may however be instances where you want to pass a parameter into the command when you call command.execute(). This is also possible. When you create a SwingCommand, you can define a generic type for a parameter. You can pass in instance of that parameter into execute(), and then access it from within the Task which is created.
The command below requires a String parameter. This is the first of the two generic types specified for the SwingCommand. The second String specified in SwingCommand
public ClearMessagesCommand() {
}
public Task<String,String> createTask() {
return new BackgroundTask<String,String>() {
public void doInBackground() throws Exception {
//get the parameter object passed to execute()
String userName = getParameters();
Statement s = connection.createStatement();
s.execute("delete from Message where User = " + userName);
}
public void doInEventThread() {
}
};
}
}
SwingCommand<String,String> c = new ClearMessagesCommand();
c.execute("Nick");
So the command above now expects to be passed a String username via the command.execute() method. Ideally it would be good to check that this has been set in doInBackground(), and if not either supply a default value or throw an Exception to indicate the command execution failed.
You probably have a lot more questions about the example above. For example, ‘How would you show progress to the user?’, ‘how does it handle any Exceptions generated during doInBackground?’, or ‘what controls which background thread runs the Task?’. I’ll address these issues in subsequent posts.
Quick Links:

