|
SYS-CON.TV Webcasts
Comments
Did you read today's front page stories & breaking news?
SYS-CON.TV
|
Top Links You Must Click On
General Java Understanding the Native Java Interface
Understanding the Native Java Interface
By: Govind Seshadri
Mar. 1, 1997 12:00 AM
The evolution of Java has been truly mind-boggling, and quite unlike anything we have ever seen before in the computing domain. Seldom does a day go by without yet another software vendor pledging allegiance to the Java paradigm. Today, few among us will question the power and flexibility of Java. Still, one may ask, "Can Java, by itself, meet all our needs?" The answer to that question was given indirectly by the designers of the language itself when they made allowances for extending the reach of Java through a standard interface utilizing native methods. So, what exactly are native methods, you ask? Simply put, they are methods which are implemented not in Java itself, but in a different language like C or C++. The methods are compiled into either a DLL or a shared library, depending on the target platform. Prior to JDK 1.1, the native method interface itself varied quite a bit, depending on the vendor implementing it. Here, the standard native method interface offered by Sun's JDK was replaced by proprietary interfaces like Microsoft's Raw Native Interface (RNI) and Netscape's Java Runtime Interface (JRI) within their respective JVM implementations. For a while, it almost seemed as if Sun's "Write Once, Run Anywhere" clarion call did not apply to Java applications using native methods! But as with everything else related to Java, the native method interface itself has come a long way since those rough and tumble days. The Java development community (at least those visionary enough to recognize the futility of having to "customize" Java programs for every vendor's JVM) heaved a collective sigh of relief when Sun announced a unifying native method interface called the Java Native Interface (JNI). The JNI, available as an integral part of JDK 1.1, was developed following extensive consultations between JavaSoft and other Java licensees. Although it borrows heavily from Netscape's JRI, JNI holds the promise of making the native method implementation truly independent of the underlying JVM. It should be noted, however, that so far Microsoft has been a holdout on adopting the JNI standard, and has announced that it would support native methods via the RNI and Java/COM interfaces instead.
Why Use Native Methods? Even if you do not have any need for accessing peripheral devices, native methods can facilitate the integration of tried and tested legacy code without causing extensive rewrite. Some developers have even rationalized that the lack of speed in Java applications is yet another reason to implement core functionality within native methods. While it is true that a C or C++ native method would be much faster than its pure Java counterpart, implementing something as native solely for the sake of speed is a short-sighted move at best! This is especially true considering that Java execution speeds have shown significant improvements over the past few months with the emergence of highly efficient Java Virtual Machines and Just-In-Time (JIT) compilers.
The Flip Side It is important to note that native methods can be used only as part of Java applications, and not within applets. The security manager implementations in both Netscape's Navigator and Microsoft's Internet Explorer browsers currently disallow the loading of applets that link to shared libraries over the network. However, there is a possibility that future implementations may allow the user to modify the default browser settings. Then, applets containing native methods can at least be run in an Intranet mode, behind a corporate firewall if need be.
Six Steps to Native Method
Success! Now, let us delve into the details with a simple example. Our example program, NativeQuote.java, invokes the native method quote which in turn displays a popular quote as output. Please note that our example programs show a strong bias towards Sun's Solaris operating environment and uses C' to implement the native method functionality.
Develop the Java Code public native void quote(); The presence of the keyword native within the declaration of the method quote indicates that the method is implemented outside of Java, in a different programming language. Also note that the native methods are always just merely declared within the class and are never implemented, unlike regular Java methods. The static block
static { The main method creates an instance of the class NativeQuote and invokes the native method quote.
Compile the Java Code to a Class File
javac NativeQuote.java
&127;
Generate the Header File javah -jni NativeQuote.java The generated header file is always named after the Java class file, which contains the native method declaration. In this case, the header file NativeQuote.h is generated by javah.
Implement the Native Method The stdio.h header file is needed in our implementation, marvinQuote.c (Listing 3), since we make use of the printf function.
Create the Shared Library or DLL.
cc -G marvinQuote.c -I$JAVAHOME/include
cl marvinQuote.c -I$JAVAHOME\include -I$JAVAHOME\ java NativeQuote we should see Marvin the Martian's famous quote as the output: "Where's the kaboom? There was supposed to be an earth-shattering kaboom!" -- Marvin the Martian
Invoking Java Methods From Native Methods On running the Java program as java Factorial we get the output: Factorial(5) = 120 We create the shared library fact containing the native implementation, as explained earlier. Also, the function prototype for computeFact is obtained from the header file, Factorial.h, that is generated by applying javah on the compiled class file. Java methods can be invoked from within native methods by following the following five-step approach: Step 1: Obtain the class type to which the Java object belongs using the GetObjectClass accessor function Step 2: Get the Java method ID, by passing the method name and its signature to the function GetMethodID. The general form of a method signature is "(argument-types)return-type" In our example, the Java method factorial takes in an int and returns a double. From Table 1, we can deduce its method signature as (I)D. Step 3: Initialize the parameters to the Java method, if any. In the above example, we initialize the integer parameter x to 5. Step 4: Use the appropriate CallMethod accessor function to invoke the Java method. For our example, since the Java method factorial returns a double we make use of the CallDoubleMethod accessor function. Table 2 shows the various CallMethod functions available, based on the return type from the Java method invocation. Step 5: Convert any returned Java object to its native method equivalent. For our example, since we have an equivalent native method type (jdouble) for the Java primitive type double, no conversion is necessary. A highlight of JNI methods is that they are source-portable across platforms as long as the target platform is JNI-compatible and the native method does not make use of any system-dependent functionality. Table 3 shows the equivalent native types for Java data types.
Accessing Java Fields From Native Methods
We see from Listing 7 how easy it is to access any Java field from within a native method. After first obtaining the class to which the parent object belongs, we obtain the field id of the target field. It is important to provide the correct field name and signature at this stage. After this, accessing or changing the value of the field is just a matter of calling the appropriate GetField() or SetField() accessor function. Tables 4 and 5 show the available functions for accessing or modifying Java fields.Throwing Exceptions Throw and ThrowNew are used to trigger exceptions from within the native method implementation. For example, consider the following code segments: (*env)->Throw(env, new java/lang/IllegalArgumentException); and (*env)->ThrowNew(env, java/lang/IllegalArgumentException, "Some diagnostic message"); Both of the above enable IllegalArgumentException to be passed over to the parent Java object. JNI also provides for more catastrophic situations by providing the FatalError accessor function. These types of errors are unrecoverable and typically result in the JVM exiting. The following code snippet is a good example of the usage of FatalError:
(*env)->FatalError(env, "Out of memory! Exiting!");
Using Native Methods in a Multithreaded Environment public synchronized native void someNativeMethod(); indicates that someNativeMethod can be used in a multithreaded environment. JNI also provides for data-locking, permitting thread-safe operations within native methods via a monitor-based mechanism. The MonitorEnter() and MonitorExit() accessor functions are instrumental in setting up a critical region for shared data, so that they can be accessed by concurrent threads of execution.
Conclusion Native methods come at a price, however. Currently, they cannot be used within downloadable applets and, consequently, are practical only within stand-alone applications. Native method development itself is not a trivial task, and needs developer expertise not only in Java but also the native implementation language. Reader Feedback: Page 1 of 1
Enterprise Open Source Magazine Latest Stories . . .
Subscribe to the World's Most Powerful Newsletters
Subscribe to Our Rss Feeds & Get Your SYS-CON News Live!
|
SYS-CON Featured Whitepapers
Most Read This Week |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||