Skip to main content

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