j4rs/api/
mod.rs

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