Archive for October, 2007

Programmer’s Notebook: Uncaught Exception Handlers

Summary: learn how to use Thread.UncaughtExceptionHandler effectively in rich client (a.k.a. Swing) applications.

UPDATE: See One More Note on Uncaught Exception Handlers for important information about exceptions thrown from modal dialogs.

Quick Review

Java 5 introduced a new way to catch unexpected exceptions: Thread.UncaughtExceptionHandler. This is an interface with the following API:

public interface UncaughtExceptionHandler {
  void uncaughtException(Thread t, Throwable e);
}

Your job is to write a class that implements this interface. You then register your implementation on a per-thread basis, or globally for every thread. To register your handler for a single thread you do something like this:

Thread.currentThread().setUncaughtExceptionHandler(new MyExceptionHandler());

To register your handler for every thread, do this:

Thread.setDefaultUncaughtExceptionHandler(new MyExceptionHandler());

Notice that setDefaultUncaughtExceptionHandler(...) is a static method in the Thread class, while setUncaughtExceptionHandler(...) is a non-static method.

A Sample Swing App

Here is a very simple Swing application that shows two buttons:

Demonstration App

If you click the “EDT Exception” button, it throws an exception on the event dispatch thread (EDT). If you click the “Thread Exception” button, it starts a background thread that throws an exception.

In both cases, a stack trace appears on the console. But the EDT exception messes up repainting, so the button does not paint properly until you move your mouse around a bit. Fortunately, Swing recovers and the app continues. Here is the complete source code:

public class Main extends JFrame {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new Main().setVisible(true);
            }
        });
    }

    // throws an exception on the event dispatch thread
    private Action edtAction = new AbstractAction("EDT Exception") {
        public void actionPerformed(ActionEvent e) {
            throw new RuntimeException("Exception generated on the EDT");
        }
    };

    // throws an exception on another thread
    private Action threadAction = new AbstractAction("Thread Exception") {
        public void actionPerformed(ActionEvent e) {
            new Thread("Sample Thread") {
                public void run() {
                    throw new RuntimeException("Exception thrown on a thread");
                }
            }.start();
        }
    };

    public Main() {
        super("Uncaught Exception Handler Demo");

        JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
        buttonPanel.add(new JButton(edtAction));
        buttonPanel.add(new JButton(threadAction));
        getContentPane().add(buttonPanel, BorderLayout.SOUTH);

        pack();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

Simple enough. Let’s add an uncaught exception handler.

MyExceptionHandler

Here is a simple uncaught exception handler that shows the exception message in a dialog box.

public class MyExceptionHandler implements Thread.UncaughtExceptionHandler {

    public void uncaughtException(final Thread t, final Throwable e) {
        if (SwingUtilities.isEventDispatchThread()) {
            showException(t, e);
        } else {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    showException(t, e);
                }
            });
        }
    }

    private void showException(Thread t, Throwable e) {
        String msg = String.format("Unexpected problem on thread %s: %s",
                t.getName(), e.getMessage());

        logException(t, e);

        // note: in a real app, you should locate the currently focused frame
        // or dialog and use it as the parent. In this example, I'm just passing
        // a null owner, which means this dialog may get buried behind
        // some other screen.
        JOptionPane.showMessageDialog(null, msg);
    }

    private void logException(Thread t, Throwable e) {
        // todo: start a thread that sends an email, or write to a log file, or
        // send a JMS message...whatever
    }
}

To use this handler, you must register it. Simply modify the code as follows:

public static void main(String[] args) {
    Thread.setDefaultUncaughtExceptionHandler(new MyExceptionHandler());

    ...

Now, whenever an “unexpected” exception occurs, MyExceptionHandler kicks in and shows this lovely dialog box:

Unexpected System Problem

As an added bonus, the GUI repaints properly when this dialog displays.

This is Not Production Quality!

This is not a production-quality example! Let’s run down a list of features you will need to consider in a “real” application.

  • Event Dispatch Thread (EDT) confinement. As our UncaughtExceptionHandler shows, your uncaughtException method must ensure it is on the EDT before showing an error dialog or interacting with other Swing GUI components.
  • Dialog ownership. If you use a dialog box, your app will need to use a proper owner Window. My example simply passes null as the owner, which is unacceptable in a real app. Failure to specify an owning window causes dialog boxes to get buried, which (as we found) causes many users to simply reboot their computers. Yes, the typical computer user does not know how to Alt-Tab through Windows, nor do they know how to use task manager to kill a process. Make sure you never bury a modal dialog!
  • Consider a non-modal dialog. Many modern apps show unexpected problems in a flashing indicator in the status bar.
  • If you use a modal dialog, include an exit button. Sometimes when you hit OK, the exception occurs again, which triggers a new dialog. If modal, users are locked in and really need an exit button to abort the app.
  • If you report errors back to “home base” via email or some other mechanism, do not do it on the event dispatch thread. Spawn a background thread.
  • Make damn sure your unexpected exception hander includes try/catch blocks so you NEVER trigger yet another exception. If you do, you can get caught in infinite recursion as you display more and more errors.
  • If your dialog is already visible, do not create another one. Consider the case when a table cell renderer throws an exception. Your dialog displays, which triggers a repaint event, which causes another exception, etc. If your dialog is already visible, ignore the next exception. Otherwise you’ll get caught in an infinite loop.
  • If you are really sneaky, you can use Robot.createScreenCapture() to capture a picture of your app. Be very, very careful about privacy concerns. This is only acceptable in an internal corporate environment…even then, there are significant ethical considerations. :-)
  • Consider the possibility of an unexpected exception before your main window appears. If your window is not even visible yet and you try to create a modal dialog using that window as its parent, your dialog won’t appear.
  • If your customers are not comfortable with automated error reporting, consider adding a button that lets them choose to optionally send an email along with a screen shot.
  • See also One More Note on Uncaught Exception Handlers for important information about exceptions thrown from modal dialogs.

As you can see, there are many details to consider. Real apps are just so darn messy…

Ahmadinejad the Programmer

Boy, what a week! Let’s kick off the weekend early. All loyal Stuff That Happens Subscribers can leave work at noon today. Enjoy this new comic…

UPDATE: Apparently nobody “gets it”. Here is some background material:

  • Ahmadinejad is the president of Iran
  • Do a Google search for Ahmadinejad quotes, or Ahmadinejad jacket
  • He is well-known for executing gays, and recently spoke at Columbia University, where he mentioned that Iran has no homosexuals, like we do in the US
  • He is also known for his pursuit of nuclear technology. He claims it is for power only. The US claims it is for bombs.

Get it now?

Ahmadinejad the Programmer

Now for a quick survey:

  1. Now you’ve gone too far, Burke. I will unsubscribe from your Atom Feed right now.
  2. You just jumped the shark.
  3. More, more, more!

Sling Blade Runner

Check out this puzzle from ITA Software. (if that link ever goes bad, here is their puzzle archive).

“How long a chain of overlapping movie titles, like Sling Blade Runner, can you find?”

Use the following listing of movie titles: MOVIES.LST. Multi-word overlaps, as in “License to Kill a Mockingbird,” are allowed. The same title may not be used more than once in a solution. Heuristic solutions that may not always produce the greatest number of titles will be accepted: seek a reasonable tradeoff of efficiency and optimality.

I started tinkering with this last week and spent several evenings working on a solution. My first attempt was a brute force algorithm that tried every possible combination.

After letting my program run 24 hours, it was still processing the third movie…out of a list of 6561 movies! Clearly brute force is not the way to go.

Building the Data Structure

My current algorithm works as follows:

  1. Load the file into memory
  2. For each movie title, create a Movie object
  3. Each Movie has a list of “prefixes” and “suffixes”. For example, prefixes for “TO KILL A MOCKINGBIRD” are “TO”, “TO KILL”, and “TO KILL A”. I put all prefixes and suffixes into hash maps. Furthermore, I have a cross reference Map that uses an integer as a key that maps to String prefix and suffix values.
  4. I then create a graph, linking every matching prefix and suffix together, using the integers from my lookup table. Thus, each Movie object now has a Set of every possible predecessor and successor movies.
  5. Prune all movies that have no predecessors or successors. This eliminates 2092 movies right away.
  6. Walk the graph…more on this in a bit

