Java Glossary : JNI

CMP home Java glossary home Menu no menu Last updated 2004-06-28 by Roedy Green ©1996-2004 Canadian Mind Products

Java definitions: 0-9 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

You are here : home : Java Glossary : J words : JNI.

JNI
Java Native Interface. How you write C/C++/assembler code native methods callable from Java. It also lets C/C++ code call Java methods. Microsoft J++ does not support JNI. Before you get too deeply into JNI check out the exec function. Also check if JConfig has what you need. It may do what you want with much less hassle. Also check out XFunction which lets you do JNI without writing any of that complicated JNI C code.

Introduction

Java Native Interface is how you write C/C++/assembler code native methods callable from Java. It also lets C/C++ code call Java methods.

The Overall Process

  1. If you need to use JNI from an Applet, order a certificate well in advance. See Signed Applet.
  2. Write a Java class containing a native method something like this Glue.java:
    // com.mindprod.JNIexper.Glue.java
    package com.mindprod.JNIexper;
    import java.io.*;
    public class Glue
       {
       static
       {
          // load Glue.dll containing Glue.nativeWidthInBits
          System.loadLibrary( "Glue" );
       }
       public static native int widthInBits( int n );
       } // end Glue
    
  3. Compile Glue.java. This step is very important. You must have a clean compile before using javah.
  4. Generate the Glue.h header file containing the prototypes of the C/C++ methods you must write with:
    javah.exe -jni -o Glue.h com.mindprod.JNIExper.Glue
    
  5. Write a C or C++ class something like this Glue.c
    // com.mindprod.JNIexper.Glue.c
    #include <windows .h>
    /* set Options | Project | Directory include path to:
    J:\j2sdk1.4.2_04\include\;error: unable to find file E:\mindprod\jgloss\include\win3sdk.html to include.include\a\
    Make sure target expert says: DLL, Win32 console, static
    Project | Options | Linker | General is no debug */

    #include "Glue.h"

    /**
    * Wrapper C native method.
    * Calculate how many bits wide a number is,
    * i.e. position of highest 1 bit.
    * @return p where 2**p is first power of two >= n.
    * e.g. binary 0001_0101 -> 5, 0xffffffff -> 32,
    * 0 -> 0, 1 -> 1, 2 -> 2, 3 -> 2, 4 -> 3
    *
    * public static native int widthInBits( int n);
    *
    * Class: cmp_JNIexper_Glue
    * Method: widthInBits
    * Signature: (I)I
    */

    JNIEXPORT jint JNICALL Java_cmp_JNIexper_Glue_widthInBits (JNIEnv *env, jclass thisClass, jint n)
    {
    ...
    return n;
    } // end widthInBits
    // end Glue
  6. Link that C code and any assembler code it calls into a DLL. The dll may contain methods from many different classes.
  7. Pre-install the DLL on the browser's classpath, e.g. J:\j2sdk1.4.2_04\jre\bin\ext (for the JDK 1.4 plug-in), WINNT\java\trustlib (for Internet Explorer), Program Files\Netscape\Communicator\java\classes (for Netscape 7.02).
  8. Use the Java native method as if it were an ordinary Java method.

Some general JNI tips

JNI Tips When Using Assembler

JNI Manipulator Functions

JNI gives you opaque access to the Java objects. You never touch the Java objects directly, you always manipulate them via rather clumsy remote access methods. It is bit like being a blind brain surgeon using barbeque tongs. The advantage is your program never need know what the actual format of the objects is. It makes it much easier to write portable C/C++ code.

Useful JNI functions to Access Parameters
type get parm put parm release parm return
Unicode String
converted to 16-bit chars
counted
GetStringChars
GetStringLength
- ReleaseStringChars NewString
UTF-8 String
converted to 8-bit chars
null delimited
GetStringUTFChars
GetStringLength
- ReleaseStringUTFChars NewStringUTF
int use parm directly - - use local jint
int[]
copy access
GetIntArrayRegion
GetArrayLength
SetIntArrayRegion - NewIntArray
int[]
direct access
GetIntArrayElements
GetArrayLength
- ReleaseIntArrayElements NewIntArray
Object use parm directly - - NewObject
NewObjectA
NewObjectV
Object[]
copy access
GetObjectRegion
GetArrayLength
SetObjectArrayRegion - NewObjectArray
Object[]
direct access
GetObjectArrayElements
GetArrayLength
- ReleaseObjectArrayElements NewObjectArray
static field in Object GetStaticFieldID
GetStaticObjectField
GetStringUTFChars
GetStaticIntField
setStaticObjectField
SetStaticIntField
- -
instance field in Object GetFieldID
GetObjectField
GetIntField
SetObjectField
SetIntField
- -
callback static method FindClass
GetStaticMethodID
CallStaticVoidMethod
CallStaticIntMethod
CallStaticObjectMethod
- - -
callback instance method FindClass
GetMethodID
CallVoidMethod
CallIntMethod
CallObjectMethod
- - -
In the above table, whereever you see "Int", you can replace it with Boolean, Byte, Char, Short, Long, Float or Double. Note these methods do not follow Java capitalisation conventions.

