JTable setRowHeight causes slow repainting

June 6th, 2009 Nick Posted in java, swing | No Comments »

I’ve spent way too much time in the last few months fixing and optimising table rendering performance for a high performance Java application I’m working on at the moment. The problems were particularly hard to find becuase the app supports customization of almost every aspect of JTable UI, with custom rendering for rows, columns and individual cells according to user preferences. Some users would get terrible performance on some days, which would seem to disappear the next.

Some of the issues were hardware and graphics driver related, and there have been various minor and major victories along the way (more about these later), but just yesterday I found a very significant defect in JTable.tableChanged().

The problem occurs when you call setRowHeight(int row, int height) on a JTable, to set a customised table row height. You only need to do this once, and that causes JTable to initialize its internal rowModel to maintain the custom row heights. From that point onwards, any update to the TableModel (even if it is just an update event for a single cell()) will be processed by JTable.tableChanged() in such a way that the entire table component is marked as dirty. So this forces the RepaintManager to repaint the entire table every time, even for single cell updates.

The code at which the long gnarled finger of suspicion points is at JTable line 4396 (in jdk 1.6.0_14) and line 3021 (in jdk 1.5.0_17), which is part of the processing for TableModel update events

// The totalRowHeight calculated below will be incorrect if
// there are variable height rows. Repaint the visible region,
// but don't return as a revalidate may be necessary as well.
if (rowModel != null) {
    repaint();
}

Essentially, if there is no internal rowModel set up (i.e the user has not yet configured any custom row heights), then the call to repaint() in this snippit causes the entire JTable to be marked as dirty - so the RepaintManager repaints the entire table component rather than just the cells affected by an update.

The effect of this is dramatic. When I run my test class (included below):

***** Running test for table Standard JTable under 1.5.0_19
With all rows default height total time spent repainting table 24 millis
Setting custom row height for one row
With one custom row height total time spent repainting table 5427 millis
Thats 226 times slower than it should be to paint the table!

Here is a Fix for the issue

I’m amazed that this one hasn’t been identified and fixed already. I have produced a workaround by extending JTable to override the tableChanged() handling for update events. Fixing this has solved major problems for our users whose tables were configured to require custom row heights.

This works fine for for me in 1.5.*.
It will also work for jdk 1.6.* but it does not support the 1.6 table row sorting (you’d need to find a way to fire sortedTableChanged() events to the sortManager to do this when the updates are processed)

import javax.swing.table.TableModel;
import javax.swing.event.TableModelEvent;
import javax.swing.*;
import java.awt.*;


/**
 * Extends JTable to fix the broken repainting for updates when there are custom height rows
 * See http://www.objectdefinitions.com/odblog/2009/jtable-setrowheight-causes-slow-repainting/
 *
 * This fix does not support row sorting in jdk1.6 -
 * to support that would require firing sortedTableChanged to sortManager
 */

public class FixedForRowHeightJTable extends JTable {

    public FixedForRowHeightJTable() {
    }

    public FixedForRowHeightJTable(TableModel tableModel) {
        super(tableModel);
    }

    public void tableChanged(TableModelEvent e) {
        //if just an update, and not a data or structure changed event or an insert or delete, use the fixed row update handling
        //otherwise call super.tableChanged to let the standard JTable update handling manage it
        if ( e != null &&
            e.getType() == TableModelEvent.UPDATE &&
            e.getFirstRow() != TableModelEvent.HEADER_ROW &&
            e.getLastRow() != Integer.MAX_VALUE) {

            handleRowUpdate(e);
        } else {
            super.tableChanged(e);
        }
    }

    /**
     * This borrows most of the logic from the superclass handling of update events, but changes the calculation of the height
     * for the dirty region to provide proper handling for repainting custom height rows
     */

    private void handleRowUpdate(TableModelEvent e) {
        int modelColumn = e.getColumn();
        int start = e.getFirstRow();
        int end = e.getLastRow();

        Rectangle dirtyRegion;
        if (modelColumn == TableModelEvent.ALL_COLUMNS) {
            // 1 or more rows changed
            dirtyRegion = new Rectangle(0, start * getRowHeight(),
                                        getColumnModel().getTotalColumnWidth(), 0);
        }
        else {
            // A cell or column of cells has changed.
            // Unlike the rest of the methods in the JTable, the TableModelEvent
            // uses the coordinate system of the model instead of the view.
            // This is the only place in the JTable where this "reverse mapping"
            // is used.
            int column = convertColumnIndexToView(modelColumn);
            dirtyRegion = getCellRect(start, column, false);
        }

        // Now adjust the height of the dirty region
        dirtyRegion.height = 0;
        for ( int row=start; row <= end; row ++ ) {
            dirtyRegion.height += getRowHeight(row)//THIS IS CHANGED TO CALCULATE THE DIRTY REGION HEIGHT CORRECTLY
        }
        repaint(dirtyRegion.x, dirtyRegion.y, dirtyRegion.width, dirtyRegion.height);
    }

}

The following Test demonstrates the problem, and that FixedForRowHeightJTable solves it

This test pops up a JTable in a frame and then changes cell values to trigger repaints.
While doing this we replace the standard RepaintManager with a TimingRepaintManager, which keeps track of the total time spent by the Swing event thread painting the table
We first run the test for a standard JTable, then for my fixed version, with the following results:

***** Running test for table Standard JTable under 1.5.0_19
With all rows default height total time spent repainting table 24 millis
Setting custom row height for one row
With one custom row height total time spent repainting table 5427 millis

***** Running test for table FixedForRowHeight JTable under 1.5.0_19
With all rows default height total time spent repainting table 2 millis
Setting custom row height for one row
With one custom row height total time spent repainting table 25 millis

Needless to say, I’ve submitted a bug report for this one!
(although technically once might argue its an RFE, since the table still gets painted, anything which degrades performance this much is a bug in my book!)

import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import javax.swing.*;
import javax.swing.event.TableModelEvent;
import java.awt.*;
import java.lang.reflect.InvocationTargetException;

/**
 * Created by IntelliJ IDEA.
 * User: Nick Ebbutt
 * Date: 06-Jun-2009
 * Time: 13:34:58
 *
 * A demonstration of JTable repainting bug for custom row heights
 */

public class DemoForJTableCustomRowHeightRepaintBug {

    private static final int COL_COUNT = 40;
    private static final int ROW_COUNT = 50;
    private static TimingRepaintManager timingRepaintManager;
    private static JTable table;
    private static FixedForRowHeightJTable fixedTable;

    public static void main(String[] args) throws Exception {
        SwingUtilities.invokeAndWait(new Runnable() {
            public void run() {
                timingRepaintManager = new TimingRepaintManager();
                RepaintManager.setCurrentManager(timingRepaintManager);
                table = new JTable(new TestTableModel());
                fixedTable = new FixedForRowHeightJTable(new TestTableModel());
            }
        });

        runTestForTable("Standard JTable", table);
        runTestForTable("FixedForRowHeight JTable", fixedTable);
    }

    private static void runTestForTable(String description, final JTable table) throws Exception {
        System.out.println("\n***** Running test for table " + description + " under " + System.getProperty("java.version"));
        showTable(table, description);
        timingRepaintManager.resetTotalRepaintTime();
        runTableModelUpdates(table.getModel());

        System.out.println("With all rows default height total time spent repainting table " + timingRepaintManager.getTotalRepaintTime() + " millis");

        System.out.println("Setting custom row height for one row");
        setRandomRowHeight(table);

        timingRepaintManager.resetTotalRepaintTime();
        runTableModelUpdates(table.getModel());
        System.out.println("With one custom row height total time spent repainting table " + timingRepaintManager.getTotalRepaintTime() + " millis");
    }

    private static void setRandomRowHeight(final JTable table) throws InterruptedException, InvocationTargetException {
        //now just set one row to a custom height and run the repaint timing test again
        SwingUtilities.invokeAndWait(
            new Runnable() {
                public void run() {
                    table.setRowHeight(getRandomRowOrCol(ROW_COUNT), 100);
                }
            }
        );
    }

    public static void showTable(final JTable t, final String name) throws Exception {
        SwingUtilities.invokeAndWait(new Runnable() {
            public void run() {
                JFrame f = new JFrame(name);
                f.getContentPane().add(new JScrollPane(t));
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                f.setSize(new Dimension(1024,768));
                f.setVisible(true);
            }
        });
    }

