Java Native Interface/JNI

Java run time environment(JRE) executes Java byte Code(JBC) from class binaries. These byte codes are platform independent. However system applications may need to access some of the system resources which are strictly platform dependent. Java cannot access this directly from java context. There is a provision in Java to interface with native binaries. This interface is well known as Java Native Interface or JNI in short. JNI in the gateway between JAVA and native binaries, written in C/C++/VB or any other compiled languages.

Java Code

public class MyJNI {
  static {
    try{
      /* MyJNI.dll (Windows) or libMyJNI.so (Linux) */
      System.loadLibrary("MyJNI");
    } catch(java.lang.UnsatisfiedLinkError err){
      /* Error output when native binary not present */
      System.out.println("Unable to load MyJNI library");
      System.exit(0);
10      }
11    }
12    private boolean z;
13    private byte    b;
14    private char    c;
15    private short   s;
16    private int     i;
17    private long    j;
18    private float   f;
19    private double  d;
20    private String  str;
21 
22    /* JNI simple interface take no arguments and no returns */
23    private native void HelloWorld();
24    /* JNI Call by Value */
25    private native int CallC (boolean z,
26                              byte    b,
27                              char    c,
28                              short   s,
29                              int     i,
30                              long    j,
31                              float   f,
32                              double  d,
33                              String  str);
34    /* JNI Call by Value */
35    private native void SetMembers();
36   
37    public static void main(String[] args) {
38      MyJNI myjni = new MyJNI();
39      myjni.HelloWorld();
40      System.out.println("Calling by Value");
41      myjni.CallC (true,
42                   (byte)'J',
43                   'K',
44                   (short)10,
45                   100,
46                   (long)1000,
47                   (float)1.2,
48                   1.5,
49                   "Hello From Java!");
50        myjni.= true;
51        myjni.= (byte)'C';
52        myjni.= 'D';
53        myjni.= 20;
54        myjni.= 200;
55        myjni.= 2000;
56        myjni.= (float)2.2;
57        myjni.= 2.5;
58        myjni.str = "Hello from Java!";
59        System.out.println("Calling by Reference");
60        myjni.SetMembers();
61        System.out.println("End of Calling by Reference");
62        System.out.println("Java Members (changed)");
63        System.out.println("z (boolean) = " + myjni.z);
64        System.out.println("b (byte) = " + (char)myjni.b);
65        System.out.println("c (char) = " + (char)myjni.c);
66        System.out.println("s (short) = " + (short)myjni.s);
67        System.out.println("i (int) = " + myjni.i);
68        System.out.println("l (long) = " + myjni.j);
69        System.out.println("f (float) = " + myjni.f);
70        System.out.println("d (double) = " + myjni.d);
71        System.out.println("str (string) = " + myjni.str);
72     }
73  }
Above code has three functions which are declared with native keyword. Native keyword informs Java Compiler that the function can be linked even if the definition of HelloWorld(), CallC() and SetMembers() do not exist. This is similar to importing a function from DLL/SO file. Java compiler puts these interfaces in import table or checks import table of DLL/so during runtime linking. At this point if we try to execute this code we will get error as Java runtime will fail to load import library MyJNI.dll or libMyJNI.so.
>javac MyJNI.java
>java MyJNI
Unable to load MyJNI library

Interface generation

We need a tool called JAVAH which generates this interface. JAVAH comes with JDK and no additional installation or setup is required. Javah can be executed in the same way as we execute comiler javac or java. Javah takes byte code file name (without .class extension) as argument. It process than binary and generates a C/C++ header file in the same name.
>javah MyJNI

JNI header file

