3

I have an JAVA application in that I want to pass the object as a parameter to the C code using JNI and again I want to receive the object from the C code to JAVA using JNI.

In JAVA side i have simply created an application and pass it to the method as shown bellow

JlibFprint.fp_image_data fpimg = new JlibFprint.fp_image_data();   //object to be pass  
 //fp_image_data is the static inner class of the class JlibFprint

JlibFprint.fp_image_data fpimg1 = new JlibFprint.fp_image_data();   //received object

This object is pass to the method like

fpimg1 = JlibFprint.binary_image(fpimg);

And JNI code for the method is as shown bellow :

JNIEXPORT jobject JNICALL Java_jlibfprint_JlibFprint_binary_1image(JNIEnv *env, jclass jcls,jobject imgobj)
{
    struct fp_img img;
    struct fp_img *imgptr;
    imgptr = &img;
    jfp2cfp(env,imgobj,imgptr);     
    fp_init();
    imgptr = fp_img_binarize(imgptr);
    cfp2jfp(env, imgobj, imgptr);
    fp_exit();
    return imgobj;
}

void jfp2cfp(JNIEnv* env, jobject obj, fp_img *fpd)
{
    /* Determines all the fields of the object */
    jclass fpClass = env->FindClass("jlibfprint/JlibFprint$fp_image_data");
            jfieldID height;
            jfieldID width;
            jfieldID length;
            jfieldID data;
            jbyteArray dataArray;

            height = env->GetFieldID(fpClass, "height", "I");
            width = env->GetFieldID(fpClass, "width", "I");
            length = env->GetFieldID(fpClass, "length", "I");
            data = env->GetFieldID(fpClass, "data", "[B");

         /* Starts to fill fpd */
       fpd->height = env->GetIntField(obj, height);
        fpd->width = env->GetIntField(obj, width);
        fpd->length = env->GetIntField(obj, length);
        printf("\n height :%d",fpd->height);
        printf("\n width  :%d",fpd->width);
        printf("\n length :%d",fpd->length);
        dataArray = static_cast<jbyteArray>(env->GetObjectField(obj, data));
        env->GetByteArrayRegion(dataArray, 0, FP_PRINT_DATA_DATA_SIZE, (jbyte*)fpd->data);

}

void cfp2jfp(JNIEnv* env, jobject obj, fp_img* fpd)
{
    /* Determines all the fields of the object */
    jclass fpClass = env->FindClass("jlibfprint/JlibFprint$fp_image_data");
        jfieldID height;
        jfieldID width;
        jfieldID length;
        jfieldID data;

        jbyteArray dataArray;
        height = env->GetFieldID(fpClass, "height", "I");
        width = env->GetFieldID(fpClass, "width", "I");
        length = env->GetFieldID(fpClass, "length", "I");
        data = env->GetFieldID(fpClass, "data", "[B");

        /* Starts to fill the obj */
        env->SetIntField(obj, height, fpd->height);
        env->SetIntField(obj, width, fpd->width);
        env->SetIntField(obj, length, fpd->length);

        dataArray = env->NewByteArray(FP_PRINT_DATA_DATA_SIZE);
        env->SetByteArrayRegion(dataArray, 0, FP_PRINT_DATA_DATA_SIZE, (jbyte*)fpd->data);

        env->SetObjectField(obj, data, dataArray);
}

But after returning from these functions of JNI code at the JAVA side the method shows the exception like

java.lang.ArrayIndexOutOfBoundsException
    at jlibfprint.JlibFprint.binary_image(Native Method)
    at jlibfprint.SampleRun.main(SampleRun.java:96)

i.e objects are not process correctly and it does not return any thing from the JNI layer. But I am not getting what should I change in the JNI code so that it will return the correct object.

Please suggest me any solution.

rachana
  • 3,344
  • 7
  • 30
  • 49

1 Answers1

1

Both GetByteArrayRegion() and SetByteArrayRegion() can throw ArrayIndexOutOfBoundsException if the specified region goes outside the bounds of the array.

Your call to SetByteArrayRegion() looks correct -- it immediately follows creation of the array of the required size.

Verify that the size of the array on entry, accessed by GetByteArrayRegion(), is at least FP_PRINT_DATA_DATA_SIZE.

By the way, an alternative is to use GetByteArrayElements() and ReleaseByteArrayElements(), which may return a pointer to the same memory Java uses for the Array.

Andy Thomas
  • 84,978
  • 11
  • 107
  • 151
  • Thank you.Can you please explain with the example that what should i use in replace with `SetByteArrayRegion()` – rachana Aug 22 '13 at 13:28
  • I don't see anything incorrect with your call to SetByteArrayRegion(). My first step would be to verify that the array passed to GetByteArrayRegion() has at least FP_PRINT_DATA_SIZE length. After you get this working, if you consider it useful to try to avoid copying the array, here are some details on GetByteArrayElements/ReleaseByteArrayElements() - http://stackoverflow.com/questions/8439233/how-to-convert-jbytearray-to-native-char-in-jni . – Andy Thomas Aug 22 '13 at 13:35
  • I have set the value for FP_PRINT_DATA_SIZE is 327680 and its quite enough compare with the size of input array – rachana Aug 22 '13 at 13:56
  • Is it *smaller* than the size of the input array? It it's larger, you will get an ArrayIndexOutOfBoundsException in your call to GetByteArrayRegion(). If you know the array is smaller than FP_PRINT_DATA_SIZE, you could change the third argument to GetByteArrayRegion() to the length of the input array. Better yet, request the *minimum* of FP_PRINT_DATA_SIZE and the length of the array. – Andy Thomas Aug 22 '13 at 14:21
  • no the value of FP_PRINT_DATA_SIZE is greater than the size of input array – rachana Aug 23 '13 at 05:21
  • 1
    Then that's the problem. See http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#wp6212 : THROWS: ArrayIndexOutOfBoundsException: if one of the indexes in the region is not valid. – Andy Thomas Aug 23 '13 at 12:40