Nimbus is getting a new home

December 17th, 2007

I’ve already mentioned that the upcoming Nimbus look-and-feel is not being hosted in a usual location (javax.swing package). While at first the NimbusLookAndFeel class was in a sun.swing.plaf.nimbus package, it has now been moved to a new home – com.sun.java.swing.plaf.nimbus, whose parent package also hosts GTK, Motif and Windows look-and-feels.

Bug 6616742 is the reason – Nimbus was not available in unsigned applets due to class loading restrictions on internal sun.swing packages. It also fixes 6618285 – using the NimbusLookAndFeel class directly in the source code.

The threading rules in Swing are one of the main reasons for the perceived slowness of Java desktop applications (it remains to be seen whether JavaFX will address this issue for anything other than toy demo applications that have started appearing lately). This article by John Zukowski is an excellent overview of the threading rules in Swing since its inception, and how to write applications that do not break the thread safety.

All different EDT-related rules pretty much boil down to one simple thing – anything that affects pixels on the screen should be done on EDT. However, these rules are all concerned about the where and not the when. And the when is very important. Here is the rule all Swing applications should abide by:

Event listener logic that affects pixels on the screen should schedule its execution after the current event has been processed by all registered event listeners.

The reason for doing this is simple – your application listener is not the only one that is registered for that specific event type. You might think that it’s the only one – after all, you know your application code inside out. However, there is much more going on “under the hood”, and that depends on the look-and-feel you’re using.

The look-and-feel doesn’t have any magic way of tracking the application state. Since the UI delegates use the same event / listener mechanism to track changes to control models, they rightfully expect the application logic to not interfere with the component state while the current change is being processed.

A simple example – application logic registers an action listener on a button. The action listener logic changes the UI state of the application (transitions to the next screen of the wizard, for example) and hides this button. After this action listener is done, the next action listener is called. What does it see? Surprise surprise – the button is no longer in the same state as it was when it was actually pressed.

The solution is very simple – wrap your action listener logic in SwingUtilities.invokeLater. I know, it makes the code ugly. I know, it makes the code go so much to the right side of your editor that you start hating Swing and contemplate switching to VB. But it is simply the right thing to do. You wouldn’t want any other listener to do that to the event source component, so why are you doing it yourself?

As i was looking through the folder structure of the latest Mustang Update N, a thought occurred to me to look inside the rt.jar. There are some interesting things there, and this entry will look at the class names of Nimbus look and feel.

The Nimbus classes are located in the sun.swing.plaf.nimbus package, and as Jasper already mentioned, most of the code is generated from a designer tool. While the source code of Update N is not yet available, you can still see some quirks of the auto-generated code. Let’s run a simple utility that prints out the top ten list of JRE classes with the longest names:

92:InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState
91:InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneIconifyButtonWindowNotFocusedState
91:InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowMaximizedState
89:InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneCloseButtonWindowNotFocusedState
88:InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMenuButtonWindowNotFocusedState
78:InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonPainter
77:InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneIconifyButtonPainter
75:InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneCloseButtonPainter
74:InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMenuButtonPainter
61:java_beans_beancontext_BeanContextSupport_PersistenceDelegate

Restricting this list to public classes only (classes with public modifier, which can still be in internal sun packages), we get

78:InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonPainter
77:InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneIconifyButtonPainter
75:InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneCloseButtonPainter
74:InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMenuButtonPainter
53:OptionPaneOptionPaneMessageAreaOptionPaneLabelPainter
45:MemberSubmissionAddressingWSDLParserExtension
40:ContentHandlerAlreadyRegisteredException
40:SQLIntegrityConstraintViolationException
40:SocketFactoryContactInfoListIteratorImpl
39:SpinnerSpinnerFormattedTextFieldPainter

Obviously, the class name length has nothing to do with the functionality itself. It just shows how easy it is to overlook something when you’re auto-generating code.

Going a little back to Mustang update 2, the first list looks like this:

61:java_beans_beancontext_BeanContextSupport_PersistenceDelegate
59:javax_swing_tree_DefaultMutableTreeNode_PersistenceDelegate
52:javax_swing_DefaultComboBoxModel_PersistenceDelegate
50:javax_swing_border_MatteBorder_PersistenceDelegate
48:java_util_AbstractCollection_PersistenceDelegate
48:javax_swing_DefaultListModel_PersistenceDelegate
47:UnsafeQualifiedStaticCharacterFieldAccessorImpl
47:java_awt_GridBagConstraints_PersistenceDelegate
47:java_awt_font_TextAttribute_PersistenceDelegate
46:javax_swing_ToolTipManager_PersistenceDelegate

