Published on Jul 15 2013 in Java

Java hosting clients may want to use some of the vast native methods library pool from inside their Java/JSP code. It may save coding time and also give better performance. Here we give a short example of how to link C and Java code in our Java hosting environment.

Create Java class containing native method

Let's create a Java class with native method declaration (NativeMethodLibrary.java). It accepts single numerical (integer) argument and prints its root. In case of no argument call or parse error default value is used. The code also prints library name expected on your OS (in Linux lib prefix and .so suffix are added). Moving the example from command line Java to JSP will require only changes in the below file so that it becomes valid JSP code.

class NativeMethodLibrary {
 public native int root (int number);
 static private String libname = "NativeMethodLibrary";

// load library explicitly
 static {
  System.loadLibrary (libname); 

// absolute path in the below call required
// System.load ("/home/username/"+System.mapLibraryName(libname))
 }

 public static void main (String[] args) {
 int number = 8;
 if (args.length > 0) {
  try {
     number = Integer.parseInt(args[0]);
 } catch (NumberFormatException e) {
 System.err.println("Argument must be an integer. Using default value of 8");
 }
 }
 System.out.println("Expected library file name: "+System.mapLibraryName(libname));
 NativeMethodLibrary nml = new NativeMethodLibrary ();
 System.out.println(nml.root (number));
 }
}

As an alternative to System.loadLibrary you can use System.load. Just comment out first and uncomment the second. Full path to library (a directory) will be required. Library filename part will be autogenerated. As API doc read system.loadLibrary(String libname) allows you to load it from default Java library path or path specified with -Djava.library.path while system.load(String filename) allows you to specify full (absolute) path to the library inside the code. With the second approach you will not need to set java.library.path or LD_LIBRARY_PATH.

Compile Java class nad generate C headers

Compile the Java class and generate JNI header file with necessary declarations.

javac NativeMethodLibrary.java
javah -jni NativeMethodLibrary

The generated header file will have #include <jni.h> inside so we will need to specify correct include path when compiling it. It also contains JNI method declaration with single integer argument.

JNIEXPORT jint JNICALL Java_NativeMethodLibrary_root (JNIEnv *, jobject, jint);

Prepare and compile C code of the library

Here is the example code of NativeMethodLibrary.c:

#include "NativeMethodLibrary.h" /* Autogenerated earlier. */
JNIEXPORT jint JNICALL Java_NativeMethodLibrary_root(JNIEnv *env, jobject this, jint number) {
 return number * number;
} 

Complie native C code with:

gcc -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -o libNativeMethodLibrary.so -shared -fPIC NativeMethodLibrary.c

Note that two include paths are specfied as jni.h resides in include while jni_md.h resides in include/linux . The shared parameter causes a shared object generated that can then be linked with other objects if necessary. The fPIC stands for Position Independent Code. It means the generated code does not have to be located at a specific address in order to work. Code compiled with -fPIC, is suitable for inclusion in a library.

Call native method from Java (command line)

Call the native method from Java if you used System.loadLibrary

java -Djava.library.path=. NativeMethodLibrary
LD_LIBRARY_PATH=. java NativeMethodLibrary 4
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.

Note that -Djava.library.path will overwrite LD_LIBRARY_PATH.

The other way is to call the native method from Java if you used System.load

java NativeMethodLibrary 12

The whole cycle is illustrated on below figure.

jni native method in Java cycle
### Calling native methods from JSP/servlet code

To call native method from JSP the recommended way is to load the library in a class and then instantiate the class in JSP and finally execute native method. Here we needed to move the class into a package test to be able to import the class into JSP as classes of default package cannot be imported. This example is based on tomcat 7.0.37 in JVM Host environment.

cd && mkdir lib
javac NativeMethodLibrary.java -d appservers/apache-tomcat-7.0.37/webapps/ROOT/WEB-INF/classes
javah -jni -classpath appservers/apache-tomcat-7.0.37/webapps/ROOT/WEB-INF/classes test.NativeMethodLibrary
gcc -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -o lib/libNativeMethodLibrary.so -shared -fPIC NativeMethodLibrary.c
java -Djava.library.path=lib -classpath appservers/apache-tomcat-7.0.37/webapps/ROOT/WEB-INF/classes test/NativeMethodLibrary 7 

Now edit appservers/apache-tomcat-7.0.37/webapps/ROOT/native.jsp and insert:

<%@page import="java.util.*" %>
<%@page import="test.NativeMethodLibrary" %>
<%
int number = 12; 
Properties props = System.getProperties();                                                                                                                        
out.println("java.library.path="+props.get("java.library.path")+"</br>"); 
try { 
 NativeMethodLibrary nml = new NativeMethodLibrary ();
 out.println(nml.root(number));
} catch (Exception e) {
 System.err.println("Exception: "+e.getMessage());
 e.printStackTrace();
} 
%> 

And finally access the servlet with http://username.jvmhost.net/native.jsp

Notes

then make sure you included platform dependent include directory in gcc command line (specifying just the include directory is not enough as jni_md.h resides in a subdirectory like linux).

nm libNativeMethodLibrary.so
00000000000004ec T Java_NativeMethodLibrary_root

As you can see, using native methods in Java is quite simple. In case of any issues please contact JVM Host support.