0

I am trying to call the following java method from C++

final String[] games = GameLoader.listGames();

where GameLoader was imported with import player.GameLoader;. The GameLoader class exists within a jar file. I am trying to use JNI to load the jar file from C++ and then call the above method. The following is my C++ code which I have tried to extend from this SO post.

#include <iostream>
#include <string.h>
#include <jni.h>
#include <stdlib.h>

using namespace std;

#define PATH_SEPARATOR ';' 
#define USER_CLASSPATH "Ludii-0.3.0.jar" 

#define GAME_LOADER     "player/GameLoader"


JNIEnv *env;
JavaVM *jvm;
jint res;

void initJVM() {

#ifdef JNI_VERSION_1_2
 JavaVMInitArgs vm_args;
 JavaVMOption options[1];
 options[0].optionString =
 "-Djava.class.path=" USER_CLASSPATH;
 vm_args.version = 0x00010002;
 vm_args.options = options;
 vm_args.nOptions = 1;
 vm_args.ignoreUnrecognized = JNI_TRUE;
 /* Create the Java VM */
 res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
#else
 JDK1_1InitArgs vm_args;
 char classpath[1024];
 vm_args.version = 0x00010001;
 JNI_GetDefaultJavaVMInitArgs(&vm_args);
/* Append USER_CLASSPATH to the default system class path */
 sprintf(classpath, "%s%c%s",
vm_args.classpath, PATH_SEPARATOR, USER_CLASSPATH);
 vm_args.classpath = classpath;
 /* Create the Java VM */
 res = JNI_CreateJavaVM(&jvm, &env, &vm_args);
#endif /* JNI_VERSION_1_2 */
}

void closeJVM() {
    jvm->DestroyJavaVM();
}

int main() {

    initJVM();

    jclass gameLoader = env->FindClass(GAME_LOADER);
    //final String[] games = GameLoader.listGames();
    jmethodID mid = env->GetMethodID(gameLoader,"listGames","()[Ljava/lang/String;");
    jobjectArray stringArray = (jobjectArray) env->CallObjectMethod(gameLoader,mid);

    closeJVM();

}

I am able to successfully create the JVM and the code compiles however at runtime I get the error

# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x00007f0e835619d7, pid=3504, tid=0x00007f0e84028740
#
# JRE version: OpenJDK Runtime Environment (8.0_222-b10) (build 1.8.0_222-8u222-b10-1ubuntu1~16.04.1-b10)
# Java VM: OpenJDK 64-Bit Server VM (25.222-b10 mixed mode linux-amd64 compressed oops)
# Problematic frame:
# V  [libjvm.so+0x6849d7]
#
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
#
# An error report file with more information is saved as:
# /home/alex/jni_examples/hs_err_pid3504.log
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.java.com/bugreport/crash.jsp
#
Aborted (core dumped)

I think that the error is related to the CallObjectMethod call but I'm not sure. Also I am using Java 8

EDIT:

It looks like mid evaluated to 0 which means that the method was not found. I guess this is the problem however I'm not sure why it wasn't found. This was checked with

int main() {

    initJVM();

    jclass gameLoader = env->FindClass("player/GameLoader");
    //final String[] games = GameLoader.listGames();

    if(gameLoader == NULL)
    {
        cout << "Could not load class!" << endl;
        return 1;
    }

    jmethodID mid = env->GetMethodID(gameLoader,"listGames","()[Ljava/lang/String;");

    if(mid == NULL)
    {
        cout << "Could not load method!" << endl;
        return 1;
    }
    jobjectArray stringArray = (jobjectArray) env->CallObjectMethod(gameLoader,mid);
     if(stringArray == NULL)
    {
        cout << "Could not load object!" << endl;
        return 1;
    }


    closeJVM();

}

ANOTHER EDIT:

So it looks like the problem was that listGames() should be found with GetStaticMethodID rather than GetMethodID and it should be called with CallStaticObjectMethod rather than CallObjectMethod. Thank you.

user1893354
  • 5,778
  • 12
  • 46
  • 83
  • Check the result of find class – geckos Sep 14 '19 at 02:47
  • @geckos I am able to print it – user1893354 Sep 14 '19 at 03:12
  • 1
    @user1893354 -- You really should, in your code, verify that the class method was found, and not claim without proof that the value was found. – PaulMcKenzie Sep 14 '19 at 03:40
  • @PaulMcKenzie You are right, ```mid``` evaluated to 0. But I don't know why, it's definitely a method. I will add an edit – user1893354 Sep 14 '19 at 03:59
  • 1
    You should check *all* of your return values, including the return of `FindClass`. – PaulMcKenzie Sep 14 '19 at 04:19
  • @PaulMcKenzie I added an additional edit showing how I checked. It looks like ```FindClass``` is working but ```GetMethodID``` is not. Also, I looked in the JAR and it looks like ```GameLoader``` is a ```public final class``` with a private constructor. I'm not sure if that makes a difference. – user1893354 Sep 14 '19 at 04:39
  • 5
    `GameLoader.listGames();` makes it look like `listGames` is a static method. In that case you should be using `GetStaticMethodID` instead of `GetMethodID`. And similarly, you should use `CallStaticObjectMethod` instead of `CallObjectMethod`. – Michael Sep 14 '19 at 06:44
  • Check your return values like before you use like you check your account balance before spend, both would save you from trouble – geckos Sep 14 '19 at 15:08
  • @Michael it looks like that worked. Thank you and feel free to add it as an answer and I will accept it. – user1893354 Sep 14 '19 at 18:23

0 Answers0