Reading the data file, building the graph, and pruning the unwanted movies takes less than a second. Walking the graph is where it gets interesting.

Walking the Graph

Do this for each movie:

  1. The first movie is the starting point. Its distance is 0.
  2. Find all predecessors. Their distance is 1.
  3. Recursively continue, incrementing the count by 1 each time.

It’s a wee bit more complicated than that, but not much. For each “starting movie”, I create a new linked list. Each node in the list contains the distance from the starting point, a reference to the current movie, a reference to the starting point, and a link to the predecessor node in the linked list.

By keeping these linked list nodes (along with an extra Map in each Movie), I can avoid hitting the same movie more than once per starting point.

Lessons Learned

The graph contains *many* cycles. Thus, depending on the order in which you traverse the predecessors, you get different counts. On average, I find chains around 200 movie titles long. This takes around 15 seconds on my computer. I just randomly shuffle the children and keep re-running the app, hoping to find a longer chain.

I only have a single CPU machine. My algorithm is memory intensive due to all of the linked lists, but that’s no problem for several thousand movies. If I had more CPUs, I could very easily make it threaded to (hopefully) improve performance. For millions of titles, you’d have to come up with a different approach I’m sure.

It turns out that pruning those 2092 movies hardly matters in terms of runtime performance. That was a big surprise.

Just for Fun

I didn’t do this to apply for a job at ITA. It just looked like a good challenge. It turned out to be a bit harder than I first anticipated. I really don’t have any ideas for reliably finding the “longest” chains.

The Longest Chain

Here is my best chain so far.

