Tuesday, September 23, 2008

Be careful when setting java.ext.dirs to include your JARs

When I found out that you can set java.ext.dirs to link a directory of JARs to your program, I was very happy, as was this guy. Normal way of linking JARs:
java -cp lib/helloworld.jar lib/foo.jar lib/bar.jar lib/jar.jar lib/cookie.jar ...
Easy way:
java -Djava.ext.dirs=lib ...
It's so much easier than adding 20 JARs to your classpath right? I found out that (surprise, surprise) this approach can cause pain too.

Sure it will work most of the time, but it turns out java.ext.dirs is already set to a directory in your JRE, such as C:\Program Files\Java\jdk1.6.0_07\jre\lib\ext. Ext stands for extension, which you can read about at Sun, but the takeaway is that you probably want to append to it rather than replace it. The more correct usage:
java -Djava.ext.dirs=jarlibdir;"C:\Program Files\Java\jdk1.6.0_07\jre\lib\ext" ...
Just make sure you have the right ext path for the JRE you're using.

In case you're wondering, I was getting an exception "com.jcraft.jsch.JSchException: Session.connect: java.security.NoSuchAlgorithmException: DH KeyPairGenerator not available" when I replaced the extension directory with my own, starting my program from the command line. It seemed strange since it was working in Eclipse. I found out after a while of poking around that SunJCE was involved. And where are the SunJCE JARs? That's right, in jre\lib\ext, which I had unwittingly unlinked. The lesson I guess is to be aware of what you're doing when you take shortcuts.

Classpath is ignored when using java -jar

I have an application that I packaged in a JAR, and I needed to add a directory to the classpath so my logging library Logback could find the configuration file I created (using a wizard). So I figured I could just add it using -cp:
java -cp mydir -jar myjar.jar
But no, the classpath doesn't get set when you use the -jar option (you can check this by calling System.getProperty("java.class.path")). It turns out you have to add it to the manifest file when creating the JAR:
Class-Path: classpath/
Also notice the slash at the end. It's not optional if you want to specify a directory. Don't ask me why, but it will only work if you add the slash.

Sunday, September 14, 2008

Using reflection to integrate with the OS X application menu

It took a while for me to find this solution, so I figured I'd post it.

The Java Swing application I've been working on at Aspera had been using the com.apple.mrj and com.apple.eawt libraries to handle OS X events such as the user selecting About, Preferences, or Quit from the Application Menu. The MRJ library is actually deprecated, but I was using it to prevent the default dialog from popping, because I didn't know you have to call setHandled() on the event, but I digress.

Apple actually provides a pretty neat solution that allows you to use com.apple.eawt without breaking builds on other platforms. It uses reflection to make the Apple-specific com.apple.eawt calls, so your application doesn't have to import them.

To integrate with their code, you just have to add the OSXAdapter.java class from their sample to your application. Then register for the events you want to handle (see the registerForMacOSXEvents() method in the irexample file MyApp.java), and specify the method you want to get callbacks for that event on.

More on integrating Java (Swing/SWT) with OS X in Apple's guide.