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 `class_name`.
627    /// The array will have the `InvocationArg`s populated.
628    /// The `InvocationArg`s __must__ be of type _class_name_.
629    #[deprecated(since = "0.15.0", note = "Please use `java_list` instead")]
630    pub fn create_java_list(
631        &self,
632        class_name: &str,
633        inv_args: &[InvocationArg],
634    ) -> errors::Result<Instance> {
635        Jvm::do_create_java_list(self.jni_env, class_name, inv_args)
636    }
637
638    /// Creates a new Java List with elements of the class `inner_class_name`.
639    pub fn java_list<'a>(
640        &self,
641        inner_class_name: impl Into<&'a str>,
642        inv_args: Vec<impl TryInto<InvocationArg, Error=J4RsError>>,
643    ) -> errors::Result<Instance> {
644        let v: Result<Vec<InvocationArg>, J4RsError> =
645            inv_args.into_iter().map(|arg| arg.try_into()).collect();
646        Self::do_create_java_list(self.jni_env, inner_class_name.into(), v?.as_ref())
647    }
648
649    fn do_create_java_list(
650        jni_env: *mut JNIEnv,
651        class_name: &str,
652        inv_args: &[InvocationArg],
653    ) -> errors::Result<Instance> {
654        debug(&format!(
655            "Creating a java list of class {} with {} elements",
656            class_name,
657            inv_args.len()
658        ));
659        unsafe {
660            // Factory invocation - first argument: create a jstring to pass as argument for the class_name
661            let class_name_jstring: jstring =
662                jni_utils::global_jobject_from_str(class_name, jni_env)?;
663
664            // Factory invocation - rest of the arguments: Create a new object list of class InvocationArg
665            let size = inv_args.len() as i32;
666            let array_ptr = {
667                let j = (opt_to_res(cache::get_jni_new_object_array())?)(
668                    jni_env,
669                    size,
670                    cache::get_invocation_arg_class()?,
671                    ptr::null_mut(),
672                );
673                jni_utils::create_global_ref_from_local_ref(j, jni_env)?
674            };
675            let mut inv_arg_jobjects: Vec<jobject> = Vec::with_capacity(size as usize);
676
677            // Factory invocation - rest of the arguments: populate the array
678            for i in 0..size {
679                // Create an InvocationArg Java Object
680                let inv_arg_java = inv_args[i as usize].as_java_ptr_with_global_ref(jni_env)?;
681                // Set it in the array
682                (opt_to_res(cache::get_jni_set_object_array_element())?)(
683                    jni_env,
684                    array_ptr,
685                    i,
686                    inv_arg_java,
687                );
688                inv_arg_jobjects.push(inv_arg_java);
689            }
690            // Call the method of the factory that instantiates a new Java Array of `class_name`.
691            // This returns a Instance that acts like a proxy to the Java world.
692            let java_instance = (opt_to_res(cache::get_jni_call_static_object_method())?)(
693                jni_env,
694                cache::get_factory_class()?,
695                cache::get_factory_create_java_list_method()?,
696                class_name_jstring,
697                array_ptr,
698            );
699
700            // Check for exceptions before creating the globalref
701            Self::do_return(jni_env, ())?;
702
703            let java_instance_global_instance =
704                jni_utils::create_global_ref_from_local_ref(java_instance, jni_env)?;
705            // Prevent memory leaks from the created local references
706            for inv_arg_jobject in inv_arg_jobjects {
707                jni_utils::delete_java_ref(jni_env, inv_arg_jobject);
708            }
709            jni_utils::delete_java_ref(jni_env, array_ptr);
710            jni_utils::delete_java_ref(jni_env, class_name_jstring);
711
712            // Create and return the Instance
713            Self::do_return(
714                jni_env,
715                Instance {
716                    jinstance: java_instance_global_instance,
717                    class_name: class_name.to_string(),
718                    skip_deleting_jobject: false,
719                },
720            )
721        }
722    }
723
724    /// Creates a new Java Map with keys of class `key_class_name` and values of class `value_class_name`.
725    pub fn java_map<'a>(
726        &self,
727        key_class_name: impl Into<&'a str>,
728        value_class_name: impl Into<&'a str>,
729        inv_args: HashMap<
730            impl TryInto<InvocationArg, Error=J4RsError>,
731            impl TryInto<InvocationArg, Error=J4RsError>,
732        >,
733    ) -> errors::Result<Instance> {
734        let mut inv_args_results: Vec<Result<InvocationArg, J4RsError>> =
735            Vec::with_capacity(inv_args.len() * 2);
736        let mut i = 0;
737        let mut inv_args = inv_args;
738
739        for (key, val) in inv_args.drain() {
740            inv_args_results.insert(i, key.try_into());
741            i += 1;
742            inv_args_results.insert(i, val.try_into());
743            i += 1;
744        }
745        let inv_args: Result<Vec<InvocationArg>, J4RsError> = inv_args_results
746            .into_iter()
747            .map(|arg| arg.try_into())
748            .collect();
749        Self::do_create_java_map(
750            self.jni_env,
751            key_class_name.into(),
752            value_class_name.into(),
753            inv_args?.as_ref(),
754        )
755    }
756
757    fn do_create_java_map(
758        jni_env: *mut JNIEnv,
759        key_class_name: &str,
760        value_class_name: &str,
761        inv_args: &[InvocationArg],
762    ) -> errors::Result<Instance> {
763        debug(&format!(
764            "Creating a java map with keys of class {} and values of class {} with {} elements",
765            key_class_name,
766            value_class_name,
767            inv_args.len() / 2
768        ));
769        unsafe {
770            // Factory invocation - first argument: create a jstring to pass as argument for the key_class_name
771            let key_class_name_jstring: jstring =
772                jni_utils::global_jobject_from_str(key_class_name, jni_env)?;
773            // Factory invocation - second argument: create a jstring to pass as argument for the value_class_name
774            let value_class_name_jstring: jstring =
775                jni_utils::global_jobject_from_str(value_class_name, jni_env)?;
776
777            // Factory invocation - rest of the arguments: Create a new object list of class InvocationArg
778            let size = inv_args.len() as i32;
779            let array_ptr = {
780                let j = (opt_to_res(cache::get_jni_new_object_array())?)(
781                    jni_env,
782                    size,
783                    cache::get_invocation_arg_class()?,
784                    ptr::null_mut(),
785                );
786                jni_utils::create_global_ref_from_local_ref(j, jni_env)?
787            };
788            let mut inv_arg_jobjects: Vec<jobject> = Vec::with_capacity(size as usize);
789
790            // Factory invocation - rest of the arguments: populate the array
791            for i in 0..size {
792                // Create an InvocationArg Java Object
793                let inv_arg_java = inv_args[i as usize].as_java_ptr_with_global_ref(jni_env)?;
794                // Set it in the array
795                (opt_to_res(cache::get_jni_set_object_array_element())?)(
796                    jni_env,
797                    array_ptr,
798                    i,
799                    inv_arg_java,
800                );
801                inv_arg_jobjects.push(inv_arg_java);
802            }
803            // Call the method of the factory that instantiates a new Java Map with keys of `key_class_name`
804            // and values of `value_class_name`.
805            // This returns a Instance that acts like a proxy to the Java world.
806            let java_instance = (opt_to_res(cache::get_jni_call_static_object_method())?)(
807                jni_env,
808                cache::get_factory_class()?,
809                cache::get_factory_create_java_map_method()?,
810                key_class_name_jstring,
811                value_class_name_jstring,
812                array_ptr,
813            );
814
815            // Check for exceptions before creating the globalref
816            Self::do_return(jni_env, ())?;
817
818            let java_instance_global_instance =
819                jni_utils::create_global_ref_from_local_ref(java_instance, jni_env)?;
820            // Prevent memory leaks from the created local references
821            for inv_arg_jobject in inv_arg_jobjects {
822                jni_utils::delete_java_ref(jni_env, inv_arg_jobject);
823            }
824            jni_utils::delete_java_ref(jni_env, array_ptr);
825            jni_utils::delete_java_ref(jni_env, value_class_name_jstring);
826            jni_utils::delete_java_ref(jni_env, key_class_name_jstring);
827
828            // Create and return the Instance
829            Self::do_return(
830                jni_env,
831                Instance {
832                    jinstance: java_instance_global_instance,
833                    class_name: "".to_string(),
834                    skip_deleting_jobject: false,
835                },
836            )
837        }
838    }
839
840    /// 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.
841    pub fn invoke(
842        &self,
843        instance: &Instance,
844        method_name: &str,
845        inv_args: &[impl Borrow<InvocationArg>],
846    ) -> errors::Result<Instance> {
847        debug(&format!(
848            "Invoking method {} of class {} using {} arguments",
849            method_name,
850            instance.class_name,
851            inv_args.len()
852        ));
853        unsafe {
854            // First argument: create a jstring to pass as argument for the method_name
855            let method_name_jstring: jstring =
856                jni_utils::global_jobject_from_str(method_name, self.jni_env)?;
857
858            // Rest of the arguments: Create a new objectarray of class InvocationArg
859            let size = inv_args.len() as i32;
860            let array_ptr = {
861                let j = (opt_to_res(cache::get_jni_new_object_array())?)(
862                    self.jni_env,
863                    size,
864                    cache::get_invocation_arg_class()?,
865                    ptr::null_mut(),
866                );
867                jni_utils::create_global_ref_from_local_ref(j, self.jni_env)?
868            };
869            let mut inv_arg_jobjects: Vec<jobject> = Vec::with_capacity(size as usize);
870
871            // Rest of the arguments: populate the array
872            for i in 0..size {
873                // Create an InvocationArg Java Object
874                let inv_arg_java =
875                    inv_args[i as usize].borrow().as_java_ptr_with_global_ref(self.jni_env)?;
876                // Set it in the array
877                (opt_to_res(cache::get_jni_set_object_array_element())?)(
878                    self.jni_env,
879                    array_ptr,
880                    i,
881                    inv_arg_java,
882                );
883                inv_arg_jobjects.push(inv_arg_java);
884            }
885
886            // Call the method of the instance
887            let java_instance = (opt_to_res(cache::get_jni_call_object_method())?)(
888                self.jni_env,
889                instance.jinstance,
890                cache::get_invoke_method()?,
891                method_name_jstring,
892                array_ptr,
893            );
894
895            // Check for exceptions before creating the globalref
896            Self::do_return(self.jni_env, ())?;
897
898            let java_instance_global_instance =
899                jni_utils::create_global_ref_from_local_ref(java_instance, self.jni_env)?;
900            // Prevent memory leaks from the created local references
901            for inv_arg_jobject in inv_arg_jobjects {
902                jni_utils::delete_java_ref(self.jni_env, inv_arg_jobject);
903            }
904            jni_utils::delete_java_ref(self.jni_env, array_ptr);
905            jni_utils::delete_java_ref(self.jni_env, method_name_jstring);
906
907            // Create and return the Instance
908            Self::do_return(
909                self.jni_env,
910                Instance {
911                    jinstance: java_instance_global_instance,
912                    class_name: cache::UNKNOWN_FOR_RUST.to_string(),
913                    skip_deleting_jobject: false,
914                },
915            )
916        }
917    }
918
919    /// Retrieves the field `field_name` of a created `Instance`.
920    pub fn field(&self, instance: &Instance, field_name: &str) -> errors::Result<Instance> {
921        debug(&format!(
922            "Retrieving field {} of class {}",
923            field_name, instance.class_name
924        ));
925        unsafe {
926            // First argument: create a jstring to pass as argument for the field_name
927            let field_name_jstring: jstring =
928                jni_utils::global_jobject_from_str(field_name, self.jni_env)?;
929
930            // Call the method of the instance
931            let java_instance = (opt_to_res(cache::get_jni_call_object_method())?)(
932                self.jni_env,
933                instance.jinstance,
934                cache::get_field_method()?,
935                field_name_jstring,
936            );
937
938            // Check for exceptions before creating the globalref
939            Self::do_return(self.jni_env, ())?;
940
941            let java_instance_global_instance =
942                jni_utils::create_global_ref_from_local_ref(java_instance, self.jni_env)?;
943            // Prevent memory leaks from the created local references
944            jni_utils::delete_java_ref(self.jni_env, field_name_jstring);
945
946            // Create and return the Instance
947            Self::do_return(
948                self.jni_env,
949                Instance {
950                    jinstance: java_instance_global_instance,
951                    class_name: cache::UNKNOWN_FOR_RUST.to_string(),
952                    skip_deleting_jobject: false,
953                },
954            )
955        }
956    }
957
958    /// Retrieves the field `field_name` of a static class.
959    pub fn static_class_field(
960        &self,
961        class_name: &str,
962        field_name: &str,
963    ) -> errors::Result<Instance> {
964        debug(&format!(
965            "Retrieving field {} of static class {}",
966            field_name, class_name
967        ));
968        let i = self.static_class(class_name)?;
969        self.field(&i, field_name)
970    }
971
972    /// Invokes the method `method_name` of a created `Instance`, passing an array of `InvocationArg`s.
973    /// 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.
974    pub fn invoke_to_channel(
975        &self,
976        instance: &Instance,
977        method_name: &str,
978        inv_args: &[impl Borrow<InvocationArg>],
979    ) -> errors::Result<InstanceReceiver> {
980        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()));
981        unsafe {
982            // Create the channel
983            let (sender, rx) = channel();
984            let tx = Box::new(sender);
985            // First argument: the address of the channel Sender
986            let raw_ptr = Box::into_raw(tx);
987            // Find the address of tx
988            let address_string = format!("{:p}", raw_ptr);
989            let address = u64::from_str_radix(&address_string[2..], 16).unwrap();
990
991            // Second argument: create a jstring to pass as argument for the method_name
992            let method_name_jstring: jstring =
993                jni_utils::global_jobject_from_str(method_name, self.jni_env)?;
994
995            // Rest of the arguments: Create a new objectarray of class InvocationArg
996            let size = inv_args.len() as i32;
997            let array_ptr = {
998                let j = (opt_to_res(cache::get_jni_new_object_array())?)(
999                    self.jni_env,
1000                    size,
1001                    cache::get_invocation_arg_class()?,
1002                    ptr::null_mut(),
1003                );
1004                jni_utils::create_global_ref_from_local_ref(j, self.jni_env)?
1005            };
1006            let mut inv_arg_jobjects: Vec<jobject> = Vec::with_capacity(size as usize);
1007
1008            // Rest of the arguments: populate the array
1009            for i in 0..size {
1010                // Create an InvocationArg Java Object
1011                let inv_arg_java =
1012                    inv_args[i as usize].borrow().as_java_ptr_with_global_ref(self.jni_env)?;
1013                // Set it in the array
1014                (opt_to_res(cache::get_jni_set_object_array_element())?)(
1015                    self.jni_env,
1016                    array_ptr,
1017                    i,
1018                    inv_arg_java,
1019                );
1020                inv_arg_jobjects.push(inv_arg_java);
1021            }
1022
1023            // Call the method of the instance
1024            (opt_to_res(cache::get_jni_call_void_method())?)(
1025                self.jni_env,
1026                instance.jinstance,
1027                cache::get_invoke_to_channel_method()?,
1028                address,
1029                method_name_jstring,
1030                array_ptr,
1031            );
1032
1033            // Check for exceptions before creating the globalref
1034            Self::do_return(self.jni_env, ())?;
1035
1036            // Prevent memory leaks from the created local references
1037            for inv_arg_jobject in inv_arg_jobjects {
1038                jni_utils::delete_java_ref(self.jni_env, inv_arg_jobject);
1039            }
1040            jni_utils::delete_java_ref(self.jni_env, array_ptr);
1041            jni_utils::delete_java_ref(self.jni_env, method_name_jstring);
1042
1043            // Create and return the Instance
1044            Self::do_return(self.jni_env, InstanceReceiver::new(rx, address))
1045        }
1046    }
1047
1048    /// Initializes a callback channel via a Java Instance that is a `NativeCallbackToRustChannelSupport`.
1049    /// It returns a Result of `InstanceReceiver` that may be used to get an underlying `Receiver<Instance>`.
1050    /// The `NativeCallbackToRustChannelSupport` Instance which is passed as argument, will be sending `Instance`s via this Receiver.
1051    pub fn init_callback_channel(&self, instance: &Instance) -> errors::Result<InstanceReceiver> {
1052        debug("Initializing callback channel");
1053        unsafe {
1054            // Create the channel
1055            let (sender, rx) = channel();
1056            let tx = Box::new(sender);
1057            // First argument: the address of the channel Sender
1058            let raw_ptr = Box::into_raw(tx);
1059            // Find the address of tx
1060            let address_string = format!("{:p}", raw_ptr);
1061            let address = u64::from_str_radix(&address_string[2..], 16).unwrap();
1062
1063            // Call the method of the instance
1064            (opt_to_res(cache::get_jni_call_void_method())?)(
1065                self.jni_env,
1066                instance.jinstance,
1067                cache::get_init_callback_channel_method()?,
1068                address,
1069            );
1070
1071            // Create and return the Instance
1072            Self::do_return(self.jni_env, InstanceReceiver::new(rx, address))
1073        }
1074    }
1075
1076    /// 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.
1077    pub fn invoke_static(
1078        &self,
1079        class_name: &str,
1080        method_name: &str,
1081        inv_args: &[impl Borrow<InvocationArg>],
1082    ) -> errors::Result<Instance> {
1083        debug(&format!(
1084            "Invoking static method {} of class {} using {} arguments",
1085            method_name,
1086            class_name,
1087            inv_args.len()
1088        ));
1089        unsafe {
1090            // Factory invocation - first argument: create a jstring to pass as argument for the class_name
1091            let class_name_jstring: jstring =
1092                jni_utils::global_jobject_from_str(class_name, self.jni_env)?;
1093            // Call the method of the factory that creates a Instance for static calls to methods of class `class_name`.
1094            // This returns a Instance that acts like a proxy to the Java world.
1095            let tmp_java_instance = (opt_to_res(cache::get_jni_call_static_object_method())?)(
1096                self.jni_env,
1097                cache::get_factory_class()?,
1098                cache::get_factory_create_for_static_method()?,
1099                class_name_jstring,
1100            );
1101
1102            // First argument: create a jstring to pass as argument for the method_name
1103            let method_name_jstring: jstring =
1104                jni_utils::global_jobject_from_str(method_name, self.jni_env)?;
1105
1106            // Rest of the arguments: Create a new objectarray of class InvocationArg
1107            let size = inv_args.len() as i32;
1108            let array_ptr = {
1109                let j = (opt_to_res(cache::get_jni_new_object_array())?)(
1110                    self.jni_env,
1111                    size,
1112                    cache::get_invocation_arg_class()?,
1113                    ptr::null_mut(),
1114                );
1115                jni_utils::create_global_ref_from_local_ref(j, self.jni_env)?
1116            };
1117            let mut inv_arg_jobjects: Vec<jobject> = Vec::with_capacity(size as usize);
1118            // Rest of the arguments: populate the array
1119            for i in 0..size {
1120                // Create an InvocationArg Java Object
1121                let inv_arg_java =
1122                    inv_args[i as usize].borrow().as_java_ptr_with_global_ref(self.jni_env)?;
1123                // Set it in the array
1124                (opt_to_res(cache::get_jni_set_object_array_element())?)(
1125                    self.jni_env,
1126                    array_ptr,
1127                    i,
1128                    inv_arg_java,
1129                );
1130                inv_arg_jobjects.push(inv_arg_java);
1131            }
1132            // Call the method of the instance
1133            let java_instance = (opt_to_res(cache::get_jni_call_object_method())?)(
1134                self.jni_env,
1135                tmp_java_instance,
1136                cache::get_invoke_static_method()?,
1137                method_name_jstring,
1138                array_ptr,
1139            );
1140            // Delete temp ref
1141            jni_utils::delete_java_local_ref(self.jni_env, tmp_java_instance);
1142            jni_utils::delete_java_ref(self.jni_env, class_name_jstring);
1143            // Check for exceptions before creating the globalref
1144            Self::do_return(self.jni_env, ())?;
1145
1146            // Prevent memory leaks from the created local references
1147            for inv_arg_jobject in inv_arg_jobjects {
1148                jni_utils::delete_java_ref(self.jni_env, inv_arg_jobject);
1149            }
1150            jni_utils::delete_java_ref(self.jni_env, array_ptr);
1151            jni_utils::delete_java_ref(self.jni_env, method_name_jstring);
1152
1153            // Create and return the Instance.
1154            Self::do_return(
1155                self.jni_env,
1156                Instance::from_jobject_with_global_ref(java_instance)?,
1157            )
1158        }
1159    }
1160
1161    /// Creates a clone of the provided Instance
1162    pub fn clone_instance(&self, instance: &Instance) -> errors::Result<Instance> {
1163        unsafe {
1164            // Call the clone method
1165            let java_instance = (opt_to_res(cache::get_jni_call_static_object_method())?)(
1166                self.jni_env,
1167                cache::get_class_to_invoke_clone_and_cast()?,
1168                cache::get_clone_static_method()?,
1169                instance.jinstance,
1170            );
1171
1172            // Create and return the Instance
1173            Self::do_return(
1174                self.jni_env,
1175                Instance::from_jobject_with_global_ref(java_instance)?,
1176            )
1177        }
1178    }
1179
1180    /// 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.
1181    pub fn cast(&self, from_instance: &Instance, to_class: &str) -> errors::Result<Instance> {
1182        debug(&format!("Casting to class {}", to_class));
1183        unsafe {
1184            // First argument is the jobject that is inside the from_instance
1185            // Second argument: create a jstring to pass as argument for the to_class
1186            let to_class_jstring: jstring =
1187                jni_utils::global_jobject_from_str(to_class, self.jni_env)?;
1188
1189            // Call the cast method
1190            let java_instance = (opt_to_res(cache::get_jni_call_static_object_method())?)(
1191                self.jni_env,
1192                cache::get_class_to_invoke_clone_and_cast()?,
1193                cache::get_cast_static_method()?,
1194                from_instance.jinstance,
1195                to_class_jstring,
1196            );
1197
1198            // Check for exceptions before creating the globalref
1199            Self::do_return(self.jni_env, ())?;
1200
1201            // Prevent memory leaks from the created local references
1202            jni_utils::delete_java_ref(self.jni_env, to_class_jstring);
1203
1204            // Create and return the Instance
1205            Self::do_return(
1206                self.jni_env,
1207                Instance::from_jobject_with_global_ref(java_instance)?,
1208            )
1209        }
1210    }
1211
1212    /// Checks whether an Instance a is equal to some InvocationArg. 
1213    /// 
1214    /// The check is actually against the Java `Object.equals`, taking into consideration the possibility of null.
1215    /// `NullPointerException` will not be thrown, even if one of the inputs is null.
1216    pub fn check_equals(&self, instance: impl Borrow<Instance>, inv_arg: impl Borrow<InvocationArg>) -> errors::Result<bool> {
1217        debug(&format!("Checking equality between instances of {} and {}", instance.borrow().class_name(), inv_arg.borrow().class_name()));
1218        unsafe {
1219            // Create InvocationArg Java Objects
1220            let inv_arg_java_b = inv_arg.borrow().as_java_ptr_with_global_ref(self.jni_env)?;
1221            // Call the checkEquals method
1222            let java_boolean = (opt_to_res(cache::get_jni_call_boolean_method())?)(
1223                self.jni_env,
1224                instance.borrow().jinstance,
1225                cache::get_check_equals_method()?,
1226                inv_arg_java_b,
1227            );
1228
1229            // Create and return the boolean
1230            Self::do_return(
1231                self.jni_env,
1232                java_boolean,
1233            )
1234        }
1235    }
1236
1237    /// Consumes an `Instance` and returns its jobject. The returned jobject is a JNI local reference.
1238    pub fn instance_into_raw_object(&self, instance: Instance) -> errors::Result<jobject> {
1239        debug(&format!("Getting the raw jobject from instance of class {}", instance.borrow().class_name()));
1240        // Call the getObjectMethod. This returns a localref
1241        let object_instance = unsafe {
1242            (opt_to_res(cache::get_jni_call_object_method())?)(
1243                self.jni_env,
1244                instance.jinstance,
1245                cache::get_get_object_method()?,
1246        )};
1247
1248        Self::do_return(
1249            self.jni_env,
1250            object_instance,
1251        )
1252    }
1253
1254    /// Consumes the `Jvm` and returns its `JNIEnv`
1255    pub fn into_raw(self) -> *mut JNIEnv {
1256        debug("Getting the raw JNIEnv from the Jvm");
1257
1258        self.jni_env
1259    }
1260
1261    /// Returns the Rust representation of the provided instance, boxed
1262    pub fn to_rust_boxed<T>(&self, instance: Instance) -> errors::Result<Box<T>>
1263        where
1264            T: DeserializeOwned + Any,
1265    {
1266        // Define the macro inside the function in order to have access to &self
1267        macro_rules! rust_box_from_java_object {
1268            ($jni_transformation:path) => {{
1269                // Call the getObjectMethod. This returns a localref
1270                let object_instance = (opt_to_res(cache::get_jni_call_object_method())?)(
1271                    self.jni_env,
1272                    instance.jinstance,
1273                    cache::get_get_object_method()?,
1274                );
1275                let object_instance =
1276                    jni_utils::create_global_ref_from_local_ref(object_instance, self.jni_env)?;
1277                let v = Box::new($jni_transformation(object_instance, self.jni_env)?);
1278                let v_any = v as Box<dyn Any>;
1279
1280                jni_utils::delete_java_ref(self.jni_env, object_instance);
1281
1282                match v_any.downcast::<T>() {
1283                    Ok(v) => Ok(v),
1284                    Err(error) => Err(errors::J4RsError::RustError(format!(
1285                        "Could not downcast to Rust type: {:?}",
1286                        error
1287                    ))),
1288                }
1289            }};
1290        }
1291
1292        let t_type = TypeId::of::<T>();
1293        
1294
1295        unsafe {
1296            // Call the getClassName method. This returns a localref
1297            let object_class_name_instance = (opt_to_res(cache::get_jni_call_object_method())?)(
1298                self.jni_env,
1299                instance.jinstance,
1300                cache::get_get_object_class_name_method()?,
1301            );
1302            let object_class_name_instance = jni_utils::create_global_ref_from_local_ref(
1303                object_class_name_instance,
1304                self.jni_env,
1305            )?;
1306            let class_name = &(jni_utils::string_from_jobject(object_class_name_instance, self.jni_env)?);
1307            jni_utils::delete_java_ref(self.jni_env, object_class_name_instance);
1308            if t_type == TypeId::of::<String>() && JavaClass::String.get_class_str() == class_name {
1309                rust_box_from_java_object!(jni_utils::string_from_jobject)
1310            } else if t_type == TypeId::of::<i32>()
1311                && (JavaClass::Integer.get_class_str() == class_name || PRIMITIVE_INT == class_name)
1312            {
1313                rust_box_from_java_object!(jni_utils::i32_from_jobject)
1314            } else if t_type == TypeId::of::<i8>()
1315                && (JavaClass::Byte.get_class_str() == class_name || PRIMITIVE_BYTE == class_name)
1316            {
1317                rust_box_from_java_object!(jni_utils::i8_from_jobject)
1318            } else if t_type == TypeId::of::<i16>()
1319                && (JavaClass::Short.get_class_str() == class_name || PRIMITIVE_SHORT == class_name)
1320            {
1321                rust_box_from_java_object!(jni_utils::i16_from_jobject)
1322            }  else if t_type == TypeId::of::<u16>()
1323                && (JavaClass::Character.get_class_str() == class_name || PRIMITIVE_CHAR == class_name)
1324            {
1325                rust_box_from_java_object!(jni_utils::u16_from_jobject)
1326            } else if t_type == TypeId::of::<i64>()
1327                && (JavaClass::Long.get_class_str() == class_name || PRIMITIVE_LONG == class_name)
1328            {
1329                rust_box_from_java_object!(jni_utils::i64_from_jobject)
1330            } else if t_type == TypeId::of::<f32>()
1331                && (JavaClass::Float.get_class_str() == class_name || PRIMITIVE_FLOAT == class_name)
1332            {
1333                rust_box_from_java_object!(jni_utils::f32_from_jobject)
1334            } else if t_type == TypeId::of::<f64>()
1335                && (JavaClass::Double.get_class_str() == class_name
1336                || PRIMITIVE_DOUBLE == class_name)
1337            {
1338                rust_box_from_java_object!(jni_utils::f64_from_jobject)
1339            } else if t_type == TypeId::of::<Vec<i8>>()
1340                && PRIMITIVE_BYTE_ARRAY == class_name
1341            {
1342                rust_box_from_java_object!(jni_utils::i8_array_from_jobject)
1343            } else if t_type == TypeId::of::<Vec<i16>>()
1344                && PRIMITIVE_SHORT_ARRAY == class_name
1345            {
1346                rust_box_from_java_object!(jni_utils::i16_array_from_jobject)
1347            } else if t_type == TypeId::of::<Vec<u16>>()
1348                && PRIMITIVE_CHAR_ARRAY == class_name
1349            {
1350                rust_box_from_java_object!(jni_utils::u16_array_from_jobject)
1351            } else if t_type == TypeId::of::<Vec<i32>>()
1352                && PRIMITIVE_INT_ARRAY == class_name
1353            {
1354                rust_box_from_java_object!(jni_utils::i32_array_from_jobject)
1355            } else if t_type == TypeId::of::<Vec<i64>>()
1356                && PRIMITIVE_LONG_ARRAY == class_name
1357            {
1358                rust_box_from_java_object!(jni_utils::i64_array_from_jobject)
1359            } else if t_type == TypeId::of::<Vec<f32>>()
1360                && PRIMITIVE_FLOAT_ARRAY == class_name
1361            {
1362                rust_box_from_java_object!(jni_utils::f32_array_from_jobject)
1363            } else if t_type == TypeId::of::<Vec<f64>>()
1364                && PRIMITIVE_DOUBLE_ARRAY == class_name
1365            {
1366                rust_box_from_java_object!(jni_utils::f64_array_from_jobject)
1367            } else if t_type == TypeId::of::<Vec<bool>>()
1368                && PRIMITIVE_BOOLEAN_ARRAY == class_name
1369            {
1370                rust_box_from_java_object!(jni_utils::boolean_array_from_jobject)
1371            } else {
1372                Ok(Box::new(self.to_rust_deserialized(instance)?))
1373            }
1374        }
1375    }
1376
1377    /// Returns the Rust representation of the provided instance
1378    pub fn to_rust<T>(&self, instance: Instance) -> errors::Result<T>
1379        where
1380            T: DeserializeOwned + Any,
1381    {
1382        self.to_rust_boxed(instance).map(|v| *v)
1383    }
1384
1385    pub fn to_rust_deserialized<T>(&self, instance: Instance) -> errors::Result<T>
1386        where
1387            T: DeserializeOwned + Any,
1388    {
1389        unsafe {
1390            debug("Invoking the getJson method");
1391            // Call the getJson method. This returns a localref
1392            let json_instance = (opt_to_res(cache::get_jni_call_object_method())?)(
1393                self.jni_env,
1394                instance.jinstance,
1395                cache::get_get_json_method()?,
1396            );
1397            let _ = Self::do_return(self.jni_env, "")?;
1398            debug("Transforming jstring to rust String");
1399            let global_json_instance =
1400                jni_utils::create_global_ref_from_local_ref(json_instance, self.jni_env)?;
1401            let json = jni_utils::jstring_to_rust_string(self, global_json_instance as jstring)?;
1402            jni_utils::delete_java_ref(self.jni_env, global_json_instance);
1403            Self::do_return(self.jni_env, serde_json::from_str(&json)?)
1404        }
1405    }
1406
1407    /// Deploys an artifact in the default j4rs jars location.
1408    ///
1409    /// This is useful for build scripts that need jars for the runtime that can be downloaded from e.g. Maven.
1410    ///
1411    /// The function deploys __only__ the specified artifact, not its transitive dependencies.
1412    pub fn deploy_artifact<T: Any + JavaArtifact>(&self, artifact: &T) -> errors::Result<()> {
1413        let artifact = artifact as &dyn Any;
1414        if let Some(maven_artifact) = artifact.downcast_ref::<MavenArtifact>() {
1415            for repo in get_maven_settings().repos.into_iter() {
1416                let instance = self.create_instance(
1417                    "org.astonbitecode.j4rs.api.deploy.SimpleMavenDeployer",
1418                    &[InvocationArg::try_from(repo.uri)?,
1419                        InvocationArg::try_from(&maven_artifact.base)?],
1420                )?;
1421
1422                let res = self.invoke(
1423                    &instance,
1424                    "deploy",
1425                    &vec![
1426                        InvocationArg::try_from(&maven_artifact.group)?,
1427                        InvocationArg::try_from(&maven_artifact.id)?,
1428                        InvocationArg::try_from(&maven_artifact.version)?,
1429                        InvocationArg::try_from(&maven_artifact.qualifier)?,
1430                    ],
1431                );
1432
1433                if res.is_ok() {
1434                    break;
1435                }
1436            }
1437
1438            Ok(())
1439        } else if let Some(local_jar_artifact) = artifact.downcast_ref::<LocalJarArtifact>() {
1440            let instance = self.create_instance(
1441                "org.astonbitecode.j4rs.api.deploy.FileSystemDeployer",
1442                &[InvocationArg::try_from(&local_jar_artifact.base)?],
1443            )?;
1444
1445            let _ = self.invoke(
1446                &instance,
1447                "deploy",
1448                &[InvocationArg::try_from(&local_jar_artifact.path)?],
1449            )?;
1450            Ok(())
1451        } else {
1452            Err(J4RsError::GeneralError(format!(
1453                "Don't know how to deploy artifacts of {:?}",
1454                artifact.type_id()
1455            )))
1456        }
1457    }
1458
1459    /// Copies the jassets default directory and the j4rs dynamic library under the specified
1460    /// location.
1461    /// This is useful for cases when `with_base_path` method is used when building a Jvm with
1462    /// the JvmBuilder.
1463    /// Build scripts should use this method.
1464    pub fn copy_j4rs_libs_under(path: &str) -> errors::Result<()> {
1465        let mut pb = PathBuf::from(path);
1466        pb.push("deps");
1467        fs::create_dir_all(&pb)?;
1468
1469        let default_jassets_path_buf = utils::default_jassets_path()?;
1470        let default_jassets_path_string = default_jassets_path_buf.to_str().unwrap().to_owned();
1471
1472        // Copy the jassets
1473        let options = &mut fs_extra::dir::CopyOptions::new();
1474        options.overwrite = true;
1475        let _ = fs_extra::copy_items(vec![default_jassets_path_string].as_ref(), path, options)?;
1476
1477        // Copy the dynamic libraries
1478        let dynlibs: Vec<String> = {
1479            let mut dynlibs = vec![];
1480            // We try every 1 second for 10 iterations because on most systems, cargo will
1481            // parallelize the build and the dynlib might not be created yet.
1482            for _i in 0..10 {
1483                dynlibs = utils::find_j4rs_dynamic_libraries_paths()?;
1484                if dynlibs.is_empty() {
1485                    thread::sleep(time::Duration::from_millis(1000));
1486                } else {
1487                    break;
1488                }
1489            }
1490            dynlibs
1491        };
1492        if dynlibs.is_empty() {
1493            let message = format!(
1494                "No j4rs dynamic libraries found for target triple {}. \
1495                                  The host triple during build is {}.",
1496                env::var("TARGET").unwrap_or("".to_string()),
1497                env::var("HOST").unwrap_or("UNKNOWN".to_string())
1498            );
1499            println!("cargo:warning={}", message);
1500        }
1501
1502        let _ = fs_extra::copy_items(&dynlibs, &pb, options)?;
1503
1504        Ok(())
1505    }
1506
1507    /// Initiates a chain of operations on Instances.
1508    pub fn chain(&self, instance: &Instance) -> errors::Result<ChainableInstance<'_>> {
1509        ChainableInstance::new_with_instance_ref(instance, self)
1510    }
1511
1512    /// Initiates a chain of operations on Instances.
1513    pub fn into_chain(&self, instance: Instance) -> ChainableInstance<'_> {
1514        ChainableInstance::new(instance, self)
1515    }
1516
1517    /// Throws an exception in the Java World
1518    pub fn throw_invocation_exception(&self, message: &str) -> errors::Result<()> {
1519        unsafe {
1520            let _ = jni_utils::throw_exception(message, self.jni_env)?;
1521        }
1522        Ok(())
1523    }
1524
1525    pub(crate) fn do_return<T>(jni_env: *mut JNIEnv, to_return: T) -> errors::Result<T> {
1526        unsafe {
1527            if (opt_to_res(cache::get_jni_exception_check())?)(jni_env) == JNI_TRUE {
1528                let throwable = (opt_to_res(cache::get_jni_exception_occured())?)(jni_env);
1529                let throwable_string = Self::get_throwable_string(throwable, jni_env)?;
1530                (opt_to_res(cache::get_jni_exception_clear())?)(jni_env);
1531                Err(J4RsError::JavaError(throwable_string))
1532            } else {
1533                Ok(to_return)
1534            }
1535        }
1536    }
1537
1538    unsafe fn get_throwable_string(throwable: jobject, jni_env: *mut JNIEnv) -> errors::Result<String> {
1539        let java_string = (opt_to_res(cache::get_jni_call_static_object_method())?)(
1540            jni_env,
1541            cache::get_utils_class()?,
1542            cache::get_utils_exception_to_string_method()?,
1543            throwable,
1544        );
1545        let to_ret = jni_utils::string_from_jobject(java_string, jni_env);
1546        jni_utils::delete_java_local_ref(jni_env, java_string);
1547        to_ret
1548    }
1549
1550    // Retrieves a JNIEnv in the case that a JVM is already created even from another thread.
1551    fn get_created_vm() -> Option<*mut JNIEnv> {
1552        unsafe {
1553            // Get the number of the already created VMs. This is most probably 1, but we retrieve the number just in case...
1554            let mut created_vms_size: jsize = 0;
1555            tweaks::get_created_java_vms(
1556                &mut Vec::with_capacity(created_vms_size as usize),
1557                0,
1558                &mut created_vms_size,
1559            );
1560
1561            if created_vms_size == 0 {
1562                None
1563            } else {
1564                debug(&format!(
1565                    "Retrieving the first of {} created JVMs",
1566                    created_vms_size
1567                ));
1568                // Get the created VM (use 2 just in case... :) )
1569                let mut buffer: Vec<*mut JavaVM> = Vec::with_capacity(2);
1570                for _ in 0..created_vms_size {
1571                    buffer.push(ptr::null_mut());
1572                }
1573
1574                let retjint = tweaks::get_created_java_vms(
1575                    &mut buffer,
1576                    created_vms_size,
1577                    &mut created_vms_size,
1578                );
1579                if retjint == JNI_OK {
1580                    let act = (**buffer[0]).v1_4.AttachCurrentThread;
1581                    let mut jni_environment: *mut JNIEnv = ptr::null_mut();
1582                    (act)(
1583                        buffer[0],
1584                        (&mut jni_environment as *mut *mut JNIEnv) as *mut *mut c_void,
1585                        ptr::null_mut(),
1586                    );
1587                    Some(jni_environment)
1588                } else {
1589                    error(&format!(
1590                        "Error while retrieving the created JVMs: {}",
1591                        retjint
1592                    ));
1593                    None
1594                }
1595            }
1596        }
1597    }
1598
1599    fn detach_current_thread(&self) {
1600        unsafe {
1601            // Get the number of the already created VMs. This is most probably 1, but we retrieve the number just in case...
1602            let mut created_vms_size: jsize = 0;
1603            tweaks::get_created_java_vms(
1604                &mut Vec::with_capacity(created_vms_size as usize),
1605                0,
1606                &mut created_vms_size,
1607            );
1608
1609            if created_vms_size > 0 {
1610                // Get the created VM
1611                let mut buffer: Vec<*mut JavaVM> = Vec::with_capacity(created_vms_size as usize);
1612                for _ in 0..created_vms_size {
1613                    buffer.push(ptr::null_mut());
1614                }
1615
1616                let retjint = tweaks::get_created_java_vms(
1617                    &mut buffer,
1618                    created_vms_size,
1619                    &mut created_vms_size,
1620                );
1621                if retjint == JNI_OK {
1622                    let dct = (**buffer[0]).v1_4.DetachCurrentThread;
1623                    (dct)(buffer[0]);
1624                } else {
1625                    warn(&format!(
1626                        "Error while retrieving the created JVMs: {}",
1627                        retjint
1628                    ));
1629                }
1630            }
1631        }
1632    }
1633
1634    /// Returns the first `Instance` that is available from the passed `InstanceReceiver`s,
1635    /// along with the index of the receiver that was selected and actually returned the instance.
1636    ///
1637    /// This is a mostly naive implementation of select, because of [absence for selecting among mpsc channels](https://github.com/rust-lang/rust/issues/27800).
1638    pub fn select(instance_receivers: &[&InstanceReceiver]) -> errors::Result<(usize, Instance)> {
1639        loop {
1640            for (index, ir) in instance_receivers.iter().enumerate() {
1641                let res = ir.rx.try_recv();
1642                if res.is_ok() {
1643                    return Ok((index, res.unwrap()));
1644                }
1645            }
1646            thread::yield_now();
1647        }
1648    }
1649
1650    /// Returns the first `Instance` that is available from the passed `InstanceReceiver`s,
1651    /// along with the index of the receiver that was selected and actually returned the instance.
1652    ///
1653    /// If there are no instances returned for the duration defined in timeout argument, an error is returned.
1654    ///
1655    /// This is a mostly naive implementation of select, because of [absence for selecting among mpsc channels](https://github.com/rust-lang/rust/issues/27800).
1656    pub fn select_timeout(
1657        instance_receivers: &[&InstanceReceiver],
1658        timeout: &time::Duration,
1659    ) -> errors::Result<(usize, Instance)> {
1660        let start = time::Instant::now();
1661        loop {
1662            for (index, ir) in instance_receivers.iter().enumerate() {
1663                let res = ir.rx.try_recv();
1664                if res.is_ok() {
1665                    return Ok((index, res.unwrap()));
1666                }
1667            }
1668            if &start.elapsed() > timeout {
1669                return Err(J4RsError::Timeout);
1670            }
1671            thread::yield_now();
1672        }
1673    }
1674}
1675
1676impl Drop for Jvm {
1677    fn drop(&mut self) {
1678        if cache::remove_active_jvm() <= 0 {
1679            if self.detach_thread_on_drop {
1680                self.detach_current_thread();
1681            }
1682            cache::set_thread_local_env(None);
1683        }
1684    }
1685}
1686
1687/// A builder for Jvm
1688pub struct JvmBuilder<'a> {
1689    classpath_entries: Vec<ClasspathEntry<'a>>,
1690    java_opts: Vec<JavaOpt<'a>>,
1691    no_implicit_classpath: bool,
1692    detach_thread_on_drop: bool,
1693    lib_name_opt: Option<String>,
1694    skip_setting_native_lib: bool,
1695    base_path: Option<String>,
1696    maven_settings: MavenSettings,
1697    javafx: bool,
1698    default_classloader: bool,
1699    java_vm_opt: Option<*mut JavaVM>,
1700    jobject_within_valid_classloader_opt: Option<jobject>,
1701}
1702
1703impl<'a> JvmBuilder<'a> {
1704    /// Creates a new JvmBuilder.
1705    pub fn new<'b>() -> JvmBuilder<'b> {
1706        JvmBuilder {
1707            classpath_entries: Vec::new(),
1708            java_opts: Vec::new(),
1709            no_implicit_classpath: false,
1710            detach_thread_on_drop: true,
1711            lib_name_opt: None,
1712            skip_setting_native_lib: false,
1713            base_path: None,
1714            maven_settings: MavenSettings::default(),
1715            javafx: false,
1716            default_classloader: false,
1717            java_vm_opt: None,
1718            jobject_within_valid_classloader_opt: None
1719        }
1720    }
1721
1722    /// Adds a classpath entry.
1723    pub fn classpath_entry(&'a mut self, cp_entry: ClasspathEntry<'a>) -> &'a mut JvmBuilder<'a> {
1724        self.classpath_entries.push(cp_entry);
1725        self
1726    }
1727
1728    /// Adds classpath entries.
1729    pub fn classpath_entries(
1730        &'a mut self,
1731        cp_entries: Vec<ClasspathEntry<'a>>,
1732    ) -> &'a mut JvmBuilder<'a> {
1733        for cp_entry in cp_entries {
1734            self.classpath_entries.push(cp_entry);
1735        }
1736        self
1737    }
1738
1739    /// Adds a Java option.
1740    pub fn java_opt(&'a mut self, opt: JavaOpt<'a>) -> &'a mut JvmBuilder<'a> {
1741        self.java_opts.push(opt);
1742        self
1743    }
1744
1745    /// Adds Java options.
1746    pub fn java_opts(&'a mut self, opts: Vec<JavaOpt<'a>>) -> &'a mut JvmBuilder<'a> {
1747        for opt in opts {
1748            self.java_opts.push(opt);
1749        }
1750        self
1751    }
1752
1753    /// By default, the created `Jvm`s include an implicit classpath entry that includes the j4rs jar.
1754    /// When `with_no_implicit_classpath()` is called, this classpath will not be added to the Jvm.
1755    pub fn with_no_implicit_classpath(&'a mut self) -> &'a mut JvmBuilder<'a> {
1756        self.no_implicit_classpath = true;
1757        self
1758    }
1759
1760    /// When a Jvm goes out of scope and is being dropped, its current thread is being detached from the Java VM.
1761    /// A Jvm that is created with `detach_thread_on_drop(false)` will not detach the thread when being dropped.
1762    ///
1763    /// 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.
1764    /// If that Jvm detaches its current thread when being dropped, there will be problems for the Java world code to continue executing.
1765    pub fn detach_thread_on_drop(&'a mut self, detach_thread_on_drop: bool) -> &'a mut JvmBuilder<'a> {
1766        self.detach_thread_on_drop = detach_thread_on_drop;
1767        self
1768    }
1769
1770    /// In the case that the j4rs is statically linked to some other library, the Java world (j4rs.jar) needs to load that
1771    /// library instead of the default one.
1772    ///
1773    /// This function defines the native library name to load.
1774    pub fn with_native_lib_name(&'a mut self, lib_name: &str) -> &'a mut JvmBuilder<'a> {
1775        self.lib_name_opt = Some(lib_name.to_string());
1776        self
1777    }
1778
1779    /// Configures the builder not to instruct the Java world j4rs code to load the native library.
1780    /// (most probably because it is already loaded)
1781    pub fn skip_setting_native_lib(&'a mut self) -> &'a mut JvmBuilder<'a> {
1782        self.skip_setting_native_lib = true;
1783        self
1784    }
1785
1786    /// Defines the location of the jassets and deps directory.
1787    /// The jassets contains the j4rs jar and the deps the j4rs dynamic library.
1788    pub fn with_base_path(&'a mut self, base_path: &str) -> &'a mut JvmBuilder<'a> {
1789        self.base_path = Some(base_path.to_string());
1790        self
1791    }
1792
1793    /// Defines the maven settings to use for provisioning maven artifacts.
1794    pub fn with_maven_settings(&'a mut self, maven_settings: MavenSettings) -> &'a mut JvmBuilder<'a> {
1795        self.maven_settings = maven_settings;
1796        self
1797    }
1798
1799    /// Adds JavaFX support to the created JVM
1800    pub fn with_javafx_support(&'a mut self) -> &'a mut JvmBuilder<'a> {
1801        self.javafx = true;
1802        self
1803    }
1804
1805    /// Create the j4rs `Jvm` using an already created jni `JavaVM`.
1806    /// 
1807    /// Useful for Android apps, where the JVM is automatically created.
1808    pub fn with_java_vm(&'a mut self, java_vm: *mut JavaVM) -> &'a mut JvmBuilder<'a> {
1809        self.java_vm_opt = Some(java_vm);
1810        self
1811    }
1812
1813    /// Instructs j4rs to use the classloader associated to the specified `jobject` when searching for classes.
1814    /// 
1815    /// Useful for pure native Android apps.
1816    /// 
1817    /// As described in Android documentation, the native libraries that spawn new threads cannot locate and use Java classes correctly.
1818    ///
1819    /// From the [docs]((https://developer.android.com/training/articles/perf-jni#faq:-why-didnt-findclass-find-my-class)):
1820    /// 
1821    /// > You can get into trouble if you create a thread yourself (perhaps by calling pthread_create and then attaching it with AttachCurrentThread). 
1822    /// > 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 
1823    /// > instead of the one associated with your application, so attempts to find app-specific classes will fail.
1824    /// 
1825    /// 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))
1826    /// `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 
1827    /// of the `Activity` to find and use classes. `j4rs` will use the `jobject` passed here (the `jobject` of the `Activity`) to get the 
1828    /// proper classloader and use it when needed.
1829    /// #[cfg(target_os = "android")]
1830    pub fn with_classloader_of_activity(&'a mut self, jobject_within_valid_classloader: jobject) -> &'a mut JvmBuilder<'a> {
1831        self.jobject_within_valid_classloader_opt = Some(jobject_within_valid_classloader);
1832        // If the `jobject_within_valid_classloader_opt` is provided, it means that the object's classloader
1833        // should be used to load classes in case the traditional `FindClass` invocations fail.
1834        // Apply here the needed configuration for it to happen.
1835        // Do not detach the thread on drop. This would make the Activity to fail.
1836        let tmp = self.detach_thread_on_drop(false);
1837        let tmp = tmp.with_no_implicit_classpath();
1838        tmp
1839    }
1840
1841    /// `j4rs` uses a custom ClassLoader (namely the `J4rsClassLoader`),
1842    /// that allows adding jars to the classpath during runtime, after the underlying `JVM` is initialized.
1843    ///
1844    /// This function instructs the builder not to use this custom classloader, but use the default one.
1845    ///
1846    /// Please note that the `J4rsClassLoader` needs Java 9 or higher. If you use an older Java version,
1847    /// you __must__ call this function in order for `j4rs` to work.
1848    ///
1849    /// If not, you will get exceptions like the following:
1850    ///
1851    /// ```text
1852    /// java.lang.NoSuchMethodError: java.net.URLClassLoader.`<init>`(Ljava/lang/String;[Ljava/net/URL;Ljava/lang/ClassLoader;)V
1853    ///         at org.astonbitecode.j4rs.api.deploy.J4rsClassLoader.`<init>`(J4rsClassLoader.java:22)
1854    ///         at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
1855    ///         at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
1856    ///         at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
1857    ///         at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
1858    ///         at java.lang.SystemClassLoaderAction.run(ClassLoader.java:2204)
1859    ///         at java.lang.SystemClassLoaderAction.run(ClassLoader.java:2188)
1860    ///         at java.security.AccessController.doPrivileged(Native Method)
1861    ///         at java.lang.ClassLoader.initSystemClassLoader(ClassLoader.java:1449)
1862    ///         at java.lang.ClassLoader.getSystemClassLoader(ClassLoader.java:1429)
1863    /// ```
1864    pub fn with_default_classloader(&'a mut self) -> &'a mut JvmBuilder<'a> {
1865        self.default_classloader = true;
1866        self
1867    }
1868
1869    /// Creates a Jvm
1870    pub fn build(&mut self) -> errors::Result<Jvm> {
1871        if !self.default_classloader {
1872            // Define the system classloader
1873            self.java_opts.push(JavaOpt::new(
1874                "-Djava.system.class.loader=org.astonbitecode.j4rs.api.deploy.J4rsClassLoader",
1875            ));
1876            self.java_opts.push(JavaOpt::new("-Xshare:off"));
1877            self.java_opts.push(JavaOpt::new(
1878                "-Djdk.net.URLClassPath.showIgnoredClassPathEntries=true",
1879            ));
1880        }
1881
1882        let classpath = if self.no_implicit_classpath {
1883            self.classpath_entries
1884                .iter()
1885                .fold(".".to_string(), |all, elem| {
1886                    format!("{}{}{}", all, utils::classpath_sep(), elem.to_string())
1887                })
1888        } else {
1889            // The default classpath contains all the jars in the jassets directory
1890            let jassets_path = self.get_jassets_path()?;
1891            // This is the j4rs jar that should be included in the classpath
1892            let j4rs_jar_to_use = format!("j4rs-{}-jar-with-dependencies.jar", j4rs_version());
1893            let j4rs_testing_jar_to_use = format!("j4rs-testing-{}.jar", j4rs_version());
1894            let j4rs_javafx_jar_to_use = format!("j4rs-javafx-{}.jar", j4rs_version());
1895            // Filter out possible incorrect jars of j4rs
1896            let mut cp_string = String::new();
1897            for entry in std::fs::read_dir(jassets_path)? {
1898                let path = entry?.path();
1899                if let Some(file_name) = opt_to_res(path.file_name())?.to_str() {
1900                    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) {
1901                        if !cp_string.is_empty() {
1902                            cp_string.push_str(utils::classpath_sep());
1903                        }
1904                        if let Some(path) = path.to_str() {
1905                            cp_string.push_str(path);
1906                        }
1907                    }
1908                }
1909            }
1910
1911            let default_class_path = format!("-Djava.class.path={}", cp_string);
1912
1913            self.classpath_entries
1914                .iter()
1915                .fold(default_class_path, |all, elem| {
1916                    format!("{}{}{}", all, utils::classpath_sep(), elem.to_string())
1917                })
1918        };
1919        info(&format!("Setting classpath to {}", classpath));
1920
1921        // Populate the JVM Options
1922        let mut jvm_options = if self.no_implicit_classpath {
1923            vec![classpath]
1924        } else {
1925            let default_library_path = utils::java_library_path()?;
1926            info(&format!("Setting library path to {}", default_library_path));
1927            vec![classpath, default_library_path]
1928        };
1929
1930        if self.javafx {
1931            let jassets_path = self.get_jassets_path()?;
1932            let jassets_path_string = jassets_path.to_str().unwrap_or(".");
1933            let modules_path = format!("--module-path {}", jassets_path_string);
1934            jvm_options.push(modules_path);
1935            jvm_options.push(
1936                "--add-modules javafx.base,javafx.controls,javafx.graphics,javafx.fxml".to_string(),
1937            );
1938        }
1939        self.java_opts
1940            .clone()
1941            .into_iter()
1942            .for_each(|opt| jvm_options.push(opt.to_string()));
1943
1944        // Pass to the Java world the name of the j4rs library.
1945        let lib_name_opt = if self.lib_name_opt.is_none() && !self.skip_setting_native_lib && cfg!(not(target_os = "android")) {
1946            let deps_dir = utils::deps_dir()?;
1947            let found_libs: Vec<String> = if Path::new(&deps_dir).exists() {
1948                utils::find_j4rs_dynamic_libraries_names()?
1949            } else {
1950                // If deps dir is not found, fallback to default naming in order for the library to be searched in the default
1951                // library locations of the system.
1952                let default_lib_name = if cfg!(windows) {
1953                    "l4rs.dll".to_string()
1954                } else {
1955                    "libj4rs.so".to_string()
1956                };
1957                info(&format!(
1958                    "Deps directory not found. Setting the library name to search to default: {}",
1959                    default_lib_name
1960                ));
1961                vec![default_lib_name]
1962            };
1963
1964            let lib_name_opt = if !found_libs.is_empty() {
1965                let a_lib = found_libs[0].clone().replace("lib", "");
1966
1967                let dot_splitted: Vec<&str> = a_lib.split('.').collect();
1968                let name = dot_splitted[0].to_string();
1969                info(&format!(
1970                    "Passing to the Java world the name of the library to load: {}",
1971                    name
1972                ));
1973                Some(name)
1974            } else {
1975                None
1976            };
1977            lib_name_opt
1978        } else if self.lib_name_opt.is_some() && !self.skip_setting_native_lib {
1979            let name = self.lib_name_opt.clone();
1980            info(&format!(
1981                "Passing to the Java world the name of the library to load: {}",
1982                name.as_ref().unwrap()
1983            ));
1984            name
1985        } else {
1986            None
1987        };
1988
1989        provisioning::set_maven_settings(&self.maven_settings);
1990
1991        let jvm_res = if self.java_vm_opt.is_some() {
1992            // If the `java_vm` is already created and provided, just attach the current thread.
1993            set_java_vm(self.java_vm_opt.unwrap());
1994            Jvm::attach_thread()
1995        } else {
1996            Jvm::new(&jvm_options, lib_name_opt)
1997        };
1998
1999        jvm_res.and_then(|mut jvm| {
2000            if !self.detach_thread_on_drop {
2001                jvm.detach_thread_on_drop(false);
2002            }
2003            if self.jobject_within_valid_classloader_opt.is_some() {
2004                cache_classloader_of(jvm.jni_env, self.jobject_within_valid_classloader_opt.unwrap())?;
2005            }
2006            Ok(jvm)
2007        })
2008    }
2009
2010    /// Creates a Jvm, similar with an already created j4rs Jvm.
2011    ///
2012    /// _Note: The already created Jvm is a j4rs Jvm, not a Java VM._
2013    pub fn already_initialized() -> errors::Result<Jvm> {
2014        Jvm::new(&[], None)
2015    }
2016
2017    fn get_jassets_path(&self) -> errors::Result<PathBuf> {
2018        match &self.base_path {
2019            Some(base_path_string) => {
2020                let mut pb = PathBuf::from(base_path_string);
2021                pb.push("jassets");
2022                let mut global_jassets_path_opt = cache::JASSETS_PATH.lock()?;
2023                *global_jassets_path_opt = Some(pb.clone());
2024                Ok(pb)
2025            }
2026            None => utils::default_jassets_path(),
2027        }
2028    }
2029}
2030
2031/// Represents default, known Classes in Java. Can be used as class argument in `Jvm#java_list`, etc.
2032pub enum JavaClass<'a> {
2033    Void,
2034    String,
2035    Boolean,
2036    Byte,
2037    Character,
2038    Short,
2039    Integer,
2040    Long,
2041    Float,
2042    Double,
2043    List,
2044    Of(&'a str),
2045}
2046
2047impl<'a> JavaClass<'a> {
2048    pub fn get_class_str(&self) -> &'a str {
2049        match self {
2050            Self::Void => "void",
2051            Self::String => CLASS_STRING,
2052            Self::Boolean => CLASS_BOOLEAN,
2053            Self::Byte => CLASS_BYTE,
2054            Self::Character => CLASS_CHARACTER,
2055            Self::Short => CLASS_SHORT,
2056            Self::Integer => CLASS_INTEGER,
2057            Self::Long => CLASS_LONG,
2058            Self::Float => CLASS_FLOAT,
2059            Self::Double => CLASS_DOUBLE,
2060            Self::List => CLASS_LIST,
2061            Self::Of(str) => str,
2062        }
2063    }
2064}
2065
2066impl<'a> From<JavaClass<'a>> for &'a str {
2067    fn from(java_class: JavaClass<'a>) -> &'a str {
2068        java_class.get_class_str()
2069    }
2070}
2071
2072impl<'a> From<&'a str> for JavaClass<'a> {
2073    fn from(java_class: &'a str) -> JavaClass<'a> {
2074        match java_class {
2075            "void" => Self::Void,
2076            CLASS_STRING => Self::String,
2077            CLASS_BOOLEAN => Self::Boolean,
2078            CLASS_BYTE => Self::Byte,
2079            CLASS_CHARACTER => Self::Character,
2080            CLASS_SHORT => Self::Short,
2081            CLASS_INTEGER => Self::Integer,
2082            CLASS_LONG => Self::Long,
2083            CLASS_FLOAT => Self::Float,
2084            CLASS_DOUBLE => Self::Double,
2085            CLASS_LIST => Self::List,
2086            str => Self::Of(str),
2087        }
2088    }
2089}
2090
2091/// Represents Java's null. Use this to create null Objects. E.g.:
2092///
2093/// let null_integer = InvocationArg::from(Null::Integer);
2094/// let null_object = InvocationArg::from(Null::Of("some.class.Name"));
2095pub enum Null<'a> {
2096    String,
2097    Boolean,
2098    Byte,
2099    Character,
2100    Short,
2101    Integer,
2102    Long,
2103    Float,
2104    Double,
2105    List,
2106    Of(&'a str),
2107}
2108
2109/// A classpath entry.
2110#[derive(Debug, Clone)]
2111pub struct ClasspathEntry<'a>(&'a str);
2112
2113impl<'a> ClasspathEntry<'a> {
2114    pub fn new(classpath_entry: &str) -> ClasspathEntry<'_> {
2115        ClasspathEntry(classpath_entry)
2116    }
2117}
2118
2119impl<'a> ToString for ClasspathEntry<'a> {
2120    fn to_string(&self) -> String {
2121        self.0.to_string()
2122    }
2123}
2124
2125/// A Java Option.
2126#[derive(Debug, Clone)]
2127pub struct JavaOpt<'a>(&'a str);
2128
2129impl<'a> JavaOpt<'a> {
2130    pub fn new(java_opt: &str) -> JavaOpt<'_> {
2131        JavaOpt(java_opt)
2132    }
2133}
2134
2135impl<'a> ToString for JavaOpt<'a> {
2136    fn to_string(&self) -> String {
2137        self.0.to_string()
2138    }
2139}
2140
2141#[cfg(test)]
2142mod api_unit_tests {
2143    use crate::lib_unit_tests::create_tests_jvm;
2144    use super::*;
2145
2146    #[test]
2147    fn jvm_builder() -> errors::Result<()> {
2148        let res = create_tests_jvm();
2149        assert!(res.is_ok());
2150        let one_more_res = JvmBuilder::already_initialized();
2151        assert!(one_more_res.is_ok());
2152
2153        Ok(())
2154    }
2155
2156    #[test]
2157    fn test_copy_j4rs_libs_under() -> errors::Result<()> {
2158        let newdir = "./newdir";
2159        Jvm::copy_j4rs_libs_under(newdir)?;
2160
2161        let _ = std::fs::remove_dir_all(newdir);
2162
2163        Ok(())
2164    }
2165
2166    #[test]
2167    fn test_select() -> errors::Result<()> {
2168        let (tx1, rx1) = channel();
2169        let ir1 = InstanceReceiver::new(rx1, 0);
2170        let (_tx2, rx2) = channel();
2171        let ir2 = InstanceReceiver::new(rx2, 0);
2172        let (tx3, rx3) = channel();
2173        let ir3 = InstanceReceiver::new(rx3, 0);
2174
2175        thread::spawn(move || {
2176            let _ = tx3.send(Instance::new(ptr::null_mut(), CLASS_STRING).unwrap());
2177            // Block the thread as sending does not block the current thread
2178            thread::sleep(time::Duration::from_millis(10));
2179            let _ = tx1.send(Instance::new(ptr::null_mut(), CLASS_STRING).unwrap());
2180            thread::sleep(time::Duration::from_millis(10));
2181            let _ = tx3.send(Instance::new(ptr::null_mut(), CLASS_STRING).unwrap());
2182        });
2183
2184        let (index1, _) = Jvm::select(&[&ir1, &ir2, &ir3]).unwrap();
2185        let (index2, _) = Jvm::select(&[&ir1, &ir2, &ir3]).unwrap();
2186        let (index3, _) = Jvm::select(&[&ir1, &ir2, &ir3]).unwrap();
2187        assert_eq!(index1, 2);
2188        assert_eq!(index2, 0);
2189        assert_eq!(index3, 2);
2190
2191        Ok(())
2192    }
2193
2194    #[test]
2195    fn test_select_timeout() -> errors::Result<()> {
2196        let (tx1, rx1) = channel();
2197        let ir1 = InstanceReceiver::new(rx1, 0);
2198        let (tx2, rx2) = channel();
2199        let ir2 = InstanceReceiver::new(rx2, 0);
2200
2201        thread::spawn(move || {
2202            let _ = tx1.send(Instance::new(ptr::null_mut(), CLASS_STRING).unwrap());
2203            // Block the thread as sending does not block the current thread
2204            thread::sleep(time::Duration::from_millis(10));
2205            let _ = tx2.send(Instance::new(ptr::null_mut(), CLASS_STRING).unwrap());
2206        });
2207
2208        let d = time::Duration::from_millis(500);
2209        let (index1, _) = Jvm::select_timeout(&[&ir1, &ir2], &d)?;
2210        let (index2, _) = Jvm::select_timeout(&[&ir1, &ir2], &d)?;
2211        assert!(Jvm::select_timeout(&[&ir1, &ir2], &d).is_err());
2212        assert_eq!(index1, 0);
2213        assert_eq!(index2, 1);
2214
2215        Ok(())
2216    }
2217
2218    #[test]
2219    fn test_java_class_creation() -> errors::Result<()> {
2220        assert_eq!(JavaClass::Void.get_class_str(), "void");
2221        assert_eq!(JavaClass::String.get_class_str(), CLASS_STRING);
2222        assert_eq!(JavaClass::Boolean.get_class_str(), CLASS_BOOLEAN);
2223        assert_eq!(JavaClass::Byte.get_class_str(), CLASS_BYTE);
2224        assert_eq!(JavaClass::Character.get_class_str(), CLASS_CHARACTER);
2225        assert_eq!(JavaClass::Short.get_class_str(), CLASS_SHORT);
2226        assert_eq!(JavaClass::Integer.get_class_str(), CLASS_INTEGER);
2227        assert_eq!(JavaClass::Long.get_class_str(), CLASS_LONG);
2228        assert_eq!(JavaClass::Float.get_class_str(), CLASS_FLOAT);
2229        assert_eq!(JavaClass::Double.get_class_str(), CLASS_DOUBLE);
2230        assert_eq!(JavaClass::List.get_class_str(), CLASS_LIST);
2231        assert_eq!(
2232            JavaClass::Of("a.java.Class").get_class_str(),
2233            "a.java.Class"
2234        );
2235
2236        Ok(())
2237    }
2238
2239    #[test]
2240    fn test_byte_array_to_rust() -> errors::Result<()> {
2241        let jvm = create_tests_jvm()?;
2242        let rust_value: Vec<i8> = vec![-3_i8, 7_i8, 8_i8];
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_BYTE, &ia)?;
2245        let rust_value_from_java: Vec<i8> = jvm.to_rust(java_instance)?;
2246        assert_eq!(rust_value_from_java, rust_value);
2247
2248        Ok(())
2249    }
2250
2251    #[test]
2252    fn test_short_array_to_rust() -> errors::Result<()> {
2253        let jvm = create_tests_jvm()?;
2254        let rust_value: Vec<i16> = vec![-3_i16, 7_i16, 10000_i16];
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_SHORT, &ia)?;
2257        let rust_value_from_java: Vec<i16> = jvm.to_rust(java_instance)?;
2258        assert_eq!(rust_value_from_java, rust_value);
2259
2260        Ok(())
2261    }
2262
2263    #[test]
2264    fn test_char_array_to_rust() -> errors::Result<()> {
2265        let jvm = create_tests_jvm()?;
2266        let rust_value: Vec<u16> = vec![3_u16, 7_u16, 10000_u16];
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_CHAR, &ia)?;
2269        let rust_value_from_java: Vec<u16> = jvm.to_rust(java_instance)?;
2270        assert_eq!(rust_value_from_java, rust_value);
2271
2272        Ok(())
2273    }
2274
2275    #[test]
2276    fn test_int_array_to_rust() -> errors::Result<()> {
2277        let jvm = create_tests_jvm()?;
2278        let rust_value: Vec<i32> = 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_INT, &ia)?;
2281        let rust_value_from_java: Vec<i32> = jvm.to_rust(java_instance)?;
2282        assert_eq!(rust_value_from_java, rust_value);
2283
2284        Ok(())
2285    }
2286
2287    #[test]
2288    fn test_long_array_to_rust() -> errors::Result<()> {
2289        let jvm = create_tests_jvm()?;
2290        let rust_value: Vec<i64> = vec![-100_000, -1_000_000, 1_000_000];
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_LONG, &ia)?;
2293        let rust_value_from_java: Vec<i64> = jvm.to_rust(java_instance)?;
2294        assert_eq!(rust_value_from_java, rust_value);
2295
2296        Ok(())
2297    }
2298
2299    #[test]
2300    fn test_float_array_to_rust() -> errors::Result<()> {
2301        let jvm = create_tests_jvm()?;
2302        let rust_value: Vec<f32> = vec![3_f32, 7.5_f32, -1000.5_f32];
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_FLOAT, &ia)?;
2305        let rust_value_from_java: Vec<f32> = jvm.to_rust(java_instance)?;
2306        assert_eq!(rust_value_from_java, rust_value);
2307
2308        Ok(())
2309    }
2310
2311    #[test]
2312    fn test_double_array_to_rust() -> errors::Result<()> {
2313        let jvm = create_tests_jvm()?;
2314        let rust_value: Vec<f64> = vec![3_f64, 7.5_f64, -1000.5_f64];
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_DOUBLE, &ia)?;
2317        let rust_value_from_java: Vec<f64> = jvm.to_rust(java_instance)?;
2318        assert_eq!(rust_value_from_java, rust_value);
2319
2320        Ok(())
2321    }
2322
2323    #[test]
2324    fn test_boolean_array_to_rust() -> errors::Result<()> {
2325        let jvm = create_tests_jvm()?;
2326        let rust_value: Vec<bool> = vec![false, true, false];
2327        let ia: Vec<_> = rust_value.iter().map(|x| InvocationArg::try_from(x).unwrap().into_primitive().unwrap()).collect();
2328        let java_instance = jvm.create_java_array(PRIMITIVE_BOOLEAN, &ia)?;
2329        let rust_value_from_java: Vec<bool> = jvm.to_rust(java_instance)?;
2330        assert_eq!(rust_value_from_java, rust_value);
2331
2332        Ok(())
2333    }
2334
2335    #[test]
2336    fn test_int_to_rust() -> errors::Result<()> {
2337        let jvm = create_tests_jvm()?;
2338        let rust_value: i32 = 3;
2339        let ia = InvocationArg::try_from(rust_value)?.into_primitive()?;
2340        let java_instance = jvm.create_instance(CLASS_INTEGER, &[ia])?;
2341        let java_primitive_instance = jvm.invoke(&java_instance, "intValue", InvocationArg::empty())?;
2342        let rust_value_from_java: i32 = jvm.to_rust(java_instance)?;
2343        assert_eq!(rust_value_from_java, rust_value);
2344        let rust_value_from_java: i32 = jvm.to_rust(java_primitive_instance)?;
2345        assert_eq!(rust_value_from_java, rust_value);
2346
2347        Ok(())
2348    }
2349
2350    #[test]
2351    fn test_byte_to_rust() -> errors::Result<()> {
2352        let jvm = create_tests_jvm()?;
2353        let rust_value: i8 = 3;
2354        let ia = InvocationArg::try_from(rust_value)?.into_primitive()?;
2355        let java_instance = jvm.create_instance(CLASS_BYTE, &[ia])?;
2356        let java_primitive_instance = jvm.invoke(&java_instance, "byteValue", InvocationArg::empty())?;
2357        let rust_value_from_java: i8 = jvm.to_rust(java_instance)?;
2358        assert_eq!(rust_value_from_java, rust_value);
2359        let rust_value_from_java: i8 = jvm.to_rust(java_primitive_instance)?;
2360        assert_eq!(rust_value_from_java, rust_value);
2361
2362        Ok(())
2363    }
2364
2365    #[test]
2366    fn test_short_to_rust() -> errors::Result<()> {
2367        let jvm = create_tests_jvm()?;
2368        let rust_value: i16 = 3;
2369        let ia = InvocationArg::try_from(rust_value)?.into_primitive()?;
2370        let java_instance = jvm.create_instance(CLASS_SHORT, &[ia])?;
2371        let java_primitive_instance = jvm.invoke(&java_instance, "shortValue", InvocationArg::empty())?;
2372        let rust_value_from_java: i16 = jvm.to_rust(java_instance)?;
2373        assert_eq!(rust_value_from_java, rust_value);
2374        let rust_value_from_java: i16 = jvm.to_rust(java_primitive_instance)?;
2375        assert_eq!(rust_value_from_java, rust_value);
2376
2377        Ok(())
2378    }
2379
2380    #[test]
2381    fn test_char_to_rust() -> errors::Result<()> {
2382        let jvm = create_tests_jvm()?;
2383        let rust_value: u16 = 3;
2384        let ia = InvocationArg::try_from(rust_value)?.into_primitive()?;
2385        let java_instance = jvm.create_instance(CLASS_CHARACTER, &[ia])?;
2386        let java_primitive_instance = jvm.invoke(&java_instance, "charValue", InvocationArg::empty())?;
2387        let rust_value_from_java: u16 = jvm.to_rust(java_instance)?;
2388        assert_eq!(rust_value_from_java, rust_value);
2389        let rust_value_from_java: u16 = jvm.to_rust(java_primitive_instance)?;
2390        assert_eq!(rust_value_from_java, rust_value);
2391
2392        Ok(())
2393    }
2394
2395    #[test]
2396    fn test_long_to_rust() -> errors::Result<()> {
2397        let jvm = create_tests_jvm()?;
2398        let rust_value: i64 = 3;
2399        let ia = InvocationArg::try_from(rust_value)?.into_primitive()?;
2400        let java_instance = jvm.create_instance(CLASS_LONG, &[ia])?;
2401        let java_primitive_instance = jvm.invoke(&java_instance, "longValue", InvocationArg::empty())?;
2402        let rust_value_from_java: i64 = jvm.to_rust(java_instance)?;
2403        assert_eq!(rust_value_from_java, rust_value);
2404        let rust_value_from_java: i64 = jvm.to_rust(java_primitive_instance)?;
2405        assert_eq!(rust_value_from_java, rust_value);
2406
2407        Ok(())
2408    }
2409
2410    #[test]
2411    fn test_float_to_rust() -> errors::Result<()> {
2412        let jvm = create_tests_jvm()?;
2413        let rust_value: f32 = 3.3;
2414        let ia = InvocationArg::try_from(rust_value)?.into_primitive()?;
2415        let java_instance = jvm.create_instance(CLASS_FLOAT, &[ia])?;
2416        let java_primitive_instance = jvm.invoke(&java_instance, "floatValue", InvocationArg::empty())?;
2417        let rust_value_from_java: f32 = jvm.to_rust(java_instance)?;
2418        assert_eq!(rust_value_from_java, rust_value);
2419        let rust_value_from_java: f32 = jvm.to_rust(java_primitive_instance)?;
2420        assert_eq!(rust_value_from_java, rust_value);
2421
2422        Ok(())
2423    }
2424
2425    #[test]
2426    fn test_double_to_rust() -> errors::Result<()> {
2427        let jvm = create_tests_jvm()?;
2428        let rust_value: f64 = 3.3;
2429        let ia = InvocationArg::try_from(rust_value)?.into_primitive()?;
2430        let java_instance = jvm.create_instance(CLASS_DOUBLE, &[ia])?;
2431        let java_primitive_instance = jvm.invoke(&java_instance, "doubleValue", InvocationArg::empty())?;
2432        let rust_value_from_java: f64 = jvm.to_rust(java_instance)?;
2433        assert_eq!(rust_value_from_java, rust_value);
2434        let rust_value_from_java: f64 = jvm.to_rust(java_primitive_instance)?;
2435        assert_eq!(rust_value_from_java, rust_value);
2436
2437        Ok(())
2438    }
2439
2440    #[test]
2441    fn api_by_ref_or_value() -> errors::Result<()> {
2442        let jvm = create_tests_jvm()?;
2443
2444        // Instantiate
2445        let inv_arg1 = InvocationArg::try_from("some string")?;
2446        let _ = jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", &[&inv_arg1])?;
2447        let _ = jvm.create_instance("java.lang.String", &[inv_arg1])?;
2448        Ok(())
2449    }
2450
2451    #[test]
2452    fn exception_string_in_the_result() -> errors::Result<()> {
2453        let jvm = create_tests_jvm()?;
2454
2455        let res = jvm.create_instance("non.Existing", InvocationArg::empty());
2456        assert!(res.is_err());
2457        let exception_sttring = format!("{}",res.err().unwrap());
2458        assert!(exception_sttring.contains("Cannot create instance of non.Existing"));
2459        
2460        Ok(())
2461    }
2462}