Developing a JNI library involves multiple files:
- A Java file (and its .class file) that defines a
class to declare, load, and interface to the native library
- A Native C header (.h) file that declares the
native library methods
- A Native C code(.c) file that implements the
native library methods and contains the actual native code
- A Native library file loaded by Java (.dll on
Windows®, .so on AIX® and Solaris™)
The steps involved in creating a JNI library are the same for all
platforms with some minor, yet significant, differences:
- Create a Java source (.java) file that will interface to the
native library. The class must contain the following:
- One private method declaration for each native method in the library.
Use the native keyword in each declaration and no body. These are
abstract methods, the body code of which is provided by the library.
- One or more public methods to access the native methods
- A static block to load the native library file. The library can be
loaded explicitly, but the static block is most common. Below is an
example of NativeHello.java.
How to specify the library name in System.loadLibrary()is
explained in Step 5.
public class NativeHello {
static {
System.loadLibrary( "NativeHello" );
}
public void hello() {
nativehello();
}
private native void nativehello();
}
- Compile the Java source with the javac command to produce a
.class file.
For example: javac NativeHello.java
- Run javah against the .class file created in Step 2.
This creates a C header file (NativeHello.h). The header file
declares the same native methods in C format using standard JNI syntax.
For the NativeHello example above, use:
javah -jni NativeHello
This creates the following header file:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class NativeHello */
#ifndef _Included_NativeHello
#define _Included_NativeHello
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: NativeHello
* Method: nativehello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_NativeHello_nativehello(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
- Create a C code file (NativeHello.c) to implement the actual
native methods. Below, the Java_NativeHello_nativehello method
has been implemented to write "Hello World...." to
stdout. Note that this file includes the .h header file
created in the previous step.
#include "NativeHello.h"
#include <stdio.h>
/*
* Class: NativeHello
* Method: nativehello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_NativeHello_nativehello
(JNIEnv * env, jobject jobj) {
printf( "Hello World...." );
}
- Compile and link the C file into a library file. The commands to
compile and link are dependent upon the operating system platform. The
Java method, System.loadLibrary(), requires that the library
files conform to a platform-dependent naming convention.
For Unix® (Solaris and AIX), the name must start with
lib and end with .so.
For Windows, the name must end with .dll. For our
NativeHello example, the library name must be
libNativeHello.so for Unix and NativeHello.dll for
Windows.
- For AIX, an export file (NativeHello.exp) is created
to declare the library's public methods, one name per line. For example:
#! /home/sharelib/shrsub.o
Above is the full path to share the library object file:
Java_NativeHello_nativehello
Then create a shell script file (mkhello.sh) with the
following two commands to compile and link the library
(libNativeHello.so):
rm libNativeHello.so
cc -I/usr/jdk_base/include -I/usr/jdk_base/include/aix -o \
libNativeHello.so -bE:NativeHello.exp -bnoentry -bM:SRE NativeHello.c
- For Solaris, according to Essential JNI, the following
command line compiles and links the library (libNativeHello.so):
cc -I$JDK_HOME/include -I$JDK_HOME/include/solaris -G \
-o libNativeHello.so NativeHello.c
- For Windows, using Microsoft® Visual C++® V6.0, a
mkhello.bat file was created to compile and link the library
(NativeHello.dll):
set lib=d:\vc60\vc98\lib
cl /GD /LD /Id:\vc60\vc98\include
/Id:\WebSphere\AppServer35\jdk\include \NativeHello.c
- Writing and creating a JNI library module is the same, whether you
access it from a command-line Java program or from a Servlet. The most
common error associated with accessing a library is
"java.lang.UnsatisfiedLinkError". There are two reasons for this
error; either the Java virtual machine (JVM™) cannot find the library
file, or a method within the library cannot be found.
There are two key things to ensure that the JVM can locate the library
file:
- The library file name must follow a system-dependent convention (Step
5 above).
- The JVM must be able to locate the library's directory. The JVM
library search path is set through an environment variable:
For AIX, the variable is called LIBPATH; each directory
entry separated by a colon.
For Solaris, the variable is called LD_LIBRARY_PATH; each
directory entry separated by a colon.
For Windows, the variable is called PATH; each directory
entry separated by a semi-colon.
In WebSphere Application Server, these environment variables can be set
either from the AppServer > General > Environment window, or
in the WebSphere Application Server startup script, if applicable.
For WebSphere Application Server V3.02 on Windows, the library directory
must be included in the nanny.path in the admin.config
file.
If the JVM successfully locates the library
file, you might still get "java.lang.UnsatisfiedLinkError" if it
cannot find a desired method in the library. This is probably caused by a
name mismatch. When javah (Step 3 above) creates the header
(.h) file, it "mangles" the method names to include Java class
and package names. One common reason for a mismatch is when the package or
class name changed since the library was last built or if the .C
or .H files were not updated to match such changes.
You can use the following Java file (TestHello.java) to test the
example library from the command line:
public class TestHello {
public static void main( String[] args ) {
NativeHello myhello = new NativeHello();
myhello.hello();
}
}
You can use the following Java Servlet (TestHelloServlet.java)
to test the example library from WebSphere Application Server. "Hello
World...." is output to the stdout.txt file.
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.*;
public class TestHelloServlet extends HttpServlet {
public void doGet( HttpServletRequest req,
HttpServletResponse resp )
throws ServletException, IOException {
NativeHello myhello = new NativeHello();
myhello.hello();
PrintWriter out = resp.getWriter();
out.println( "<HTML><BODY>" );
out.println( "Hello written to stdout.<bR>" );
out.println( "</BODY></HTML>" );
}
}
For a comprehensive, detailed tutorial, refer to Essential JNI by
Rob Gordon, published by Prentice Hall. This book gives a brief history of
JNI as well as a good explanation of how and why file method names are as
"mangled" as they are.
|