HELP: Passing and Returning Objects using JNI

V

Vikas

Hi all,

I am learning JNI and writing small examples to understand the
concepts better. I am having trouble with the following programs and
would appreciate it if someone could help me out. I am also sending
the code and output..

Thanks,
Vikas


************************* OBJECTIVE OF THIS PROGRAM
*************************

I am creating 2 objects of type Employee {String name, int salary} ..
My aim is to pass these 2 objects to my native program (written in C)
... I then concatenate the 2 employee names and add their salaries ...
After doing this I have to create a new object of type Employee on the
C side .. Fill that object with the concatenated name, sum of salaries
.... Return that object back to the Java prg. which prints out the
member variables ....


************************* OUTPUT *************************
D:\JavaWork\JNI>java jniClasses.ReceivingObject
JAVA PASS 1
JAVA PASS 2
JAVA PASS 3
fid1 = 34
fid2 = 50
fid3 = 34
fid4 = 50
C PASS 1
stringUTFLength =24
*** C SIDE *** Combined NAMES = Larry Ellison Bill Gates
*** C SIDE *** Combined SALARY = 40000
C PASS 2
Sizeof objClass = 4
constructorMethodID = 9890712
Sizeof returnObj = 4
fid5 = 34
NULL RETURNED TO fid6
fid6 = 0
C PASS 3
C PASS 4
Exception in thread "main" java.lang.NoSuchFieldError: salary
at jniClasses.ReceivingObject.addEmployees(Native Method)
at jniClasses.ReceivingObject.main(ReceivingObject.java:22)



************************* JAVA CODE *************************

// START OF JAVA PROGRAM


package jniClasses;

import java.io.*;
import java.util.*;

class ReceivingObject {


public native Employee addEmployees(Employee s1, Employee s2); //
declaration here

public static void main(String args[]) {


System.out.println("JAVA PASS 1");
Employee e1 = new Employee("Larry Ellison ", 15000);
Employee e2 = new Employee("Bill Gates", 25000);

System.out.println("JAVA PASS 2");
ReceivingObject ro = new ReceivingObject();
System.out.println("JAVA PASS 3");

Employee e3 = ro.addEmployees(e1, e2);

System.out.println("JAVA ====== Object class "+e3.getClass());


System.out.println("JAVA PASS 4");

System.out.println("New Employee Details");
System.out.println("Empoyee Name = " + e3.name);

System.out.println("Empoyee Salary = " + e3.salary);

System.out.println("JAVA PASS 5");

}



static {

System.loadLibrary("ReceiveObject");

}


}


class Employee{

public String name;
public int salary;


public Employee(String tempName, int tempSalary) { // constructor

this.name = tempName;
this.salary = tempSalary;

}



}

// END OF JAVA PROGRAM



************************* C CODE *************************

// START OF C PROGRAM

#include "jniClasses_ReceivingObject.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>


/*
* Class: jniClasses_ReceivingObject
* Method: addEmployees
* Signature: (LjniClasses/Employee;LjniClasses/Employee;)LjniClasses/Employee;
*/
JNIEXPORT jstring JNICALL Java_jniClasses_ReceivingObject_addEmployees
(JNIEnv *env, jobject defaultObj, jobject emp1, jobject emp2) {


jfieldID fid1, fid2, fid3, fid4, fid5, fid6;
jstring firstEmployeeNameJava, secondEmployeeNameJava, myTempName;
jmethodID constructorMethodID;
jsize stringUTFLength;

jobject combinedEmployeeNameJava; // Java style strings

jint firstEmployeeSalary, secondEmployeeSalary, combinedSalary,
myTempSalary; // Java style integers
jclass objClass;
jobject returnObj; // object to be returned

const char *firstEmployeeNameC, *secondEmployeeNameC; // C style
Names
char *combinedEmployeeNameC;

jclass class1 = (*env)->GetObjectClass(env, emp1);
jclass class2 = (*env)->GetObjectClass(env, emp2);

fid1 = (*env)->GetFieldID(env, class1, "name",
"Ljava/lang/String;"); // Get Name
if (fid1 == 0) printf("NULL RETURNED TO fid1\n");
printf("fid1 = %d\n",fid1);

fid2 = (*env)->GetFieldID(env, class1, "salary","I"); // Get
Salary
if (fid2 == 0) printf("NULL RETURNED TO fid2\n");
printf("fid2 = %d\n",fid2);

fid3 = (*env)->GetFieldID(env, class2, "name", "Ljava/lang/String;");
if (fid3 == 0) printf("NULL RETURNED TO fid3\n");
printf("fid3 = %d\n",fid3);

fid4 = (*env)->GetFieldID(env, class2, "salary","I");
if (fid4 == 0) printf("NULL RETURNED TO fid4\n");
printf("fid4 = %d\n",fid4);

firstEmployeeNameJava = (*env)->GetObjectField(env, emp1, fid1); //
java type String
firstEmployeeNameC = (*env)->GetStringUTFChars(env,
firstEmployeeNameJava, NULL);
firstEmployeeSalary = (*env)->GetIntField(env, emp1, fid2);


secondEmployeeNameJava = (*env)->GetObjectField(env, emp2, fid3); //
java type String
secondEmployeeNameC = (*env)->GetStringUTFChars(env,
secondEmployeeNameJava, NULL);
secondEmployeeSalary = (*env)->GetIntField(env, emp2, fid4);

// Combined Employee's Name
combinedEmployeeNameC = (char*) malloc( strlen(firstEmployeeNameC) +
strlen(secondEmployeeNameC) + 1 ); //+ 1 for '\0'

printf("C PASS 1\n");
strcpy(combinedEmployeeNameC, "");
strcpy(combinedEmployeeNameC, firstEmployeeNameC);
strcat(combinedEmployeeNameC, secondEmployeeNameC); // perform the
string concatenation in C

combinedEmployeeNameJava = (*env)->NewStringUTF( env,
combinedEmployeeNameC ); // Construct a Java Style String Name
stringUTFLength = (*env)->GetStringUTFLength(env,
combinedEmployeeNameJava);
printf("stringUTFLength =%d\n", stringUTFLength);

combinedSalary = firstEmployeeSalary + secondEmployeeSalary; //
Combined Employee's Salary

printf("*** C SIDE *** Combined NAMES = %s\n",
combinedEmployeeNameC);
printf("*** C SIDE *** Combined SALARY = %d\n", combinedSalary);


// Now construct the new object and return that

printf("C PASS 2\n");


// Load the ReceivingObject class
objClass = (*env)->FindClass(env,"jniClasses/ReceivingObject"); //
FindClass()
if (objClass == 0) printf("NULL RETURNED in FindClass()\n");
printf("Sizeof objClass = %d\n", sizeof(objClass) );

/*************************************************************************
// Allocates a new Java object without invoking any of the
constructors
returnObj = (*env)->AllocObject(env, objClass);
if (returnObj == 0) printf("NULL RETURNED in AllocObject()\n");
printf("Sizeof returnObj = %d\n", sizeof(returnObj) );
**************************************************************************/


// Get the methodID for the constructor first
constructorMethodID = (*env)->GetMethodID(env, objClass,
"<init>","()V");

if (constructorMethodID == 0) printf("NULL RETURNED TO
constructorMethodID\n");
printf("constructorMethodID = %d\n",constructorMethodID);

// Create the object
returnObj = (*env)->NewObject(env, class1, constructorMethodID,
myTempName, myTempSalary);
printf("Sizeof returnObj = %d\n", sizeof(returnObj) );


fid5 = (*env)->GetFieldID (env, class1, "name",
"Ljava/lang/String;"); // combined name
if (fid5 == 0) printf("NULL RETURNED TO fid5\n");
printf("fid5 = %d\n",fid5);


fid6 = (*env)->GetFieldID (env, objClass, "salary", "I"); //
combined salaries
if (fid6 == 0) printf("NULL RETURNED TO fid6\n");
printf("fid6 = %d\n",fid6);


printf("C PASS 3\n");


// Filling the values to the object
(*env)->SetObjectField (env, returnObj, fid5,
combinedEmployeeNameJava);
(*env)->SetIntField (env, returnObj, fid6, combinedSalary);


printf("C PASS 4\n");


return returnObj;


}

// END OF C PROGRAM
 
G

Gordon Beaton

I am learning JNI and writing small examples to understand the
concepts better. I am having trouble with the following programs and
would appreciate it if someone could help me out. I am also sending
the code and output..

Your code fails where you try to find the "salary" field in the
ReceivingObject class, but there is no such field. Just prior to that
you successfully looked up the "name" field in the Employee class, so
there is some inconsistency here.

Either pass the right class variable, probably "class1" (not
"objClass"), or make sure that ReceivingObject has the appropriate
fields.

According to the signature of the native method, combining two
employees should create a new employee, so I suspect the real error is
that you created a new ReceivingObject to contain the values.

A few tips:

- when your method receives two objects of the same class (e.g.
Employee), you don't need to call GetObjectClass() separately for
each of them, or look up their field and method ids separately. You
can use the same class and field ids when you create the return object
later as well.

- when you create objects from JNI, it is generally much easier to
pass initial values via the constructor, rather then look up fields
and set the values individually.

- when a JNI function fails, try calling something like this to find
out why:

if ((*env)->ExceptionOccurred(env)) {
((*env)->ExceptionDescribe(env));
}

/gordon
 
V

Vikas

Thanks for the tips, gordon .. they were really helpful ... I also
managed to solve the problem I had

Thanks/Vikas
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,968
Messages
2,570,150
Members
46,697
Latest member
AugustNabo

Latest Threads

Top