Introducing JNI

The Java Native Interface (JNI) allows you to write code that utilizes code written in programming languages other than Java. If you consider the maxim “Write once, run anywhere,” you may wonder why you might want to use non-Java code. There are a number of scenarios where it is preferable or even wise 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 written in another programming language, so you don't have to rewrite these libraries or applications. For example, many organizations posses large legacy C libraries.

  • When you need to utilize a lower-level programming language, such as assembly, to provide support for a time-critical section of code.

JNI allows you to write code that supports these types of scenario. You can use JNI to declare native methods, but 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 Java objects, use them, update them, and then share them interchangeably. Like a native method using a Java object, a native method can invoke Java methods. For example, Figure 19.7 shows a native method invoking a Java method and passing parameters to it. The Java method performs some processing of the parameters, returns the result to the native method, and the native method then uses this result for some further purpose.

Figure 19.7. 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, and then the Java application handles them.

  • 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 make an informed choice of whether 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. Figure 19.8 shows the six steps you will follow to write the application.

Figure 19.8. Creating a JNI application.


As Figure 19.8 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 runtime 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:

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

Finally, you must write the main() method in the same way as you would for any other Java class. Listing 19.10 shows the completed code for this class. After you have written it, compile it as if it were any other Java class (javac HelloWorld.java).

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

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

Step 3 of the process is to generate the header file for the native method. Creating the header file is simple; at the command line, ensure that you are in the directory of the HelloWorld class file and then use javah by issuing the following command:

javah –jni HelloWorld

The jni switch instructs javah that it should output a header file for use with JNI. This is the default behavior in Java 1.2 and later, but earlier versions need the switch so that they do not output header files for use with the older JDK 1.0 native interface.

After you run javah, it creates a header file (HelloWorld.h) in the current directory. You don't need to look at this file, but if you want to open it up in a text editor, the native method signature is as follows:

Java_HelloWorld_displayHelloWorld (JNIEnv *, jobject);

All generated method signatures follow the same format:

java_packagename-classname-methodname
					

In the application you are currently writing, there is no package name, so it is omitted from the method signature. In this application, you 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, but don't panic because the code is very simple to follow. The code (shown in Listing 19.11) begins by including three header files—jni.h, 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 include 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 current object itself.

Listing 19.11. HelloWorldImp.c
#include <jni.h>
#include "HelloWorld.h"
#include <stdio.h>

JNIEXPORT void JNICALL
Java_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.8 is to compile the header and implementation file into a shared library by using your favorite C compiler. The shared library must have the same name that was used in loadLibrary() method in the Java class—namely, hello. The actually command for this operation depends on the C compiler that you use; the JDK doe not provide such a compiler, as you might expect.

You have written the application. To run it, simply execute the Java class file you wrote earlier:

java HelloWorld

When the code executes, the runtime 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 shown previously. This approach allows J2EE components to interact directly with RMI objects, thus abstracting the underlying legacy code.

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

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