Introducing JNI

The Java Native Interface (JNI) allows you to write Java programs that utilize code written in programming languages other than Java. There are several scenarios where it is preferable or necessary to use non-Java code:

  • When you require functionality not supported by the standard Java class library. For example, you may need to access parts of the Win32 API.

  • When you want to reuse a library or application already written in another programming language. For example, many organizations possess large legacy C libraries.

  • When you want to use features or capabilities of another programming language not readily available in Java.

JNI allows you to write code that supports these scenarios. You can use JNI to declare native methods, and implement the methods bodies in native code, such as C or C++. These native methods can use Java objects and methods in the same way that Java code uses them. Specifically, both native methods and Java methods can create and share Java objects. A native method can also invoke Java methods. For example, Figure 19.6 shows a native method invoking a Java method and passing parameters to it. The Java method performs some processing of the parameters, and returns the result to the native method.

Figure 19.6. Native method invoking a Java method.


Beyond these interoperability aspects, JNI allows you to perform further tasks including the following:

  • Throwing and catching exceptions from a native method to be handled in the Java caller.

  • Through the Invocation API, you can embed the JVM into native applications.

  • Special JNI functions allow native methods to load Java classes and obtain class information.

  • Native methods can use JNI to perform runtime type checking.

A full exploration of JNI is beyond the scope of this book; however, today's lesson aims to provide you with enough information to decide when you might find JNI of use. For further information on the features of JNI, please refer to Sun Microsystems' JNI specification. To complete your introduction to JNI, today's lesson shows you how to write a simple JNI application—namely, Hello World. The example uses a native C function. Although you do not need to know C to understand this example, you will need a C compiler and be able to create a shared library if you want to follow the steps outlined here. Figure 19.7 shows the six steps you will follow to write the application.

Figure 19.7. Creating a JNI application.


NOTE

The Web site accompanying this book does not include complete asant build file targets for the JNI example because the name and location of the C compiler will vary from one system to another.


As Figure 19.7 shows, the first step is to create a Java class that declares the native method. To do this, declare your class and provide the method signature for the native method. Notice that the code uses the native modifier to indicate to the compiler that the method implementation is a programming language other than Java:

class HelloWorld {
    public native void displayHelloWorld();
...
}

Later, when you write the native code, you will compile it into a shared library. To allow the JVM to load this library into the Java class, you use the loadLibrary() method of the System class in the context of a static initializer. The method takes a single parameter—a string that is the name of the library to load. You only need to pass the root of the library name, because the method modifies the name to suit the current platform. For example, it will use hello.dll on Windows or libhello.so on Solaris/Linux:

static {
    System.loadLibrary("hello");
}

Finally, you must write the Java main() method in the same way as you would for any other Java class. Listing 19.5 shows the completed code for this class.

Listing 19.5. HelloWorld.java
Package hello;

class HelloWorld {
    public native void displayHelloWorld();
    static {
        System.loadLibrary("hello");
    }

    public static void main (String args[]) {
        HelloWorld hw=new HelloWorld();
        hw.displayHelloWorld();
    }
}

For step 2, you compile in the same manner as any other Java class using the javac utility.

Step 3 for creating a JNI application is to generate the header file for the native method using the javah utility with the –jni option. By default, javah creates a C header file for each class listed on the command line and puts the file in the current directory. You can use the –d and –classpath options to javah to specify a CLASSPATH and target directory (just like javac). To generate the header file example, issue the following command (from within the Day19/examples directory):

javah –jni –classpath classes –d src hello.HelloWorld

The -jni switch instructs javah that it should output a header file for use with JNI. This is now the default behavior in Java 1.2 and later, but earlier versions required the switch. The header file, in this case, is stored in the src directory. You don't need to look at this generated header file, but if you want to open it up in a text editor, the native method signature is as follows:

Java_hello_HelloWorld_displayHelloWorld (JNIEnv *, jobject);

All generated method signatures follow the same format:

Java_packagename_classname_methodname

In this application, you will write the native method implementation after writing the Java class file, but in reality you may already have native methods you want to use. In this instance, you must ensure that the native method signature (in the method implementation in native code) matches the method signature in the generated header file.

Now you must write the native implementation of the method. The code in this application is written in C. The code (shown in Listing 19.6) begins by including three header files—jni.h, hello_HelloWorld.h, and stdio.h. All native implementations must include jni.h because this provides information that allows the native language to interact with the Java runtime. You should find this file in the include directory in your JDK. You include hello_HelloWorld.h because this is the header file that you just generated. Finally, you include stdio.h because the printf function (you use this to print Hello world) is contained within this library.

Because this is not a C tutorial, there is only one other thing to note about the code. You can see that the method accepts two parameters of the types JNIEnv and jobject. All native methods must accept these parameters. The first, JNIEnv, is an interface pointer that allows the native code to access any parameters your Java code passes to it. The second parameter, jobject, references the calling object (in other words the this object).

Listing 19.6. HelloWorld.c
#include <jni.h>
#include "hello_HelloWorld.h"
#include <stdio.h>

JNIEXPORT void JNICALL
Java_hello_HelloWorld_displayHelloWorld (JNIEnv *env, jobject obj)
{
    printf("Hello world
");
    return;
}

Now that you have written the native code, the second-to-last step shown in Figure 19.7 is to compile the header and implementation file into a shared library by using your C compiler. The shared library must have the same name that was used in loadLibrary() method in the Java class—namely, libhello.so or hello.dll. The actually command for this operation depends on the C compiler that you use; the JDK does not provide such a compiler.

To run your application the JVM has to be able to find the shared library. On a Unix system, you must place the libhello.so file in a directory included in the LD_LIBRARY_PATH environment variable. On a Windows system, place the hello.dll in the current directory or in a directory listed in the PATH environment variable.

Run the application as you would any other Java application:

java hello.HelloWorld

When the code executes, the JVM loads the shared library into the Java class. The main() method of the Java class invokes the displayHelloWorld() method of the native class, which in turn prints Hello world to the standard output.

This example has shown you how to use JNI in a very simple situation. In reality, you may have to integrate large amounts of code using JNI, so standalone command-line applications will certainly be unsuitable for use in the J2EE arena. A viable approach to making legacy code available to J2EE components is to wrap the code using JNI (as you did in the example application). Then, export the code as an RMI remote object, as Integrating with External Resources shown previously. This approach allows J2EE components to interact directly with RMI objects, thus abstracting the underlying legacy code.

NOTE

Enterprise JavaBeans are not permitted to use JNI. The usual approach to accessing native code from an EJB is to wrap an RMI-IIOP service around the native code and use this service from within the EJB.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.143.239.1