Typical JNI C Code

Here is some typical JNI C code to open a file, that lets you access a Java string inside C.

JNIEXPORT jboolean JNICALL Java_mypackage_myclass_mymethod
( JNIEnv * env, jclass callerclass, jstring filename )
...
HANDLE handle;
// convert java 16 bit filename string to 8-bit style
char * filename8 ;
filename8 = ( *env) ->GetStringUTFChars( env, filename , NULL ) ;
if ( filename8 == NULL )
{
// fail
return 0 ;
}
// get handle to file
handle = CreateFile (
filename8, // identifies the file
GENERIC_WRITE, // access (read-write) mode
FILE_SHARE_READ||FILE_SHARE_WRITE , // share mode
NULL, // address of security descriptor
OPEN_EXISTING, // how to create
0, // file attributes
NULL // handle of file with attributes to copy
);
// free 8-bit version of string.
(*env )->ReleaseStringUTFChars ( env, filename, filename8 );

Using JNI in Applets

Using native methods in a Netscape Applet is a bear because even after you manage to defang the security manager with:
PrivilegeManager.enablePrivilege( "UniversalLinkAccess" );
to let you call your native methods, System.loadLibrary("mydll") and the undocumented security system, will insist that the *.DLL file containing the your native methods and all your classes be pre-installed on the client's machine. It refuses to look for them in the jar file or on the website where CODEBASE points. You are pretty well stuck making your Applet install the necessary DLLs and class files on the client's machine. Ouch! System.loadLibrary fails for some reason if the DLL was not present at the time Netscape fired up. The System.loadLibrary can't seem to see a DLL installed dynamically. This makes no sense since the DLL is not loaded until System.loadLibrary is called. Even more baffling is why System.load would show the same behaviour. I have fooled around with this for months and I still cannot get Netscape System.loadLibrary to behave reliably and predictably. Until that problem is solved, it may be practically impossible to use JNI from Applets.

I got word from Hannu Helminen that it is possible after all. He came across this JavaWorld Article. What is says basically that Netscape and native code do work together after all. He tried it out, and to his amazement the example indeed works! But hey, he did the same things that I did, where is the catch?

The idea is that first you download the native DLL and a class file to user's local hard drive. The class file has to be in the Netscape class path, thus it uses the system class loader. Also the DLL has to be downloaded and System.load()ed before the class is referenced for the first time. It appears that netscape does some kind of checking for the DLLs already when the class is loaded. I have not yet had time to check this out myself.

To make it worse, there are fourteen security bypassing schemes you have to deal with.

The Big Problem

Netscape won't let web-loaded Applets invoke DLL code, even if they have UniversalLinkAccess permission. Further, it won't let them use a custom ClassLoader to do it indirectly. You may bypass this with the undocumented MarimbaInternalTarget class. See this link for details. Your custom classloader must do a Class.getClass() first before attempting to fulfill the request itself.

The Solution

You don't need to deal with any of this security, installing and jar-signing stuff if you use an application instead of an Applet. I strongly suggest that approach whereever possible.

I have fooled around with this over a period of six months, chasing wild goose after wild goose, and have finally came to the conclusion, in agreement with Sun's FAQ, that JNI and Applets simply don't mix. There is simply no way to get sufficient security clearance to let you directly access the DLL from a web-loaded Applet, even if you write a custom ClassLoader. One problem with doing so many tests is I could have slipped somewhere along the line, thinking I tested two cases, when I actually tested only one. The problem is the way Windows/Netscape hold onto the old code. I have not even got the method I describe below to work. It may fail too. Netscape security may apply even if you load from local hard disk.

What you have to do is use a small signed installer Applet to install a second unsigned Worker Applet on the client's local hard disk. When that second Worker Applet runs, it is totally free of security restrictions, and so can access JNI DLLs. It behaves much like an application, except it runs under a browser.

You also have to install some html in that same local directory that will load the Worker Applet from the local hard disk. It would have CODE and ARCHIVE parameters, but no CODEBASE. It defaults to the local hard disk directory where the html file lives.

You have to install the DLL in C:\program files\Netscape\Communicator\program\java\bin, which is guaranteed to be on Netscape's Windows path, where Windows looks for DLLs.

You have to install the unsigned Worker jar file in C:\program files\Netscape\Communicator\program\java\classes. Netscape totally trusts classes it loads from the local file system, even if they are not signed and have no capabilities calls.

