I’ve worked with lots of embedding APIs for C in my life (Perl/XS, PHP, PostgreSQL/libpg, etc) and I really enjoyed one aspect of the Java JNI: the Sun guys made the wise decision of putting everything into just the jni.h header file and not even providing a corresponding libjni.a. All JNI API functions are part of a large environment data structure (as function pointers) passed to all Java native functions.
I don’t know whether it was intentional or just by accident. But this is an ultra portable way for loading extensions as Dynamic Shared Objects (DSO) as there are no symbols of the application the Unix Run-Time Link-Editor (RTLD) has to resolve for the loaded DSO. This is usually a portability issue when dealing with DSOs, although I guess mostly all Unix programmers never recognize. Nice done, Sun! I would like to see more DSO based embedding APIs which use this approach.
Unfortunately, the Sun guys at the same time made a nasty error: they have a companion jni_md.h header file which stays in a sub-directory of the Sun JDK and this sub-directory is named after the Unix platform. And they are not providing something like a pkg-config(1) jni.pc or at least a jni-config script which can be used to build against the JNI API without having to know anything about the actual installation of the Sun JDK. Not so nice done, Sun!
Also, I needed 17KB of code for binding OSSP uuid to Java, as much as I needed for binding it to PHP. Compare this with other bindings: 14KB for binding to PostgreSQL’s Pg/SQL and just 6KB for binding to Perl via Perl/XS. So, from this point of view JNI becomes no plus points from me. Here Perl/XS is really the most flexible, powerful and concise API. Although I’ve to admit that Perl/XS can be cruel, too. [Yes, I know SWIG, even have used it once, but I dislike such large hammers when I just have a needle to manage]
Finally, the JNI has one major design flaw IMHO: there is no way of attaching a C object to a Java object. Instead it seems one really has to convert the C object’s pointer (void *, of arbitrary size) into a long long C integral type (also of no fixed size) and store this into a Java long (guarranteed to be 64 bit). Sure, today this works on all Unix platforms, but it would have been wise if every Java object would just contain an additional “void *" one can use in JNI for attaching arbitrary C objects without having to rely on those unsafe or at least unclean conversions.