The Source for Java Technology Collaboration
User: Password:



Tim Boudreau's Blog

April 2005 Archives


A library for creating Wizards in Swing

Posted by timboudreau on April 17, 2005 at 01:21 PM | Permalink | Comments (25)

I wrote a prototype replacement for the Wizards API in NetBeans - trying very hard to create something clean and easy to use, especially one which didn't require the user of the API to write any more code than absolutely necessary. It's now a project on java.net and I'd love to get some feedback.

The ideas are pretty simple:

  • A wizard does not alter its environment until finish() is called
  • You create a factory for panels. It supplies a list of String IDs for panels, and things call it saying "give me the panel for ID 'foo'".
  • The UI you write puts data the user enters into a Map (listen on a text field, when it changes, call settings.put ("stuffTheUserEntered", theTextField.getText()) and can enable/disable Next/Finish
  • The settings map is handled for you - if the user backs up, settings from the current pane are cleanly removed (it's really a map that delegates to a list of per-panel maps)
  • The UI is pluggable - it comes with a default one (which still needs polishing and localization) - so if you want a different UI, set of buttons, whatever, you have complete freedom to provide a custom implementation
  • Branching and wizards with indeterminate numbers of steps are supported (basically this is conditionally nesting one Wizard inside another, but the convenience class for this makes it painless)

I'm sure it's not perfect yet, and would love to get some feedback. I tried pretty hard to make the javadoc complete and full of examples. You can find the project at wizard.dev.java.net, with the javadoc available as a zip.

Writing Memory Leak Regression Tests with INSANE

Posted by timboudreau on April 17, 2005 at 11:49 AM | Permalink | Comments (6)

A couple of years ago my friend Petr Nejedly created a handy library he calls INSANE. The reason for the name was, whenever he described the idea to people, they'd say "that's insane!" (but it is now also a cute acronym). What it does is static heap analysis - that is, it writes out every object in the JVM and how they reference each other. INSANE is also useful to diagnose OutOfMemoryErrors - just allocate some large array on application start (so you can clear enough room for INSANE to work after an OOME has happened), and install a handler for OutOfMemoryErrors which will clear the array and then dump the object graph of the JVM to a file. From there, grep is a handy tool to track down what's going on.

Download the source + library for this example here. Sources and more info on INSANE can be found here.

In NetBeans, we use INSANE for analyzing memory leaks; we also use it in our regression tests. Once you know you have a memory leak, you can use INSANE to print out exactly the reference graph that is causing the leak. So, when you fix a memory leak, you can write a unit test that will fail if the leak ever reappears. Some profilers can do this sort of thing too, but it's really nice to have a regression test that will tell you exactly what's going on without having to go in and profile the app.

A few people I've talked to about this here in Brazil have said "You mean you can have a memory leak in Java?" So we'll cover this quickly: A memory leak in Java happens when you allocate some object, and your code is now done with it, but it is still referenced somewhere, so the object can't be garbage collected - it sits around forever taking up space. Eventually you can run out of memory (or at least cause swapping and thrashing) because memory is full of objects nothing will ever use again. The most typical pattern for creating such leaks is adding something to a Collection somewhere and forgetting about it.

I've put together an example with a memory leak. First, read this code and see if you can find the leak:

public class MyFrame extends javax.swing.JFrame {
    MenuAction menuAction = new MenuAction();
    public MyFrame() {
        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setBounds (20, 20, 300, 300);
        getContentPane().addMouseListener (new MouseAdapter() {
            public void mouseReleased (MouseEvent me) {
                getPopupMenu().show((Component) me.getSource(), me.getX(), me.getY());
            }
        });
    }
    
    JPopupMenu getPopupMenu() {
        JPopupMenu menu = new JPopupMenu();
        menu.add (new JMenuItem (menuAction));
        return menu;
    }
    
    static final class MenuAction extends AbstractAction {
        public MenuAction() {
            putValue (Action.NAME, "Do Something");
        }
        public void actionPerformed (ActionEvent ae) {
            System.out.println("Action performed");
        }
    }
}

The above code contains a memory leak, but it's quite subtle. So lets assume we know that what is leaking is the JPopupMenu - we create a new one each time, and somehow they're not being garbage collected. So, what we'll do is write a regression test that fails - then we'll know when the bug is fixed. I've created a parent class for my unit test, called NbTestCase (get the original source which also contains assertions for data structure sizes here) - it's actually a stripped down version of a class with the same name which is part of XTest. It has a method assertGC() which we can use to assert that an object is garbage collected.

The usage pattern is simple: Null out any references in the test itself, after storing the object in question in a weak reference:

SomeObject o = new SomeObject();
something.doSomethingWith (o);
WeakReference ref = new WeakReference (o);
o = null;
assertGC ("Object still referenced", ref);

assertGC() forces the system to garbage collect several times, and if the object is still referenced from somewhere else, it shows what's holding on to it. The source is and INSANE lib are attached. So let's write our test:

public class MyFrameTest extends NbTestCase {
    
    public MyFrameTest(String testName) {
        super(testName);
    }

    MyFrame frame = null;
    protected void setUp() throws Exception {
        frame = new MyFrame();
        frame.setVisible (true);
        Thread.currentThread().sleep (1000);
    }

    public void testLeak() throws Exception {
        System.out.println("testLeak");
        JPopupMenu popup = frame.getPopupMenu();
        popup.show (frame, 0, 0);
        Thread.currentThread().sleep (1000);
        popup.setVisible (false);
        
        WeakReference ref = new WeakReference (popup);
        popup = null;
        assertGC ("Popup menu should have been collected", ref);
    }
    
    public static Test suite() {
        TestSuite suite = new TestSuite(MyFrameTest.class);
        
        return suite;
    }
}

This is where it becomes clear just how useful INSANE is - it gives us an actual reference graph, so we know precisely where the leak occured:

Testcase: testLeak(javaapplication7.MyFrameTest):	FAILED
Popup menu should have been collected:
private static java.awt.Component java.awt.KeyboardFocusManager.focusOwner->
javaapplication7.MyFrame@d1e89e->
javaapplication7.MyFrame$MenuAction@f17a73->
javax.swing.event.SwingPropertyChangeSupport@3526b0->
javax.swing.event.EventListenerList@3ddcf1->
[Ljava.lang.Object;@105c1->
javax.swing.JMenuItem$1@f74864->
javax.swing.JMenuItem@110003->
javax.swing.JPopupMenu@627086

So, our leak is simple: When you call new JMenuItem (someAction), the JMenuItem adds a property change listener to the action, to enable/disable itself. The listener is never removed, so each time a popup is shown, it hangs around as the parent of a JMenuItem which is held in the list property change listeners on the Action.

Now, I can think of about five ways to fix this bug, from obvious and straightforward to silly. Which way would you do it?

Postcards from Brazil

Posted by timboudreau on April 14, 2005 at 05:26 PM | Permalink | Comments (9)

I've spent the last week in Brazil, doing some talks about NetBeans, how to write plug-ins, etc. What a lovely country! And so much enthusiasm for Java, open source and NetBeans!

nbInBrazil.jpg

Having all this time in airports and planes has given me a chance to work on the new editor hints plug-in for NetBeans. This module provides unobtrusive features in the editor that let you add casts, implement methods, lots of cool stuff, while staying mostly out of the way. It when my colleague Jan Lahoda and I both wrote plug-ins to do this sort of thing on the same weekend. So we banged the results together, and started getting more contributions from another community member - so other than merge conflicts because we've all been working fast and furious on it, it's coming along swimmingly. We hope to make it available on the update center soon; with this sort of thing once you lose trust, it's gone forever, so we want to make absolutely sure it's generating good code and not offering to do dumb things before making it available. If you're brave and have a checkout of NetBeans, you can find it in contrib/editorhints and contrib/editorhints/java.

edHints.png

As I said, Brazil is lovely - the climate reminds me of visiting my grandparents in Jackson, Mississippi as a boy - but maybe a bit hotter. And I've met a huge number of Java developers who have been wonderful to talk with and work with - the enthusiasm down here is infectious! And fortunately, there are wonderful beverages to cool off with - just bore hole and insert straw:

coconuts.jpg

I also met Dalibor Topic, who works on Kaffe - and he managed to get Kaffe working from NetBeans (they still need to finish implementing Swing, so you can't run NetBeans itself there - but you can install it as a "platform" and use it as the JVM you run/test in if you want).

Should there be a "codeforager.org"?

Posted by timboudreau on April 05, 2005 at 08:53 PM | Permalink | Comments (13)

(pictures in the blog are from Grenoble, France, where I spent last week helping some folks with NetBeans module projects)

A few months ago, I had an idea to write a newsreader plugin for NetBeans. So I looked around for a handy JavaMail NNTP provider. There was one out there, part of a project called Knife, which pointed me at ClasspathX, where I downloaded the sources, tried to build it (building Java with Make...ick), found I needed another library...which needed another library...and the whole thing seemed to be set up to cause maximum pain to anyone trying to just build with a standard Sun JDK - after all, I wasn't setting out to redistribute, just to test my code.

alps2.JPG

Anyway, distractions leading to distractions, I remembered that a defunct Mozilla project, Grendel, contained an NNTP provider - and plus it would be semi-license compatible with NetBeans.

So I checked out the source for Grendel, and put some work into splitting out the NNTP pieces. It's old code (pre-Collections), but basically sound, and written by some fairly respectable folks like Jamie Zawinski.

So in my spare time, I've got it working, added support for posting, written a ton of JUnit tests for it. Not finished, but I'd like to contribute it somewhere.

But the point is - how many other dead projects, open or closed source, out there, contain good code, if it were just librarified? I'd love to see a site for such things, and encouraging people to do this sort of task, providing guidelines and a place for them to live. The guts and glory might be in writing something from scratch - most developers prefer to, for better or worse. But there would be tremendous value in "liberating" some of the nifty and valuable things that are out there.

SunOffice.JPG
Who wouldn't want to work in an office with this view?!


Wicket - help test it!

Posted by timboudreau on April 05, 2005 at 10:04 AM | Permalink | Comments (4)

The folks doing Wicket, possibly the cleanest solution to POJO web apps that I've seen is at release candidate phase - they're looking for feedback. Even if you can't give them feedback, this framework is really worth a look.

The two things I find incredibly cool about it are:

  • Both the HTML and Java code needed are as simple as the things they're supposed to do
  • A real component jar - new components can be dropped in as jar files with an absolute minimum of futzing around

Have a look at the Hello World and other examples on the Quick Tour. This is excellent stuff.

The original author of Wicket is my friend Jon Locke - we had our first startup together, selling sector editors in sandwich baggies when we were 13 or so. He's worked on Win32 early on at Microsoft, then worked on AWT and Swing early on at Sun, and since has done a variety of startups. This is a guy who really knows how to do clean design.

But don't just listen to me - check it out. There was also an interesting discussion when the project was first launched on the server side.



Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds