Discussions

Performance and scalability: Invoking 'C' function in Java using JNI

  1. Invoking 'C' function in Java using JNI (1 messages)

    Hello,
    can we invoke a function of C/C++ program in java using JNI without changing code in C/C++ program Thanks
  2. Writing native methods for Java programs is a multi-step process.
    1. Begin by writing the Java program. Create a Java class that declares the native method; this class contains the declaration or signature for the native method. It also includes a main method which calls the native method.
    2. Compile the Java class that declares the native method and the main method.
    3. Generate a header file for the native method using javah with the native interface flag -jni. Once you've generated the header file you have the formal signature for your native method.
    4. Write the implementation of the native method in the programming language of your choice, such as C or C++.
    5. Compile the header and implementation files into a shared library file.
    6. Run the Java program.

    Writing the Java Program:

    The following Javacode segment defines a class named HelloWorld. This class declares one native method, implements a main method, and has a static code segment.
    class HelloWorld {
        public native void displayHelloWorld();

        static {
            System.loadLibrary("hello");
        }
        
        public static void main(String[] args) {
            new HelloWorld().displayHelloWorld();
        }
    }
    Declare a Native Method
    You must declare all methods, whether Java methods or native methods, within a class on the Java side. When you write a method implementation in a language other than Java, you must include the keyword native as part of the method's definition within the Java class. The native keyword signals to the Java compiler that the function is a native language function. It is easy to tell that the implementation for the HelloWorld class's displayHelloWorld method is written in another programming language because the native keyword appears as part of its method definition:
    public native void displayHelloWorld();
    This native method declaration in your Java class provides only the method signature for displayHelloWorld. It provides no implementation for the method. You must provide the implementation for displayHelloWorld in a separate native language source file.
    The method declaration for displayHelloWorld also indicates that the method is a public instance method, accepts no arguments, and returns no value..
    Load the Library
    You compile the native language code that implements displayHelloWorld into a shared library (you will do this in Step 5: Create a Shared Library). The runtime system later loads the shared library into the Java class that requires it. Loading the library into the Java class maps the implementation of the native method to its declaration.
    The HelloWorld class uses the System.loadLibrary method. The System.loadLibrary method loads the shared library that will be created when you compile the implementation code. Place this method within a static initializer. The argument to System.loadLibrary is the shared library name. This can be any name that you choose. The system uses a standard, but platform-specific, approach to convert the library name to a native library name. For example, the Solaris system converts the library name "hello" to libhello.so, while a Win32 system converts the same name to hello.dll.
    The following static initializer from the HelloWorld class loads the appropriate library, named hello. The runtime system executes a class's static initializer when it loads the class.
    static {
        System.loadLibrary("hello");
    }
    Write the Main Method
    The HelloWorld class, because it is an application, also includes a main method to instantiate the class and call the native method. The main method instantiates HelloWorld and calls the displayHelloWorld native method.
        public static void main(String[] args) {
            new HelloWorld().displayHelloWorld();
        }
    You can see from the code sample that you call a native method in the same manner as you call a regular method: just append the name of the method to the end of the object name, separated with a period ('.'). A matched set of parentheses, (), follow the method name and enclose any arguments to pass into the method. The displayHelloWorld method doesn't take any arguments.
    Compiling the Java Program:
    Compile the Java Program using the command “ javac HelloWorld.java” which after successful compilation would make a file HelloWorld.class.



    Generating Header File:
    Generate the Header file HelloWorld.h file using the command “javah –jni HelloWorld”.
    In this step, you use the javah utility program to generate a header file (a .h file) from the HelloWorld class. The header file provides a C function signature for the implementation of the native method displayHelloWorld defined in that class.
    Run javah now on the HelloWorld class that you created in the previous steps.
    The name of the header file is the Java class name with a .h appended to the end. For example, the command shown above will generate a file named HelloWorld.h.
    By default, javah places the new .h file in the same directory as the .class file. Use the -d option to instruct javah to place the header files in a different directory.
    The Function Definition
    Look at the header file HelloWorld.h.
    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class HelloWorld */

    #ifndef _Included_HelloWorld
    #define _Included_HelloWorld
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class: HelloWorld
     * Method: displayHelloWorld
     * Signature: ()V
     */
    JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld
      (JNIEnv *, jobject);

    #ifdef __cplusplus
    }
    #endif
    #endif
    The Java_HelloWorld_displayHelloWorld function provides the implementation for the HelloWorld class's native method displayHelloWorld, You use the same function signature when you write the implementation for the native method.
    If HelloWorld contained any other native methods, their function signatures would appear here as well.
    The name of the native language function that implements the native method consists of the prefix Java_, the package name, the class name, and the name of the native method. Between each name component is an underscore "_" separator.
    The native method displayHelloWorld within the HelloWorld class becomes Java_HelloWorld_displayHelloWorld. No package name appears in our example because HelloWorld is in the default package, which has no name.
    Notice that the implementation of the native function, as it appears in the header file, accepts two parameters even though, in its definition on the Java side, it accepts no parameters. The JNI requires every native method to have these two parameters.
    The first parameter for every native method is a JNIEnv interface pointer. It is through this pointer that your native code accesses parameters and objects passed to it from the Java application. The second parameter is jobject, which references the current object itself. In a sense, you can think of the jobject parameter as the "this" variable in Java. For a native instance method, such as the displayHelloWorld method in our example, the jobject argument is a reference to the current instance of the object. For native class methods, this argument would be a reference to the method's Java class. Our example ignores both parameters.

    Writing the Implementation of Native Method in C++:
    The function that you write must have the same function signature as the one generated by javah in the HelloWorld.h file. Recall that the function signature generated for the HelloWorld class's displayHelloWorld native method looks like this:
    JNIEXPORT void JNICALL
        Java_HelloWorld_displayHelloWorld(JNIEnv *, jobject);
    Here's the C language implementation for the native method Java_HelloWorld_displayHelloWorld. This implementation is in the file named HelloWorldImp.c.
    #include <jni.h>
    #include "HelloWorld.h"
    #include <stdio.h>

    JNIEXPORT void JNICALL
    Java_HelloWorld_displayHelloWorld(JNIEnv *env, jobject obj)
    {
        printf("Hello world!\n");
        return;
    }
    The implementation for Java_HelloWorld_displayHelloWorld is straightforward. The function uses the printf function to display the string "Hello World!" and then returns.
    The HelloWorldImp.c file includes three header files:
    1. jni.h - This header file provides information that the native language code requires to interact with the Java runtime system. When writing native methods, you must always include this file in your native language source files.
    2. HelloWorld.h - The .h file that you generated in Step 3: Create the .h File.
    3. stdio.h - The code snippet above includes stdio.h because it uses the printf function. The printf function is part of the stdio.h library.
    Creating a DLL of the C++ File:
    Remember in Step 1: Write the Java Code you used the following method call to load a shared library named hello into your program at runtime:
    System.loadLibrary("hello");
    Now you are ready to create this shared library.
    In the previous step, Step 4: Write the Native Method Implementation, you created a C file in which you wrote the implementation for the displayHelloWorld native method. You saved the native method in the file HelloWorldImp.c. Now, you must compile HelloWorldImp.c into a shared library, which you name hello to match the library name used in the System.loadLibrary method.
    Compile the native language code that you created in the previous two steps into a shared library. On Solaris, you'll create a shared library, while on Windows 95/NT you'll create a dynamic link library (DLL). Remember to specify the path or paths to all necessary header files. See Creating and Loading Shared Libraries for information on forming a shared library name.
    On Solaris, the following command builds a shared library libhello.so:
    cc -G -I/usr/local/java/include -I/usr/local/java/include/solaris \
          HelloWorldImp.c -o libhello.so
    On Win32, the following command builds a dynamic link library hello.dll using Microsoft Visual C++ 4.0:
    cl -Ic:\java\include -Ic:\java\include\win32
          -LD HelloWorldImp.c -Fehello.dll
    Of course, you need to specify the include path that corresponds to the setup on your own machine.
    Run the Java Program:
    Run the Java program using the command &#8220;java HelloWorld&#8221;.