when write once run anywhere doesn’t

Java has famously tried to sell the world of software development on the idea of Write Once Run Anywhere (WORA). Unfortunately that doesn’t work in practice, especially when you’re writing GUI applications using the stock Java release. I was reminded of this yet again when I tried for something very basic; code that would run on all platforms and return an icon resource, in this case a close button that would work across all platforms and look like it fit in with the rest of the environment.

I went looking for a close icon I could use on a button that matched the given look-and-feel. I used the following call to UIManager:

UIManager.getIcon("InternalFrame.closeIcon")

It worked beautifully on Windows 8.1, and Windows 7. I was happy until I tried to run it on three different Linux distributions, and two versions of one of the three. Instead of getting a reasonable displayable icon, I got nothing. Not a null, not an exception, nothing. Specifically, I got a transparent icon that didn’t show up on the Java UI, but never the less took up the required space. When this first happened I thought my code was at fault, until I wrote a very short routine to dump all the internal look-and-feel references and what they returned, as strings. For Windows, I got the following:

InternalFrame.closeIcon = com.sun.java.swing.plaf.windows.WindowsIconFactory$FrameButtonIcon@4f626d85

For Linux (specifically CentOS (RHEL) 6.5) I got the following:

InternalFrame.closeIcon = javax.swing.plaf.basic.BasicIconFactory$EmptyFrameIcon@492ee972

Yes, I got an EmptyFrameIcon. I went looking to see if I could find anything under all the locations that Linux likes to store icons, but found nothing I could consistently use. In the end I went back to drawing on the button face (which I’ll give an example of shortly), since all I wanted was essentially the ‘X’ that appears all over.

At this point in time (2014) it’s far too late to raise holy hell with Linux, which is where the blame essentially belongs. Instead, it goes to cement once more how painful it is to do Java UI development under Linux, unless you devote a large amount of time or use a third-party look-and-feel library like JGoodies. For the time being I’ll role my own as what I need is rather minor. But for major Java UI applications in the future I’ll more than likely wind up using JGoodies in my applications. And before you ask, there are still a lot of applications that (1) need something more substantial than JavaScript and the web, and (2) today’s Java (7 and 8) is as performant as anything else you care to develop in. Maybe more so. Take your biases and move along…

setting native desktop look and feel with java

This morning I woke up still thinking in Java and wondering why I have to live with the positively ancient and hideous Metal Look-and-Feel (LaF). Turns out I don’t have to. As you can see above, I’ve managed to turn on the Windows 8.1 LaF on my simple test framework, with just a few extra lines of code. And I’ve re-discovered it’s portable across all the environments I want to run this on (at least, the ones I care about), primarily Windows 7 and 8 and various Linux distributions and desktops. But first, the code. This is a modification to the source file TabbedTables.java, first seen in the prior post.

private static void createGUI() {try {// Set system Java L&F (Windows for Windows, Gnome for Linux...)//UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());}catch (UnsupportedLookAndFeelException |ClassNotFoundException |InstantiationException |IllegalAccessException exception) {// silently handle exception}JTabbedPane tabbedPane = new JTabbedPane();ImageIcon icon = createImageIcon("images/image.gif");tabbedPane.addTab("Tab 1", icon, makeTable(), "Sample 1");tabbedPane.setTabComponentAt(0, new CloseTabControl(tabbedPane));tabbedPane.setMnemonicAt(0, KeyEvent.VK_1);tabbedPane.addTab("Tab 2", icon, makeTable(), "Sample 2");tabbedPane.setTabComponentAt(1, new CloseTabControl(tabbedPane));tabbedPane.setMnemonicAt(1, KeyEvent.VK_2);JFrame frame = new JFrame("Test tabs and tables");frame.setContentPane(tabbedPane);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setSize(800, 600);frame.setVisible(true);}

Lines 2 through 13 are the new lines added. If you’re lazy and don’t care about defensive programming practices then you can toss the try/catch block and just copy lines 5 and 6 and be done with it. But I prefer to write a little more robustly if possible, so that try/catch block is a very small price to pay. I believe in quality code, especially in the final shipping product. In the process of adding this code block, I also removed a line of code; JFrame.setDefaultLookAndFeelDecorated(false);  . This line is no longer needed.

This code block also illustrates an interesting feature of NetBeans 8. The catch block (lines 8 through 13) is a multiple exception catch block that was introduced in Java 7. In earlier versions of Java you needed to write separate catch blocks for every exception, an onerous task, even if you had an IDE that would automatically assist you in creating them all. Once written you had all those individual catch blocks to maintain into the future. It’s no wonder that a lot of Java programmers eschewed writing try/catch unless it was absolutely necessary (as in, javac refused to compile the code). While this doesn’t remove all the pain, multiple exception catch makes it a lot easier to write, and in particular, easier to read in the future.

How does NetBeans 8 fit into this? I was given a recommendation by the NetBeans 8 IDE, and when I selected that from its dialog, it automatically reformatted the code for me. This is one key reason why a good IDE like NetBeans or IntelliJ IDE are at times vital to fast and accurate Java programming. Those tools support you in writing up-to-date compliant code, and teach you a few things along the way.

Here’s a few screen captures of the exact same code running on Linux. Both were running in VMware. The top screen capture is CentOS 6.5 (equivalent to RHEL 6.5) running Gnome 2, and the bottom is Linux Mint 15 running its Cinnamon desktop. Of note are the tabs. They don’t look quite right, and that’s because of the “hand-drawn” close button icon. I need to dig a little deeper and learn how to use a given LaF icon, using the “hand drawn” version when I can’t find it. That will make the tabs look a lot better. But for the time being, this is Good Enough.

NOTE: All my Linux installations (hardware and virtual) are now running Java 8. Even my little Raspberry Pi.