The Recipe

Just follow this recipe, if this discussion is making your brain hurt. The same technique will work for other platforms with the obvious substitutions. If you do understand it, you can create your own shortcuts.

In order to execute JNI methods from a Netscape Applet, create three jar files.

  1. installer.jar. When this signed jar is first executed, it installs the various files on the client's local hard disk, (intelligently choosing C: or D:). On subsequent executions, it notices the needed files are already installed and up-to-date and avoids that step. As soon as it has ensured it has installed the files, it uses getAppletContext().showDocument(url) to transfer control to the HTML it has downloaded on C: or D: The installer jar contains only the tools such as FileTransfer classes for help in downloading and installing the files. It does not contain any of the data to be installed. Keeping that out of installer.jar saves transferring that bulk when it is already installed.
  2. Worker.jar is for your class files that contain native methods, and the other classes you need to run the actual Applet. This jar should not be signed. If you sign it, it will slow class loading the code down. The Worker.jar will be embedded inside the toInstall jar, described shortly. The installer Applet will copy the Worker.jar to C:\program files\Netscape\Communicator\program\java\classes or perhaps D:. Thereafter, you could run it standalone via a bookmark, or you could run it via the original install.jar. The advantage of using the original install.jar is automatic updates, and automatic finding where the Worker.jar Applet is installed. The disadvantage is extra startup time and an extra annoying grant to run the program each time.
  3. toInstall.jar. This unsigned jar is just a container for the various filess you need to download namely:
    • An HTML file to invoke the actual Applet. Your installer Applet will install it in: C:\program files\Netscape\Communicator\program\java\classes, or perhaps D:.
    • Your DLL file containing the native C++/C/Assembler code. Your installer Applet will install it in C:\program files\Netscape\Communicator\program\java\bin, or perhaps D:.
    • Your worker Applet jar containing all the class files you will need to run the unsigned Applet. Your installer Applet will install it in C:\program files\Netscape\Communicator\program\java\classes or perhaps D:.

    If you use getResourceAsStream, you must use the goofy extension *.ram for resources inside your jar files because Netscape interferes with the extensions DLL, EXE, CLASS etc. If you access them via ZipFile that kludge is not necessary.

System.load vs System.loadLibrary

System.load takes a fully qualified filename, e.g. one ending in .dll. System.loadLibrary takes an unqualified filename, and appends the .dll for you. The idea is you can write more platform independent code this way. I have had more success with System.load. Check out the system property java.library.path to see where System.loadLibrary is looking for DLLs. You must explicitly load the corresponding DLL before using any class inside it. On windows that library path is supposed to contains: It depends on Applet or application, which browser and the phases of the moon what you will get.

Solaris

Make sure you put your jni shared object library on the LD_LIBRARY_PATH. If your use System.loadLibrary( "dog" ), then you must name your library file with your compiled C++ code libdog.so.

JNI and Assembler

To write the JNI code partly in assembler, there are two approaches: Microsoft C conventions return an int value in eax or a long in edx:eax. You can learn the register conventions by adding the /FA option to the project C++ settings and looking at the generated *.ASM code for C++ or C programs.

Learning More

The Java World JNI Overview is a good place to start. The definitive source of information is Sun's JNI pages which includes Beth Stearn's JNI tutorial and the JNI FAQ.

My essay has only scratched the surface. You must have a text book if you hope for any success with JNI.

book_coverEssential JNI, Java Native Interface
0-13-679895-0
Rob Gordon and Alan
amazon.com Barnes and Noble
amazon.ca chapters
amazon.co.uk amazon.de
book_coverThe Java Native Interface, Programmer's Guide and Specification
0-201-32577-2
Sheng Liang
Sun Microsystems. Does not cover Applet signing, or "obvious" JNI like accessing int parms, but he does explain many fine points well. A slim, indispensible, expensive book.
amazon.com Barnes and Noble
amazon.ca chapters
amazon.co.uk amazon.de
cxxwrap (generates JNI code) ¤ javah.exe ¤ JConfig (a JNI library) ¤ JNI++ (generates JNI) ¤ SWIG (generates JNI wrappers) ¤ UnsatisfiedLinkError ¤ XFunction


CMP logo
CMP_home
home
Canadian Mind Products CSS
HTML Checked!
ICRA ratings logo
mindprod.com IP:[24.87.56.253]
Your IP:[80.134.30.163]
You are visitor number 50309.
Please send errors, omissions and suggestions
to improve this page to Roedy Green.
You can get a fresh copy of this page from: or possibly from your local J: drive mirror:
http://mindprod.com/jgloss/jni.html J:\mindprod\jgloss\jni.html