Rust JNI can't find static method of JasperFillManager class

Hello everyone.

I'm trying to call static method of class JasperFillManager
regarding documentation

it has signature

static JasperPrint fillReport(String sourceFileName, Map<String,Object> params, JRDataSource dataSource)

so I'm trying to call it with the next code

    let cls_jr_parameters = jenv.find_class("java/util/HashMap").unwrap();
    let obj_jr_parameters = jenv.new_object(cls_jr_parameters, "()V", &[]).unwrap();

    let cls_jr_empty_datasource = jenv
        .find_class("net/sf/jasperreports/engine/JREmptyDataSource")
        .unwrap();

    let obj_jr_empty_datasource = jenv
        .new_object(cls_jr_empty_datasource, "()V", &[])
        .unwrap();

    let cls_jasper_fill_manager = jenv
        .find_class("net/sf/jasperreports/engine/JasperFillManager")
        .unwrap();

    let str_jasper_file = jenv.new_string(jasper_file).unwrap();

    let obj_jasper_print = jenv
        .call_static_method(
            cls_jasper_fill_manager,
            "fillReport",
            "(Ljava/lang/String;Ljava/util/HashMap;Lnet/sf/jasperreports/engine/JRDataSource;)Lnet/sf/jasperreports/engine/JasperPrint;",
            &[
                JValue::Object(&str_jasper_file),
                JValue::Object(&obj_jr_parameters),
                JValue::Object(&obj_jr_empty_datasource),
            ],
        )
        .unwrap();

But got exception:

Exception in thread "Thread-0" java.lang.NoSuchMethodError: fillReport

Signature on rust side seems OK, any ideas what I'm doing wrong?

Thanks!

In Java, a package-private method (no private, protected or public keyword) can only be called from code in the same package. I assume your JNI code is not somehow in the same package -- I haven't used JNI but I assume this is not possible.

Nevermind. I see now that the method signature is from the javadoc, and the method is therefore public. I don't know why you can't call it.

I believe the Ljava/util/HashMap; parameter in your method signature needs to be Ljava/util/Map; to match the erased type of the actual static method parameter. For a simpler reproduction:

use jni::objects::{JObject, JValue};
use jni::{InitArgsBuilder, JNIVersion, JavaVM};

fn main() -> jni::errors::Result<()> {
    let jvm_args = InitArgsBuilder::new()
        .version(JNIVersion::V8)
        .option("-Xcheck:jni")
        .build()
        .unwrap();
    let jvm = JavaVM::new(jvm_args).unwrap();
    let mut env = jvm.attach_current_thread()?;
    let cls = env.find_class("java/util/Map")?;
    env.call_static_method(
        cls,
        "copyOf",
        "(Ljava/util/HashMap;)Ljava/util/Map;",
        &[JValue::Object(&JObject::null())],
    )?;
    Ok(())
}

fails with a NoSuchMethodError, but with Ljava/util/Map; it fails with the expected NullPointerException instead.

3 Likes

this code works fine, thanks

    let cls_jr_empty_datasource = jenv
        .find_class("net/sf/jasperreports/engine/JREmptyDataSource")
        .unwrap();

    let obj_jr_empty_datasource = jenv
        .new_object(cls_jr_empty_datasource, "()V", &[])
        .unwrap();

    let cls_jasper_fill_manager = jenv
        .find_class("net/sf/jasperreports/engine/JasperFillManager")
        .unwrap();

    let str_jasper_file = jenv.new_string(jasper_file).unwrap();

    let obj_jasper_print = jenv
        .call_static_method(
            cls_jasper_fill_manager,
            "fillReport",
            "(Ljava/lang/String;Ljava/util/Map;Lnet/sf/jasperreports/engine/JRDataSource;)Lnet/sf/jasperreports/engine/JasperPrint;",
            &[
                JValue::Object(&str_jasper_file),
                JValue::Object(&obj_jr_parameters),
                JValue::Object(&obj_jr_empty_datasource),
            ],
        )
        .unwrap().l().unwrap();