Java On OSX

[ Start > PikeDevel > Java On OSX ] [ Edit this Page | Show Page Versions | Show Raw Source ]


The Java module in Pike is handy to have around, especially if you ever find yourself needing to do something that's not easy in Pike but simple in Java (there are a lot of java libraries out there). However, it's sometimes tricky to get the module enabled, especially so on Mac OSX.

Java on OSX has a long and complicated history, and the fact that Apple has effectively abandoned Java support means that there's little up-to-date information about how to get things working. The fact that Apple has a whole bunch of machinery designed to make Java work in a more Apple-like way is both nice and frustrating. This rears its head when you're trying to get embedded Java support working, for example when we want to use the Java module in Pike.

The problem

Unlike on most other OS where we might be using java, on OSX, the Java module links against the JavaVM framework. This framework isn't actually java, but a shim that will a) will select a version of Java that's been installed and pass control to that version of Java or b) if Java isn't installed, prompt the user to install Java. The framework effectively decouples the Java API from the java installation allowing easier management of multiple (or no version) of Java without producing linker errors.

If you run the java command, you'll notice that it automatically selects a version or prompts you to install one. If you look at where that command lives, it's a symbolic link to a command in the JavaVM.framework folder. However, this isn't really java, it's a stub that looks for the correct version and executes it.

Applications linked against the JNI (such as the Pike module for Java) work the same way. The JavaVM framework provides all of the JNI APIs but these are just stubs… when the JavaVM framework is loaded, it looks at all of the JVMs installed (they're in /Library/Java/JavaVirtualMachines) and picks the most suitable one for the job.

The problem is that Apple stopped shipping Java years ago, and handed the task of development to Oracle. You can still get an Apple JVM, but it's old (1.6) and unsupported. Unfortunately, the JRE and JDK shipped by Oracle while compatible with the JavaVM framework for certain capabilities (applets and the command-line tools), aren't set up to provide the features required for JNI. So, when your app loads the JavaVM[1] framework and attempts to fire up a JVM for JNI, it passes over the newer versions and will load up that old Apple VM (assuming it's been installed).

Obviously, that's not what we want, and it's rather confusing… it doesn't seem that Apple has documented this very well, if at all.

Fixing the problem

Luckily, all of the bits and pieces are available in the Oracle JDK (I'd assume the JRE as well, but I haven't tested that). We just need to do a little legwork.

The process consists of 2 steps.

  • Modifying the JavaVirtualMachine bundle to advertise the capabilities we need. That will cause the JavaVM framework to attempt to use Oracle's JVM.
  • Linking the java dynamic libraries to a location that the JavaVM framework expects them. This step is necessary because otherwise, the framework will select our newer JDK, but the dynamic loader will throw an error because the JDK installation doesn't put the libraries where they need to be [2].

So, here's what we need to do, assuming we've already installed JDK 1.8 update 131 from Oracle:

First, edit the PLIST file to add the required capabilities.

sudo vi /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Info.plist 

Look for the JavaVM -> JVMCapabilities section of the plist file. Add "JNI" and "BundledApp" to this array, like so:

<array> 
<string>JNI</string> 
<string>BundledApp</string> 
<string>CommandLine</string> 
</array> 

Next, create the location the libraries need to be, and link them there:

sudo mkdir -p /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/bundle/Libraries

sudo ln -s /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/server/libjvm.dylib /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/bundle/Libraries/libjvm.dylib

And that should be it. If your pike hasn't been compiled with Java support, you might need to re-compile and verify that it's found everything. Recent versions of Pike 8.0 should enable Java support assuming that you haven't moved any of the system libraries around.

You can verify that all is working as expected by checking to see that Java is an available feature when you run pike --info, and also the version reported by Java is correct:

$ pike

Pike v8.1 release 9 running Hilfe v3.5 (Incremental Pike Frontend)

> (string)Java.pkg.java.lang.System.getProperty("java.version");

(1) Result: "1.8.0_131"

[1] Technically, this task is performed by the private JavaLaunching framework, but that's a minor detail.

[2] There are, depending on where you get your JVM, actually multiple versions of the VM that are tuned for specific uses. We're assuming that our primary purpose will be for headless server-type applications, so we will go with the server-tuned jvm implementation.


Powered by PikeWiki2

 
gotpike.org | Copyright © 2004 - 2009 | Pike is a trademark of Department of Computer and Information Science, Linköping University