Calling NAG Library Routines from Java - Example 1

NAG Technical Report 2/2009

Calling NAG Library Routines from Java


Start of report   Skip to Example 2   Skip to Example 3   Skip to Example 4   Skip to Example 5   Skip to Example 6

4.1. Example 1

A simple example – the Y(x) Bessel Function routine s17acc

Here we show how to call a NAG C Library function with only one return value: the Y(x) Bessel function s17acc.

Contents

  1. Function prototype from the NAG C Library Manual
  2. Declaring the native function in our Java program
  3. Loading the interface library
  4. Compiling the Java program
  5. Generating a header file for use by C
  6. Implementing the native function in C code
  7. Building the shareable library or DLL
  8. Running the program
  9. Quick summary of how to build the Bessel function example
  1. Function prototype from the NAG C Library Manual

    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 Y(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.

  2. Declaring the native function in our Java program

    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.

  3. Loading the interface libraryAs discussed earlier, we will need to build a shared library to contain the native method that interfaces between Java and the NAG C Library. Something else that the Java program must do is to load that interface library, which we name nagCJavaInterface. We do that by passing its name to a Java System.LoadLibrary() call. Note that even though the interface library may have a slightly different name depending on the operating system, the LoadLibrary call will sort it out. For example, under Solaris, the Java code
      System.loadLibrary("nagCJavaInterface");
    

    will search for a library named "libnagCJavaInterface.so", whereas under Microsoft Windows it will search for a library named "nagCJavaInterface.dll".

  4. Compiling the Java programHere is the complete source code of our Java program Bessel.java, including the native method declaration and the LoadLibrary call.
      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.

  5. Generating a header file for use by CHaving compiled Bessel.java, we can use the javah tool that comes with the JDK to create a header file that the C compiler can use. Here is the command that does it:
      % 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:

    • It includes the header file <jni.h>; this header file comes with the JDK.
    • The declaration of our native function that the C compiler will see looks like this:
        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.

    • The types JNIEnv, jobject and jdouble are also defined via <jni.h>. They are defined in terms of machine-dependent C types. For example, the type jdouble is the Java double type, which equates to the C double type.
    • From the C point of view, our function has three arguments rather than just one. The first two arguments, of types JNIEnv* and jobject, give our C code access to the Java environment. In this simple case, the third argument is the only one we are interested in – it is the argument x of the Y(x) Bessel function, that we will pass to the NAG C Library.
  6. Implementing the native function in C codeNow that we have created the header file Bessel.h, we can write our C code implementation of Java_Bessel_s17acc. Here it is, in file BesselImp.c:
      #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:

    • We must include the appropriate NAG C Library header files <nag.h> and <nags.h>.
    • We need to declare a variable fail of type NagError to pass as the fail argument to s17acc. As recommended in the NAG C Library manual, we make fail a static variable so that we do not need to initialize it.
    • We simply pass on the return value of the call of s17acc directly back to the Java caller.
  7. Building the shareable library or DLLThis step is operating-system dependent.
    • Compiling on Linux

      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.

    • Compiling under Microsoft Windows using Visual C++

      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.dll
      

      As 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.

  8. Running the programAssuming that all has gone well, we can run the program using the command
      % java Bessel 1.0
    

    The 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.

    • Linux machines

      The environment variable is named LD_LIBRARY_PATH, and is a colon-separated list of directories to be searched for libnagCJavaInterface.so and libnagc_nag.so. For example, if you use the C shell, the command
        % setenv LD_LIBRARY_PATH .:/opt/NAG/cll6a09dhl/lib
      

      will ensure that both the current directory (.) and directory /opt/NAG/cll6a09dhl get searched.

    • Other UNIX machines

      The environment variable will probably be discussed in the man page for the ld command, or a command referenced by that man page. Common names are LD_LIBRARY_PATH and SHLIB_PATH.
    • Microsoft Windows

      There is no separate environment variable used for locating DLLs. The PATH variable is searched, so CLW3209DA_nag.dll and nagCJavaInterface.dll must reside somewhere in your PATH.
  9. Quick summary of how to build the Bessel function exampleGiven the two source files Bessel.java and BesselImp.c, issue the following commands:
    • Compile the Java class:

        % javac Bessel.java
      
    • Create the header file:

        % javah -jni Bessel
      
    • Compile the interface library:
      • (Linux x86)

          % 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 -lpthread
        

        where /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.

      • (Microsoft Windows/Visual C++)

          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.

    • Run the Java program:

        % java Bessel 1.0
      

Start of report   Skip to Example 2   Skip to Example 3   Skip to Example 4   Skip to Example 5   Skip to Example 6
Copyright 2009 Numerical Algorithms Group

Page last updated 2011-01-25 annak

[NP3671]