Note carefully javah has generated three function prototype in C/C++. JNIEXPORT macro tells C/C++ compiler to export these functions from DLL/SO library so that java run time can import these calls. Additionally there are two extra arguments for each functions. First argument is of type JNIEnv which gives a pointer to Java Runtime Environment. JNIEnv provides many interfaces which native binary can utilize. Next argument is of type jobject which represents the object itself or the "this" pointer. We can access members of the object through this arguments. Next arguments are same as declared in java. jboolean, jbyte, jchar, jshort, jlong, jfloat, jdouble are type definition of unsigned char, signed char, signed short, short, int, long, float and double in C/C++. Now java treats strings as UNICODE thus jstring is not directly char * but conversions are possible in both the ways. Java_MyJNI_HelloWorld() is the equivalent JNI prototype of HelloWorld() in java. Java_MyJNI_CallC() is the equivalent JNI prototype of CallC() in java. It is calling all arguments by value. Java_MyJNI_SetMembers() is the equivalent JNI prototype of SetMembers() in java. Call by reference in java is not possible but this is equivalent of Call by reference in java. It access and modify member variables of MyJNI class object and thus can be treated as the equivalent of call by reference.
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class MyJNI */
#ifndef _Included_MyJNI
#define _Included_MyJNI
#ifdef __cplusplus
extern "C" {
#endif
10  /* * Class: MyJNI * Method: HelloWorld * Signature: ()V */
11  JNIEXPORT void JNICALL Java_MyJNI_HelloWorld
12    (JNIEnv *, jobject);
13 
14  /* * Class: MyJNI * Method: CallC * Signature: (ZBCSIJFDLjava/lang/String;)I */
15  JNIEXPORT jint JNICALL Java_MyJNI_CallC
16    (JNIEnv *, jobject, jboolean, jbyte, jchar, jshort, jint, jlong, jfloat, jdouble, jstring);
17 
18  /* * Class: MyJNI * Method: SetJMembers * Signature: ()V */
19  JNIEXPORT void JNICALL Java_MyJNI_SetMembers
20    (JNIEnv *, jobject);
21 
22  #ifdef __cplusplus
23  }
24  #endif
25  #endif
26 

Implement Native C/C++ Code

Now we will implement these interfaces in a dynamic link library in Windows or in a shared library in Linux. We need to create a C/C++ source file and may need to copy this header file to the same solder. Here we created MyJNI.c in the same folder where MyJNI.h is located. We should include this file and implement the definitions. jchar, jshort, jlong, jfloat, jdouble are type definition of char, short, long, float and double in C/C++ thus we can used these directly. But this is not true for the strings. Java treats strings char char as UNICODE. There are two bytes for each characters. First character contains ASCII value and next one contains language id. jstring can be converted to UTF char* and vice versa. GetStringUTFChars() can be used to extract char* from Java strings. NewStringUTF() is used to allocate a new UNICODE string from UTF char*. ReleaseStringUTFChars() is used to release existing buffer. Here in Java_MyJNI_CallC(), we are simply printing the values passed from Java in C/C++ context and directly returning to Java context. Java_MyJNI_SetMembers() is not taking arguments. Every C++/Java function has inherent member called this pointer. This function uses this object to access all member variables. JNI can access and modify all the members. These are the steps to access/modify Java members.
  • GetObjectClass() returns the class reference
  • GetFieldID() returns field ID of the member from class reference
  • GetXXXXField()/GetObjectField returns an instance of the member variable from object
  • SetXXXXField()/SetObjectField can be used to set the member value
This is only a demo however in practical situation where system applications need to access system resources can open device files and can call mmap/ioctls/sysctl etc to interface deeper inside into system side.
/* MyJNI.cpp : Defines the entry point for the DLL application. */
#include <stdio.h>
#include "MyJNI.h"
BOOL APIENTRY DllMain ( HANDLE hModule, 
                        DWORD  ul_reason_for_call, 
                        LPVOID lpReserved
                      )
{
    return TRUE;
10  }
11 
12  JNIEXPORT void JNICALL Java_MyJNI_HelloWorld (JNIEnv *,
13                                                jobject)
14  {
15    printf ("Hello World!\n");
16 
17  }
18 
19  JNIEXPORT jint JNICALL Java_MyJNI_CallC (JNIEnv * env,
20                                           jobject  jobj,
21                                           jboolean z,
22                                           jbyte    b,
23                                           jchar    c,
24                                           jshort   s,
25                                           jint     i,
26                                           jlong    j,
27                                           jfloat   f,
28                                           jdouble  d,
29                                           jstring  str)
30  {
31 
32    printf ("Arguments from Java \n");
33    printf ("z (boolean) = %s\n", b ? "true" : "false");
34    printf ("b (byte) = %c\n", b);
35    printf ("c (char) = %c\n", c);
36    printf ("s (short) = %d\n", (int)s);
37    printf ("i (int) = %d\n", i);
38    printf ("l (long) = %ld\n",j);
39    printf ("f (float) = %f\n", f);
40    printf ("d (double) = %f\n", d);
41    printf ("str (string) = %s\n", env->GetStringUTFChars( str, NULL));
42    return 0;
43  }
44 
45  JNIEXPORT void JNICALL Java_MyJNI_SetMembers (JNIEnv * env,
46                                                jobject jobj)
47  {
48 
49    jfieldID fidnum;
50    printf ("Java members (from C)\n");
51 
52     /* Get a reference to this object's class */
53     jclass thisclass = env->GetObjectClass(jobj);
54   
55    /* ============== Boolean ================= */
56    /* Boolean Get the Field ID of the instance */
57    fidnum = env->GetFieldID(thisclass, "z", "Z");
58    if (NULL == fidnum) {
59      return;
60    }
61    /* Get the boolean given the Field ID */
62    jboolean z = env->GetBooleanField(jobj, fidnum);
63    printf ("z (boolean) = %s\n", z ? "true" : "false");
64   
65    /* Change the variable */
66    z = false;
67    env->SetBooleanField(jobj, fidnum, z);
68 
69    /* ============== Byte ================= */
70    /* Byte Get the Field ID of the instance */
71    fidnum = env->GetFieldID(thisclass, "b", "B");
72    if (NULL == fidnum) {
73      return;
74    }
75    /* Get the byte given the Field ID */
76    jbyte b = env->GetByteField(jobj, fidnum);
77    printf ("b (byte) = %c\n", b);
78   
79    /* Change the variable */
80    b += 1;
81    env->SetByteField(jobj, fidnum, b);
82    
83    /* ============== Char ================= */
84    /* Char Get the Field ID of the instance */
85    fidnum = env->GetFieldID(thisclass, "c", "C");
86    if (NULL == fidnum) {
87      return;
88    }
89    /* Get the char given the Field ID */
90    jchar c = env->GetCharField(jobj, fidnum);
91    printf ("c (char) = %c\n", c);
92   
93    /* Change the variable */
94    c += 1;
95    env->SetCharField(jobj, fidnum, c);
96 
97    /* ============== Short ================= */
98    /* Short Get the Field ID of the instance */
99    fidnum = env->GetFieldID(thisclass, "s", "S");
100    if (NULL == fidnum) {
101      return;
102    }
103    /* Get the short given the Field ID */
104    jshort s = env->GetShortField(jobj, fidnum);
105    printf ("s (short) = %d\n", s);
106   
107    /* Change the variable */
108    s = 10;
109    env->SetShortField(jobj, fidnum, s);
110 
111    /* ============== Int ================= */
112    /* Int Get the Field ID of the instance */
113    fidnum = env->GetFieldID(thisclass, "i", "I");
114    if (NULL == fidnum) {
115      return;
116    }
117    /* Get the int given the Field ID */
118    jint i = env->GetIntField(jobj, fidnum);
119    printf ("i (int) = %d\n", i);
120   
121    /* Change the variable */
122    i = 100;
123    env->SetIntField(jobj, fidnum, i);
124 
125    /* ============== Long ================= */
126    /* Long Get the Field ID of the instance */
127    fidnum = env->GetFieldID(thisclass, "j", "J");
128    if (NULL == fidnum) {
129      return;
130    }
131    /* Get the long given the Field ID */
132    jlong j = env->GetLongField(jobj, fidnum);
133    printf ("l (long) = %ld\n", j);
134   
135    /* Change the variable */
136    j = 1000;
137    env->SetLongField(jobj, fidnum, j);
138 
139    /* ============== Float ================= */
140    /* Float Get the Field ID of the instance */
141    fidnum = env->GetFieldID(thisclass, "f", "F");
142    if (NULL == fidnum) {
143      return;
144    }
145    /* Get the float given the Field ID */
146    jfloat f = env->GetFloatField(jobj, fidnum);
147    printf ("f (float) = %f\n", f);
148   
149    /* Change the variable */
150    f = (float)1.2;
151    env->SetFloatField(jobj, fidnum, f);
152 
153    /* ============== Double ================= */
154    /* Double Get the Field ID of the instance */
155    fidnum = env->GetFieldID(thisclass, "d", "D");
156    if (NULL == fidnum) {
157      return;
158    }
159    /* Get the Double given the Field ID */
160    jdouble d = env->GetDoubleField(jobj, fidnum);
161    printf ("d (double) = %f\n", d);
162   
163    /* Change the variable */
164    d = 1.5;
165    env->SetDoubleField(jobj, fidnum, d);
166 
167    /* ============== String ================= */
168    /* String Get the Field ID of the instance */
169     fidnum = env->GetFieldID(thisclass, "str", "Ljava/lang/String;");
170    if (NULL == fidnum) {
171      return;
172    }
173    /* Get the String given the Field ID */
174    jstring str = (jstring)env->GetObjectField(jobj, fidnum);
175    printf ("str (string) = %s\n", env->GetStringUTFChars(str, NULL));
176 
177    /* Change the variable */
178    env->ReleaseStringUTFChars(str, "str");
179    str = env->NewStringUTF ((const char *) "Hello form C!");
180    env->SetObjectField(jobj, fidnum, str);
181 
182  }

JNI Block Diagram

JNI Block Diagram

JNI Execution

Now we need to place this native library in the same folder where our Java Class binary located and can run. If we have more than one class We can also place this in the Java Runtime binary folder jre\bin if we want to access this from more than one java class located in different folder.
:>java MyJNI
Hello World!
Calling by Value
Arguments from Java
z (boolean)  = true
b (byte)     = J
c (char)     = K
s (short)    = 10
i (int)      = 100
l (long)     = 1000
f (float)    = 1.200000
d (double)   = 1.500000
str (string) = Hello From Java!
Calling by Reference
Java members (from C)
z (boolean)  = true
b (byte)     = C
c (char)     = D
s (short)    = 20
i (int)      = 200
l (long)     = 2000
f (float)    = 2.200000
d (double)   = 2.500000
str (string) = Hello from Java!
End of Calling by Reference
Java Members (changed)
z (boolean)  = false
b (byte)     = D
c (char)     = E
s (short)    = 10
i (int)      = 100
l (long)     = 1000
f (float)    = 1.2
d (double)   = 1.5
str (string) = Hello form C!

You have viewed 1 page out of 248. Your C learning is 0.00% complete. Login to check your learning progress.

 Vote 0

Similar topics related to this section

dynamic linking methods, implicit dynamic linking, explicit dynamic linking, access C from VB, JNI C from Java, arithmeic math, trigonometric,

# C Programming Language (Prentice Hall Software)
# Let Us C Paperback - 2006 by Yashavant Kanetkar
# Understanding and Using C Pointers Core techniques for memory management
# Data Structures Using C and C++ Paperback - 1998
# Data Structures In C Paperback - August 11, 2008 by Noel Kalicharan