1use 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
52include!(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#[derive(Clone)]
85pub struct Jvm {
86 pub(crate) jni_env: *mut JNIEnv,
87 detach_thread_on_drop: bool,
88}
89
90impl Jvm {
91 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 pub fn attach_thread() -> errors::Result<Jvm> {
98 Self::create_jvm(&[], None)
99 }
100
101 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 pub fn detach_thread_on_drop(&mut self, detach: bool) {
118 self.detach_thread_on_drop = detach;
119 }
120
121 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 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 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 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 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 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 let class_name_jstring: jstring =
355 jni_utils::global_jobject_from_str(&class_name, self.jni_env)?;
356
357 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 for i in 0..size {
372 let inv_arg_java =
374 (&inv_args[i as usize]).borrow().as_java_ptr_with_global_ref(self.jni_env)?;
375 (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 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 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 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 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 pub fn static_class(&self, class_name: &str) -> errors::Result<Instance> {
420 debug(&format!("Retrieving static class {}", class_name));
421 unsafe {
422 let class_name_jstring: jstring =
424 jni_utils::global_jobject_from_str(&class_name, self.jni_env)?;
425
426 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 Self::do_return(
439 self.jni_env,
440 Instance::from_jobject_with_global_ref(java_instance)?,
441 )
442 }
443 }
444
445 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 let class_name_jstring: jstring =
461 jni_utils::global_jobject_from_str(&class_name, self.jni_env)?;
462
463 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 for i in 0..size {
478 let inv_arg_java =
480 inv_args[i as usize].borrow().as_java_ptr_with_global_ref(self.jni_env)?;
481 (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 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 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 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 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 #[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 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 let class_name_jstring: jstring =
560 jni_utils::global_jobject_from_str(&class_name, jni_env)?;
561
562 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 for i in 0..size {
577 let inv_arg_java = inv_args[i as usize].as_java_ptr_with_global_ref(jni_env)?;
579 (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 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 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 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 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 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 let key_class_name_jstring: jstring =
670 jni_utils::global_jobject_from_str(&key_class_name, jni_env)?;
671 let value_class_name_jstring: jstring =
673 jni_utils::global_jobject_from_str(&value_class_name, jni_env)?;
674
675 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 for i in 0..size {
690 let inv_arg_java = inv_args[i as usize].as_java_ptr_with_global_ref(jni_env)?;
692 (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 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 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 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 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 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 let method_name_jstring: jstring =
754 jni_utils::global_jobject_from_str(&method_name, self.jni_env)?;
755
756 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 for i in 0..size {
771 let inv_arg_java =
773 inv_args[i as usize].borrow().as_java_ptr_with_global_ref(self.jni_env)?;
774 (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 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 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 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 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 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 let field_name_jstring: jstring =
826 jni_utils::global_jobject_from_str(&field_name, self.jni_env)?;
827
828 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 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 jni_utils::delete_java_ref(self.jni_env, field_name_jstring);
843
844 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 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 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 let (sender, rx) = channel();
882 let tx = Box::new(sender);
883 let raw_ptr = Box::into_raw(tx);
885 let address_string = format!("{:p}", raw_ptr);
887 let address = u64::from_str_radix(&address_string[2..], 16).unwrap();
888
889 let method_name_jstring: jstring =
891 jni_utils::global_jobject_from_str(&method_name, self.jni_env)?;
892
893 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 for i in 0..size {
908 let inv_arg_java =
910 inv_args[i as usize].borrow().as_java_ptr_with_global_ref(self.jni_env)?;
911 (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 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 Self::do_return(self.jni_env, ())?;
933
934 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 Self::do_return(self.jni_env, InstanceReceiver::new(rx, address))
943 }
944 }
945
946 pub fn init_callback_channel(&self, instance: &Instance) -> errors::Result<InstanceReceiver> {
950 debug(&format!("Initializing callback channel"));
951 unsafe {
952 let (sender, rx) = channel();
954 let tx = Box::new(sender);
955 let raw_ptr = Box::into_raw(tx);
957 let address_string = format!("{:p}", raw_ptr);
959 let address = u64::from_str_radix(&address_string[2..], 16).unwrap();
960
961 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 Self::do_return(self.jni_env, InstanceReceiver::new(rx, address))
971 }
972 }
973
974 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 let class_name_jstring: jstring =
990 jni_utils::global_jobject_from_str(&class_name, self.jni_env)?;
991 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 let method_name_jstring: jstring =
1002 jni_utils::global_jobject_from_str(&method_name, self.jni_env)?;
1003
1004 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 for i in 0..size {
1018 let inv_arg_java =
1020 inv_args[i as usize].borrow().as_java_ptr_with_global_ref(self.jni_env)?;
1021 (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 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 Self::do_return(self.jni_env, ())?;
1040
1041 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 Self::do_return(
1050 self.jni_env,
1051 Instance::from_jobject_with_global_ref(java_instance)?,
1052 )
1053 }
1054 }
1055
1056 pub fn clone_instance(&self, instance: &Instance) -> errors::Result<Instance> {
1058 unsafe {
1059 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 Self::do_return(
1069 self.jni_env,
1070 Instance::from_jobject_with_global_ref(java_instance)?,
1071 )
1072 }
1073 }
1074
1075 pub fn cast(&self, from_instance: &Instance, to_class: &str) -> errors::Result<Instance> {
1077 debug(&format!("Casting to class {}", to_class));
1078 unsafe {
1079 let to_class_jstring: jstring =
1082 jni_utils::global_jobject_from_str(&to_class, self.jni_env)?;
1083
1084 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 Self::do_return(self.jni_env, ())?;
1095
1096 jni_utils::delete_java_ref(self.jni_env, to_class_jstring);
1098
1099 Self::do_return(
1101 self.jni_env,
1102 Instance::from_jobject_with_global_ref(java_instance)?,
1103 )
1104 }
1105 }
1106
1107 pub fn to_rust_boxed<T>(&self, instance: Instance) -> errors::Result<Box<T>>
1109 where
1110 T: DeserializeOwned + Any,
1111 {
1112 macro_rules! rust_box_from_java_object {
1114 ($jni_transformation:path) => {{
1115 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 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 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 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 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 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 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 let dynlibs: Vec<String> = {
1292 let mut dynlibs = vec![];
1293 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 pub fn chain(&self, instance: &Instance) -> errors::Result<ChainableInstance> {
1322 ChainableInstance::new_with_instance_ref(&instance, &self)
1323 }
1324
1325 pub fn into_chain(&self, instance: Instance) -> ChainableInstance {
1327 ChainableInstance::new(instance, &self)
1328 }
1329
1330 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 fn get_created_vm() -> Option<*mut JNIEnv> {
1355 unsafe {
1356 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 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 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 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 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 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
1490pub 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 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 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 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 pub fn java_opt(&'a mut self, opt: JavaOpt<'a>) -> &'a mut JvmBuilder {
1540 self.java_opts.push(opt);
1541 self
1542 }
1543
1544 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 pub fn with_no_implicit_classpath(&'a mut self) -> &'a mut JvmBuilder {
1555 self.no_implicit_classpath = true;
1556 self
1557 }
1558
1559 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 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 pub fn skip_setting_native_lib(&'a mut self) -> &'a mut JvmBuilder {
1581 self.skip_setting_native_lib = true;
1582 self
1583 }
1584
1585 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 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 pub fn with_javafx_support(&'a mut self) -> &'a mut JvmBuilder {
1600 self.javafx = true;
1601 self
1602 }
1603
1604 pub fn with_default_classloader(&'a mut self) -> &'a mut JvmBuilder {
1626 self.default_classloader = true;
1627 self
1628 }
1629
1630 pub fn build(&mut self) -> errors::Result<Jvm> {
1632 if !self.default_classloader {
1633 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 let jassets_path = self.get_jassets_path()?;
1652 let all_jars = get_dir_content(&jassets_path)?.files;
1653 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 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 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 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 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 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
1778pub 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
1838pub 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#[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#[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 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 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 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}