    public static void runTableModelUpdates(final TableModel tableModel) {
        for ( int loop=0; loop < 500; loop++) {
            try {
                SwingUtilities.invokeAndWait(new Runnable() {
                    public void run() {
                        tableModel.setValueAt("Wibble", getRandomRowOrCol(ROW_COUNT), getRandomRowOrCol(COL_COUNT));
                    }
                });
                Thread.sleep(10);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    //record the total time spent repainting
    private static class TimingRepaintManager extends RepaintManager {
        private volatile long totalTime;

        public void paintDirtyRegions() {
            long startTime = System.currentTimeMillis();
            super.paintDirtyRegions();
            totalTime += System.currentTimeMillis() - startTime;
        }

        public long getTotalRepaintTime() {
            return totalTime;
        }

        public void resetTotalRepaintTime() {
            this.totalTime = 0;
        }
    }


    private static class TestTableModel extends DefaultTableModel {
        public TestTableModel() {
            setColumnCount(COL_COUNT);
            for (int row=0; row < ROW_COUNT; row++) {
                addRow(createRow(row));
            }
        }

        private Object[] createRow(int row) {
            String[] colNames = new String[COL_COUNT];
            for ( int col=0; col<COL_COUNT; col++) {
                colNames[col] = "Cell " + row + ":" + col;
            }
            return colNames;
        }
    }

    public static int getRandomRowOrCol(int maxVal) {
        return (int)(Math.random() * maxVal);
    }

    /**
     * Extends JTable to fix the broken repainting for updates when there are custom height rows
     *
     * This fix does not support row sorting in jdk1.6 -
     * to support that would require firing sortedTableChanged to sortManager
     */

    private static class FixedForRowHeightJTable extends JTable {

        public FixedForRowHeightJTable(TableModel tableModel) {
            super(tableModel);
        }

        public void tableChanged(TableModelEvent e) {

            //if just an update, and not a data or structure changed event or an insert or delete, use the fixed row update handling
            //otherwise call super.tableChanged to let the standard JTable update handling manage it
            if ( e != null &&
                e.getType() == TableModelEvent.UPDATE &&
                e.getFirstRow() != TableModelEvent.HEADER_ROW &&
                e.getLastRow() != Integer.MAX_VALUE) {

                handleRowUpdate(e);
            } else {
                super.tableChanged(e);
            }
        }

        /**
         * This borrows most of the logic from the superclass handling of update events, but changes the calculation of the height
         * for the dirty region to provide proper handling for repainting custom height rows
         */

        private void handleRowUpdate(TableModelEvent e) {
            int modelColumn = e.getColumn();
            int start = e.getFirstRow();
            int end = e.getLastRow();

            Rectangle dirtyRegion;
            if (modelColumn == TableModelEvent.ALL_COLUMNS) {
                // 1 or more rows changed
                dirtyRegion = new Rectangle(0, start * getRowHeight(),
                                            getColumnModel().getTotalColumnWidth(), 0);
            }
            else {
                // A cell or column of cells has changed.
                // Unlike the rest of the methods in the JTable, the TableModelEvent
                // uses the coordinate system of the model instead of the view.
                // This is the only place in the JTable where this "reverse mapping"
                // is used.
                int column = convertColumnIndexToView(modelColumn);
                dirtyRegion = getCellRect(start, column, false);
            }

            // Now adjust the height of the dirty region
            dirtyRegion.height = 0;
            for ( int row=start; row <= end; row ++ ) {
                dirtyRegion.height += getRowHeight(row)//THIS IS CHANGED TO CALCULATE THE DIRTY REGION HEIGHT CORRECTLY
            }
            repaint(dirtyRegion.x, dirtyRegion.y, dirtyRegion.width, dirtyRegion.height);
        }
    }

}

AddThis Social Bookmark Button

SwingCommand part 7 - Composite Commands

May 2nd, 2009 Nick Posted in Uncategorized | No Comments »

There are occasions when it is useful to group commands together and execute several in combination. SwingCommand provides a CompositeCommandTask which allows you to group commands together in a single task which executes them sequentially.

TaskListeners on the CompositeCommandTask receive progress events as each child command is processed. The example below creates a String progress value to describe the current child command being processed.

Cancelling the composite task will cause the currently processing child command to be cancelled (if it supports cancellation), and will cause the composite task to abort processing for any remaining child command tasks. The reverse is also true - if the currently processing child command’s task is cancelled, this will cause the composite task to be cancelled and abort early.

If any of the child commands fail, the composite task will also fail with a CompositeCommandTaskException, which has the child task’s exception as the cause.

Here is a simple example of a composite task:

SwingCommand loadThreeUsersMessages = new SwingCommand() {
 
      protected Task createTask() {
 
          CompositeCommandTask compositeTask = new CompositeCommandTask() {
 
              //let's send progress as a String update to any TaskListeners
              protected String getProgress(int currentCommandId, int totalCommands, Task currentChildCommand) {
                  return "Now running number " + currentCommandId + " out of " + totalCommands + " commands";
              }
          };
 
          compositeTask.addCommand(new LoadMessagesCommand("User 1"));
          compositeTask.addCommand(new LoadMessagesCommand("User 2"));
          compositeTask.addCommand(new LoadMessagesCommand("User 3"));
          return compositeTask;
      }
  };
 
  loadThreeUsersMessages.execute();

The composite task implements the getProgress() method to generate the String used in the progress event.

AddThis Social Bookmark Button

SwingCommand part 6 - Cancellation

May 2nd, 2009 Nick Posted in Uncategorized | No Comments »

SwingCommand has support for cancellable commands. To support cancellation for a Task it is easiest if the Task extends InterruptibleTask. InterruptibleTask helps to implement the logic necessary to interrupt the background thread if cancel() is called.

When you invoke command.execute() on a SwingCommand instance, a reference to the Task which is created to handle the execution is returned from the execute() method. Getting the reference to the Task which is running can be useful - for Tasks which support the cancel operation, you may want to cancel the task by calling task.cancel()

To implement cancellation correctly, your Task must be able to respond to the interrupt request. There are two main cases to consider:

  • Interrupting a task which is blocked on IO, or on some other blocking call
  • Interrupting a task programatically by checking the isInterrupted() status periodically

Lets look at the first of these, interrupting a task which is blocking:

//interruping a background task with cancel

  SwingCommand c = new SwingCommand() {
      protected Task createTask() {

            return new InterruptibleTask() {

                public void doInBackground() throws InterruptedException {
                    Thread.sleep(10000);
                }

                public void doInEventThreadIfNotCancelled() {
                    textArea.setText("Task was not cancelled");
                }
        };
      }
  };

  //let's pop up a option pane if the task is cancelled
  c.addTaskListener(new TaskListenerAdapter() {
      public void cancelled(Task task) {
              JOptionPane.showMessageDialog(frame, "Task was Cancelled");
      }
  });


  Task t = c.execute();

  //and then a while later, from any thread...
  t.cancel();

In the example above the Task extends InterruptibleTask. During the doInBackground() handler, the background thread is blocked for a time in the call to Thread.sleep(). When task.cancel() is called by any thread, the InterruptibleTask logic will trigger thread.interrupt() on the background thread. This will result in an InterruptedException being generated by the sleep() method. Because cancel() was called, InterruptibleTask will catch the InterruptedException and the Task will finish with cancelled set to true (task.isCancelled() == true).

Note, if cancel() had not been called, the exception would have been allowed to propagate through causing TaskListeners to receive an error message, and the command would finish in the ERROR state.


What if cancel is called before or after the background processing?

If the call to task.cancel() occurs before background processing starts, the doInBackground() handler will not be called, and cancellation will always succeed. If the call to cancel() occurs after doInBackground() has returned, the call to cancel will not succeed - in this case and the task will end with isCancelled() == false.

The doInEventThreadIfNotCancelled() method is only called if the task is not cancelled. (You could alternatively override doEvenIfCancelled() if you wanted to do some post-processing in the event thread regardless of cancellation).


Interrupting a background task using custom logic

Sometimes a blocked thread will not respond automatically to thread.interrupt(). This is the case during some JDBC queries, for example. In some cases you have to implement extra logic to interrupt the task, and you can do this by overriding the doInterrupt() method. This example demonstrates how you might handle interrupting a query on a JDBC statement:

SwingCommand c = new SwingCommand() {
      protected Task createTask() {

            return new InterruptibleTask() {

                private volatile Statement s;
                private StringBuffer text = new StringBuffer();

                public void doInBackground() throws SQLException {
                    s = connection.createStatement();
                    ResultSet r = s.executeQuery("select text from Message");
                    while(r.next()) {
                        text.append(r.getString(1)).append("\n");
                    }
                }

                protected void doInterrupt() throws SQLException {
                    Statement statement = this.s;
                    if ( s != null) {
                        s.cancel();
                    }
                }

                public void doInEventThreadIfNotCancelled() {
                    textArea.setText(text.toString());
                }
        };
      }
  };

  Task t = c.execute();

  //do something else for a while, and then call:
  t.cancel();

In the example above, the basic processing is the same, but this time we have also overridden the doInterrupt() method, to close the JDBC statement if the background query is interrupted. This should cause the statement.executeQuery() method to throw an SQLException. Because cancel() has been called, this exception will be treated as the result of a cancellation, and the task will end with isCancelled() == true.

Interrupting a task by checking isInterrupted()

There’s just one more case to demonstrate. How to handle a non-blocking cancel. This is actually quite simple - you just need to periodically check the isInterrupted() method, and return early if necessary:

SwingCommand c = new SwingCommand() {
      protected Task createTask() {
         
        return new InterruptibleTask() {

            public void doInBackground() throws InterruptedException {
                for ( int loop=0; loop < 1000000; loop ++) {
                    if ( isInterrupted() ) {
                        break;
                    }
                }
            }

            public void doInEventThreadIfNotCancelled() {
                textArea.setText("Task was not cancelled");
            }
        };
      }
  };

Here the doInBackground() handler enters a processing loop. On each iteration, it checks to see if isInterrupted() is true - this will be the case if cancel() has been called on the task. If so, it breaks the loop to end the processing early.

Note that a task is considered cancelled even if the doInBackground() does not return early, provided that when the doInBackground() method returns the background thread still has its interrupted flag set.

Whew - that’s a fair amout to absorb. But the thing to take away is that extending InterruptedTask makes handling cancel easy. There is very little boilerplate code in the examples above, and you shouldn’t need to work too hard to get this right.

AddThis Social Bookmark Button

SwingCommand part 5 - Task States and Error Handling

May 2nd, 2009 Nick Posted in Uncategorized | No Comments »

Task States

It is possible to find the current state of a Task instance directly by calling task.getExecutionState();
There are six ExecutionStates: NOT_RUN, PENDING, STARTED, SUCCESS, CANCELLED and ERROR.

A task starts in state NOT_RUN. During the call to command.execute(), the state will change to PENDING. Once task processing begins (which may be some time later if the task is queued for processing or is blocked waiting for a thread from a thread pool) the state moves to STARTED. The final state will be either SUCCESS , ERROR or CANCELLED. In the case that a Task finishes in ERROR, you can obtain the Exception that caused the error by calling task.getExecutionException()

If a SwingCommand task handler method ( doInEventThread() or doInBackground() ) throws an Exception the task is considered to have failed. Any TaskListeners will receive an error() callback, and the task will end in the ExecutionState.ERROR state.

Ending a SwingCommand Early

If an exception is thrown during doInBackground(), the doInEventThread() handler will not be called. Instead, processing is aborted before doInEventThread() is called. This makes sense, because in most cases we will not want to update the ui if, for example, a task loading data failed.

However, sometimes you will need to perform error handling in the Swing Event Thread, or perform some processing whether or not an error occurs or the task is cancelled. For example you may need to stop a progress animation. These cases are best handled by a TaskListener, in the error() method, or the finished() method respectively.

The finished() callback always occurs, no matter if the task succeeds, fails or is cancelled. The TaskListener methods also allow you to perform handling only on success (the success() callback).

SwingCommand c = new SwingCommand() {
      protected Task createTask() {
          return new BackgroundTask() {

              public void doInBackground() throws Exception {
                  //this method throws an exception instead of returning
                  throw new Exception("woe");
              }

              public void doInEventThread() throws Exception {
                  //So we'll never get here!
              }
          };
      }
  };

     
  c.addTaskListener(new TaskListenerAdapter() {
     
      public void error(Task task, Throwable error) {
          JOptionPane.showMessageDialog(frame, "Failed to Load Messages");
      }
   
      public void finished(Task task) {
          //the task will finish in the ERROR state since doInBackground() threw an Exception
          assert(Task.ExecutionState.ERROR == task.getExecutionState());
       
          //we can get the error back by calling task.getExecutionException()
          Throwable cause = task.getExecutionException();
      }
  });

  c.execute();

AddThis Social Bookmark Button

SwingCommand part 4 - Using Executors

May 2nd, 2009 Nick Posted in Uncategorized | No Comments »

SwingCommand has good integration with the java.util.Concurrent, and this allows you to control which background thread will be used to run your BackgroundTask. You can specify the Executor which should be used by your SwingCommand.

This can be very useful. For example, there are occasions when it is important to ensure that only one background task of a certain type runs at a time. You could achieve this in the UI by disabling buttons or menu items while a command executes - but if your SwingCommand instance is shared and used in many areas of your application it might be nice to provide a firmer guarantee.

To achieve this, you could associate a single thread Executor with a SwingCommand. If the SwingCommand is executed again before the initial task has finished running, the second Task will then be queued to start processing once the first has completed. (Sometimes it is also a good idea to cancel the currently processing task at this point.)

//To make doubly sure no two tasks execute concurrently for this command
//we can associate a single thread executor -

  command.setExecutor(Executors.newSingleThreadExecutor());

Using a Thread Pool

Alternatively, let’s pretend there are many different commands in our app, and our users hammer buttons as if they were being paid per click. We don’t want to risk spamming the database server with requests (it’s probably on its last legs anyway!). A good solution might be to impose a limit on the number of threads which can concurrently run background tasks in our application. Once these threads are active, any new Tasks will be queued until there is a spare thread.

We could do this very easily by specifying a fixed size thread pool for all our SwingCommands. For example, the following code snippit creates a thread pool limited to three threads, and associates it with a command.

//using a Thread pool Executor

  Executor e = Executors.newFixedThreadPool(3);
  command.setExecutor(e);

To make sure all our commands use the thread pool, we could subclass SwingCommand to supply the thread pool Executor as the default. We could then use ThreadPoolSwingCommand instead of the basic SwingCommand throughout out application.

//Let's extend SwingCommand so that by default our commands share a fixed size thread pool:
//Our SwingCommand classes should all subclass this instead

  static abstract class ThreadPoolSwingCommand<P> extends SwingCommand<P> {

      private static Executor threadPoolExecutor = Executors.newFixedThreadPool(3);

      protected Executor getDefaultBackgroundTaskExecutor() {
          return threadPoolExecutor;
      }

  }

Setting one-off Executors

Similar to TaskListeners, you can also specify an Executor to be used for a one-off invocation of a SwingCommand. You do this by passing an Executor into the execute() method.

//specify an Executor for a single execution

  SwingCommand<String> c = new LoadMessagesCommand("My User");
  c.execute(Executors.newSingleThreadExecutor());

Processing a SwingCommand synchronously on a background thread

Passing in an Executor to the execute() method in this way can change the way in which a SwingCommand is processed. Usually, a SwingCommand launched from a background thread is processed asynchronously, so as not to block the thread which calls execute(). However, in some cases you may want the current background thread to be used for the execution of the Task - in which case the call to command.execute() will not return until the Task has finished processing. In this case, you can pass in a simple Executor which runs the command on the current thread:

//use an Executor which makes a SwingCommand execute synchronously from a background thread
 
  SwingCommand<String> c = new LoadMessagesCommand("My User");
  c.execute(new Executor() {
    public void execute(Runnable command) {
      //just process synchronously in the current thread
      command.run();
    }
  });

In the above example the normal threading guarantees for the Task are still obeyed - doInEventThread() will still be run in the Swing Event Thread. The background thread will be blocked while the EventThread does its work.

It should be clear from the above that the ability to set the Executor for a SwingCommand or a Task gives you a lot of control over the threads used to process your SwingCommands.

AddThis Social Bookmark Button

SwingCommand part 3 - Showing Progress

May 2nd, 2009 Nick Posted in Uncategorized | No Comments »

The progress animation triggered by the TaskListener in the previous example would at least inform the user that a SwingCommand is executing. However, sometimes it may be useful for a long running command to show publish interim results, or simply post more descriptive progress updates.

To help with this, as well as allowing you to set the generic type for parameters to your SwingCommand, the SwingCommand class also allows you to specify a generic type for a progress update. The following example demonstrates perhaps a simple case, in which an Integer progress event is fired:

//Here I have updated the ClearMessagesCommand to publish progress messages as a Integer

  class ClearMessagesCommand extends SwingCommand<String,Integer> {

      public ClearMessagesCommand() {
      }

      public Task<String,Integer> createTask() {

          return new BackgroundTask<String,Integer>() {

              public void doInBackground() throws Exception {
                  fireProgress(1);

                  String userName = getParameters();
                  Statement s = connection.createStatement();
                  s.execute("delete from Message where User = " + userName);

                  fireProgress(2);
              }

              public void doInEventThread() {
                  fireProgress(3);   
              }
          };
      }
  }

  SwingCommand<String,Integer> c = new ClearMessagesCommand();
  c.execute("Nick");

  c.addTaskListener(new TaskListenerAdapter<Integer>() {
      public void progress(Task task, Integer progressDescription) {
          progressLabel.setText("stage " + String.valueOf(progressDescription));
      }
  });

You could do more sophisticated things by firing progress, and progress events make it easy to update the UI safely - remember that these messages are received on the Swing Event thread even if fired from doInBackground().

For example, you could use this mechanism to gradually populate messages into an ‘inbox’ while reading mail messages from a remote server. Or you could send an Integer progress update representing a percentage value, to update a progress bar with the current percentage of work completed by a background thread.

AddThis Social Bookmark Button

SwingCommand part 2 - TaskListeners

May 2nd, 2009 Nick Posted in Uncategorized | No Comments »

A common requirement in User Interfaces is to show an animation while a background task runs. How might this be accomplished using SwingCommand?

The following example shows how you can add a TaskListener to a command. The TaskListener will receive events as the command executes, and start and stop an animation.

TaskListeners always receive their events on the Swing Event thread, even if the Task is a BackgroundTask.

//TaskListeners always receive their events on the Swing event thread

  LoadMessagesCommand command = new LoadMessagesCommand("My User");

  TaskListener animationListener = new TaskListenerAdapter() {
      public void pending(Task task) {
          startProgressAnimation();
      }

      public void finished(Task task) {
          stopProgressAnimation();
      }
  };

  command.addTaskListener(animationListener);
  command.execute();
  }

Every time the command is executed, the SwingCommand creates a new Task instance, and the TaskListener will receive events while the Task runs.

It’s also possible to add a TaskListener to receive events for a single SwingCommand invocation, by passing it as a parameter to the execute() method:

//Let's add a listener to perform some error handling..
//this listener will not receive events on any subsequent executions:

  LoadMessagesCommand command = new LoadMessagesCommand("My User");

  command.execute(new TaskListenerAdapter() {
      public void error(Task task, Throwable error) {
          System.err.println("Oops, perhaps our database is offline");
      }
  });

The ability to add TaskListeners to SwingCommands is a key feature of SwingCommand, and it can be very convenient. For example, you could create several different commands and add a shared listener which tracks the background tasks and updates progress components on your UI. This listener will receive events on the Swing Event thread whenever one of the commands is executed, no matter which thread or which class actually executes it.

Separation of concerns

TaskListeners are a very useful mechanism. They allow you to separate the logic for handling progress animations, and error handling logic, from the main Task processing logic. You could, for example, provide a TaskListener which performs consistent error handling for all of the commands in your application. This is a good separation of concerns. Mixing that logic in with the main command processing logic tends to clutter the code up and make classes less easy to reuse - and there are certainly cases where you might want different error handling depending on the situation in which the SwingCommand is executed (e.g. if it’s executed in response to a button click in a Dialog, you may want to show an error message in the Dialog, rather than the main UI frame). To achive this, you can add some listeners to the SwingCommand, which will get notified whenever it is executed, and others context specific listeners can be passed as parameters to SwingCommand.execute().

The TaskListener interface

There are actually six listener methods in the TaskListener interface. Some of these are always invoked, others are only invoked if a Task enters a specific state (ERROR or SUCCESS) for example. To avoid having to implement all the methods, you can extend TaskListenerAdapter.

The full definition of the TaskListener interface is below:

/**
 * Implement this interface to listen to the progress of a task
 * Callbacks on this interface are guaranteed to be received on the AWT event thread,
 * even if fired by an BackgroundTask
 *
 * These callbacks provide an easy and safe way to update the UI to show the progress of a task.
 *
 * <P> - the type of Progress object that this TaskListener will receive
 */

public interface TaskListener<P> {

    /**
     * The callback to pending is triggered when command.execute() is called
     * If the Executor running the task queues the task or blocks waiting for
     * a thread, it may be some time before started() is invoked.
     */

    void pending(Task task);

    /**
     * Called when the task starts processing. For BackgroundTask this callback
     * takes place just before doInBackground is called. For SimpleTask just before doInEventThread
     */

    void started(Task task);

    /**
     * This callback may take place at any time during task execution, to indicate progress
     */

    void progress(Task task, P progress);

    /**
     * Called when the task reaches the SUCCESS end state
     * This callback takes place once a command has successfully completed (was not cancelled,
     * and did not throw an Exception resulting in a callback to error())
     */

    void success(Task task);

    /**
     * Called when the task reaches the ERROR end state
     * This callback takes place if an exception is raised during task execution, which prevents
     * successful completion
     */

    void error(Task task, Throwable error);

    /**
     * Called when the task reaches the CANCELLED end state
     * This callback takes place if the task is cancelled.
     * @param task
     */

    void cancelled(Task task);

    /**
     * This callback always takes place once the task has finished, whether or not the
     * task finishes in ERROR, SUCCESS or CANCELLED states
     */

    void finished(Task task);
}

AddThis Social Bookmark Button

SwingCommand part 1 - Creating Commands

May 2nd, 2009 Nick Posted in Uncategorized | No Comments »

This is the first of several blog posts in which I’ll demonstrate how to use the SwingCommand library to handle background tasks in Swing apps.

SwingCommand version 2 has recently been released, and it contains a lot of improvements.

It’s high time to add more documentation!

First let’s recap a bit.

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

//This SwingCommand creates a Task which runs only on the Swing Event thread:
  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 to load user messages:
  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:

//Use a JTextField as a Task parameter

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 actually defines what type of progress object the command can publish - but we’ll talk more about that in another example.

class ClearMessagesCommand extends SwingCommand<String,String> {

      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.

AddThis Social Bookmark Button

Lower bound wildcards in java - how to apply the get and put principle

February 27th, 2009 Nick Posted in java | No Comments »

I recently saw an answer to a generics question on stack overflow where an incorrect (or at least misleading) answer has been voted up to the top. Sometimes generics are just a bit unintuitive.

Here is the example in a nutshell -

addToList(List<? super Shape> l) {
    l.add(new Object());
}

 // That should work, right? After all List<? super Shape> is defined so that it can hold
 // any objects which implement a superclass of Shape....

In fact, the code above would not compile. That’s the wrong way to interpret the lower bound wildcard. The variable l actually refers to a List which may be restricted to contain any valid superclass of Shape - and we don’t know exactly what type of List it is from the lower bound reference. It may actually be a List of Drawable (if Shape extends Drawable), in which case adding an Object to it would not be safe - since an Object is not necessarily Drawable. It may also be a List of Shape instances (since Shape is the lower bound), in which case adding an Object to it would also be invalid. That’s why the example above does not compile.

There is an important principle set out in Java Generics and Collections, called the ‘Get and Put Principle’ which helps to explain when to use lower and upper bound wildcards

Use an extends wildcard when you only get values out of a structure, use super wildcard when you only put values into a structure, and don’t use a wildcard when you both get and put.

Proper use of this rule can make your classes more flexible. Partly to make sure I’ve got this fully straightened out in my own head, I’ve put together an example which illustrates the get and put principle, and helps to explain upper and lower bound wildcards:

// Example of the Get and Put Principle

    private class Drawable {}

    private class Shape extends Drawable {}

    private class Circle extends Shape {}

    public void testPut() {
        putShapeInto(new ArrayList<Object>());
        putShapeInto(new ArrayList<Drawable>());
        putShapeInto(new ArrayList<Shape>());
    }

    //flexible - can take a List of any superclass of Shape
    public void putShapeInto(List<? super Shape> l) {
        // We don't know what the type of list l is, but we know it is Shape or a superclass of Shape
        // therefore, it is always a valid destination for Shape instances
        l.add(new Shape());

        // l.add(new Object());
        // ...will not compile. The type of l may be any valid superclass of Shape
        // e.g. It may be <Drawable>, not all Objects are Drawable

        // Shape s = l.get(0);
        // ...will not compile. l may be List<Drawable>, and not all Drawables are necessarily Shapes
    }

    public void testGet() {
        getShapeFrom(new ArrayList<Shape>());
        getShapeFrom(new ArrayList<Circle>());
    }

    //flexible - can take a List of any subclass of Shape
    public void getShapeFrom(List<? extends Shape> l) {
        //  because the upper bound of list l is Shape, l is always a valid source for Shape instances
        Shape s = l.get(0);

        //  l.add(new Shape());
        //  ...will not compile, the type of l may be List<Circle>. Shapes are not all Circles
    }

    // Can add and get, but not very flexible - the method is limited to accepting Lists of type <Shape>
    public void testPutAndGet(List<Shape> l) {
        Shape s = l.get(0);
        Drawable d = l.get(0);
        l.add(new Shape());       
        l.add(new Circle());
    }

AddThis Social Bookmark Button

Maven License Plugin - open source just got a little bit easier

January 23rd, 2009 Nick Posted in musings, random, rants and opinions | 1 Comment »

Isn’t it nice when once in a blue moon somebody comes up with an innovation which makes your life just that little bit simpler - one of those little things which you soon take for granted, but you’d miss immediately if it was not longer there. In this case, it’s the Maven 2 plugin which automatically updates your source files with the appropriate license details

Maven License Plugin

Until now this was an annoyingly manual task which was just not quite annoying or time consuming enough to merit spending time automating. It was one of those borderline annoyances which you plan to do something about, every time it bites you. However, in this case it is always the last action before cutting a release, when you’ve had a long hard day, the caffeine has worn off (and it’s a bit late for another dose, you’d never get to sleep) - you’re running on an empty tank and you’d really like to get out of the door. Over time these small tasks stack up and sap your energy.

That’s why this plugin is such a life saver. It does just what it says on the tin, took about 5 minutes to set up and get going - saving time the very first time I used it, even taking into account the minimal setup and configuration.

Every time I find a new plugin like this I’m reminded of some of the great things about maven - particularly the convention over configuration, which facilitates such easy sharing and reuse, and allows most features to ‘just work’. For sure, there is a learning curve associated with maven - but once you’re up to speed the benefit really shows.

AddThis Social Bookmark Button

Careful how you synchronize toArray()

January 21st, 2009 Nick Posted in java | No Comments »

This is a fuller version of the code snippit I submitted to the interesting thread on java synchronization issues on stack overflow

In general it can be tempting to assume too much thread safety when using synchronized collection classes.
It can be easy to think they somehow grant you more protection than they actually do, and forget to hold the lock between calls.

If have seen this mistake a few times:

List<String> l = Collections.synchronizedList(new ArrayList<String>());
 String[] s = l.toArray(new String[l.size()]);

In the second line above, the toArray and size() methods are both thread safe in their own right, but the size() is evaluated separately from the toArray(), and the lock on the List is not held between these two calls. If you run this code with another thread concurrently removing items from the list, sooner or later you will end up with a new String[] returned which is larger than required to hold all the elements in the list, and has null values in the tail, which will quite possibly result in a NullPointerException eventually.

It is easy to think that because the two method calls to the List occur in a single line of code this is somehow an atomic operation, but it is not. The best way to fix this is to hold the lock explicitly :

List<String> l = Collections.synchronizedList(new ArrayList<String>());

 synchronized(l) {
    String[] s = l.toArray(new String[l.size()]);
 }

AddThis Social Bookmark Button

Browser search bar integration for safari bookshelf

January 12th, 2009 Nick Posted in rants and opinions, web | No Comments »

For quite a while now I’ve been a subscriber to O’Reilly Safari bookself. It contains a lot of useful books on a wide range of technical and programming topics.

Although there is a good selection of material on the site, I haven’t really made the best use of it until now, since it has in general been less accessible than other web resources (it has just been easier to google for material than navigate to the O’Reilly site, log in, find the right page, search for books etc.). This devalued the whole Safari experience.

However, there is now a search bar plugin for safari (available from the tools page) which allows you to treat Safari as a search engine, and makes its contents available from the search box integrated with your browser toolbar. This innovation is the killer feature for Safari, in my opinion. Suddenly it makes the Safari content as accessible as the rest of the web. Suddenly, I don’t seem to have enough slots in my Safari bookshelf account any more, and the subscription fee seems quite reasonable.

AddThis Social Bookmark Button

New Open Source SwingCommand library released

September 29th, 2008 Nick Posted in java, swing | No Comments »

I finally completed open sourcing SwingCommand - an open source library which provides support for the command pattern in Swing. In addition to synchronous commands, it provides support for asynchronous commands, which hides away the gory threading details in a similar way to the SwingWorker class - but in SwingCommand the asynchronous commands are integrated into the overall framework, and there are a few extra features, such as composite command support and command execution observers, which I hope make it more flexible.

Reusable Commands

One other key difference from SwingWorker is that in SwingCommand each command instance is reusable (a command can be executed more than once). Each time you call myCommand.execute() a ‘CommandExecution’ instance, which encapsulates the state for that execution. In the SwingWorker provided as part of jdk 1.6, each SwingWorker instance is designed to be executed only once. In SwingCommand there is nothing to stop you creating a new Command instance each time if you prefer, but you also have the flexibility to create a shared Command instance up front, pass references to it around your application and execute it many times if necessary.

Why a new Command library?

There is nothing fundamentally new about using the command pattern in Swing apps, but arguably there is a need for a reusable library to address this problem in a focused manner, and provide a richer set of features than SwingWorker, without attempting to solve every other ui programming issue along the way. SwingCommand has certainly been a useful library in my projects already. It does hide away a lot of the more complex threading issues, typically produces ‘clean code’, and make it easy to build a Swing application around chunks of reusable logic wrapped as Command classes. Integration with java.util.concurrent framework makes it really easy to handle threading issues which would otherwise be complex (e.g to prevent two instances of the same asynchronous command from running simulatenously would be a one line change, by using Executors.newSingleThreadExecutor()).

A milestone

This is the first of Object Definitions’ libraries which I have open sourced. It is fair to say it has been more work than I anticipated to get it to this stage - so it is probably time to crack open a bottle of something fizzy. Once I have recovered from the hangover I’ll post more about it here. In the meantime there is a lot more information on the website and you can download swingcommand library here

AddThis Social Bookmark Button

Getting the NSLU2 to work with vista (why is vista configuration such a mess?)

September 24th, 2008 Nick Posted in hardware, rants and opinions | No Comments »

I just switched to using a Linksys NSLU2 NAS server for my home network.
It’s a great little thing - you just connect USB drives to it and set up network shares using the nifty web administration utitlity.

None of this is new - the NSLU2 (or ’slug’, as they are known) has been around for years now, and there is a cottage industry of linux kernal hackers grown around it, to adapt it to do such things as streaming music etc.

What is new, however, is that it won’t work from Vista. Works like a breeze from XP.

The linksys requires LM authentication which was supported by default by XP, but apparently it is disabled by default in vista, which only supports NTLM out of the box. I suspect this is not Linksys’ fault. The upshot of this is that from your spanking new Vista laptop you won’t be able to log in to your home network devices any more. You’ll probably forget they’re even there. The only person who will be able to log into them will be the guy parked up in a van outside who’s hacking into your wireless network using Windows XP. How’s that for security?

The forums are riddled with messages complaining about this, for the linksys and other similar NAS devices.
Most of them refer to now defunct Vista system administration tools
Searching the Vista help gets you nowhere.

Eventually I found a post which contained a registry setting to edit, to enable LM authentication
A registry setting to edit? Whooo, lucky me.
Welcome to the wonderful world of Windows usability.
If there is a utility which allows you to set this, I’d love to know, Windows admin dialogs are a joy.
I suspect I might be still looking at Christmas given how intuitive the admin is in Vista.

Anyhow, the magic LmCompatibilityLevel registry setting can be changed to let you log in to your network devices:

Note, changing this won’t make your network more secure. However, for your home network it should be OK, provided you’ve secured your wireless network. Any self respecting burgler walking into your appartment probably won’t be spending too much time trying to hack your network passwords. They’ll simply pick up your NAS drive and stick it under their shirt while pretending to read the gas meter, and make off with any win XP laptops (although they may leave your vista machine where it is)

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\LSA\

Change the key LmCompatibilityLevel from "3" to "1".

You can edit the registry using the regedit utility
Have fun!

AddThis Social Bookmark Button

WinSplit Revolution - awesome - just, awesome.

July 10th, 2008 Nick Posted in hardware | No Comments »

I’ve been looking for a screen splitting program for quite a while but somehow this one passed my by.

There isn’t much point in the 4 million pixels in the HP LP3065 unless you can easily arrange windows on the screen. Typically when working I need to run 15 to 20 apps, and view several simultaneously - and its a real waste of pixels if you can only maximise one window.

For a while, I was forced to go back to two screen setup, with smaller monitors, just so that I could easily maximise two apps.
But now that 30 inch monster is back on my desk, ooooh yea, with WinSplit revolution will never be separated again.

It’s a great little app which gives you hotkeys you can use to reposition windows in the screen either left or right, top or botton, in the corners or maximised. You can also use it to move windows between screens. That’s just the beginning - there are plenty of other great features too. So much so that I even find it useful on the 1280×1024 monitors at work. And, did I mention, it’s free? Forget windows power toys - this has instantly become my number one favorite windows utility.

Of course the real irony is that a free utility like this can provide such a huge improvement to the ‘windows’ os, which, despite costing hundreds of dollars, doesn’t even seem to offer the basic minimum ‘window management’ features required to use the software at a professional level. The windowing in vista, if you discount the ridiculous flip 3d switching gimmick, doesn’t seem to have changed an awful lot since win 3.1, when we all used to make do with 640×480 resolutions. Both linux and the Mac offer virtual desktops these days. There was a power toy which simulated this for XP, but it seems to have diasppeared from the Vista world. If anyone finds an equivalent, please let me know!

AddThis Social Bookmark Button

Don’t be lazy

June 18th, 2008 Nick Posted in java, rants and opinions | No Comments »

I’ve seen far too many bugs lately involving lazy initialization.

In theory lazy initialization might seem a good idea. Why do extra work up front which might not be needed?

The problem is that it is simply too easy to introduce bugs when state is not completely loaded after construction. For example, a project I recently worked on made extensive use of init() methods to lazily load state on demand. The constructor would not do the initialization. Instead, each getter method would check whether initialization was complete, and if not kick off an init() process before returning data.

The reasons for this were fairly convincing. The loading of the state required a call to a database, which was slow. But the problem was that this lazy initialization pattern soon spread throughout the codebase like a rash. Soon it was rare to find a class which was guaranteed to be ready for use after construction.

Still other classes used lazy loading as a speculative performance optimization. Table models received events which invalidated their internal state. Instead of recalculating immediately, they propagated events to their listeners, delaying their own recalculation. The models would recalculate only when a client class called a method to ask for data, and forced a the model to check its state and recalculate. Again, it sounded good - why take on the overhead of immediate recalculation if your class has no active listeners?

The original coders left. The code base gradually expanded to more than a million lines of code. New programmers arrived. Predictable problems emerged.

In many cases, the new programmers would simply fail to realise that an object was not ready to use until its init method had been called. Init methods had become a part of some key abstractions - although in the vast majority of cases subclasses did not actually require any special initialization. So most objects ended up with an init method, just to cater for the few places where it was really necessary. Confusion reigned as to when and where initialization should take place.

Interfaces changed and classes needed to be modified. It was all too easy to forget to check the initialization status when adding methods to existing classes. Strange errors occurred in the table models. The data had changed some time before, but since the recalculations were delayed it was hard to work out from the logs what had actually triggered the problem. Sometimes the original coders had made mistakes and forgotten to check the initialization state for some methods. In other subclasses had overridden methods and forgotten to include the check. Problems could emerge quite unexpectedly, such as when the ordering of client class method calls changed. This often caused strange data corruption or null pointer exceptions at runtime, which brought down components.

The basic OO tenet that an object should be ready for use after construction really does make a lot of sense - try very hard to avoid lazy initialization wherever possible. It may seem clever, but it probably isn’t. If it seems that lazy initialization is the only way, reconsider every alternative design. Simplicity is key, and lazy initialization or lazy recalculation is rarely, if ever, simple. You’ll likely save yourself some headaches in the short term, and in the long term you’ll save even more for those who inherit the code once you have moved on.

AddThis Social Bookmark Button

Calculating a color gradient

May 12th, 2008 Nick Posted in java, swing | No Comments »

For a recent project I needed some code to calculate a colour gradient for different points on a scatter chart. The class below performs a simple interpolation between two colours, if you feed it a value between 0 and 1.

public class GradientCalculator {
        private Color c1;
        private Color c2;

        public GradientCalculator(Color c1, Color c2) {
            this.c1 = c1;
            this.c2 = c2;
        }

        /**
         * @param ratio - value between 0 and 1
         */

        public Color getColor(double ratio) {
            int red = (int) (c1.getRed() * (1 - ratio) + c2.getRed() * ratio);
            int green = (int) (c1.getGreen() * (1 - ratio) + c2.getGreen() * ratio);
            int blue = (int) (c1.getBlue() * (1 - ratio) + c2.getBlue() * ratio);
            int alpha  =(int) (c1.getAlpha() * (1 - ratio) + c2.getAlpha() * ratio);
            return new Color(red, green, blue, alpha);
        }
    }

AddThis Social Bookmark Button