j4rs/api/
mod.rs

1// Copyright 2018 astonbitecode
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::any::{Any, TypeId};
16use std::collections::HashMap;
17use std::convert::TryFrom;
18use std::env;
19use std::ops::Drop;
20use std::os::raw::c_void;
21use std::path::{Path, PathBuf};
22use std::ptr;
23use std::sync::mpsc::channel;
24use std::{fs, thread, time};
25use std::borrow::Borrow;
26
27use jni_sys::{
28    self, jint, jobject, jsize, jstring, JNIEnv, JavaVM, JavaVMInitArgs, JavaVMOption,
29    JNI_EDETACHED, JNI_EEXIST, JNI_EINVAL, JNI_ENOMEM, JNI_ERR, JNI_EVERSION, JNI_OK, JNI_TRUE,
30    JNI_VERSION_1_6,
31};
32use libc::c_char;
33use serde::de::DeserializeOwned;
34
35use instance::{ChainableInstance, Instance, InstanceReceiver};
36
37use crate::{errors, set_java_vm};
38use crate::errors::{opt_to_res, J4RsError};
39use crate::jni_utils;
40use crate::provisioning;
41use crate::provisioning::{get_maven_settings, JavaArtifact, LocalJarArtifact, MavenArtifact};
42use crate::utils;
43use crate::{api_tweaks as tweaks, cache, InvocationArg, MavenSettings};
44
45use self::tweaks::cache_classloader_of;
46
47use super::logger::{debug, error, info, warn};
48
49pub(crate) mod instance;
50pub(crate) mod invocation_arg;
51
52// Initialize the environment
53include!(concat!(env!("OUT_DIR"), "/j4rs_init.rs"));
54
55const CLASS_STRING: &str = "java.lang.String";
56const CLASS_BOOLEAN: &str = "java.lang.Boolean";
57const CLASS_BYTE: &str = "java.lang.Byte";
58const CLASS_CHARACTER: &str = "java.lang.Character";
59const CLASS_SHORT: &str = "java.lang.Short";
60const CLASS_INTEGER: &str = "java.lang.Integer";
61const CLASS_LONG: &str = "java.lang.Long";
62const CLASS_FLOAT: &str = "java.lang.Float";
63const CLASS_DOUBLE: &str = "java.lang.Double";
64const CLASS_LIST: &str = "java.util.List";
65pub(crate) const PRIMITIVE_BOOLEAN: &str = "boolean";
66pub(crate) const PRIMITIVE_BYTE: &str = "byte";
67pub(crate) const PRIMITIVE_SHORT: &str = "short";
68pub(crate) const PRIMITIVE_INT: &str = "int";
69pub(crate) const PRIMITIVE_LONG: &str = "long";
70pub(crate) const PRIMITIVE_FLOAT: &str = "float";
71pub(crate) const PRIMITIVE_DOUBLE: &str = "double";
72pub(crate) const PRIMITIVE_CHAR: &str = "char";
73
74pub(crate) const PRIMITIVE_BOOLEAN_ARRAY: &str = "[Z";
75pub(crate) const PRIMITIVE_BYTE_ARRAY: &str = "[B";
76pub(crate) const PRIMITIVE_SHORT_ARRAY: &str = "[S";
77pub(crate) const PRIMITIVE_INT_ARRAY: &str = "[I";
78pub(crate) const PRIMITIVE_LONG_ARRAY: &str = "[J";
79pub(crate) const PRIMITIVE_FLOAT_ARRAY: &str = "[F";
80pub(crate) const PRIMITIVE_DOUBLE_ARRAY: &str = "[D";
81pub(crate) const PRIMITIVE_CHAR_ARRAY: &str = "[C";
82
83pub(crate) const CLASS_NATIVE_CALLBACK_TO_RUST_CHANNEL_SUPPORT: &str =
84    "org.astonbitecode.j4rs.api.invocation.NativeCallbackToRustChannelSupport";
85pub(crate) const CLASS_J4RS_EVENT_HANDLER: &str =
86    "org.astonbitecode.j4rs.api.jfx.handlers.J4rsEventHandler";
87pub(crate) const CLASS_J4RS_FXML_LOADER: &str =
88    "org.astonbitecode.j4rs.api.jfx.J4rsFxmlLoader";
89pub const _JNI_VERSION_10: jint = 0x000a0000;
90
91pub type Callback = fn(Jvm, Instance) -> ();
92
93/// Holds the assets for the JVM
94#[derive(Clone)]
95pub struct Jvm {
96    pub(crate) jni_env: *mut JNIEnv,
97    detach_thread_on_drop: bool,
98}
99
100impl Jvm {
101    /// Creates a new Jvm.
102    pub fn new(jvm_options: &[String], lib_name_to_load: Option<String>) -> errors::Result<Jvm> {
103        Self::create_jvm(jvm_options, lib_name_to_load)
104    }
105
106    /// Attaches the current thread to an active JavaVM
107    pub fn attach_thread() -> errors::Result<Jvm> {
108        Self::create_jvm(&[], None)
109    }
110
111    /// Attaches the current thread to an active JavaVM and instructs that the Jvm will detach the Java JVM
112    /// from the thread when the rust Jvm is dropped.
113    ///
114    /// This is useful when creating a Jvm while on a Thread that is created in the Java world.
115    /// When this Jvm is dropped, we don't want to detach the thread from the Java VM.
116    pub fn attach_thread_with_no_detach_on_drop() -> errors::Result<Jvm> {
117        let mut jvm = Jvm::attach_thread()?;
118        jvm.detach_thread_on_drop(false);
119        Ok(jvm)
120    }
121
122    /// If false, the thread will not be detached when the Jvm is being dropped.
123    /// This is useful when creating a Jvm while on a Thread that is created in the Java world.
124    /// When this Jvm is dropped, we don't want to detach the thread from the Java VM.
125    ///
126    /// It prevents errors like: `attempting to detach while still running code`
127    pub fn detach_thread_on_drop(&mut self, detach: bool) {
128        self.detach_thread_on_drop = detach;
129    }
130
131    /// Creates a new Jvm.
132    /// If a JavaVM is already created by the current process, it attempts to attach the current thread to it.
133    fn create_jvm(jvm_options: &[String], lib_name_to_load: Option<String>) -> errors::Result<Jvm> {
134        debug("Creating a Jvm");
135        let mut jvm: *mut JavaVM = ptr::null_mut();
136        let mut jni_environment: *mut JNIEnv = ptr::null_mut();
137
138        // Create the Jvm atomically
139        let _g = cache::MUTEX.lock()?;
140
141        let result = if let Some(env) = cache::get_thread_local_env_opt() {
142            debug("A JVM is already created for this thread. Retrieving it...");
143            jni_environment = env;
144
145            JNI_OK
146        } else {
147            let created_vm = Self::get_created_vm();
148
149            let res_int = if created_vm.is_some() {
150                debug("A JVM is already created by another thread. Retrieving it...");
151                jni_environment = created_vm.unwrap();
152
153                JNI_OK
154            } else {
155                info("No JVMs exist. Creating a new one...");
156                let mut cstrings_to_drop: Vec<*mut c_char> = Vec::with_capacity(jvm_options.len());
157                let mut jvm_options_vec: Vec<JavaVMOption> = jvm_options
158                    .iter()
159                    .map(|opt| {
160                        let cstr = utils::to_c_string(opt);
161                        let jo = JavaVMOption {
162                            optionString: cstr,
163                            extraInfo: ptr::null_mut() as *mut c_void,
164                        };
165                        cstrings_to_drop.push(cstr);
166                        jo
167                    })
168                    .collect();
169
170                let mut jvm_arguments = JavaVMInitArgs {
171                    version: JNI_VERSION_1_6,
172                    nOptions: jvm_options.len() as i32,
173                    options: jvm_options_vec.as_mut_ptr(),
174                    ignoreUnrecognized: JNI_TRUE,
175                };
176
177                let int_result = tweaks::create_java_vm(
178                    &mut jvm,
179                    (&mut jni_environment as *mut *mut JNIEnv) as *mut *mut c_void,
180                    (&mut jvm_arguments as *mut JavaVMInitArgs) as *mut c_void,
181                );
182
183                cstrings_to_drop
184                    .into_iter()
185                    .for_each(|s| unsafe {utils::drop_c_string(s)});
186
187                int_result
188            };
189
190            res_int
191        };
192
193        if result != JNI_OK {
194            let error_message = match result {
195                JNI_EDETACHED => "thread detached from the JVM",
196                JNI_EEXIST => "JVM already created",
197                JNI_EINVAL => "invalid arguments",
198                JNI_ENOMEM => "not enough memory",
199                JNI_ERR => "unknown error",
200                JNI_EVERSION => "JNI version error",
201                _ => "unknown JNI error value",
202            };
203
204            Err(J4RsError::JavaError(
205                format!("Could not create the JVM: {}", error_message).to_string(),
206            ))
207        } else {
208            let jvm = unsafe { Self::try_from(jni_environment)? };
209            if let Some(libname) = lib_name_to_load {
210                // Pass to the Java world the name of the j4rs library.
211                debug(&format!(
212                    "Initializing NativeCallbackSupport with libname {}",
213                    libname
214                ));
215                jvm.invoke_static(
216                    CLASS_NATIVE_CALLBACK_TO_RUST_CHANNEL_SUPPORT,
217                    "initialize",
218                    &[InvocationArg::try_from(libname)?],
219                )?;
220                debug("NativeCallbackSupport initialized");
221            }
222
223            Ok(jvm)
224        }
225    }
226
227    pub unsafe fn try_from(jni_environment: *mut JNIEnv) -> errors::Result<Jvm> {
228        if cache::get_thread_local_env_opt().is_none() {
229            // Create and set the environment in Thread Local
230            let _ = cache::get_jni_get_method_id().or_else(|| {
231                cache::set_jni_get_method_id(Some((**jni_environment).v1_6.GetMethodID))
232            });
233            let _ = cache::get_jni_get_static_method_id().or_else(|| {
234                cache::set_jni_get_static_method_id(Some(
235                    (**jni_environment).v1_6.GetStaticMethodID,
236                ))
237            });
238            let _ = cache::get_jni_new_object()
239                .or_else(|| cache::set_jni_new_object(Some((**jni_environment).v1_6.NewObject)));
240            let _ = cache::get_jni_new_string_utf().or_else(|| {
241                cache::set_jni_new_string_utf(Some((**jni_environment).v1_6.NewStringUTF))
242            });
243            let _ = cache::get_jni_get_string_utf_chars().or_else(|| {
244                cache::set_jni_get_string_utf_chars(Some(
245                    (**jni_environment).v1_6.GetStringUTFChars,
246                ))
247            });
248            let _ = cache::get_jni_release_string_utf_chars().or_else(|| {
249                cache::set_jni_release_string_utf_chars(Some(
250                    (**jni_environment).v1_6.ReleaseStringUTFChars,
251                ))
252            });
253            let _ = cache::get_jni_call_object_method().or_else(|| {
254                cache::set_jni_call_object_method(Some((**jni_environment).v1_6.CallObjectMethod))
255            });
256            let _ = cache::get_jni_call_boolean_method().or_else(|| {
257                cache::set_jni_call_boolean_method(Some((**jni_environment).v1_6.CallBooleanMethod))
258            });
259            let _ = cache::get_jni_call_byte_method().or_else(|| {
260                cache::set_jni_call_byte_method(Some((**jni_environment).v1_6.CallByteMethod))
261            });
262            let _ = cache::get_jni_call_short_method().or_else(|| {
263                cache::set_jni_call_short_method(Some((**jni_environment).v1_6.CallShortMethod))
264            });
265            let _ = cache::get_jni_call_char_method().or_else(|| {
266                cache::set_jni_call_char_method(Some((**jni_environment).v1_6.CallCharMethod))
267            });
268            let _ = cache::get_jni_call_int_method().or_else(|| {
269                cache::set_jni_call_int_method(Some((**jni_environment).v1_6.CallIntMethod))
270            });
271            let _ = cache::get_jni_call_long_method().or_else(|| {
272                cache::set_jni_call_long_method(Some((**jni_environment).v1_6.CallLongMethod))
273            });
274            let _ = cache::get_jni_call_float_method().or_else(|| {
275                cache::set_jni_call_float_method(Some((**jni_environment).v1_6.CallFloatMethod))
276            });
277            let _ = cache::get_jni_call_double_method().or_else(|| {
278                cache::set_jni_call_double_method(Some((**jni_environment).v1_6.CallDoubleMethod))
279            });
280            let _ = cache::get_jni_call_void_method().or_else(|| {
281                cache::set_jni_call_void_method(Some((**jni_environment).v1_6.CallVoidMethod))
282            });
283            let _ = cache::get_jni_call_static_object_method().or_else(|| {
284                cache::set_jni_call_static_object_method(Some(
285                    (**jni_environment).v1_6.CallStaticObjectMethod,
286                ))
287            });
288            let _ = cache::get_jni_get_array_length().or_else(|| {
289                cache::set_jni_get_array_length(Some(
290                    (**jni_environment).v1_6.GetArrayLength,
291                ))
292            });
293            let _ = cache::get_jni_get_byte_array_elements().or_else(|| {
294                cache::set_jni_get_byte_array_elements(Some(
295                    (**jni_environment).v1_6.GetByteArrayElements,
296                ))
297            });
298            let _ = cache::get_jni_release_byte_array_elements().or_else(|| {
299                cache::set_jni_release_byte_array_elements(Some(
300                    (**jni_environment).v1_6.ReleaseByteArrayElements,
301                ))
302            });
303            let _ = cache::get_jni_get_short_array_elements().or_else(|| {
304                cache::set_jni_get_short_array_elements(Some(
305                    (**jni_environment).v1_6.GetShortArrayElements,
306                ))
307            });
308            let _ = cache::get_jni_release_short_array_elements().or_else(|| {
309                cache::set_jni_release_short_array_elements(Some(
310                    (**jni_environment).v1_6.ReleaseShortArrayElements,
311                ))
312            });
313            let _ = cache::get_jni_get_char_array_elements().or_else(|| {
314                cache::set_jni_get_char_array_elements(Some(
315                    (**jni_environment).v1_6.GetCharArrayElements,
316                ))
317            });
318            let _ = cache::get_jni_release_char_array_elements().or_else(|| {
319                cache::set_jni_release_char_array_elements(Some(
320                    (**jni_environment).v1_6.ReleaseCharArrayElements,
321                ))
322            });
323            let _ = cache::get_jni_get_int_array_elements().or_else(|| {
324                cache::set_jni_get_int_array_elements(Some(
325                    (**jni_environment).v1_6.GetIntArrayElements,
326                ))
327            });
328            let _ = cache::get_jni_release_int_array_elements().or_else(|| {
329                cache::set_jni_release_int_array_elements(Some(
330                    (**jni_environment).v1_6.ReleaseIntArrayElements,
331                ))
332            });
333            let _ = cache::get_jni_get_long_array_elements().or_else(|| {
334                cache::set_jni_get_long_array_elements(Some(
335                    (**jni_environment).v1_6.GetLongArrayElements,
336                ))
337            });
338            let _ = cache::get_jni_release_long_array_elements().or_else(|| {
339                cache::set_jni_release_long_array_elements(Some(
340                    (**jni_environment).v1_6.ReleaseLongArrayElements,
341                ))
342            });
343            let _ = cache::get_jni_get_float_array_elements().or_else(|| {
344                cache::set_jni_get_float_array_elements(Some(
345                    (**jni_environment).v1_6.GetFloatArrayElements,
346                ))
347            });
348            let _ = cache::get_jni_release_float_array_elements().or_else(|| {
349                cache::set_jni_release_float_array_elements(Some(
350                    (**jni_environment).v1_6.ReleaseFloatArrayElements,
351                ))
352            });
353            let _ = cache::get_jni_get_double_array_elements().or_else(|| {
354                cache::set_jni_get_double_array_elements(Some(
355                    (**jni_environment).v1_6.GetDoubleArrayElements,
356                ))
357            });
358            let _ = cache::get_jni_release_double_array_elements().or_else(|| {
359                cache::set_jni_release_double_array_elements(Some(
360                    (**jni_environment).v1_6.ReleaseDoubleArrayElements,
361                ))
362            });
363            let _ = cache::get_jni_get_boolean_array_elements().or_else(|| {
364                cache::set_jni_get_boolean_array_elements(Some(
365                    (**jni_environment).v1_6.GetBooleanArrayElements,
366                ))
367            });
368            let _ = cache::get_jni_release_boolean_array_elements().or_else(|| {
369                cache::set_jni_release_boolean_array_elements(Some(
370                    (**jni_environment).v1_6.ReleaseBooleanArrayElements,
371                ))
372            });
373            let _ = cache::get_jni_new_object_array().or_else(|| {
374                cache::set_jni_new_object_array(Some((**jni_environment).v1_6.NewObjectArray))
375            });
376            let _ = cache::get_jni_set_object_array_element().or_else(|| {
377                cache::set_jni_set_object_array_element(Some(
378                    (**jni_environment).v1_6.SetObjectArrayElement,
379                ))
380            });
381            let ec = cache::get_jni_exception_check().or_else(|| {
382                cache::set_jni_exception_check(Some((**jni_environment).v1_6.ExceptionCheck))
383            });
384            let ed = cache::get_jni_exception_describe().or_else(|| {
385                cache::set_jni_exception_describe(Some((**jni_environment).v1_6.ExceptionDescribe))
386            });
387            let _ = cache::get_jni_exception_occured().or_else(|| {
388                cache::set_jni_exception_occured(Some((**jni_environment).v1_6.ExceptionOccurred))
389            });
390            let exclear = cache::get_jni_exception_clear().or_else(|| {
391                cache::set_jni_exception_clear(Some((**jni_environment).v1_6.ExceptionClear))
392            });
393            let _ = cache::get_jni_delete_local_ref().or_else(|| {
394                cache::set_jni_delete_local_ref(Some((**jni_environment).v1_6.DeleteLocalRef))
395            });
396            let _ = cache::get_jni_delete_global_ref().or_else(|| {
397                cache::set_jni_delete_global_ref(Some((**jni_environment).v1_6.DeleteGlobalRef))
398            });
399            let _ = cache::get_jni_new_global_ref().or_else(|| {
400                cache::set_jni_new_global_ref(Some((**jni_environment).v1_6.NewGlobalRef))
401            });
402            let _ = cache::get_jni_throw_new()
403                .or_else(|| cache::set_jni_throw_new(Some((**jni_environment).v1_6.ThrowNew)));
404            let _ = cache::get_is_same_object()
405                .or_else(|| cache::set_is_same_object(Some((**jni_environment).v1_6.IsSameObject)));
406
407            match (ec, ed, exclear) {
408                (Some(ec), Some(ed), Some(exclear)) => {
409                    if (ec)(jni_environment) == JNI_TRUE {
410                        (ed)(jni_environment);
411                        (exclear)(jni_environment);
412                        Err(J4RsError::JavaError(
413                            "The VM cannot be started... Please check the logs.".to_string(),
414                        ))
415                    } else {
416                        let jvm = Jvm {
417                            jni_env: jni_environment,
418                            detach_thread_on_drop: true,
419                        };
420
421                        cache::set_thread_local_env(Some(jni_environment));
422                        cache::add_active_jvm();
423
424                        Ok(jvm)
425                    }
426                }
427                (_, _, _) => Err(J4RsError::JniError("Could not initialize the JVM: Error while trying to retrieve JNI functions.".to_string())),
428            }
429        } else {
430            // Use the environment from the Thread Local
431            let jvm = Jvm {
432                jni_env: jni_environment,
433                detach_thread_on_drop: true,
434            };
435
436            cache::set_thread_local_env(Some(jni_environment));
437            cache::add_active_jvm();
438
439            Ok(jvm)
440        }
441    }
442
443    /// Creates an `Instance` of the class `class_name`, passing an array of `InvocationArg`s to construct the instance.
444    pub fn create_instance(
445        &self,
446        class_name: &str,
447        inv_args: &[impl Borrow<InvocationArg>],
448    ) -> errors::Result<Instance> {
449        debug(&format!(
450            "Instantiating class {} using {} arguments",
451            class_name,
452            inv_args.len()
453        ));
454        unsafe {
455            // Factory invocation - first argument: create a jstring to pass as argument for the class_name
456            let class_name_jstring: jstring =
457                jni_utils::global_jobject_from_str(class_name, self.jni_env)?;
458
459            // Factory invocation - rest of the arguments: Create a new objectarray of class InvocationArg
460            let size = inv_args.len() as i32;
461            let array_ptr = {
462                let j = (opt_to_res(cache::get_jni_new_object_array())?)(
463                    self.jni_env,
464                    size,
465                    cache::get_invocation_arg_class()?,
466                    ptr::null_mut(),
467                );
468                jni_utils::create_global_ref_from_local_ref(j, self.jni_env)?
469            };
470            let mut inv_arg_jobjects: Vec<jobject> = Vec::with_capacity(size as usize);
471
472            // Factory invocation - rest of the arguments: populate the array
473            for i in 0..size {
474                // Create an InvocationArg Java Object
475                let inv_arg_java =
476                    inv_args[i as usize].borrow().as_java_ptr_with_global_ref(self.jni_env)?;
477                // Set it in the array
478                (opt_to_res(cache::get_jni_set_object_array_element())?)(
479                    self.jni_env,
480                    array_ptr,
481                    i,
482                    inv_arg_java,
483                );
484                inv_arg_jobjects.push(inv_arg_java);
485            }
486            // Call the method of the factory that instantiates a new class of `class_name`.
487            // This returns a Instance that acts like a proxy to the Java world.
488            let java_instance = (opt_to_res(cache::get_jni_call_static_object_method())?)(
489                self.jni_env,
490                cache::get_factory_class()?,
491                cache::get_factory_instantiate_method()?,
492                class_name_jstring,
493                array_ptr,
494            );
495
496            // Check for exceptions before creating the globalref
497            Self::do_return(self.jni_env, ())?;
498
499            let java_instance_global_instance =
500                jni_utils::create_global_ref_from_local_ref(java_instance, self.jni_env)?;
501            // Prevent memory leaks from the created local references
502            jni_utils::delete_java_ref(self.jni_env, array_ptr);
503            jni_utils::delete_java_ref(self.jni_env, class_name_jstring);
504            for inv_arg_jobject in inv_arg_jobjects {
505                jni_utils::delete_java_ref(self.jni_env, inv_arg_jobject);
506            }
507
508            // Create and return the Instance
509            Self::do_return(
510                self.jni_env,
511                Instance {
512                    jinstance: java_instance_global_instance,
513                    class_name: class_name.to_string(),
514                    skip_deleting_jobject: false,
515                },
516            )
517        }
518    }
519
520    /// Retrieves the static class `class_name`.
521    pub fn static_class(&self, class_name: &str) -> errors::Result<Instance> {
522        debug(&format!("Retrieving static class {}", class_name));
523        unsafe {
524            // Factory invocation - first argument: create a jstring to pass as argument for the class_name
525            let class_name_jstring: jstring =
526                jni_utils::global_jobject_from_str(class_name, self.jni_env)?;
527
528            // Call the method of the factory that creates a Instance for static calls to methods of class `class_name`.
529            // This returns a Instance that acts like a proxy to the Java world.
530            let java_instance = (opt_to_res(cache::get_jni_call_static_object_method())?)(
531                self.jni_env,
532                cache::get_factory_class()?,
533                cache::get_factory_create_for_static_method()?,
534                class_name_jstring,
535            );
536
537            jni_utils::delete_java_ref(self.jni_env, class_name_jstring);
538
539            // Create and return the Instance.
540            Self::do_return(
541                self.jni_env,
542                Instance::from_jobject_with_global_ref(java_instance)?,
543            )
544        }
545    }
546
547    /// Creates a new Java Array with elements of the class `class_name`.
548    /// The array will have the `InvocationArg`s populated.
549    /// The `InvocationArg`s __must__ be of type _class_name_.
550    pub fn create_java_array(
551        &self,
552        class_name: &str,
553        inv_args: &[impl Borrow<InvocationArg>],
554    ) -> errors::Result<Instance> {
555        debug(&format!(
556            "Creating a java array of class {} with {} elements",
557            class_name,
558            inv_args.len()
559        ));
560        unsafe {
561            // Factory invocation - first argument: create a jstring to pass as argument for the class_name
562            let class_name_jstring: jstring =
563                jni_utils::global_jobject_from_str(class_name, self.jni_env)?;
564
565            // Factory invocation - rest of the arguments: Create a new objectarray of class InvocationArg
566            let size = inv_args.len() as i32;
567            let array_ptr = {
568                let j = (opt_to_res(cache::get_jni_new_object_array())?)(
569                    self.jni_env,
570                    size,
571                    cache::get_invocation_arg_class()?,
572                    ptr::null_mut(),
573                );
574                jni_utils::create_global_ref_from_local_ref(j, self.jni_env)?
575            };
576            let mut inv_arg_jobjects: Vec<jobject> = Vec::with_capacity(size as usize);
577
578            // Factory invocation - rest of the arguments: populate the array
579            for i in 0..size {
580                // Create an InvocationArg Java Object
581                let inv_arg_java =
582                    inv_args[i as usize].borrow().as_java_ptr_with_global_ref(self.jni_env)?;
583                // Set it in the array
584                (opt_to_res(cache::get_jni_set_object_array_element())?)(
585                    self.jni_env,
586                    array_ptr,
587                    i,
588                    inv_arg_java,
589                );
590                inv_arg_jobjects.push(inv_arg_java);
591            }
592            // Call the method of the factory that instantiates a new Java Array of `class_name`.
593            // This returns a Instance that acts like a proxy to the Java world.
594            let java_instance = (opt_to_res(cache::get_jni_call_static_object_method())?)(
595                self.jni_env,
596                cache::get_factory_class()?,
597                cache::get_factory_create_java_array_method()?,
598                class_name_jstring,
599                array_ptr,
600            );
601
602            // Check for exceptions before creating the globalref
603            Self::do_return(self.jni_env, ())?;
604
605            let java_instance_global_instance =
606                jni_utils::create_global_ref_from_local_ref(java_instance, self.jni_env)?;
607            // Prevent memory leaks from the created local references
608            for inv_arg_jobject in inv_arg_jobjects {
609                jni_utils::delete_java_ref(self.jni_env, inv_arg_jobject);
610            }
611            jni_utils::delete_java_ref(self.jni_env, array_ptr);
612            jni_utils::delete_java_ref(self.jni_env, class_name_jstring);
613
614            // Create and return the Instance
615            Self::do_return(
616                self.jni_env,
617                Instance {
618                    jinstance: java_instance_global_instance,
619                    class_name: class_name.to_string(),
620                    skip_deleting_jobject: false,
621                },
622            )
623        }
624    }
625
626    /// Creates a new Java List with elements of the class `inner_class_name`.
627    pub fn java_list<'a>(
628        &self,
629        inner_class_name: impl Into<&'a str>,
630        inv_args: Vec<impl TryInto<InvocationArg, Error=J4RsError>>,
631    ) -> errors::Result<Instance> {
632        let v: Result<Vec<InvocationArg>, J4RsError> =
633            inv_args.into_iter().map(|arg| arg.try_into()).collect();
634        Self::do_create_java_list(self.jni_env, inner_class_name.into(), v?.as_ref())
635    }
636
637    fn do_create_java_list(
638        jni_env: *mut JNIEnv,
639        class_name: &str,
640        inv_args: &[InvocationArg],
641    ) -> errors::Result<Instance> {
642        debug(&format!(
643            "Creating a java list of class {} with {} elements",
644            class_name,
645            inv_args.len()
646        ));
647        unsafe {
648            // Factory invocation - first argument: create a jstring to pass as argument for the class_name
649            let class_name_jstring: jstring =
650                jni_utils::global_jobject_from_str(class_name, jni_env)?;
651
652            // Factory invocation - rest of the arguments: Create a new object list of class InvocationArg
653            let size = inv_args.len() as i32;
654            let array_ptr = {
655                let j = (opt_to_res(cache::get_jni_new_object_array())?)(
656                    jni_env,
657                    size,
658                    cache::get_invocation_arg_class()?,
659                    ptr::null_mut(),
660                );
661                jni_utils::create_global_ref_from_local_ref(j, jni_env)?
662            };
663            let mut inv_arg_jobjects: Vec<jobject> = Vec::with_capacity(size as usize);
664
665            // Factory invocation - rest of the arguments: populate the array
666            for i in 0..size {
667                // Create an InvocationArg Java Object
668                let inv_arg_java = inv_args[i as usize].as_java_ptr_with_global_ref(jni_env)?;
669                // Set it in the array
670                (opt_to_res(cache::get_jni_set_object_array_element())?)(
671                    jni_env,
672                    array_ptr,
673                    i,
674                    inv_arg_java,
675                );
676                inv_arg_jobjects.push(inv_arg_java);
677            }
678            // Call the method of the factory that instantiates a new Java Array of `class_name`.
679            // This returns a Instance that acts like a proxy to the Java world.
680            let java_instance = (opt_to_res(cache::get_jni_call_static_object_method())?)(
681                jni_env,
682                cache::get_factory_class()?,
683                cache::get_factory_create_java_list_method()?,
684                class_name_jstring,
685                array_ptr,
686            );
687
688            // Check for exceptions before creating the globalref
689            Self::do_return(jni_env, ())?;
690
691            let java_instance_global_instance =
692                jni_utils::create_global_ref_from_local_ref(java_instance, jni_env)?;
693            // Prevent memory leaks from the created local references
694            for inv_arg_jobject in inv_arg_jobjects {
695                jni_utils::delete_java_ref(jni_env, inv_arg_jobject);
696            }
697            jni_utils::delete_java_ref(jni_env, array_ptr);
698            jni_utils::delete_java_ref(jni_env, class_name_jstring);
699
700            // Create and return the Instance
701            Self::do_return(
702                jni_env,
703                Instance {
704                    jinstance: java_instance_global_instance,
705                    class_name: class_name.to_string(),
706                    skip_deleting_jobject: false,
707                },
708            )
709        }
710    }
711
712    /// Creates a new Java Map with keys of class `key_class_name` and values of class `value_class_name`.
713    pub fn java_map<'a>(
714        &self,
715        key_class_name: impl Into<&'a str>,
716        value_class_name: impl Into<&'a str>,
717        inv_args: HashMap<
718            impl TryInto<InvocationArg, Error=J4RsError>,
719            impl TryInto<InvocationArg, Error=J4RsError>,
720        >,
721    ) -> errors::Result<Instance> {
722        let mut inv_args_results: Vec<Result<InvocationArg, J4RsError>> =
723            Vec::with_capacity(inv_args.len() * 2);
724        let mut i = 0;
725        let mut inv_args = inv_args;
726
727        for (key, val) in inv_args.drain() {
728            inv_args_results.insert(i, key.try_into());
729            i += 1;
730            inv_args_results.insert(i, val.try_into());
731            i += 1;
732        }
733        let inv_args: Result<Vec<InvocationArg>, J4RsError> = inv_args_results
734            .into_iter()
735            .map(|arg| arg.try_into())
736            .collect();
737        Self::do_create_java_map(
738            self.jni_env,
739            key_class_name.into(),
740            value_class_name.into(),
741            inv_args?.as_ref(),
742        )
743    }
744
745    fn do_create_java_map(
746        jni_env: *mut JNIEnv,
747        key_class_name: &str,
748        value_class_name: &str,
749        inv_args: &[InvocationArg],
750    ) -> errors::Result<Instance> {
751        debug(&format!(
752            "Creating a java map with keys of class {} and values of class {} with {} elements",
753            key_class_name,
754            value_class_name,
755            inv_args.len() / 2
756        ));
757        unsafe {
758            // Factory invocation - first argument: create a jstring to pass as argument for the key_class_name
759            let key_class_name_jstring: jstring =
760                jni_utils::global_jobject_from_str(key_class_name, jni_env)?;
761            // Factory invocation - second argument: create a jstring to pass as argument for the value_class_name
762            let value_class_name_jstring: jstring =
763                jni_utils::global_jobject_from_str(value_class_name, jni_env)?;
764
765            // Factory invocation - rest of the arguments: Create a new object list of class InvocationArg
766            let size = inv_args.len() as i32;
767            let array_ptr = {
768                let j = (opt_to_res(cache::get_jni_new_object_array())?)(
769                    jni_env,
770                    size,
771                    cache::get_invocation_arg_class()?,
772                    ptr::null_mut(),
773                );
774                jni_utils::create_global_ref_from_local_ref(j, jni_env)?
775            };
776            let mut inv_arg_jobjects: Vec<jobject> = Vec::with_capacity(size as usize);
777
778            // Factory invocation - rest of the arguments: populate the array
779            for i in 0..size {
780                // Create an InvocationArg Java Object
781                let inv_arg_java = inv_args[i as usize].as_java_ptr_with_global_ref(jni_env)?;
782                // Set it in the array
783                (opt_to_res(cache::get_jni_set_object_array_element())?)(
784                    jni_env,
785                    array_ptr,
786                    i,
787                    inv_arg_java,
788                );
789                inv_arg_jobjects.push(inv_arg_java);
790            }
791            // Call the method of the factory that instantiates a new Java Map with keys of `key_class_name`
792            // and values of `value_class_name`.
793            // This returns a Instance that acts like a proxy to the Java world.
794            let java_instance = (opt_to_res(cache::get_jni_call_static_object_method())?)(
795                jni_env,
796                cache::get_factory_class()?,
797                cache::get_factory_create_java_map_method()?,
798                key_class_name_jstring,
799                value_class_name_jstring,
800                array_ptr,
801            );
802
803            // Check for exceptions before creating the globalref
804            Self::do_return(jni_env, ())?;
805
806            let java_instance_global_instance =
807                jni_utils::create_global_ref_from_local_ref(java_instance, jni_env)?;
808            // Prevent memory leaks from the created local references
809            for inv_arg_jobject in inv_arg_jobjects {
810                jni_utils::delete_java_ref(jni_env, inv_arg_jobject);
811            }
812            jni_utils::delete_java_ref(jni_env, array_ptr);
813            jni_utils::delete_java_ref(jni_env, value_class_name_jstring);
814            jni_utils::delete_java_ref(jni_env, key_class_name_jstring);
815
816            // Create and return the Instance
817            Self::do_return(
818                jni_env,
819                Instance {
820                    jinstance: java_instance_global_instance,
821                    class_name: "".to_string(),
822                    skip_deleting_jobject: false,
823                },
824            )
825        }
826    }
827
828    /// Invokes the method `method_name` of a created `Instance`, passing an array of `InvocationArg`s. It returns an `Instance` as the result of the invocation.
829    pub fn invoke(
830        &self,
831        instance: &Instance,
832        method_name: &str,
833        inv_args: &[impl Borrow<InvocationArg>],
834    ) -> errors::Result<Instance> {
835        debug(&format!(
836            "Invoking method {} of class {} using {} arguments",
837            method_name,
838            instance.class_name,
839            inv_args.len()
840        ));
841        unsafe {
842            // First argument: create a jstring to pass as argument for the method_name
843            let method_name_jstring: jstring =
844                jni_utils::global_jobject_from_str(method_name, self.jni_env)?;
845
846            // Rest of the arguments: Create a new objectarray of class InvocationArg
847            let size = inv_args.len() as i32;
848            let array_ptr = {
849                let j = (opt_to_res(cache::get_jni_new_object_array())?)(
850                    self.jni_env,
851                    size,
852                    cache::get_invocation_arg_class()?,
853                    ptr::null_mut(),
854                );
855                jni_utils::create_global_ref_from_local_ref(j, self.jni_env)?
856            };
857            let mut inv_arg_jobjects: Vec<jobject> = Vec::with_capacity(size as usize);
858
859            // Rest of the arguments: populate the array
860            for i in 0..size {
861                // Create an InvocationArg Java Object
862                let inv_arg_java =
863                    inv_args[i as usize].borrow().as_java_ptr_with_global_ref(self.jni_env)?;
864                // Set it in the array
865                (opt_to_res(cache::get_jni_set_object_array_element())?)(
866                    self.jni_env,
867                    array_ptr,
868                    i,
869                    inv_arg_java,
870                );
871                inv_arg_jobjects.push(inv_arg_java);
872            }
873
874            // Call the method of the instance
875            let java_instance = (opt_to_res(cache::get_jni_call_object_method())?)(
876                self.jni_env,
877                instance.jinstance,
878                cache::get_invoke_method()?,
879                method_name_jstring,
880                array_ptr,
881            );
882
883            // Check for exceptions before creating the globalref
884            Self::do_return(self.jni_env, ())?;
885
886            let java_instance_global_instance =
887                jni_utils::create_global_ref_from_local_ref(java_instance, self.jni_env)?;
888            // Prevent memory leaks from the created local references
889            for inv_arg_jobject in inv_arg_jobjects {
890                jni_utils::delete_java_ref(self.jni_env, inv_arg_jobject);
891            }
892            jni_utils::delete_java_ref(self.jni_env, array_ptr);
893            jni_utils::delete_java_ref(self.jni_env, method_name_jstring);
894
895            // Create and return the Instance
896            Self::do_return(
897                self.jni_env,
898                Instance {
899                    jinstance: java_instance_global_instance,
900                    class_name: cache::UNKNOWN_FOR_RUST.to_string(),
901                    skip_deleting_jobject: false,
902                },
903            )
904        }
905    }
906
907    /// Retrieves the field `field_name` of a created `Instance`.
908    pub fn field(&self, instance: &Instance, field_name: &str) -> errors::Result<Instance> {
909        debug(&format!(
910            "Retrieving field {} of class {}",
911            field_name, instance.class_name
912        ));
913        unsafe {
914            // First argument: create a jstring to pass as argument for the field_name
915            let field_name_jstring: jstring =
916                jni_utils::global_jobject_from_str(field_name, self.jni_env)?;
917
918            // Call the method of the instance
919            let java_instance = (opt_to_res(cache::get_jni_call_object_method())?)(
920                self.jni_env,
921                instance.jinstance,
922                cache::get_field_method()?,
923                field_name_jstring,
924            );
925
926            // Check for exceptions before creating the globalref
927            Self::do_return(self.jni_env, ())?;
928
929            let java_instance_global_instance =
930                jni_utils::create_global_ref_from_local_ref(java_instance, self.jni_env)?;
931            // Prevent memory leaks from the created local references
932            jni_utils::delete_java_ref(self.jni_env, field_name_jstring);
933
934            // Create and return the Instance
935            Self::do_return(
936                self.jni_env,
937                Instance {
938                    jinstance: java_instance_global_instance,
939                    class_name: cache::UNKNOWN_FOR_RUST.to_string(),
940                    skip_deleting_jobject: false,
941                },
942            )
943        }
944    }
945
946    /// Retrieves the field `field_name` of a static class.
947    pub fn static_class_field(
948        &self,
949        class_name: &str,
950        field_name: &str,
951    ) -> errors::Result<Instance> {
952        debug(&format!(
953            "Retrieving field {} of static class {}",
954            field_name, class_name
955        ));
956        let i = self.static_class(class_name)?;
957        self.field(&i, field_name)
958    }
959
960    /// Invokes the method `method_name` of a created `Instance`, passing an array of `InvocationArg`s.
961    /// It returns a Result of `InstanceReceiver` that may be used to get an underlying `Receiver<Instance>`. The result of the invocation will come via this Receiver.
962    pub fn invoke_to_channel(
963        &self,
964        instance: &Instance,
965        method_name: &str,
966        inv_args: &[impl Borrow<InvocationArg>],
967    ) -> errors::Result<InstanceReceiver> {
968        debug(&format!("Invoking method {} of class {} using {} arguments. The result of the invocation will come via an InstanceReceiver", method_name, instance.class_name, inv_args.len()));
969        unsafe {
970            // Create the channel
971            let (sender, rx) = channel();
972            let tx = Box::new(sender);
973            // First argument: the address of the channel Sender
974            let raw_ptr = Box::into_raw(tx);
975            // Find the address of tx
976            let address_string = format!("{:p}", raw_ptr);
977            let address = u64::from_str_radix(&address_string[2..], 16).unwrap();
978
979            // Second argument: create a jstring to pass as argument for the method_name
980            let method_name_jstring: jstring =
981                jni_utils::global_jobject_from_str(method_name, self.jni_env)?;
982
983            // Rest of the arguments: Create a new objectarray of class InvocationArg
984            let size = inv_args.len() as i32;
985            let array_ptr = {
986                let j = (opt_to_res(cache::get_jni_new_object_array())?)(
987                    self.jni_env,
988                    size,
989                    cache::get_invocation_arg_class()?,
990                    ptr::null_mut(),
991                );
992                jni_utils::create_global_ref_from_local_ref(j, self.jni_env)?
993            };
994            let mut inv_arg_jobjects: Vec<jobject> = Vec::with_capacity(size as usize);
995
996            // Rest of the arguments: populate the array
997            for i in 0..size {
998                // Create an InvocationArg Java Object
999                let inv_arg_java =
1000                    inv_args[i as usize].borrow().as_java_ptr_with_global_ref(self.jni_env)?;
1001                // Set it in the array
1002                (opt_to_res(cache::get_jni_set_object_array_element())?)(
1003                    self.jni_env,
1004                    array_ptr,
1005                    i,
1006                    inv_arg_java,
1007                );
1008                inv_arg_jobjects.push(inv_arg_java);
1009            }
1010
1011            // Call the method of the instance
1012            (opt_to_res(cache::get_jni_call_void_method())?)(
1013                self.jni_env,
1014                instance.jinstance,
1015                cache::get_invoke_to_channel_method()?,
1016                address,
1017                method_name_jstring,
1018                array_ptr,
1019            );
1020
1021            // Check for exceptions before creating the globalref
1022            Self::do_return(self.jni_env, ())?;
1023
1024            // Prevent memory leaks from the created local references
1025            for inv_arg_jobject in inv_arg_jobjects {
1026                jni_utils::delete_java_ref(self.jni_env, inv_arg_jobject);
1027            }
1028            jni_utils::delete_java_ref(self.jni_env, array_ptr);
1029            jni_utils::delete_java_ref(self.jni_env, method_name_jstring);
1030
1031            // Create and return the Instance
1032            Self::do_return(self.jni_env, InstanceReceiver::new(rx, address))
1033        }
1034    }
1035
1036    /// Initializes a callback channel via a Java Instance that is a `NativeCallbackToRustChannelSupport`.
1037    /// It returns a Result of `InstanceReceiver` that may be used to get an underlying `Receiver<Instance>`.
1038    /// The `NativeCallbackToRustChannelSupport` Instance which is passed as argument, will be sending `Instance`s via this Receiver.
1039    pub fn init_callback_channel(&self, instance: &Instance) -> errors::Result<InstanceReceiver> {
1040        debug("Initializing callback channel");
1041        unsafe {
1042            // Create the channel
1043            let (sender, rx) = channel();
1044            let tx = Box::new(sender);
1045            // First argument: the address of the channel Sender
1046            let raw_ptr = Box::into_raw(tx);
1047            // Find the address of tx
1048            let address_string = format!("{:p}", raw_ptr);
1049            let address = u64::from_str_radix(&address_string[2..], 16).unwrap();
1050
1051            // Call the method of the instance
1052            (opt_to_res(cache::get_jni_call_void_method())?)(
1053                self.jni_env,
1054                instance.jinstance,
1055                cache::get_init_callback_channel_method()?,
1056                address,
1057            );
1058
1059            // Create and return the Instance
1060            Self::do_return(self.jni_env, InstanceReceiver::new(rx, address))
1061        }
1062    }
1063
1064    /// Invokes the static method `method_name` of the class `class_name`, passing an array of `InvocationArg`s. It returns an `Instance` as the result of the invocation.
1065    pub fn invoke_static(
1066        &self,
1067        class_name: &str,
1068        method_name: &str,
1069        inv_args: &[impl Borrow<InvocationArg>],
1070    ) -> errors::Result<Instance> {
1071        debug(&format!(
1072            "Invoking static method {} of class {} using {} arguments",
1073            method_name,
1074            class_name,
1075            inv_args.len()
1076        ));
1077        unsafe {
1078            // Factory invocation - first argument: create a jstring to pass as argument for the class_name
1079            let class_name_jstring: jstring =
1080                jni_utils::global_jobject_from_str(class_name, self.jni_env)?;
1081            // Call the method of the factory that creates a Instance for static calls to methods of class `class_name`.
1082            // This returns a Instance that acts like a proxy to the Java world.
1083            let tmp_java_instance = (opt_to_res(cache::get_jni_call_static_object_method())?)(
1084                self.jni_env,
1085                cache::get_factory_class()?,
1086                cache::get_factory_create_for_static_method()?,
1087                class_name_jstring,
1088            );
1089
1090            // First argument: create a jstring to pass as argument for the method_name
1091            let method_name_jstring: jstring =
1092                jni_utils::global_jobject_from_str(method_name, self.jni_env)?;
1093
1094            // Rest of the arguments: Create a new objectarray of class InvocationArg
1095            let size = inv_args.len() as i32;
1096            let array_ptr = {
1097                let j = (opt_to_res(cache::get_jni_new_object_array())?)(
1098                    self.jni_env,
1099                    size,
1100                    cache::get_invocation_arg_class()?,
1101                    ptr::null_mut(),
1102                );
1103                jni_utils::create_global_ref_from_local_ref(j, self.jni_env)?
1104            };
1105            let mut inv_arg_jobjects: Vec<jobject> = Vec::with_capacity(size as usize);
1106            // Rest of the arguments: populate the array
1107            for i in 0..size {
1108                // Create an InvocationArg Java Object
1109                let inv_arg_java =
1110                    inv_args[i as usize].borrow().as_java_ptr_with_global_ref(self.jni_env)?;
1111                // Set it in the array
1112                (opt_to_res(cache::get_jni_set_object_array_element())?)(
1113                    self.jni_env,
1114                    array_ptr,
1115                    i,
1116                    inv_arg_java,
1117                );
1118                inv_arg_jobjects.push(inv_arg_java);
1119            }
1120            // Call the method of the instance
1121            let java_instance = (opt_to_res(cache::get_jni_call_object_method())?)(
1122                self.jni_env,
1123                tmp_java_instance,
1124                cache::get_invoke_static_method()?,
1125                method_name_jstring,
1126                array_ptr,
1127            );
1128            // Delete temp ref
1129            jni_utils::delete_java_local_ref(self.jni_env, tmp_java_instance);
1130            jni_utils::delete_java_ref(self.jni_env, class_name_jstring);
1131            // Check for exceptions before creating the globalref
1132            Self::do_return(self.jni_env, ())?;
1133
1134            // Prevent memory leaks from the created local references
1135            for inv_arg_jobject in inv_arg_jobjects {
1136                jni_utils::delete_java_ref(self.jni_env, inv_arg_jobject);
1137            }
1138            jni_utils::delete_java_ref(self.jni_env, array_ptr);
1139            jni_utils::delete_java_ref(self.jni_env, method_name_jstring);
1140
1141            // Create and return the Instance.
1142            Self::do_return(
1143                self.jni_env,
1144                Instance::from_jobject_with_global_ref(java_instance)?,
1145            )
1146        }
1147    }
1148
1149    /// Creates a clone of the provided Instance
1150    pub fn clone_instance(&self, instance: &Instance) -> errors::Result<Instance> {
1151        unsafe {
1152            // Call the clone method
1153            let java_instance = (opt_to_res(cache::get_jni_call_static_object_method())?)(
1154                self.jni_env,
1155                cache::get_class_to_invoke_clone_and_cast()?,
1156                cache::get_clone_static_method()?,
1157                instance.jinstance,
1158            );
1159
1160            // Create and return the Instance
1161            Self::do_return(
1162                self.jni_env,
1163                Instance::from_jobject_with_global_ref(java_instance)?,
1164            )
1165        }
1166    }
1167
1168    /// Invokes the static method `method_name` of the class `class_name`, passing an array of `InvocationArg`s. It returns an `Instance` as the result of the invocation.
1169    pub fn cast(&self, from_instance: &Instance, to_class: &str) -> errors::Result<Instance> {
1170        debug(&format!("Casting to class {}", to_class));
1171        unsafe {
1172            // First argument is the jobject that is inside the from_instance
1173            // Second argument: create a jstring to pass as argument for the to_class
1174            let to_class_jstring: jstring =
1175                jni_utils::global_jobject_from_str(to_class, self.jni_env)?;
1176
1177            // Call the cast method
1178            let java_instance = (opt_to_res(cache::get_jni_call_static_object_method())?)(
1179                self.jni_env,
1180                cache::get_class_to_invoke_clone_and_cast()?,
1181                cache::get_cast_static_method()?,
1182                from_instance.jinstance,
1183                to_class_jstring,
1184            );
1185
1186            // Check for exceptions before creating the globalref
1187            Self::do_return(self.jni_env, ())?;
1188
1189            // Prevent memory leaks from the created local references
1190            jni_utils::delete_java_ref(self.jni_env, to_class_jstring);
1191
1192            // Create and return the Instance
1193            Self::do_return(
1194                self.jni_env,
1195                Instance::from_jobject_with_global_ref(java_instance)?,
1196            )
1197        }
1198    }
1199
1200    /// Checks whether an Instance a is equal to some InvocationArg. 
1201    /// 
1202    /// The check is actually against the Java `Object.equals`, taking into consideration the possibility of null.
1203    /// `NullPointerException` will not be thrown, even if one of the inputs is null.
1204    pub fn check_equals(&self, instance: impl Borrow<Instance>, inv_arg: impl Borrow<InvocationArg>) -> errors::Result<bool> {
1205        debug(&format!("Checking equality between instances of {} and {}", instance.borrow().class_name(), inv_arg.borrow().class_name()));
1206        unsafe {
1207            // Create InvocationArg Java Objects
1208            let inv_arg_java_b = inv_arg.borrow().as_java_ptr_with_global_ref(self.jni_env)?;
1209            // Call the checkEquals method
1210            let java_boolean = (opt_to_res(cache::get_jni_call_boolean_method())?)(
1211                self.jni_env,
1212                instance.borrow().jinstance,
1213                cache::get_check_equals_method()?,
1214                inv_arg_java_b,
1215            );
1216
1217            // Create and return the boolean
1218            Self::do_return(
1219                self.jni_env,
1220                java_boolean,
1221            )
1222        }
1223    }
1224
1225    /// Consumes an `Instance` and returns its jobject. The returned jobject is a JNI local reference.
1226    pub fn instance_into_raw_object(&self, instance: Instance) -> errors::Result<jobject> {
1227        debug(&format!("Getting the raw jobject from instance of class {}", instance.borrow().class_name()));
1228        // Call the getObjectMethod. This returns a localref
1229        let object_instance = unsafe {
1230            (opt_to_res(cache::get_jni_call_object_method())?)(
1231                self.jni_env,
1232                instance.jinstance,
1233                cache::get_get_object_method()?,
1234        )};
1235
1236        Self::do_return(
1237            self.jni_env,
1238            object_instance,
1239        )
1240    }
1241
1242    /// Consumes the `Jvm` and returns its `JNIEnv`
1243    pub fn into_raw(self) -> *mut JNIEnv {
1244        debug("Getting the raw JNIEnv from the Jvm");
1245
1246        self.jni_env
1247    }
1248
1249    /// Returns the Rust representation of the provided instance, boxed
1250    pub fn to_rust_boxed<T>(&self, instance: Instance) -> errors::Result<Box<T>>
1251        where
1252            T: DeserializeOwned + Any,
1253    {
1254        // Define the macro inside the function in order to have access to &self
1255        macro_rules! rust_box_from_java_object {
1256            ($jni_transformation:path) => {{
1257                // Call the getObjectMethod. This returns a localref
1258                let object_instance = (opt_to_res(cache::get_jni_call_object_method())?)(
1259                    self.jni_env,
1260                    instance.jinstance,
1261                    cache::get_get_object_method()?,
1262                );
1263                let object_instance =
1264                    jni_utils::create_global_ref_from_local_ref(object_instance, self.jni_env)?;
1265                let v = Box::new($jni_transformation(object_instance, self.jni_env)?);
1266                let v_any = v as Box<dyn Any>;
1267
1268                jni_utils::delete_java_ref(self.jni_env, object_instance);
1269
1270                match v_any.downcast::<T>() {
1271                    Ok(v) => Ok(v),
1272                    Err(error) => Err(errors::J4RsError::RustError(format!(
1273                        "Could not downcast to Rust type: {:?}",
1274                        error
1275                    ))),
1276                }
1277            }};
1278        }
1279
1280        let t_type = TypeId::of::<T>();
1281        
1282
1283        unsafe {
1284            // Call the getClassName method. This returns a localref
1285            let object_class_name_instance = (opt_to_res(cache::get_jni_call_object_method())?)(
1286                self.jni_env,
1287                instance.jinstance,
1288                cache::get_get_object_class_name_method()?,
1289            );
1290            let object_class_name_instance = jni_utils::create_global_ref_from_local_ref(
1291                object_class_name_instance,
1292                self.jni_env,
1293            )?;
1294            let class_name = &(jni_utils::string_from_jobject(object_class_name_instance, self.jni_env)?);
1295            jni_utils::delete_java_ref(self.jni_env, object_class_name_instance);
1296            if t_type == TypeId::of::<String>() && JavaClass::String.get_class_str() == class_name {
1297                rust_box_from_java_object!(jni_utils::string_from_jobject)
1298            } else if t_type == TypeId::of::<i32>()
1299                && (JavaClass::Integer.get_class_str() == class_name || PRIMITIVE_INT == class_name)
1300            {
1301                rust_box_from_java_object!(jni_utils::i32_from_jobject)
1302            } else if t_type == TypeId::of::<i8>()
1303                && (JavaClass::Byte.get_class_str() == class_name || PRIMITIVE_BYTE == class_name)
1304            {
1305                rust_box_from_java_object!(jni_utils::i8_from_jobject)
1306            } else if t_type == TypeId::of::<i16>()
1307                && (JavaClass::Short.get_class_str() == class_name || PRIMITIVE_SHORT == class_name)
1308            {
1309                rust_box_from_java_object!(jni_utils::i16_from_jobject)
1310            }  else if t_type == TypeId::of::<u16>()
1311                && (JavaClass::Character.get_class_str() == class_name || PRIMITIVE_CHAR == class_name)
1312            {
1313                rust_box_from_java_object!(jni_utils::u16_from_jobject)
1314            } else if t_type == TypeId::of::<i64>()
1315                && (JavaClass::Long.get_class_str() == class_name || PRIMITIVE_LONG == class_name)
1316            {
1317                rust_box_from_java_object!(jni_utils::i64_from_jobject)
1318            } else if t_type == TypeId::of::<f32>()
1319                && (JavaClass::Float.get_class_str() == class_name || PRIMITIVE_FLOAT == class_name)
1320            {
1321                rust_box_from_java_object!(jni_utils::f32_from_jobject)
1322            } else if t_type == TypeId::of::<f64>()
1323                && (JavaClass::Double.get_class_str() == class_name
1324                || PRIMITIVE_DOUBLE == class_name)
1325            {
1326                rust_box_from_java_object!(jni_utils::f64_from_jobject)
1327            } else if t_type == TypeId::of::<Vec<i8>>()
1328                && PRIMITIVE_BYTE_ARRAY == class_name
1329            {
1330                rust_box_from_java_object!(jni_utils::i8_array_from_jobject)
1331            } else if t_type == TypeId::of::<Vec<i16>>()
1332                && PRIMITIVE_SHORT_ARRAY == class_name
1333            {
1334                rust_box_from_java_object!(jni_utils::i16_array_from_jobject)
1335            } else if t_type == TypeId::of::<Vec<u16>>()
1336                && PRIMITIVE_CHAR_ARRAY == class_name
1337            {
1338                rust_box_from_java_object!(jni_utils::u16_array_from_jobject)
1339            } else if t_type == TypeId::of::<Vec<i32>>()
1340                && PRIMITIVE_INT_ARRAY == class_name
1341            {
1342                rust_box_from_java_object!(jni_utils::i32_array_from_jobject)
1343            } else if t_type == TypeId::of::<Vec<i64>>()
1344                && PRIMITIVE_LONG_ARRAY == class_name
1345            {
1346                rust_box_from_java_object!(jni_utils::i64_array_from_jobject)
1347            } else if t_type == TypeId::of::<Vec<f32>>()
1348                && PRIMITIVE_FLOAT_ARRAY == class_name
1349            {
1350                rust_box_from_java_object!(jni_utils::f32_array_from_jobject)
1351            } else if t_type == TypeId::of::<Vec<f64>>()
1352                && PRIMITIVE_DOUBLE_ARRAY == class_name
1353            {
1354                rust_box_from_java_object!(jni_utils::f64_array_from_jobject)
1355            } else if t_type == TypeId::of::<Vec<bool>>()
1356                && PRIMITIVE_BOOLEAN_ARRAY == class_name
1357            {
1358                rust_box_from_java_object!(jni_utils::boolean_array_from_jobject)
1359            } else {
1360                Ok(Box::new(self.to_rust_deserialized(instance)?))
1361            }
1362        }
1363    }
1364
1365    /// Returns the Rust representation of the provided instance
1366    pub fn to_rust<T>(&self, instance: Instance) -> errors::Result<T>
1367        where
1368            T: DeserializeOwned + Any,
1369    {
1370        self.to_rust_boxed(instance).map(|v| *v)
1371    }
1372
1373    pub fn to_rust_deserialized<T>(&self, instance: Instance) -> errors::Result<T>
1374        where
1375            T: DeserializeOwned + Any,
1376    {
1377        unsafe {
1378            debug("Invoking the getJson method");
1379            // Call the getJson method. This returns a localref
1380            let json_instance = (opt_to_res(cache::get_jni_call_object_method())?)(
1381                self.jni_env,
1382                instance.jinstance,
1383                cache::get_get_json_method()?,
1384            );
1385            let _ = Self::do_return(self.jni_env, "")?;
1386            debug("Transforming jstring to rust String");
1387            let global_json_instance =
1388                jni_utils::create_global_ref_from_local_ref(json_instance, self.jni_env)?;
1389            let json = jni_utils::jstring_to_rust_string(self, global_json_instance as jstring)?;
1390            jni_utils::delete_java_ref(self.jni_env, global_json_instance);
1391            Self::do_return(self.jni_env, serde_json::from_str(&json)?)
1392        }
1393    }
1394
1395    /// Deploys an artifact in the default j4rs jars location.
1396    ///
1397    /// This is useful for build scripts that need jars for the runtime that can be downloaded from e.g. Maven.
1398    ///
1399    /// The function deploys __only__ the specified artifact, not its transitive dependencies.
1400    pub fn deploy_artifact<T: Any + JavaArtifact>(&self, artifact: &T) -> errors::Result<()> {
1401        let artifact = artifact as &dyn Any;
1402        if let Some(maven_artifact) = artifact.downcast_ref::<MavenArtifact>() {
1403            for repo in get_maven_settings().repos.into_iter() {
1404                let instance = self.create_instance(
1405                    "org.astonbitecode.j4rs.api.deploy.SimpleMavenDeployer",
1406                    &[InvocationArg::try_from(repo.uri)?,
1407                        InvocationArg::try_from(&maven_artifact.base)?],
1408                )?;
1409
1410                let res = self.invoke(
1411                    &instance,
1412                    "deploy",
1413                    &vec![
1414                        InvocationArg::try_from(&maven_artifact.group)?,
1415                        InvocationArg::try_from(&maven_artifact.id)?,
1416                        InvocationArg::try_from(&maven_artifact.version)?,
1417                        InvocationArg::try_from(&maven_artifact.qualifier)?,
1418                    ],
1419                );
1420
1421                if res.is_ok() {
1422                    break;
1423                }
1424            }
1425
1426            Ok(())
1427        } else if let Some(local_jar_artifact) = artifact.downcast_ref::<LocalJarArtifact>() {
1428            let instance = self.create_instance(
1429                "org.astonbitecode.j4rs.api.deploy.FileSystemDeployer",
1430                &[InvocationArg::try_from(&local_jar_artifact.base)?],
1431            )?;
1432
1433            let _ = self.invoke(
1434                &instance,
1435                "deploy",
1436                &[InvocationArg::try_from(&local_jar_artifact.path)?],
1437            )?;
1438            Ok(())
1439        } else {
1440            Err(J4RsError::GeneralError(format!(
1441                "Don't know how to deploy artifacts of {:?}",
1442                artifact.type_id()
1443            )))
1444        }
1445    }
1446
1447    /// Copies the jassets default directory and the j4rs dynamic library under the specified
1448    /// location.
1449    /// This is useful for cases when `with_base_path` method is used when building a Jvm with
1450    /// the JvmBuilder.
1451    /// Build scripts should use this method.
1452    pub fn copy_j4rs_libs_under<P: AsRef<Path>>(path: P) -> errors::Result<()> {
1453        let mut pb = PathBuf::from(path.as_ref());
1454        pb.push("deps");
1455        fs::create_dir_all(&pb)?;
1456
1457        let default_jassets_path_buf = utils::default_jassets_path()?;
1458        let default_jassets_path_string = default_jassets_path_buf.to_str().unwrap().to_owned();
1459
1460        // Copy the jassets
1461        let options = &mut fs_extra::dir::CopyOptions::new();
1462        options.overwrite = true;
1463        let _ = fs_extra::copy_items(vec![default_jassets_path_string].as_ref(), path, options)?;
1464
1465        // Copy the dynamic libraries
1466        let dynlibs: Vec<String> = {
1467            let mut dynlibs = vec![];
1468            // We try every 1 second for 10 iterations because on most systems, cargo will
1469            // parallelize the build and the dynlib might not be created yet.
1470            for _i in 0..10 {
1471                dynlibs = utils::find_j4rs_dynamic_libraries_paths()?;
1472                if dynlibs.is_empty() {
1473                    thread::sleep(time::Duration::from_millis(1000));
1474                } else {
1475                    break;
1476                }
1477            }
1478            dynlibs
1479        };
1480        if dynlibs.is_empty() {
1481            let message = format!(
1482                "No j4rs dynamic libraries found for target triple {}. \
1483                                  The host triple during build is {}.",
1484                env::var("TARGET").unwrap_or("".to_string()),
1485                env::var("HOST").unwrap_or("UNKNOWN".to_string())
1486            );
1487            println!("cargo:warning={}", message);
1488        }
1489
1490        let _ = fs_extra::copy_items(&dynlibs, &pb, options)?;
1491
1492        Ok(())
1493    }
1494
1495    /// Initiates a chain of operations on Instances.
1496    pub fn chain(&self, instance: &Instance) -> errors::Result<ChainableInstance<'_>> {
1497        ChainableInstance::new_with_instance_ref(instance, self)
1498    }
1499
1500    /// Initiates a chain of operations on Instances.
1501    pub fn into_chain(&self, instance: Instance) -> ChainableInstance<'_> {
1502        ChainableInstance::new(instance, self)
1503    }
1504
1505    /// Throws an exception in the Java World
1506    pub fn throw_invocation_exception(&self, message: &str) -> errors::Result<()> {
1507        unsafe {
1508            let _ = jni_utils::throw_exception(message, self.jni_env)?;
1509        }
1510        Ok(())
1511    }
1512
1513    pub(crate) fn do_return<T>(jni_env: *mut JNIEnv, to_return: T) -> errors::Result<T> {
1514        unsafe {
1515            if (opt_to_res(cache::get_jni_exception_check())?)(jni_env) == JNI_TRUE {
1516                let throwable = (opt_to_res(cache::get_jni_exception_occured())?)(jni_env);
1517                let throwable_string = Self::get_throwable_string(throwable, jni_env)?;
1518                (opt_to_res(cache::get_jni_exception_clear())?)(jni_env);
1519                Err(J4RsError::JavaError(throwable_string))
1520            } else {
1521                Ok(to_return)
1522            }
1523        }
1524    }
1525
1526    unsafe fn get_throwable_string(throwable: jobject, jni_env: *mut JNIEnv) -> errors::Result<String> {
1527        let java_string = (opt_to_res(cache::get_jni_call_static_object_method())?)(
1528            jni_env,
1529            cache::get_utils_class()?,
1530            cache::get_utils_exception_to_string_method()?,
1531            throwable,
1532        );
1533        let to_ret = jni_utils::string_from_jobject(java_string, jni_env);
1534        jni_utils::delete_java_local_ref(jni_env, java_string);
1535        to_ret
1536    }
1537
1538    // Retrieves a JNIEnv in the case that a JVM is already created even from another thread.
1539    fn get_created_vm() -> Option<*mut JNIEnv> {
1540        unsafe {
1541            // Get the number of the already created VMs. This is most probably 1, but we retrieve the number just in case...
1542            let mut created_vms_size: jsize = 0;
1543            tweaks::get_created_java_vms(
1544                &mut Vec::with_capacity(created_vms_size as usize),
1545                0,
1546                &mut created_vms_size,
1547            );
1548
1549            if created_vms_size == 0 {
1550                None
1551            } else {
1552                debug(&format!(
1553                    "Retrieving the first of {} created JVMs",
1554                    created_vms_size
1555                ));
1556                // Get the created VM (use 2 just in case... :) )
1557                let mut buffer: Vec<*mut JavaVM> = Vec::with_capacity(2);
1558                for _ in 0..created_vms_size {
1559                    buffer.push(ptr::null_mut());
1560                }
1561
1562                let retjint = tweaks::get_created_java_vms(
1563                    &mut buffer,
1564                    created_vms_size,
1565                    &mut created_vms_size,
1566                );
1567                if retjint == JNI_OK {
1568                    let act = (**buffer[0]).v1_4.AttachCurrentThread;
1569                    let mut jni_environment: *mut JNIEnv = ptr::null_mut();
1570                    (act)(
1571                        buffer[0],
1572                        (&mut jni_environment as *mut *mut JNIEnv) as *mut *mut c_void,
1573                        ptr::null_mut(),
1574                    );
1575                    Some(jni_environment)
1576                } else {
1577                    error(&format!(
1578                        "Error while retrieving the created JVMs: {}",
1579                        retjint
1580                    ));
1581                    None
1582                }
1583            }
1584        }
1585    }
1586
1587    fn detach_current_thread(&self) {
1588        unsafe {
1589            // Get the number of the already created VMs. This is most probably 1, but we retrieve the number just in case...
1590            let mut created_vms_size: jsize = 0;
1591            tweaks::get_created_java_vms(
1592                &mut Vec::with_capacity(created_vms_size as usize),
1593                0,
1594                &mut created_vms_size,
1595            );
1596
1597            if created_vms_size > 0 {
1598                // Get the created VM
1599                let mut buffer: Vec<*mut JavaVM> = Vec::with_capacity(created_vms_size as usize);
1600                for _ in 0..created_vms_size {
1601                    buffer.push(ptr::null_mut());
1602                }
1603
1604                let retjint = tweaks::get_created_java_vms(
1605                    &mut buffer,
1606                    created_vms_size,
1607                    &mut created_vms_size,
1608                );
1609                if retjint == JNI_OK {
1610                    let dct = (**buffer[0]).v1_4.DetachCurrentThread;
1611                    (dct)(buffer[0]);
1612                } else {
1613                    warn(&format!(
1614                        "Error while retrieving the created JVMs: {}",
1615                        retjint
1616                    ));
1617                }
1618            }
1619        }
1620    }
1621
1622    /// Returns the first `Instance` that is available from the passed `InstanceReceiver`s,
1623    /// along with the index of the receiver that was selected and actually returned the instance.
1624    ///
1625    /// This is a mostly naive implementation of select, because of [absence for selecting among mpsc channels](https://github.com/rust-lang/rust/issues/27800).
1626    pub fn select(instance_receivers: &[&InstanceReceiver]) -> errors::Result<(usize, Instance)> {
1627        loop {
1628            for (index, ir) in instance_receivers.iter().enumerate() {
1629                let res = ir.rx.try_recv();
1630                if res.is_ok() {
1631                    return Ok((index, res.unwrap()));
1632                }
1633            }
1634            thread::yield_now();
1635        }
1636    }
1637
1638    /// Returns the first `Instance` that is available from the passed `InstanceReceiver`s,
1639    /// along with the index of the receiver that was selected and actually returned the instance.
1640    ///
1641    /// If there are no instances returned for the duration defined in timeout argument, an error is returned.
1642    ///
1643    /// This is a mostly naive implementation of select, because of [absence for selecting among mpsc channels](https://github.com/rust-lang/rust/issues/27800).
1644    pub fn select_timeout(
1645        instance_receivers: &[&InstanceReceiver],
1646        timeout: &time::Duration,
1647    ) -> errors::Result<(usize, Instance)> {
1648        let start = time::Instant::now();
1649        loop {
1650            for (index, ir) in instance_receivers.iter().enumerate() {
1651                let res = ir.rx.try_recv();
1652                if res.is_ok() {
1653                    return Ok((index, res.unwrap()));
1654                }
1655            }
1656            if &start.elapsed() > timeout {
1657                return Err(J4RsError::Timeout);
1658            }
1659            thread::yield_now();
1660        }
1661    }
1662}
1663
1664impl Drop for Jvm {
1665    fn drop(&mut self) {
1666        if cache::remove_active_jvm() <= 0 {
1667            if self.detach_thread_on_drop {
1668                self.detach_current_thread();
1669            }
1670            cache::set_thread_local_env(None);
1671        }
1672    }
1673}
1674
1675/// A builder for Jvm
1676pub struct JvmBuilder<'a> {
1677    classpath_entries: Vec<ClasspathEntry>,
1678    java_opts: Vec<JavaOpt<'a>>,
1679    no_implicit_classpath: bool,
1680    detach_thread_on_drop: bool,
1681    lib_name_opt: Option<String>,
1682    skip_setting_native_lib: bool,
1683    base_path: Option<PathBuf>,
1684    maven_settings: MavenSettings,
1685    javafx: bool,
1686    default_classloader: bool,
1687    java_vm_opt: Option<*mut JavaVM>,
1688    jobject_within_valid_classloader_opt: Option<jobject>,
1689}
1690
1691impl<'a> JvmBuilder<'a> {
1692    /// Creates a new JvmBuilder.
1693    pub fn new<'b>() -> JvmBuilder<'b> {
1694        JvmBuilder {
1695            classpath_entries: Vec::new(),
1696            java_opts: Vec::new(),
1697            no_implicit_classpath: false,
1698            detach_thread_on_drop: true,
1699            lib_name_opt: None,
1700            skip_setting_native_lib: false,
1701            base_path: None,
1702            maven_settings: MavenSettings::default(),
1703            javafx: false,
1704            default_classloader: false,
1705            java_vm_opt: None,
1706            jobject_within_valid_classloader_opt: None
1707        }
1708    }
1709
1710    /// Adds a classpath entry.
1711    pub fn classpath_entry(&'a mut self, cp_entry: ClasspathEntry) -> &'a mut JvmBuilder<'a> {
1712        self.classpath_entries.push(cp_entry);
1713        self
1714    }
1715
1716    /// Adds classpath entries.
1717    pub fn classpath_entries(
1718        &'a mut self,
1719        cp_entries: Vec<ClasspathEntry>,
1720    ) -> &'a mut JvmBuilder<'a> {
1721        for cp_entry in cp_entries {
1722            self.classpath_entries.push(cp_entry);
1723        }
1724        self
1725    }
1726
1727    /// Adds a Java option.
1728    pub fn java_opt(&'a mut self, opt: JavaOpt<'a>) -> &'a mut JvmBuilder<'a> {
1729        self.java_opts.push(opt);
1730        self
1731    }
1732
1733    /// Adds Java options.
1734    pub fn java_opts(&'a mut self, opts: Vec<JavaOpt<'a>>) -> &'a mut JvmBuilder<'a> {
1735        for opt in opts {
1736            self.java_opts.push(opt);
1737        }
1738        self
1739    }
1740
1741    /// By default, the created `Jvm`s include an implicit classpath entry that includes the j4rs jar.
1742    /// When `with_no_implicit_classpath()` is called, this classpath will not be added to the Jvm.
1743    pub fn with_no_implicit_classpath(&'a mut self) -> &'a mut JvmBuilder<'a> {
1744        self.no_implicit_classpath = true;
1745        self
1746    }
1747
1748    /// When a Jvm goes out of scope and is being dropped, its current thread is being detached from the Java VM.
1749    /// A Jvm that is created with `detach_thread_on_drop(false)` will not detach the thread when being dropped.
1750    ///
1751    /// This is useful when in the Java world a native method is called and in the native code someone needs to create a j4rs Jvm.
1752    /// If that Jvm detaches its current thread when being dropped, there will be problems for the Java world code to continue executing.
1753    pub fn detach_thread_on_drop(&'a mut self, detach_thread_on_drop: bool) -> &'a mut JvmBuilder<'a> {
1754        self.detach_thread_on_drop = detach_thread_on_drop;
1755        self
1756    }
1757
1758    /// In the case that the j4rs is statically linked to some other library, the Java world (j4rs.jar) needs to load that
1759    /// library instead of the default one.
1760    ///
1761    /// This function defines the native library name to load.
1762    pub fn with_native_lib_name(&'a mut self, lib_name: &str) -> &'a mut JvmBuilder<'a> {
1763        self.lib_name_opt = Some(lib_name.to_string());
1764        self
1765    }
1766
1767    /// Configures the builder not to instruct the Java world j4rs code to load the native library.
1768    /// (most probably because it is already loaded)
1769    pub fn skip_setting_native_lib(&'a mut self) -> &'a mut JvmBuilder<'a> {
1770        self.skip_setting_native_lib = true;
1771        self
1772    }
1773
1774    /// Defines the location of the jassets and deps directory.
1775    /// The jassets contains the j4rs jar and the deps the j4rs dynamic library.
1776    pub fn with_base_path<P: AsRef<Path>>(&'a mut self, base_path: P) -> &'a mut JvmBuilder<'a> {
1777        self.base_path = Some(base_path.as_ref().to_owned());
1778        self
1779    }
1780
1781    /// Defines the maven settings to use for provisioning maven artifacts.
1782    pub fn with_maven_settings(&'a mut self, maven_settings: MavenSettings) -> &'a mut JvmBuilder<'a> {
1783        self.maven_settings = maven_settings;
1784        self
1785    }
1786
1787    /// Adds JavaFX support to the created JVM
1788    pub fn with_javafx_support(&'a mut self) -> &'a mut JvmBuilder<'a> {
1789        self.javafx = true;
1790        self
1791    }
1792
1793    /// Create the j4rs `Jvm` using an already created jni `JavaVM`.
1794    /// 
1795    /// Useful for Android apps, where the JVM is automatically created.
1796    pub fn with_java_vm(&'a mut self, java_vm: *mut JavaVM) -> &'a mut JvmBuilder<'a> {
1797        self.java_vm_opt = Some(java_vm);
1798        self
1799    }
1800
1801    /// Instructs j4rs to use the classloader associated to the specified `jobject` when searching for classes.
1802    /// 
1803    /// Useful for pure native Android apps.
1804    /// 
1805    /// As described in Android documentation, the native libraries that spawn new threads cannot locate and use Java classes correctly.
1806    ///
1807    /// From the [docs]((https://developer.android.com/training/articles/perf-jni#faq:-why-didnt-findclass-find-my-class)):
1808    /// 
1809    /// > You can get into trouble if you create a thread yourself (perhaps by calling pthread_create and then attaching it with AttachCurrentThread). 
1810    /// > Now there are no stack frames from your application. If you call FindClass from this thread, the JavaVM will start in the "system" class loader 
1811    /// > instead of the one associated with your application, so attempts to find app-specific classes will fail.
1812    /// 
1813    /// Even if it is proposed to load and cache classes during `JNI_OnLoad` (j4rs documentation about it [here](https://github.com/astonbitecode/j4rs?tab=readme-ov-file#j4rs-in-android))
1814    /// `JNI_OnLoad` is not called on pure native apps. One solution to find the app-specific classes in this case, is to use the classloader 
1815    /// of the `Activity` to find and use classes. `j4rs` will use the `jobject` passed here (the `jobject` of the `Activity`) to get the 
1816    /// proper classloader and use it when needed.
1817    /// #[cfg(target_os = "android")]
1818    pub fn with_classloader_of_activity(&'a mut self, jobject_within_valid_classloader: jobject) -> &'a mut JvmBuilder<'a> {
1819        self.jobject_within_valid_classloader_opt = Some(jobject_within_valid_classloader);
1820        // If the `jobject_within_valid_classloader_opt` is provided, it means that the object's classloader
1821        // should be used to load classes in case the traditional `FindClass` invocations fail.
1822        // Apply here the needed configuration for it to happen.
1823        // Do not detach the thread on drop. This would make the Activity to fail.
1824        let tmp = self.detach_thread_on_drop(false);
1825        let tmp = tmp.with_no_implicit_classpath();
1826        tmp
1827    }
1828
1829    /// `j4rs` uses a custom ClassLoader (namely the `J4rsClassLoader`),
1830    /// that allows adding jars to the classpath during runtime, after the underlying `JVM` is initialized.
1831    ///
1832    /// This function instructs the builder not to use this custom classloader, but use the default one.
1833    ///
1834    /// Please note that the `J4rsClassLoader` needs Java 9 or higher. If you use an older Java version,
1835    /// you __must__ call this function in order for `j4rs` to work.
1836    ///
1837    /// If not, you will get exceptions like the following:
1838    ///
1839    /// ```text
1840    /// java.lang.NoSuchMethodError: java.net.URLClassLoader.`<init>`(Ljava/lang/String;[Ljava/net/URL;Ljava/lang/ClassLoader;)V
1841    ///         at org.astonbitecode.j4rs.api.deploy.J4rsClassLoader.`<init>`(J4rsClassLoader.java:22)
1842    ///         at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
1843    ///         at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
1844    ///         at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
1845    ///         at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
1846    ///         at java.lang.SystemClassLoaderAction.run(ClassLoader.java:2204)
1847    ///         at java.lang.SystemClassLoaderAction.run(ClassLoader.java:2188)
1848    ///         at java.security.AccessController.doPrivileged(Native Method)
1849    ///         at java.lang.ClassLoader.initSystemClassLoader(ClassLoader.java:1449)
1850    ///         at java.lang.ClassLoader.getSystemClassLoader(ClassLoader.java:1429)
1851    /// ```
1852    pub fn with_default_classloader(&'a mut self) -> &'a mut JvmBuilder<'a> {
1853        self.default_classloader = true;
1854        self
1855    }
1856
1857    /// Creates a Jvm
1858    pub fn build(&mut self) -> errors::Result<Jvm> {
1859        if !self.default_classloader {
1860            // Define the system classloader
1861            self.java_opts.push(JavaOpt::new(
1862                "-Djava.system.class.loader=org.astonbitecode.j4rs.api.deploy.J4rsClassLoader",
1863            ));
1864            self.java_opts.push(JavaOpt::new("-Xshare:off"));
1865            self.java_opts.push(JavaOpt::new(
1866                "-Djdk.net.URLClassPath.showIgnoredClassPathEntries=true",
1867            ));
1868        }
1869
1870        let classpath = if self.no_implicit_classpath {
1871            self.classpath_entries
1872                .iter()
1873                .fold(".".to_string(), |all, elem| {
1874                    format!("{}{}{}", all, utils::classpath_sep(), elem.to_string())
1875                })
1876        } else {
1877            // The default classpath contains all the jars in the jassets directory
1878            let jassets_path = self.get_jassets_path()?;
1879            // This is the j4rs jar that should be included in the classpath
1880            let j4rs_jar_to_use = format!("j4rs-{}-jar-with-dependencies.jar", j4rs_version());
1881            let j4rs_testing_jar_to_use = format!("j4rs-testing-{}.jar", j4rs_version());
1882            let j4rs_javafx_jar_to_use = format!("j4rs-javafx-{}.jar", j4rs_version());
1883            // Filter out possible incorrect jars of j4rs
1884            let mut cp_string = String::new();
1885            for entry in std::fs::read_dir(jassets_path)? {
1886                let path = entry?.path();
1887                if let Some(file_name) = opt_to_res(path.file_name())?.to_str() {
1888                    if !file_name.contains("j4rs-") || file_name.ends_with(&j4rs_jar_to_use) || file_name.ends_with(&j4rs_testing_jar_to_use)  || file_name.ends_with(&j4rs_javafx_jar_to_use) {
1889                        if !cp_string.is_empty() {
1890                            cp_string.push_str(utils::classpath_sep());
1891                        }
1892                        if let Some(path) = path.to_str() {
1893                            cp_string.push_str(path);
1894                        }
1895                    }
1896                }
1897            }
1898
1899            let default_class_path = format!("-Djava.class.path={}", cp_string);
1900
1901            self.classpath_entries
1902                .iter()
1903                .fold(default_class_path, |all, elem| {
1904                    format!("{}{}{}", all, utils::classpath_sep(), elem.to_string())
1905                })
1906        };
1907        info(&format!("Setting classpath to {}", classpath));
1908
1909        // Populate the JVM Options
1910        let mut jvm_options = if self.no_implicit_classpath {
1911            vec![classpath]
1912        } else {
1913            let default_library_path = utils::java_library_path()?;
1914            info(&format!("Setting library path to {}", default_library_path));
1915            vec![classpath, default_library_path]
1916        };
1917
1918        if self.javafx {
1919            let jassets_path = self.get_jassets_path()?;
1920            let jassets_path_string = jassets_path.to_str().unwrap_or(".");
1921            let modules_path = format!("--module-path {}", jassets_path_string);
1922            jvm_options.push(modules_path);
1923            jvm_options.push(
1924                "--add-modules javafx.base,javafx.controls,javafx.graphics,javafx.fxml".to_string(),
1925            );
1926        }
1927        self.java_opts
1928            .clone()
1929            .into_iter()
1930            .for_each(|opt| jvm_options.push(opt.to_string()));
1931
1932        // Pass to the Java world the name of the j4rs library.
1933        let lib_name_opt = if self.lib_name_opt.is_none() && !self.skip_setting_native_lib && cfg!(not(target_os = "android")) {
1934            let deps_dir = utils::deps_dir()?;
1935            let found_libs: Vec<String> = if Path::new(&deps_dir).exists() {
1936                utils::find_j4rs_dynamic_libraries_names()?
1937            } else {
1938                // If deps dir is not found, fallback to default naming in order for the library to be searched in the default
1939                // library locations of the system.
1940                let default_lib_name = if cfg!(windows) {
1941                    "l4rs.dll".to_string()
1942                } else {
1943                    "libj4rs.so".to_string()
1944                };
1945                info(&format!(
1946                    "Deps directory not found. Setting the library name to search to default: {}",
1947                    default_lib_name
1948                ));
1949                vec![default_lib_name]
1950            };
1951
1952            let lib_name_opt = if !found_libs.is_empty() {
1953                let a_lib = found_libs[0].clone().replace("lib", "");
1954
1955                let dot_splitted: Vec<&str> = a_lib.split('.').collect();
1956                let name = dot_splitted[0].to_string();
1957                info(&format!(
1958                    "Passing to the Java world the name of the library to load: {}",
1959                    name
1960                ));
1961                Some(name)
1962            } else {
1963                None
1964            };
1965            lib_name_opt
1966        } else if self.lib_name_opt.is_some() && !self.skip_setting_native_lib {
1967            let name = self.lib_name_opt.clone();
1968            info(&format!(
1969                "Passing to the Java world the name of the library to load: {}",
1970                name.as_ref().unwrap()
1971            ));
1972            name
1973        } else {
1974            None
1975        };
1976
1977        provisioning::set_maven_settings(&self.maven_settings);
1978
1979        let jvm_res = if self.java_vm_opt.is_some() {
1980            // If the `java_vm` is already created and provided, just attach the current thread.
1981            set_java_vm(self.java_vm_opt.unwrap());
1982            Jvm::attach_thread()
1983        } else {
1984            Jvm::new(&jvm_options, lib_name_opt)
1985        };
1986
1987        jvm_res.and_then(|mut jvm| {
1988            if !self.detach_thread_on_drop {
1989                jvm.detach_thread_on_drop(false);
1990            }
1991            if self.jobject_within_valid_classloader_opt.is_some() {
1992                cache_classloader_of(jvm.jni_env, self.jobject_within_valid_classloader_opt.unwrap())?;
1993            }
1994            Ok(jvm)
1995        })
1996    }
1997
1998    /// Creates a Jvm, similar with an already created j4rs Jvm.
1999    ///
2000    /// _Note: The already created Jvm is a j4rs Jvm, not a Java VM._
2001    pub fn already_initialized() -> errors::Result<Jvm> {
2002        Jvm::new(&[], None)
2003    }
2004
2005    fn get_jassets_path(&self) -> errors::Result<PathBuf> {
2006        match &self.base_path {
2007            Some(base_path_string) => {
2008                let mut pb = PathBuf::from(base_path_string);
2009                pb.push("jassets");
2010                let mut global_jassets_path_opt = cache::JASSETS_PATH.lock()?;
2011                *global_jassets_path_opt = Some(pb.clone());
2012                Ok(pb)
2013            }
2014            None => utils::default_jassets_path(),
2015        }
2016    }
2017}
2018
2019/// Represents default, known Classes in Java. Can be used as class argument in `Jvm#java_list`, etc.
2020pub enum JavaClass<'a> {
2021    Void,
2022    String,
2023    Boolean,
2024    Byte,
2025    Character,
2026    Short,
2027    Integer,
2028    Long,
2029    Float,
2030    Double,
2031    List,
2032    Of(&'a str),
2033}
2034
2035impl<'a> JavaClass<'a> {
2036    pub fn get_class_str(&self) -> &'a str {
2037        match self {
2038            Self::Void => "void",
2039            Self::String => CLASS_STRING,
2040            Self::Boolean => CLASS_BOOLEAN,
2041            Self::Byte => CLASS_BYTE,
2042            Self::Character => CLASS_CHARACTER,
2043            Self::Short => CLASS_SHORT,
2044            Self::Integer => CLASS_INTEGER,
2045            Self::Long => CLASS_LONG,
2046            Self::Float => CLASS_FLOAT,
2047            Self::Double => CLASS_DOUBLE,
2048            Self::List => CLASS_LIST,
2049            Self::Of(str) => str,
2050        }
2051    }
2052}
2053
2054impl<'a> From<JavaClass<'a>> for &'a str {
2055    fn from(java_class: JavaClass<'a>) -> &'a str {
2056        java_class.get_class_str()
2057    }
2058}
2059
2060impl<'a> From<&'a str> for JavaClass<'a> {
2061    fn from(java_class: &'a str) -> JavaClass<'a> {
2062        match java_class {
2063            "void" => Self::Void,
2064            CLASS_STRING => Self::String,
2065            CLASS_BOOLEAN => Self::Boolean,
2066            CLASS_BYTE => Self::Byte,
2067            CLASS_CHARACTER => Self::Character,
2068            CLASS_SHORT => Self::Short,
2069            CLASS_INTEGER => Self::Integer,
2070            CLASS_LONG => Self::Long,
2071            CLASS_FLOAT => Self::Float,
2072            CLASS_DOUBLE => Self::Double,
2073            CLASS_LIST => Self::List,
2074            str => Self::Of(str),
2075        }
2076    }
2077}
2078
2079/// Represents Java's null. Use this to create null Objects. E.g.:
2080///
2081/// let null_integer = InvocationArg::from(Null::Integer);
2082/// let null_object = InvocationArg::from(Null::Of("some.class.Name"));
2083pub enum Null<'a> {
2084    String,
2085    Boolean,
2086    Byte,
2087    Character,
2088    Short,
2089    Integer,
2090    Long,
2091    Float,
2092    Double,
2093    List,
2094    Of(&'a str),
2095}
2096
2097/// A classpath entry.
2098#[derive(Debug, Clone)]
2099pub struct ClasspathEntry(PathBuf);
2100
2101impl ClasspathEntry {
2102    pub fn new<P: AsRef<Path>>(classpath_entry: P) -> ClasspathEntry {
2103        ClasspathEntry(classpath_entry.as_ref().to_owned())
2104    }
2105}
2106
2107impl ToString for ClasspathEntry {
2108    fn to_string(&self) -> String {
2109        self.0.to_string_lossy().to_string()
2110    }
2111}
2112
2113/// A Java Option.
2114#[derive(Debug, Clone)]
2115pub struct JavaOpt<'a>(&'a str);
2116
2117impl<'a> JavaOpt<'a> {
2118    pub fn new(java_opt: &str) -> JavaOpt<'_> {
2119        JavaOpt(java_opt)
2120    }
2121}
2122
2123impl<'a> ToString for JavaOpt<'a> {
2124    fn to_string(&self) -> String {
2125        self.0.to_string()
2126    }
2127}
2128
2129#[cfg(test)]
2130mod api_unit_tests {
2131    use crate::lib_unit_tests::create_tests_jvm;
2132    use super::*;
2133
2134    #[test]
2135    fn jvm_builder() -> errors::Result<()> {
2136        let res = create_tests_jvm();
2137        assert!(res.is_ok());
2138        let one_more_res = JvmBuilder::already_initialized();
2139        assert!(one_more_res.is_ok());
2140
2141        Ok(())
2142    }
2143
2144    #[test]
2145    fn test_copy_j4rs_libs_under() -> errors::Result<()> {
2146        let newdir = "./newdir";
2147        Jvm::copy_j4rs_libs_under(newdir)?;
2148
2149        let _ = std::fs::remove_dir_all(newdir);
2150
2151        Ok(())
2152    }
2153
2154    #[test]
2155    fn test_select() -> errors::Result<()> {
2156        let (tx1, rx1) = channel();
2157        let ir1 = InstanceReceiver::new(rx1, 0);
2158        let (_tx2, rx2) = channel();
2159        let ir2 = InstanceReceiver::new(rx2, 0);
2160        let (tx3, rx3) = channel();
2161        let ir3 = InstanceReceiver::new(rx3, 0);
2162
2163        thread::spawn(move || {
2164            let _ = tx3.send(Instance::new(ptr::null_mut(), CLASS_STRING).unwrap());
2165            // Block the thread as sending does not block the current thread
2166            thread::sleep(time::Duration::from_millis(10));
2167            let _ = tx1.send(Instance::new(ptr::null_mut(), CLASS_STRING).unwrap());
2168            thread::sleep(time::Duration::from_millis(10));
2169            let _ = tx3.send(Instance::new(ptr::null_mut(), CLASS_STRING).unwrap());
2170        });
2171
2172        let (index1, _) = Jvm::select(&[&ir1, &ir2, &ir3]).unwrap();
2173        let (index2, _) = Jvm::select(&[&ir1, &ir2, &ir3]).unwrap();
2174        let (index3, _) = Jvm::select(&[&ir1, &ir2, &ir3]).unwrap();
2175        assert_eq!(index1, 2);
2176        assert_eq!(index2, 0);
2177        assert_eq!(index3, 2);
2178
2179        Ok(())
2180    }
2181
2182    #[test]
2183    fn test_select_timeout() -> errors::Result<()> {
2184        let (tx1, rx1) = channel();
2185        let ir1 = InstanceReceiver::new(rx1, 0);
2186        let (tx2, rx2) = channel();
2187        let ir2 = InstanceReceiver::new(rx2, 0);
2188
2189        thread::spawn(move || {
2190            let _ = tx1.send(Instance::new(ptr::null_mut(), CLASS_STRING).unwrap());
2191            // Block the thread as sending does not block the current thread
2192            thread::sleep(time::Duration::from_millis(10));
2193            let _ = tx2.send(Instance::new(ptr::null_mut(), CLASS_STRING).unwrap());
2194        });
2195
2196        let d = time::Duration::from_millis(500);
2197        let (index1, _) = Jvm::select_timeout(&[&ir1, &ir2], &d)?;
2198        let (index2, _) = Jvm::select_timeout(&[&ir1, &ir2], &d)?;
2199        assert!(Jvm::select_timeout(&[&ir1, &ir2], &d).is_err());
2200        assert_eq!(index1, 0);
2201        assert_eq!(index2, 1);
2202
2203        Ok(())
2204    }
2205
2206    #[test]
2207    fn test_java_class_creation() -> errors::Result<()> {
2208        assert_eq!(JavaClass::Void.get_class_str(), "void");
2209        assert_eq!(JavaClass::String.get_class_str(), CLASS_STRING);
2210        assert_eq!(JavaClass::Boolean.get_class_str(), CLASS_BOOLEAN);
2211        assert_eq!(JavaClass::Byte.get_class_str(), CLASS_BYTE);
2212        assert_eq!(JavaClass::Character.get_class_str(), CLASS_CHARACTER);
2213        assert_eq!(JavaClass::Short.get_class_str(), CLASS_SHORT);
2214        assert_eq!(JavaClass::Integer.get_class_str(), CLASS_INTEGER);
2215        assert_eq!(JavaClass::Long.get_class_str(), CLASS_LONG);
2216        assert_eq!(JavaClass::Float.get_class_str(), CLASS_FLOAT);
2217        assert_eq!(JavaClass::Double.get_class_str(), CLASS_DOUBLE);
2218        assert_eq!(JavaClass::List.get_class_str(), CLASS_LIST);
2219        assert_eq!(
2220            JavaClass::Of("a.java.Class").get_class_str(),
2221            "a.java.Class"
2222        );
2223
2224        Ok(())
2225    }
2226
2227    #[test]
2228    fn test_byte_array_to_rust() -> errors::Result<()> {
2229        let jvm = create_tests_jvm()?;
2230        let rust_value: Vec<i8> = vec![-3_i8, 7_i8, 8_i8];
2231        let ia: Vec<_> = rust_value.iter().map(|x| InvocationArg::try_from(x).unwrap().into_primitive().unwrap()).collect();
2232        let java_instance = jvm.create_java_array(PRIMITIVE_BYTE, &ia)?;
2233        let rust_value_from_java: Vec<i8> = jvm.to_rust(java_instance)?;
2234        assert_eq!(rust_value_from_java, rust_value);
2235
2236        Ok(())
2237    }
2238
2239    #[test]
2240    fn test_short_array_to_rust() -> errors::Result<()> {
2241        let jvm = create_tests_jvm()?;
2242        let rust_value: Vec<i16> = vec![-3_i16, 7_i16, 10000_i16];
2243        let ia: Vec<_> = rust_value.iter().map(|x| InvocationArg::try_from(x).unwrap().into_primitive().unwrap()).collect();
2244        let java_instance = jvm.create_java_array(PRIMITIVE_SHORT, &ia)?;
2245        let rust_value_from_java: Vec<i16> = jvm.to_rust(java_instance)?;
2246        assert_eq!(rust_value_from_java, rust_value);
2247
2248        Ok(())
2249    }
2250
2251    #[test]
2252    fn test_char_array_to_rust() -> errors::Result<()> {
2253        let jvm = create_tests_jvm()?;
2254        let rust_value: Vec<u16> = vec![3_u16, 7_u16, 10000_u16];
2255        let ia: Vec<_> = rust_value.iter().map(|x| InvocationArg::try_from(x).unwrap().into_primitive().unwrap()).collect();
2256        let java_instance = jvm.create_java_array(PRIMITIVE_CHAR, &ia)?;
2257        let rust_value_from_java: Vec<u16> = jvm.to_rust(java_instance)?;
2258        assert_eq!(rust_value_from_java, rust_value);
2259
2260        Ok(())
2261    }
2262
2263    #[test]
2264    fn test_int_array_to_rust() -> errors::Result<()> {
2265        let jvm = create_tests_jvm()?;
2266        let rust_value: Vec<i32> = vec![-100_000, -1_000_000, 1_000_000];
2267        let ia: Vec<_> = rust_value.iter().map(|x| InvocationArg::try_from(x).unwrap().into_primitive().unwrap()).collect();
2268        let java_instance = jvm.create_java_array(PRIMITIVE_INT, &ia)?;
2269        let rust_value_from_java: Vec<i32> = jvm.to_rust(java_instance)?;
2270        assert_eq!(rust_value_from_java, rust_value);
2271
2272        Ok(())
2273    }
2274
2275    #[test]
2276    fn test_long_array_to_rust() -> errors::Result<()> {
2277        let jvm = create_tests_jvm()?;
2278        let rust_value: Vec<i64> = vec![-100_000, -1_000_000, 1_000_000];
2279        let ia: Vec<_> = rust_value.iter().map(|x| InvocationArg::try_from(x).unwrap().into_primitive().unwrap()).collect();
2280        let java_instance = jvm.create_java_array(PRIMITIVE_LONG, &ia)?;
2281        let rust_value_from_java: Vec<i64> = jvm.to_rust(java_instance)?;
2282        assert_eq!(rust_value_from_java, rust_value);
2283
2284        Ok(())
2285    }
2286
2287    #[test]
2288    fn test_float_array_to_rust() -> errors::Result<()> {
2289        let jvm = create_tests_jvm()?;
2290        let rust_value: Vec<f32> = vec![3_f32, 7.5_f32, -1000.5_f32];
2291        let ia: Vec<_> = rust_value.iter().map(|x| InvocationArg::try_from(x).unwrap().into_primitive().unwrap()).collect();
2292        let java_instance = jvm.create_java_array(PRIMITIVE_FLOAT, &ia)?;
2293        let rust_value_from_java: Vec<f32> = jvm.to_rust(java_instance)?;
2294        assert_eq!(rust_value_from_java, rust_value);
2295
2296        Ok(())
2297    }
2298
2299    #[test]
2300    fn test_double_array_to_rust() -> errors::Result<()> {
2301        let jvm = create_tests_jvm()?;
2302        let rust_value: Vec<f64> = vec![3_f64, 7.5_f64, -1000.5_f64];
2303        let ia: Vec<_> = rust_value.iter().map(|x| InvocationArg::try_from(x).unwrap().into_primitive().unwrap()).collect();
2304        let java_instance = jvm.create_java_array(PRIMITIVE_DOUBLE, &ia)?;
2305        let rust_value_from_java: Vec<f64> = jvm.to_rust(java_instance)?;
2306        assert_eq!(rust_value_from_java, rust_value);
2307
2308        Ok(())
2309    }
2310
2311    #[test]
2312    fn test_boolean_array_to_rust() -> errors::Result<()> {
2313        let jvm = create_tests_jvm()?;
2314        let rust_value: Vec<bool> = vec![false, true, false];
2315        let ia: Vec<_> = rust_value.iter().map(|x| InvocationArg::try_from(x).unwrap().into_primitive().unwrap()).collect();
2316        let java_instance = jvm.create_java_array(PRIMITIVE_BOOLEAN, &ia)?;
2317        let rust_value_from_java: Vec<bool> = jvm.to_rust(java_instance)?;
2318        assert_eq!(rust_value_from_java, rust_value);
2319
2320        Ok(())
2321    }
2322
2323    #[test]
2324    fn test_int_to_rust() -> errors::Result<()> {
2325        let jvm = create_tests_jvm()?;
2326        let rust_value: i32 = 3;
2327        let ia = InvocationArg::try_from(rust_value)?.into_primitive()?;
2328        let java_instance = jvm.create_instance(CLASS_INTEGER, &[ia])?;
2329        let java_primitive_instance = jvm.invoke(&java_instance, "intValue", InvocationArg::empty())?;
2330        let rust_value_from_java: i32 = jvm.to_rust(java_instance)?;
2331        assert_eq!(rust_value_from_java, rust_value);
2332        let rust_value_from_java: i32 = jvm.to_rust(java_primitive_instance)?;
2333        assert_eq!(rust_value_from_java, rust_value);
2334
2335        Ok(())
2336    }
2337
2338    #[test]
2339    fn test_byte_to_rust() -> errors::Result<()> {
2340        let jvm = create_tests_jvm()?;
2341        let rust_value: i8 = 3;
2342        let ia = InvocationArg::try_from(rust_value)?.into_primitive()?;
2343        let java_instance = jvm.create_instance(CLASS_BYTE, &[ia])?;
2344        let java_primitive_instance = jvm.invoke(&java_instance, "byteValue", InvocationArg::empty())?;
2345        let rust_value_from_java: i8 = jvm.to_rust(java_instance)?;
2346        assert_eq!(rust_value_from_java, rust_value);
2347        let rust_value_from_java: i8 = jvm.to_rust(java_primitive_instance)?;
2348        assert_eq!(rust_value_from_java, rust_value);
2349
2350        Ok(())
2351    }
2352
2353    #[test]
2354    fn test_short_to_rust() -> errors::Result<()> {
2355        let jvm = create_tests_jvm()?;
2356        let rust_value: i16 = 3;
2357        let ia = InvocationArg::try_from(rust_value)?.into_primitive()?;
2358        let java_instance = jvm.create_instance(CLASS_SHORT, &[ia])?;
2359        let java_primitive_instance = jvm.invoke(&java_instance, "shortValue", InvocationArg::empty())?;
2360        let rust_value_from_java: i16 = jvm.to_rust(java_instance)?;
2361        assert_eq!(rust_value_from_java, rust_value);
2362        let rust_value_from_java: i16 = jvm.to_rust(java_primitive_instance)?;
2363        assert_eq!(rust_value_from_java, rust_value);
2364
2365        Ok(())
2366    }
2367
2368    #[test]
2369    fn test_char_to_rust() -> errors::Result<()> {
2370        let jvm = create_tests_jvm()?;
2371        let rust_value: u16 = 3;
2372        let ia = InvocationArg::try_from(rust_value)?.into_primitive()?;
2373        let java_instance = jvm.create_instance(CLASS_CHARACTER, &[ia])?;
2374        let java_primitive_instance = jvm.invoke(&java_instance, "charValue", InvocationArg::empty())?;
2375        let rust_value_from_java: u16 = jvm.to_rust(java_instance)?;
2376        assert_eq!(rust_value_from_java, rust_value);
2377        let rust_value_from_java: u16 = jvm.to_rust(java_primitive_instance)?;
2378        assert_eq!(rust_value_from_java, rust_value);
2379
2380        Ok(())
2381    }
2382
2383    #[test]
2384    fn test_long_to_rust() -> errors::Result<()> {
2385        let jvm = create_tests_jvm()?;
2386        let rust_value: i64 = 3;
2387        let ia = InvocationArg::try_from(rust_value)?.into_primitive()?;
2388        let java_instance = jvm.create_instance(CLASS_LONG, &[ia])?;
2389        let java_primitive_instance = jvm.invoke(&java_instance, "longValue", InvocationArg::empty())?;
2390        let rust_value_from_java: i64 = jvm.to_rust(java_instance)?;
2391        assert_eq!(rust_value_from_java, rust_value);
2392        let rust_value_from_java: i64 = jvm.to_rust(java_primitive_instance)?;
2393        assert_eq!(rust_value_from_java, rust_value);
2394
2395        Ok(())
2396    }
2397
2398    #[test]
2399    fn test_float_to_rust() -> errors::Result<()> {
2400        let jvm = create_tests_jvm()?;
2401        let rust_value: f32 = 3.3;
2402        let ia = InvocationArg::try_from(rust_value)?.into_primitive()?;
2403        let java_instance = jvm.create_instance(CLASS_FLOAT, &[ia])?;
2404        let java_primitive_instance = jvm.invoke(&java_instance, "floatValue", InvocationArg::empty())?;
2405        let rust_value_from_java: f32 = jvm.to_rust(java_instance)?;
2406        assert_eq!(rust_value_from_java, rust_value);
2407        let rust_value_from_java: f32 = jvm.to_rust(java_primitive_instance)?;
2408        assert_eq!(rust_value_from_java, rust_value);
2409
2410        Ok(())
2411    }
2412
2413    #[test]
2414    fn test_double_to_rust() -> errors::Result<()> {
2415        let jvm = create_tests_jvm()?;
2416        let rust_value: f64 = 3.3;
2417        let ia = InvocationArg::try_from(rust_value)?.into_primitive()?;
2418        let java_instance = jvm.create_instance(CLASS_DOUBLE, &[ia])?;
2419        let java_primitive_instance = jvm.invoke(&java_instance, "doubleValue", InvocationArg::empty())?;
2420        let rust_value_from_java: f64 = jvm.to_rust(java_instance)?;
2421        assert_eq!(rust_value_from_java, rust_value);
2422        let rust_value_from_java: f64 = jvm.to_rust(java_primitive_instance)?;
2423        assert_eq!(rust_value_from_java, rust_value);
2424
2425        Ok(())
2426    }
2427
2428    #[test]
2429    fn api_by_ref_or_value() -> errors::Result<()> {
2430        let jvm = create_tests_jvm()?;
2431
2432        // Instantiate
2433        let inv_arg1 = InvocationArg::try_from("some string")?;
2434        let _ = jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", &[&inv_arg1])?;
2435        let _ = jvm.create_instance("java.lang.String", &[inv_arg1])?;
2436        Ok(())
2437    }
2438
2439    #[test]
2440    fn exception_string_in_the_result() -> errors::Result<()> {
2441        let jvm = create_tests_jvm()?;
2442
2443        let res = jvm.create_instance("non.Existing", InvocationArg::empty());
2444        assert!(res.is_err());
2445        let exception_sttring = format!("{}",res.err().unwrap());
2446        assert!(exception_sttring.contains("Cannot create instance of non.Existing"));
2447        
2448        Ok(())
2449    }
2450}