A simple example – the Y0 (x) Bessel Function routine s17acc |
According to the C Library Manual, the prototype for function s17acc looks like this:
#include <nag.h> #include <nags.h> double s17acc(double x, NagError *fail);The function takes two arguments. The first one is of type double and is the argument x of the Bessel function Y0 (x). The second argument to s17acc is the error handling argument fail which is of type NagError. See the NAG C Library Manual for more information about the NagError type; we need not be overly concerned with it here except to know that it allows us to get feedback regarding the correct operation of s17acc.
To keep things as simple as possible for this first example, we are not going to try to pass the contents of the NagError structure back to Java. Thus, in our Java program, we declare the function like this:
// Declaration of the Native (C) function private native double s17acc(double x);i.e. a method with a double argument, returning double. The native keyword tells the Java compiler that the method will be implemented outside Java.
System.loadLibrary("nagCJavaInterface");will search for a library named "libnagCJavaInterface.so", whereas under Microsoft Windows it will search for a library named "nagCJavaInterface.dll".
public class Bessel { // Declaration of the Native (C) function private native double s17acc(double x); static { // The runtime system executes a class's static // initializer when it loads the class. System.loadLibrary("nagCJavaInterface"); } // The main program public static void main(String[] args) { double x, y; int i; /* Check that we've been given an argument */ if (args.length != 1) { System.out.println("Usage: java Bessel x"); System.out.println(" Computes Y0 Bessel function of argument x"); System.exit(1); } // Create an object of class Bessel Bessel bess = new Bessel(); /* Convert the command line argument to a double */ x = new Double(args[0]).doubleValue(); System.out.println(); System.out.println("Calls of NAG Y0 Bessel function routine s17acc"); for (i = 0; i < 10; i++) { /* Call method s17acc of object bess */ y = bess.s17acc(x); System.out.println("Y0(" + x + ") is " + y); /* Increase x and repeat */ x = x + 0.25; } } }
The main program simply gets a value of x from the command line, and calls the native method using that argument and nine other arguments derived from it.
Now that we have written our Java program, which includes the native declaration of function s17acc, we can compile it with the following command:
% javac Bessel.java
If all goes well, the compiler should produce a file named Bessel.class.
% javah -jni Bessel
After this, you should have a file named Bessel.h which looks like this:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class Bessel */ #ifndef _Included_Bessel #define _Included_Bessel #ifdef __cplusplus extern "C" { #endif /* * Class: Bessel * Method: s17acc * Signature: (D)D */ JNIEXPORT jdouble JNICALL Java_Bessel_s17acc (JNIEnv *, jobject, jdouble); #ifdef __cplusplus } #endif #endif
Points to note about this header file:
JNIEXPORT jdouble JNICALL Java_Bessel_s17acc (JNIEnv *, jobject, jdouble);
The function is named Java_Bessel_s17acc, showing the Java class in which it is declared.
JNIEXPORT and JNICALL are defined via <jni.h>. They are used to alter calling conventions on some systems and need not concern us here.
#include <jni.h> /* Java Native Interface headers */ #include "Bessel.h" /* Auto-generated header created by javah -jni */ #include <nag.h> /* NAG C Library headers */ #include <nags.h> /* Our C definition of the function s17acc declared in Bessel.java */ JNIEXPORT jdouble JNICALL Java_Bessel_s17acc(JNIEnv *env, jobject obj, jdouble x) { double y = 0.0; static NagError fail; /* Tell the routine we want no output messages on failure */ fail.print = Nag_FALSE; /* Call the Y0(x) Bessel function s17acc */ y = s17acc(x, &fail); if (fail.code != 0) printf("Error: s17acc returned fail code %d for argument %g\n", fail.code, x); return y; }Points to note:
First compile the file BesselImp.c:
% gcc -c -fPIC -I/opt/jdk1.6.0_11/include -I/opt/jdk1.6.0_11/include/linux \ -I/opt/NAG/cll6a09dhl/include BesselImp.c
Note the -I switches telling the C compiler where to look for header files. The first directory mentioned, /opt/jdk1.6.0_11/include, must locate the jni.h header file. The second directory, /opt/jdk1.6.0_11/include/linux, is machine dependent and is needed by jni.h to find type definitions. At least the linux element of this name will alter depending on your machine type. The third and fourth include directories should point to the location of the NAG C Library header files installed on your system.
When BesselImp.c has successfully compiled, turn it into a shareable object with the command
% ld -G -z defs BesselImp.o -o libnagCJavaInterface.so \ /opt/NAG/cll6a09dhl/lib/libnagc_nag.so -lm -lc -lpthread
The -G flag means create a shareable object. The -z defs flag means fail to link unless all symbols are resolved at link time. This flag is not strictly necessary, but it can avoid egregious "failed to load library" Java run-time messages (egregious because the messages may refer to libnagCJavaInterface.so when the problem really is due to something else needed by that library). The -o flag names the shareable library as libnagCJavaInterface.so, the name needed by the LoadLibrary() call in our Java code. Finally, the -lm and -lc flags ensure that we link with required system mathematical and C run-time libraries.
Note that on other UNIX machines it may be necessary to add further libraries at link time, depending on the operating system and the version of the NAG Library being used.
We compile and build the DLL in one step:
C:\> cl -Ic:\jdk1.6.0_11\include -Ic:\jdk1.6.0_11\include\win32 -I"c:\Program Files\NAG\CL09\clw3209dal\include" /Gz -LD BesselImp.c "c:\Program Files\NAG\CL09\clw3209dal\lib\CLW3209DA_nag.lib" -FenagCJavaInterface.dllAs under UNIX, the three -I switches tell the C compiler where to look for header files. The first directory mentioned, c:\jdk1.6.0_11\include, must locate the jni.h header file. The second directory, c:\jdk1.6.0_11\include\win32, is the Windows version of the machine-dependent directory needed by jni.h to find type definitions. The third include directory, "c:\Program Files\NAG\CL09\clw3209dal\include", should point to the location of the NAG C Library header files on your system. "c:\Program Files\NAG\CL09\clw3209dal\lib\CLW3209DA_nag.lib" is the location of the NAG C Library installed on your system. The /Gz compiler option (use the __stdcall calling convention) is IMPORTANT. Without it, the code may compile and link, and even start running, but eventually it may cause an access violation. The -LD flag means "build a DLL". The -Fe flag names the output file as nagCJavaInterface.dll.
% java Bessel 1.0The expected output looks like this:
Calls of NAG Y0 Bessel function routine s17acc Y0(1.0) is 0.08825696421567678 Y0(1.25) is 0.25821685159454094 Y0(1.5) is 0.38244892379775886 Y0(1.75) is 0.4654926286469062 Y0(2.0) is 0.510375672649745 Y0(2.25) is 0.5200647624572783 Y0(2.5) is 0.4980703596152319 Y0(2.75) is 0.44865872156913167 Y0(3.0) is 0.37685001001279034 Y0(3.25) is 0.288286902673087
Tip: If you get a Java error message saying that the interface library nagCJavaInterface cannot be found, or that the NAG C Library cannot be found, you may need to set an environment variable to tell the system where to look. The environment variable name is operating-system dependent.
% setenv LD_LIBRARY_PATH .:/opt/NAG/cll6a09dhl/libwill ensure that both the current directory (.) and directory /opt/NAG/cll6a09dhl get searched.
% javac Bessel.java
% javah -jni Bessel
% gcc -c -fPIC -I/opt/jdk1.6.0_11/include -I/opt/jdk1.6.0_11/include/linux \ -I/opt/NAG/cll6a09dhl/include BesselImp.c % ld -G -z defs BesselImp.o -o libnagCJavaInterface.so \ /opt/NAG/cll6a09dhl/lib/libnagc_nag.so -lm -lc -lpthreadwhere /opt/jdk1.6.0_11/include, /opt/jdk1.6.0_11/include/linux, /opt/NAG/cll6a09dhl/include and /opt/NAG/cll6a09dhl/lib are directory names appropriate to your Java and NAG C Library installations.
C:\> cl -Ic:\jdk1.6.0_11\include -Ic:\jdk1.6.0_11\include\win32 -I"c:\Program Files\NAG\CL09\clw3209dal\include" /Gz -LD BesselImp.c "c:\Program Files\NAG\CL09\clw3209dal\lib\CLW3209DA_nag.lib" -FenagCJavaInterface.where c:\jdk1.6.0_11\include, c:\jdk1.6.0_11\include\win32, "c:\Program Files\NAG\CL09\clw3209dal\include" and "c:\Program Files\NAG\CL09\clw3209dal\lib" are directory names appropriate to your Java and NAG C Library installations.
% java Bessel 1.0