1. MRS PARKER AND THE VICIOUS CIRCLE
2. CIRCLE OF FRIENDS
3. FRIENDS WITH MONEY
4. MONEY FOR NOTHING
5. NOTHING BUT TROUBLE
6. TROUBLE IN PARADISE
7. PARADISE ROAD
8. ROAD GAMES
9. GAMES PEOPLE PLAY NEW YORK
10. NEW YORK NEW YORK
11. NEW YORK COP
12. COP LAND
13. LAND OF THE DEAD
14. DEAD MAN
15. MAN ON FIRE
16. FIRE IN THE SKY
17. SKY HIGH
18. HIGH SPIRITS
19. SPIRITS OF THE DEAD
20. THE DEAD GIRL
21. GIRL IN THE CADILLAC
22. CADILLAC MAN
23. MAN OF THE HOUSE
24. THE HOUSE OF THE DEAD
25. DEAD BANG
26. BANG BANG YOURE DEAD
27. DEAD MAN WALKING
28. WALKING AND TALKING
29. TALKING ABOUT SEX
30. SEX AND THE OTHER MAN
31. MAN TROUBLE
32. TROUBLE EVERY DAY
33. DAY OF THE WOMAN
34. THE WOMAN IN RED
35. RED HEAT
36. HEAT AND DUST
37. DUST TO GLORY
38. GLORY ROAD
39. ROAD HOUSE
40. HOUSE PARTY
41. PARTY MONSTER
42. MONSTER IN A BOX
43. BOX OF MOON LIGHT
44. LIGHT OF DAY
45. DAY FOR NIGHT
46. NIGHT OF THE LIVING DEAD
47. DEAD OF NIGHT
48. NIGHT MOTHER
49. MOTHER JUGS AND SPEED
50. SPEED 2 CRUISE CONTROL
51. CONTROL ROOM
52. ROOM AT THE TOP
53. TOP GUN
54. GUN CRAZY
55. CRAZY AS HELL
56. HELL NIGHT
57. NIGHT AND THE CITY
58. CITY BY THE SEA
59. SEA OF LOVE
60. LOVE AND DEATH
61. DEATH WISH V THE FACE OF DEATH
62. DEATH BECOMES HER
63. HER MAJESTY MRS BROWN
64. BROWN SUGAR
65. SUGAR AND SPICE
66. SPICE WORLD
67. WORLD TRADE CENTER
68. CENTER STAGE
69. STAGE FRIGHT
70. FRIGHT NIGHT
71. NIGHT AND DAY
72. DAY OF THE DEAD
73. DEAD END
74. END OF DAYS
75. DAYS OF HEAVEN
76. HEAVEN CAN WAIT
77. WAIT UNTIL DARK
78. DARK CITY
79. CITY OF JOY
80. JOY RIDE
81. RIDE THE HIGH COUNTRY
82. COUNTRY LIFE
83. LIFE AS A HOUSE
84. HOUSE OF FRANKENSTEIN
85. FRANKENSTEIN AND THE MONSTER FROM HELL
86. HELL UP IN HARLEM
87. HARLEM RIVER DRIVE
88. DRIVE ME CRAZY
89. CRAZY PEOPLE
90. PEOPLE WILL TALK
91. TALK OF ANGELS
92. ANGELS WITH DIRTY FACES
93. FACES OF DEATH 4
94. 4 LITTLE GIRLS
95. GIRLS OF SUMMER
96. SUMMER CATCH
97. CATCH A FIRE
98. FIRE ON THE MOUNTAIN
99. THE MOUNTAIN MEN
100. MEN WITH GUNS
101. GUNS OF THE MAGNIFICENT SEVEN
102. THE MAGNIFICENT SEVEN RIDE
103. RIDE WITH THE DEVIL
104. THE DEVIL RIDES OUT
105. OUT OF THE PAST
106. PAST MIDNIGHT
107. MIDNIGHT RUN
108. RUN SILENT RUN DEEP
109. DEEP BLUE
110. BLUE CAR
111. CAR 54 WHERE ARE YOU
112. YOU LIGHT UP MY LIFE
113. LIFE WITH FATHER
114. FATHER OF THE BRIDE
115. BRIDE OF THE WIND
116. THE WIND AND THE LION
117. THE LION KING
118. KING OF THE JUNGLE
119. JUNGLE 2 JUNGLE
120. JUNGLE BOOK
121. BOOK OF LIFE
122. LIFE IS BEAUTIFUL
123. BEAUTIFUL GIRLS
124. GIRLS WILL BE GIRLS
125. GIRLS GIRLS GIRLS
126. GIRLS JUST WANT TO HAVE FUN
127. FUN AND FANCY FREE
128. FREE WILLY 2 THE ADVENTURE HOME
129. HOME ALONE 3
130. 3 NINJAS KICK BACK
131. BACK TO SCHOOL
132. SCHOOL OF ROCK
133. ROCK STAR
134. STAR TREK IV THE VOYAGE HOME
135. HOME ALONE
136. ALONE IN THE DARK
137. DARK STAR
138. STAR TREK THE MOTION PICTURE
139. PICTURE BRIDE
140. BRIDE OF THE MONSTER
141. MONSTER HOUSE
142. HOUSE PARTY 3
143. 3 NINJAS KNUCKLE UP
144. UP CLOSE AND PERSONAL
145. PERSONAL BEST
146. BEST OF THE BEST
147. THE BEST OF EVERYTHING
148. EVERYTHING RELATIVE
149. RELATIVE FEAR
150. FEAR X
151. X THE MAN WITH THE X RAY EYES
152. EYES OF AN ANGEL
153. ANGEL BABY
154. BABY SECRET OF THE LOST LEGEND
155. LEGEND OF THE LOST
156. THE LOST BOYS
157. BOYS LIFE
158. LIFE OR SOMETHING LIKE IT
159. IT TAKES TWO
160. TWO FRIENDS
161. FRIENDS AND LOVERS
162. LOVERS AND OTHER STRANGERS
163. STRANGERS WHEN WE MEET
164. MEET JOE BLACK
165. BLACK AND WHITE
166. WHITE HUNTER BLACK HEART
167. HEART CONDITION
168. CONDITION RED
169. RED EYE
170. EYE FOR AN EYE
171. AN EYE FOR AN EYE
172. EYE OF GOD
173. GOD TOLD ME TO
174. TO DIE FOR
175. FOR THE BOYS
176. BOYS ON THE SIDE
177. SIDE OUT
178. OUT COLD
179. COLD FEVER
180. FEVER PITCH
181. PITCH BLACK
182. BLACK HAWK DOWN
183. DOWN TO EARTH
184. EARTH GIRLS ARE EASY
185. EASY COME EASY GO
186. GO NOW
187. NOW YOU SEE HIM NOW YOU DONT
188. DONT GO IN THE HOUSE
189. HOUSE OF DRACULA
190. DRACULA DEAD AND LOVING IT
191. IT HAPPENED AT THE WORLDS FAIR
192. FAIR GAME
193. GAME OF DEATH
194. DEATH WISH
195. WISH UPON A STAR
196. A STAR IS BORN
197. BORN AMERICAN
198. AMERICAN ME
199. ME MYSELF I
200. I LOVE YOU TO DEATH
201. DEATH SHIP
202. SHIP OF FOOLS
203. FOOLS RUSH IN
204. IN PRAISE OF OLDER WOMEN
205. WOMEN IN LOVE
206. LOVE WALKED IN
207. IN COLD BLOOD
208. BLOOD DIAMOND
209. DIAMOND MEN
210. MEN CRY BULLETS
211. BULLETS OVER BROADWAY
212. BROADWAY DANNY ROSE
213. ROSE RED
214. RED RIVER
215. RIVER OF NO RETURN
216. RETURN TO HORROR HIGH
217. HIGH SCHOOL HIGH
218. HIGH CRIMES
219. CRIMES OF THE HEART
220. THE HEART OF ME
221. ME WITHOUT YOU
222. YOU ONLY LIVE ONCE
223. ONCE AROUND
224. AROUND THE BEND
225. BEND OF THE RIVER
226. THE RIVER WILD
227. WILD THINGS
228. THINGS TO COME
229. COME AND GET IT
230. IT HAPPENED ONE NIGHT
231. ONE NIGHT WITH THE KING
232. THE KING AND I
233. I WANT TO LIVE
234. LIVE FOREVER
235. FOREVER YOUNG
236. YOUNG SHERLOCK HOLMES
237. SHERLOCK HOLMES AND THE VOICE OF TERROR
238. TERROR BY NIGHT
239. NIGHT FALLS ON MANHATTAN
240. MANHATTAN MURDER MYSTERY
241. MYSTERY ALASKA
242. ALASKA SPIRIT OF THE WILD
243. THE WILD ONE
244. ONE EIGHT SEVEN
245. SEVEN YEARS IN TIBET

