4

It seems like Java for OS X 2013-004 (Java 1.6.0_51) breaks some Swing applications: https://discussions.apple.com/message/22279872?tstart=0#22279872?tstart=0 http://www.mathworks.com/matlabcentral/answers/79489

My app loads, but it doesn't repaint normally. Clicking on buttons and other operations actually seem to work, but the display only gets updated if the frame is resized - really bizarre.

Does anyone have any idea what the problem could be?

Edit: Seems like Apple fixed it: http://lists.apple.com/archives/java-dev/2013/Jun/msg00055.html

Peter Tseng
  • 13,613
  • 4
  • 67
  • 57

3 Answers3

3

Update 2013-06-21: this answer contains some workarounds and alternatives that may be useful, but @sidney-markowitz-biomatters' answer contains the correct code fix - the LAF needs to be set from the event thread!

The recent problems seem to be related to the updates breaking the Aqua Look and Feel (LAF), which is the default for Swing apps on Mac OS X.

If you need the Aqua LAF then there are not too many options. You might need to wait for the next Java update from Apple (I'm assuming they will fix this with priority, given it's their own LAF). You could also try using the Java Application Bundler (i.e. bundle the Oracle JRE and avoid using the system's JRE).

If you can get by with a different LAF then your app should work as normal. It did for PaperCut, at least (the 003 update caused some window focus problems, the 004 update caused mayhem).

Some options:

  • Using the Java version-specific cross platform LAF from Java code (e.g. Nimbus or Metal):

    UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName())
    
  • Setting a specific LAF from Java code:

    UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel")
    
  • Overriding the default LAF from terminal:

    java -Dswing.defaultlaf=javax.swing.plaf.nimbus.NimbusLookAndFeel MyApp
    

In our case we were explicitly calling UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()) in our code and wanted a workaround that didn't involve a code change (i.e. a hotfix), so we needed to override the default system LAF as follows.

  • Overriding the system LAF from terminal:

    java -Dswing.systemlaf=javax.swing.plaf.nimbus.NimbusLookAndFeel ...
    
  • Overriding the system LAF from an Info.plist file (if you have bundled as a Mac application, also works for the other VM options) (e.g. at My.app/Contents/Info.plist).

    You want to add -Dswing.systemlaf=javax.swing.plaf.nimbus.NimbusLookAndFeel to the end of the <string> value for the VMOptions <key>. The options are space separated, just like from the terminal. E.g. if you already have a useScreenMenuBar option:

    <key>VMOptions</key>
    <string>-Dcom.apple.macos.useScreenMenuBar=true -Dswing.systemlaf=javax.swing.plaf.nimbus.NimbusLookAndFeel</string>
    


Edit: @trashgod asked for a reproducible example. I'm not sure what the full scope of the problems with the 004 update are, but here's a simple reproduction:

Update 2013-06-21 - the wrong way, reproducing the error:

public class AquaLafTest {
    public static void main(String[] args) throws Exception {
        javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName());
        javax.swing.JOptionPane.showMessageDialog(null, "msg");
    }
}
  1. Run with the Apple JRE that comes with the 004 update (e.g. at /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home). Observe that the message is not visible, the dialog icon is not visible and the button is not visibly clickable.

  2. Run with an older Apple JRE or another JRE. Observe that the dialog displays as expected.

Update 2013-06-21 - the right way, on the event thread, works correctly:

public class AquaLafTest {
    public static void main(String[] args) throws Exception {
        javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
            public void run() {
                try {
                    javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName());
                    javax.swing.JOptionPane.showMessageDialog(null, "msg");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}
Community
  • 1
  • 1
Tom Clift
  • 2,365
  • 23
  • 20
  • I haven't been able to reproduce this effect. Can you cite a reference or [sscce](http://sscce.org/) that exhibits the problem you describe? – trashgod Jun 20 '13 at 11:30
  • Interesting, I'm using Aqua, so I'll see if changing the look and feel has an effect. But we can't release using Nimbus L&F, so I might have to bundle the JRE into the app, which is recommended by Oracle anyway. – Peter Tseng Jun 20 '13 at 17:46
  • +1 for update; see also [*Initial Threads*](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html). – trashgod Jun 21 '13 at 07:47
2

You used to be able to get away with calling UIManager.setLookAndFeel() from outside of the Swing thread. It is probably pretty common to start out an application by setting the look and feel with that call. The Swing API docs say that the only Swing methods you may call from outside that thread are the ones labeled as thread-safe in the method's JavaDoc. setLookAndFeel is not labeled thread-safe. Since it doesn't involve displaying anything when it is called and it has always just worked, I'd bet that many people think of it as a way to set some configuration settings before actually doing anything GUI, and simply call it once and forget about it.

Try wrapping the call to UIManager.setLookAndFeel the same way you do with all other Swing methods to ensure it runs in the Swing thread (sometimes referred to as the dispatch or UI thread). Also look for other Swing methods that you call outside the Swing thread in case this update has made other methods more picky in terms of their thread-safety.

[added example code]

@tom-clift added to his answer excellent examples showing failing code and properly working code. I am updating this answer to include a snippet of his example to show what you should replace your call to setLookAndFeel with. Do the same to with any other calls to UIManager methods, for example if you set specific properties in it.

    javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
        public void run() {
            try {
                javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
  • Excellent! Thank you. I'm going to edit my answer to point to yours as the correct answer, but I'd also like to add a reproduction to show how it should be done (on the event thread). If you would prefer to add this to your answer instead then let me know. – Tom Clift Jun 21 '13 at 01:35
  • @TomClift, We have some utility classes we use to make it easy to wrap a Swing call to ensure it is in the proper thread. I assume that most people writing Swing applications have something similar. If you have a simple standalone example of properly calling setLookAndFeel, go ahead. BTW, when I fixed our application I was extra cautious and included the call to javax.swing.UIManager.getSystemLookAndFeelClassName() in the wrapper. As for a reproducible test case, I haven't figured out a minimum case that is guaranteed to fail, some simple ones only sometimes fail, our real app always did. – Sidney Markowitz - Biomatters Jun 21 '13 at 01:59
2

This issue has been resolved by Apple releasing a revised version of these updates: http://support.apple.com/kb/DL1572 (10.7, 10.8+) http://support.apple.com/kb/DL1573 (10.6-only)

swingler
  • 111
  • 2