JNI Information Page



This page is intended to give you some assistance with using XML in your project and give you information to help you decide if that is the direction you want to go.

What is JNI?

JNI stands for Java Native Interface. It is a mechanism to have Java programs call functions written in C or C++. Typically you would want to do this because you have a large legacy code base in C/C++ and you don't want to have to rewrite all of it for use in a newer application which is based in Java. In some cases one might also use this to improve speed though that will only happen in certain special situations. When a Java program invokes a JNI method the call itself typically takes slightly longer so what the body of the call does would have to be appreciably faster than similar code would be in Java. For some application, such as those that are mostly numerical, this is quite possible, for many others it probably isn't worth the effort.

Why use JNI for the project?

For this project I'm forcing you to write some of it in C++ because one of the course objectives is that you learn that language. However, other parts of the project will clearly be easier to do in Java (you could do them in C++ but not with the standard libraries). Given these facts, having a way to directly call C++ code from inside of Java could have significant advantages. Given that your alternative is to write files that are read in by the C++ programs, using JNI in this instance can give you a significant speed boost. It is much like the case of dealing with legacy code.

Using JNI basically allows you to make one single program for the application instead of a number of programs that communicate with one another. It should be noted that some people prefer the philosophy of many small programs that each do their job well over a single monolithic program that handles all the functions. Each approach has its own advantages. In this situation the "monolithic" approach can be significantly faster and it can have a lower memory overhead as well.

If you use JNI you will also be able to avoid writing extra code thats writes out instructions in text files and then parses it on the other side. Of course, the tradeoff for this is that you have to learn how to use JNI. Also, JNI makes your Java code less portable. This is because the C++ code must be compiled to libraries on any system that you want to use it on. This is not a huge factor in the case of this project because you already have to compile your C++ code. The only trick left is making sure that your Java code can find it and use it.

How to use JNI in the project:

There is a lot more to the JNI than I will go into here. If you really want to learn it I would recommend "The Java Native Interface: Programmer's Guide and Specification" by Sheng Liang. For the purposes of this class you only have to read the first 80 or so pages and use the reference at the end. The links below should give you enough information that you don't need to buy the book to go this route for this course.

Here I'm going to run through a simple example. You can specify a native method in any class simply by putting the native keyword in the declaration and putting a semicolon at the end as you would with an abstract method. The native method can be static or not. The class below shows an example:

public class NativeCaller {

public native int mandelbrotIteration(double real,double imaginary);
public static void main(String[] args) {

NativeCaller nc=new NativeCaller();
for(double x=-1.0; x<1.0; x+=0.01) {

for(double y=-1.0; y<1.0; y+=0.01) {

int iterCnt=nc.mandelbrotIteration(x,y);
// Draw it into diagram.

}

}

}

static {

System.loadLibrary("NativeCaller");

}

}

The first method in this class is the native method that we are going to implement in C++. After that there is a main method for the class that we can use to run the program. The last part of the class is something that you probably haven't seen before. It is a construct called a static initializer. You don't have to use these very often so they weren't discussed last semester. However, JNI is one place where they definitely come in handy. The code in a static initializer will always be run before any method or field of the class is accessed. In this case, the only thing it is supposed to do is load in the library that will contain our native method. This tells the JVM to go look for a library file with the given name in the library path. Under Windows that would be "NativeCaller.dll" and under Linux it is "libNativeCaller.so". If you are working under Windows and want the same make file to work under Linux you can compile to one of those names and simply copy it it the other one so it will be found either way. Where the library is looked for varies by platform and I won't do into details here. Just know that under my environment when I grade your code that the current directory (".") will be in that path so you can have your library files copied into whatever directory your Java code is run from and things should go smoothly. There is also a System.load function that can be used to load any file, even if it doesn't follow the naming conventions. I've found that the fact that Cygwin uses a separate directory structure causes problems for this under Windows, but if you are doing all your work in Linux and don't want your libraries in the Java directory please feel free to use that. Lastly, when you issue the java command you can also specify system properties with the -D option. If the property you set is java.library.path, that can tell Java where to look for libraries. Unfortunately this also has problems under Cygwin.

Of course, all this has been said with no word as to how you will go about producing that library file. Once you have the Java code with native methods you can compile it with javac like normal (or in Eclipse or Together). Then run "javah NativeCaller" (or whatever you call the class you are working on) from the command line (you can set up tools under an IDE to do this if you want to take the time).This will produce a C++ header file with the same name as the class. In this case it is NativeCaller.h. As it says, you don't want to edit that file. However, you do want to implement the method(s) that is declared in it. One easy way to do this is to copy that file to a file of the same name with .cpp at the end. Now edit that file so that it includes the header file (add #include<NativeCaller.h> at the top in this example) and write the implementation for the function. For this particular case, the code that I have written looks like this.

#include "NativeCaller.h"

/* * Class: NativeCaller
* Method: mandelbrotIteration
* Signature: (DD)I
*/
JNIEXPORT jint JNICALL Java_NativeCaller_mandelbrotIteration (JNIEnv *env, jobject obj, jdouble x, jdouble y) {

bool flag=true;
nt cnt=0;
double r=x;
double i=y;
while(cnt<100 && flag) {

r=r*r-i*i-x;
i=2.0*r*i-y;
if(r*r+i*i>4) flag=false;

}
return cnt;

}

In the project you would have code here that calls other classes that you have written and does various things with trees, binary files, etc. The last thing you have to do is compile this file. If you are using g++ you should specify the -shared option when you link. This tells the compiler to produce a shared library file. The gcc man pages have warnings about this being delicate, but I haven't run into any problems with it myself. Make sure the output file has the right name (use the -o option) and that it goes into the right location (see the discussion above). Once that is done you should be able to run your Java program in the normal way. If you get a message saying something like "no NativeCaller in java.library.path" that means that it is not finding the library in the path that it has. Check the name of your file and use the "set" command to see what the LD_LIBRARY_PATH variable has been set to. If it hasn't been set then type in "export LD_LIBRARY_PATH=.". If it has been set, but "." is not part of it then do "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.". If the file has the proper name and is in your current directory then Java should find it.

This example is simple because the only information being passed between the native code and Java is primitive data which doesn't require special functions to deal with. In your project you will have to communicate much more complex data between the Java and C++ codes. This requires the use of a fairly large number of functions that you can call in your natice code. See the references below for more information on that or come talk to me for assistance. This discussion gives you a running start so that you can get things set up and, just as importantly, so that you can see what you are up against if you take this route.

JNI References:

Book: "The Java Native Interface: Programmer's Guide and Specification" by Sheng Liang published by Addison Wesley, 1999.

Sun JNI Documentation (thru 1.4)

Sun JNI Tutorial (Version 1.1)

Enhancing programs with JNI (at JavaWorld)

JNI How-To

Using JNI (at ACM Crossroads)