Conflicting JDK Advice?

On September 25 I mentioned that SwingWorker is fixed and you should upgrade your JDK.

Then, on October 2, I suggest you should avoid Java 6 thanks to a horrific bug with the Windows XP Look and Feel.

I just noticed the contradiction and had a good laugh at myself.

To summarize, if you are already using Java 6, definitely get the latest release because it really does fix a lot of bugs, including several nasty VM crashes and memory leaks.

Just avoid the XP Look and Feel.

Origins of @InsertNameHere

I just recently caught on to the trend of referencing other people’s blog comments by prefixing their username with @. I like it. And I’m probably one of the last people on Planet Internet to “catch on”. (again, feeling behind the curve here)

Did this convention originate with Twitter?

I fully expect a whole bunch of comments where you guys reference each other’s comments with @.

Go.

Rich Client Developers: Avoid Java 6?

You might not see this bug on your PC. If you have ten customers, you might not see it. If you have 100…or 1000…or more…you may regret deploying your Swing application using Java 6. Because if you use a certain type of table header renderer along with the Windows XP Look and Feel, you will eventually see large numbers of exceptions.

java.lang.NullPointerException
  at com.sun.java.swing.plaf.windows.WindowsTableHeaderUI$XPDefaultRenderer.paint(
         Unknown Source)
  at javax.swing.CellRendererPane.paintComponent(Unknown Source)
  at javax.swing.plaf.basic.BasicTableHeaderUI.paintCell(Unknown Source)
  at javax.swing.plaf.basic.BasicTableHeaderUI.paint(Unknown Source)
  at javax.swing.plaf.ComponentUI.update(Unknown Source)
  at javax.swing.JComponent.paintComponent(Unknown Source)
  at javax.swing.JComponent.paint(Unknown Source)
  etc...

Here is a lesson-learned about customer error reporting. A vast majority of customers will not report errors. So we embedded an uncaught exception handler that reports all “unexpected” errors via Email. We quickly discovered that under Java 6, we see thousands of these errors. Yes, thousands.

The Worst Bug in Client Java?

This is actually a family of related problems, but they most commonly manifest themselves when using custom table header renderers.

The actual bug ID is 6429812. It only has four votes, and was originally reported under JRE 1.5. We saw it occasionally under Java 5, but it is far more common when running under Java 6.

Duplicating the Bug with Glazed Lists

Let’s start with a simple POJO:

public class Bug {
    private final int legCount;
    private final String name;

    public Bug(String name, int legCount) {
        this.name = name;
        this.legCount = legCount;
    }

    public String getName() {
        return name;
    }

    public int getLegCount() {
        return legCount;
    }
}

And here is a little Java app that shows a sortable table of bugs:

public class TableDemo extends JFrame {
  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        try {
          UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception ignored) {
          // ignored
        }
        new TableDemo().setVisible(true);
      }
    });
  }

  public TableDemo() {
    super("Table Demo");

    add(new JScrollPane(createTable()), BorderLayout.CENTER);
    pack();
    setLocationByPlatform(true);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  }

  private Component createTable() {
    TableFormat<Bug> tableFormat = new BeanTableFormat<Bug>(
        Bug.class,
        new String[]{"legCount", "name"},
        new String[]{"Legs", "Name"});
    EventList<Bug> bugList = new BasicEventList<Bug>();
    bugList.addAll(Arrays.asList(new Bug("Aidan", 8), new Bug("Tanner", 6),
        new Bug("Eric", 4), new Bug("Jen", 8), new Bug("Dexter", 6)));

    SortedList<Bug> sortedBugList = new SortedList<Bug>(bugList, null);
    TableModel tableModel = new EventTableModel<Bug>(
        sortedBugList, tableFormat);
    JTable table = new JTable(tableModel);
    new TableComparatorChooser<Bug>(table, sortedBugList, false);
    return table;
  }
}

The TableComparatorChooser wraps the default table header renderer with one that provides sort arrows. This is what triggers the bug.

When you run the app, it looks like this:

Sortable Table

Duplicating the bug is easy. After starting the app, change the Windows display settings from XP to “Classic”:

  1. Right click the desktop
  2. Select Properties
  3. Change the Theme to Windows Classic

This Glazed Lists example app immediately starts spewing exceptions and no longer paints. You have to exit the app.

But but but…

The above scenario is a reliable way to reproduce the bug, but it is contrived. Most users don’t change to Classic Windows look. However we discovered many additional ways to duplicate the problem:

  • While your app is running, go to another PC and establish a Remote Desktop connection to your PC. Boom!
  • Share your desktop via WebEx. Boom!
  • Run a program like ZoomText. Boom!
  • Make the mistake of using certain screen savers. When the screen saver starts, Boom!

In summary, many programs affect the Windows graphics display mode. When these take over, your Java app will often crash.

One Partial Fix

I found a workaround. I wrote a custom table header renderer from scratch that duplicates the XP look without wrapping the original XP renderer.

We deployed this and it fixed the issue for most customers, but now we find that Java still blows up, it just happens slightly less often. Instead of blowing up in the table header renderers, it now blows up in either the BasicComboBoxUI$Handler or in WindowsTabbedPaneUI.paintRotatedSkin. Again, the same kinds of scenarios trigger these horrible bugs, although they are harder to replicate on demand.

We only know about these bugs because we deploy Swing apps to large numbers of customers and we have an automated error reporting infrastructure. Without our tools, I’m not sure we’d really know the magnitude of this problem.

Another fix is to avoid the Windows XP native look and feel.

In Summary…

These bugs are all related. There appears to be a fundamental flaw in the XP look and feel that makes rich client Java apps a highly risky proposition if your customers use XP. (which constitutes about 99% of our users, unfortunately).

I am writing about this in order to raise awareness of these highly important bugs. Leaving comments on the bug parade and voting for this bug is not enough. I cannot imagine a more important bug than one that causes the application to abort simply because someone walked away from their computer and a screen saver kicked in.

Please, Sun, fix these problems in JDK 1.6.0 update 3.