and the second list looks like this

40:ContentHandlerAlreadyRegisteredException
40:SQLIntegrityConstraintViolationException
40:SocketFactoryContactInfoListIteratorImpl
38:DOM2DTMdefaultNamespaceDeclarationNode
38:FormatFlagsConversionMismatchException
38:TaggedProfileTemplateFactoryFinderImpl
37:Canonicalizer20010315ExclOmitComments
37:Canonicalizer20010315ExclWithComments
37:IEEE754FloatingPointEncodingAlgorithm
37:RelationServiceNotRegisteredException

So, what is your best example of the longest Java class name that you have ever seen? Can anybody beat 92 characters? .NET comes quite close with 86 characters :)

It looks like anywhere you go in the Java blogosphere, people are only talking about Java 6 (or lack of thereof) on Leopard. Some say that the only reason they bought Leopard was for Java 6, some say that their honeymoon with Apple is over, and some say that Java 6 will be available shortly as a separate download. And 99% of the postings and the comments seem to agree – Java 6 should have been included in the golden master of Leopard.

I thought about it over the weekend, and time and time again i reach the same pragmatical conclusion – i don’t care about Java 6 on OS X, and for that matter i don’t care about OS X as a Java development platform. While that may sound as a harsh statement, allow me just a few minutes of your time.

I’ve already written that for me, an operating system is just another layer in my development platform. I’m more interested in the tools that i’m using and the applications that i’m developing. The things that i said 30 months ago are still true – i’m perfectly OK with my Windows machines, because i rarely directly interact with the operating system. I might have switched to Linux, but pragmatically speaking, it is not a good business proposition. Between 150$ for the OEM version of Vista and spending 12 hours to download and install OS and configuring the Java environment (on my free time during the weekend away from the family), i choose Vista any given day.

Now, let’s talk some numbers. The market share for Windows is somewhere in 90-95% range, and while sales of Mac machines rise on the quarterly basis, so do sales of Windows boxes. One might say that these numbers are for all the machines, including corporate environments and home machines for non-IT people. I’m looking at the visitor stats of this blog, which is has a very specific technical orientation, and the numbers are a little surpising:

  • Windows has 75.6%
  • Linux has 14.4%
  • Mac has 9.6%

Yes, even for such a narrowly oriented site that mainly talks about Swing, Java2D and other UI topics in Java land, Mac doesn’t even cross into double digits. While you do see most of JavaOne presenters use Mac laptops, it doesn’t really extrapolate to the wider audience of JavaOne attendees, and most certainly it doesn’t extrapolate to the much wider audience of Java developers. The same applies to the blogosphere numbers – i’m sure i have seen about 20-30 rant entries on this subject over the past three days. Let’s be generous and say it’s an even hundred. Is that a lot? It is a lot of noise, but compared to the number of blogs tracked by JavaBlogs (2261) it’s not that much.

Looking at the bug reports for my open-source projects over the past three years, i see only two Linux-specific bugs and one Mac-specific bug. Yes, about 500 bugs reported via the bug trackers, forums, mailing lists and direct mail, and only one Mac-specific bug. Of course, most of these bugs were cross-platform, but so have been the bug fixes.

Now, your numbers might be different. If you’re developing a commercial product that is targeting multiple OSes, you might not be in a position similar to mine. However, if you spend more money to provide support for Mac than what you make from product sales on Mac, that is a bad business proposition. In this case, you might as well say that your product is for Windows only (like the vast majority of all the applications out there) and don’t mention that it’s in Java (so you don’t get blog-flamed about lack of cross-platform support). Of course, in this case you might as well switch to Win32, MFC, WinForms, WPF or whatever technology is pimped that year, but that is a topic for another blog entry.

This is, of course, my personal subjective opinion. I never had a Mac, and this fact alone might severely skew my judgement. But on the other hand, i never had to have a Mac as a Java developer. And this most certainly doesn’t change now with Leopard. Windows (in its various flavors) was and remains my only Java development platform (both at work and at home), with Linux and Mac delegated to what they really are at this point in time – alternative OSes with minor penetration to be installed for platform-specific bug fixes. Will this change in the future? I’m a pragmatic person, so i don’t say “no”.