Skip to main content

j4rs/
lib.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
15//! # j4rs
16//!
17//![![crates.io](https://img.shields.io/crates/v/j4rs.svg)](https://crates.io/crates/j4rs)
18//![![Maven Central](https://img.shields.io/badge/Maven%20Central-0.25.1-blue.svg)](https://central.sonatype.com/artifact/io.github.astonbitecode/j4rs/)
19//!![Build](https://github.com/astonbitecode/j4rs/actions/workflows/ci-workflow.yml/badge.svg)
20//!
21//!j4rs stands for __'Java for Rust'__ and allows effortless calls to Java code from Rust and vice-versa.
22//! 
23//! Please see the [README](https://github.com/astonbitecode/j4rs) for more details
24extern crate libc;
25#[macro_use]
26extern crate log;
27extern crate serde;
28extern crate serde_json;
29
30use futures::channel::oneshot;
31use std::mem;
32use std::os::raw::c_void;
33use std::sync::mpsc::Sender;
34
35pub use jni_sys;
36use jni_sys::{jlong, jobject, jstring, JNIEnv};
37
38pub use api::instance::Instance;
39pub use api::instance::InstanceReceiver;
40
41pub use self::api::invocation_arg::InvocationArg;
42pub use self::api::Callback;
43pub use self::api::ClasspathEntry;
44pub use self::api::JavaClass;
45pub use self::api::JavaOpt;
46pub use self::api::Jvm;
47pub use self::api::JvmBuilder;
48pub use self::api::Null;
49pub use self::api_tweaks::{get_created_java_vms, set_java_vm};
50pub use self::jni_utils::jstring_to_rust_string;
51pub use self::provisioning::LocalJarArtifact;
52pub use self::provisioning::MavenArtifact;
53pub use self::provisioning::MavenArtifactRepo;
54pub use self::provisioning::MavenSettings;
55
56mod api;
57pub(crate) mod api_tweaks;
58pub mod async_api;
59mod cache;
60pub mod errors;
61pub mod jfx;
62mod jni_utils;
63mod logger;
64pub mod prelude;
65mod provisioning;
66mod utils;
67
68/// Creates a new JVM, using the provided classpath entries and JVM arguments
69pub fn new_jvm(
70    classpath_entries: Vec<ClasspathEntry>,
71    java_opts: Vec<JavaOpt>,
72) -> errors::Result<Jvm> {
73    JvmBuilder::new()
74        .classpath_entries(classpath_entries)
75        .java_opts(java_opts)
76        .build()
77}
78
79#[no_mangle]
80pub extern "C" fn Java_org_astonbitecode_j4rs_api_invocation_NativeCallbackToRustChannelSupport_docallbacktochannel(
81    _jni_env: *mut JNIEnv,
82    _class: *const c_void,
83    ptr_address: jlong,
84    java_instance: jobject,
85) {
86    let mut jvm = Jvm::attach_thread()
87        .expect("Could not create a j4rs Jvm while invoking callback to channel.");
88    jvm.detach_thread_on_drop(false);
89    let instance_res = Instance::from_jobject_with_global_ref(java_instance);
90    if let Ok(instance) = instance_res {
91        let p = ptr_address as *mut Sender<Instance>;
92        let tx = unsafe { Box::from_raw(p) };
93
94        let result = tx.send(instance);
95        mem::forget(tx);
96        if let Err(error) = result {
97            panic!(
98                "Could not send to the defined callback channel: {:?}",
99                error
100            );
101        }
102    } else {
103        panic!("Could not create Rust Instance from the Java Instance object...");
104    }
105}
106
107#[no_mangle]
108pub extern "C" fn Java_org_astonbitecode_j4rs_api_invocation_NativeCallbackToRustFutureSupport_docallbacktochannel(
109    _jni_env: *mut JNIEnv,
110    _class: *const c_void,
111    ptr_address: jlong,
112    java_instance: jobject,
113) {
114    let mut jvm = Jvm::attach_thread().expect(
115        "Could not create a j4rs Jvm while invoking callback to channel for completing a Future.",
116    );
117    jvm.detach_thread_on_drop(false);
118    let instance_res = Instance::from_jobject_with_global_ref(java_instance);
119    if let Ok(instance) = instance_res {
120        let p = ptr_address as *mut oneshot::Sender<errors::Result<Instance>>;
121        let tx = unsafe { Box::from_raw(p) };
122
123        let result = tx.send(Ok(instance));
124        if let Err(_) = result {
125            panic!("Could not send to the defined callback channel to complete the future");
126        }
127    } else {
128        panic!("Could not create Rust Instance from the Java Instance object...");
129    }
130}
131
132#[no_mangle]
133pub unsafe extern "C" fn Java_org_astonbitecode_j4rs_api_invocation_NativeCallbackToRustFutureSupport_failcallbacktochannel(
134    _jni_env: *mut JNIEnv,
135    _class: *const c_void,
136    ptr_address: jlong,
137    stacktrace: jstring,
138) {
139    let mut jvm = Jvm::attach_thread().expect(
140        "Could not create a j4rs Jvm while invoking callback to channel for failing a Future.",
141    );
142    jvm.detach_thread_on_drop(false);
143    let stacktrace = jstring_to_rust_string(&jvm, stacktrace);
144    if let Ok(st) = stacktrace {
145        let p = ptr_address as *mut oneshot::Sender<errors::Result<Instance>>;
146        let tx = unsafe { Box::from_raw(p) };
147
148        let result = tx.send(Err(errors::J4RsError::JavaError(st)));
149        if let Err(_) = result {
150            panic!("Could not send to the defined callback channel to fail a future");
151        }
152    } else {
153        panic!("Could not create Rust String from the Java jstring while invoking callback to channel for failing a Future...");
154    }
155}
156
157#[cfg(test)]
158mod lib_unit_tests {
159    use std::collections::HashMap;
160    use std::convert::TryFrom;
161    use std::path::MAIN_SEPARATOR;
162    use std::ptr::null_mut;
163    use std::thread::JoinHandle;
164    use std::{thread, time};
165    use std::sync::{LazyLock, Mutex};
166    use crate::api::{self, JavaClass};
167    use crate::provisioning::JavaArtifact;
168    use crate::{LocalJarArtifact, MavenArtifactRepo, MavenSettings, Null};
169    use super::utils::jassets_path;
170    use super::{errors, InvocationArg, Jvm, JvmBuilder, MavenArtifact};
171
172    static SYNC_GUARD: LazyLock<Mutex<()>> = LazyLock::new(|| {
173        Mutex::new(())
174    });
175
176    pub(crate) fn create_tests_jvm() -> errors::Result<Jvm> {
177        let jvm: Jvm = JvmBuilder::new().build()?;
178        {
179            let _guard = SYNC_GUARD.lock().unwrap();
180            jvm.deploy_artifact(&MavenArtifact::from(format!("io.github.astonbitecode:j4rs-testing:{}", api::j4rs_version()).as_str()))?;
181        }
182        Ok(jvm)
183    }
184
185    #[test]
186    fn create_instance_and_invoke() -> errors::Result<()> {
187        let jvm = create_tests_jvm()?;
188        let instantiation_args = vec![InvocationArg::try_from("arg from Rust")?];
189        let instance = jvm.create_instance("java.lang.String", instantiation_args.as_ref());
190        match instance {
191            Ok(i) => {
192                let invocation_args = vec![InvocationArg::try_from(" ")?];
193                let invocation_result = jvm.invoke(&i, "split", &invocation_args);
194                assert!(invocation_result.is_ok());
195            }
196            Err(error) => {
197                panic!("ERROR when creating Instance: {:?}", error);
198            }
199        };
200
201        let instantiation_args_2 = vec![InvocationArg::try_from("arg from Rust")?];
202        let instance_2 = jvm.create_instance("java.lang.String", instantiation_args_2.as_ref());
203        match instance_2 {
204            Ok(i) => {
205                let invocation_args = vec![InvocationArg::try_from(" ")?];
206                let invocation_result = jvm.invoke(&i, "split", &invocation_args);
207                assert!(invocation_result.is_ok());
208            }
209            Err(error) => {
210                panic!("ERROR when creating Instance: {:?}", error);
211            }
212        };
213
214        let static_invocation_result =
215            jvm.invoke_static("java.lang.System", "currentTimeMillis", InvocationArg::empty());
216        assert!(static_invocation_result.is_ok());
217
218        Ok(())
219    }
220
221    #[test]
222    fn init_callback_channel() -> errors::Result<()> {
223        let jvm = create_tests_jvm()?;
224        match jvm.create_instance(
225            "org.astonbitecode.j4rs.tests.MySecondTest",
226            InvocationArg::empty(),
227        ) {
228            Ok(i) => {
229                let instance_receiver_res = jvm.init_callback_channel(&i);
230                assert!(instance_receiver_res.is_ok());
231                let instance_receiver = instance_receiver_res?;
232                assert!(jvm.invoke(&i, "performCallback", InvocationArg::empty()).is_ok());
233                let res_chan = instance_receiver.rx().recv();
234                let i = res_chan?;
235                let res_to_rust = jvm.to_rust(i);
236                assert!(res_to_rust.is_ok());
237                let _: String = res_to_rust?;
238                let millis = time::Duration::from_millis(500);
239                thread::sleep(millis);
240            }
241            Err(error) => {
242                panic!("ERROR when creating Instance: {:?}", error);
243            }
244        }
245
246        Ok(())
247    }
248
249    #[test]
250    fn callback_to_channel() -> errors::Result<()> {
251        let jvm = create_tests_jvm()?;
252        match jvm.create_instance(
253            "org.astonbitecode.j4rs.tests.MySecondTest",
254            InvocationArg::empty(),
255        ) {
256            Ok(i) => {
257                let instance_receiver_res =
258                    jvm.invoke_to_channel(&i, "performCallback", InvocationArg::empty());
259                assert!(instance_receiver_res.is_ok());
260                let instance_receiver = instance_receiver_res?;
261                let res_chan = instance_receiver.rx().recv();
262                let i = res_chan?;
263                let res_to_rust = jvm.to_rust(i);
264                assert!(res_to_rust.is_ok());
265                let _: String = res_to_rust?;
266                let millis = time::Duration::from_millis(500);
267                thread::sleep(millis);
268            }
269            Err(error) => {
270                panic!("ERROR when creating Instance: {:?}", error);
271            }
272        }
273
274        Ok(())
275    }
276
277    #[test]
278    fn multiple_callbacks_to_channel() -> errors::Result<()> {
279        let jvm = create_tests_jvm()?;
280        match jvm.create_instance(
281            "org.astonbitecode.j4rs.tests.MySecondTest",
282            InvocationArg::empty(),
283        ) {
284            Ok(i) => {
285                let instance_receiver_res =
286                    jvm.invoke_to_channel(&i, "performTenCallbacks", InvocationArg::empty());
287                assert!(instance_receiver_res.is_ok());
288                let instance_receiver = instance_receiver_res?;
289                for _i in 0..10 {
290                    let thousand_millis = time::Duration::from_millis(1000);
291                    let res_chan = instance_receiver.rx().recv_timeout(thousand_millis);
292                    let i = res_chan.unwrap();
293                    let res_to_rust = jvm.to_rust(i);
294                    assert!(res_to_rust.is_ok());
295                    let _: String = res_to_rust?;
296                }
297                let millis = time::Duration::from_millis(500);
298                thread::sleep(millis);
299            }
300            Err(error) => {
301                panic!("ERROR when creating Instance: {:?}", error);
302            }
303        }
304
305        Ok(())
306    }
307
308    #[test]
309    fn multiple_callbacks_to_channel_from_multiple_threads() -> errors::Result<()> {
310        let jvm = create_tests_jvm()?;
311        match jvm.create_instance(
312            "org.astonbitecode.j4rs.tests.MySecondTest",
313            InvocationArg::empty(),
314        ) {
315            Ok(i) => {
316                let instance_receiver_res =
317                    jvm.invoke_to_channel(&i, "performCallbackFromTenThreads", InvocationArg::empty());
318                assert!(instance_receiver_res.is_ok());
319                let instance_receiver = instance_receiver_res?;
320                for _i in 0..10 {
321                    let thousand_millis = time::Duration::from_millis(1000);
322                    let res_chan = instance_receiver.rx().recv_timeout(thousand_millis);
323                    let i = res_chan.unwrap();
324                    let res_to_rust = jvm.to_rust(i);
325                    assert!(res_to_rust.is_ok());
326                    let _: String = res_to_rust?;
327                }
328                let millis = time::Duration::from_millis(500);
329                thread::sleep(millis);
330            }
331            Err(error) => {
332                panic!("ERROR when creating Instance:  {:?}", error);
333            }
334        }
335
336        Ok(())
337    }
338
339    // #[test]
340    // #[ignore]
341    fn _memory_leaks_invoke_instances_to_channel() -> errors::Result<()> {
342        let jvm = create_tests_jvm()?;
343        match jvm.create_instance(
344            "org.astonbitecode.j4rs.tests.MySecondTest",
345            InvocationArg::empty(),
346        ) {
347            Ok(instance) => {
348                for i in 0..100000000 {
349                    let instance_receiver = jvm
350                        .invoke_to_channel(&instance, "performCallback", InvocationArg::empty())
351                        ?;
352                    let thousand_millis = time::Duration::from_millis(1000);
353                    let res = instance_receiver.rx().recv_timeout(thousand_millis);
354                    if i % 100000 == 0 {
355                        println!("{}: {}", i, res.is_ok());
356                    }
357                }
358            }
359            Err(error) => {
360                panic!("ERROR when creating Instance: {:?}", error);
361            }
362        }
363
364        let thousand_millis = time::Duration::from_millis(1000);
365        thread::sleep(thousand_millis);
366
367        Ok(())
368    }
369
370    #[test]
371    fn clone_instance() -> errors::Result<()> {
372        let jvm = create_tests_jvm()?;
373        // Create a MyTest instance
374        let i_result =
375            jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty());
376        assert!(i_result.is_ok());
377        let i_arg = i_result?;
378
379        // Create two clones of the instance
380        let i1 = jvm.clone_instance(&i_arg)?;
381        let i2 = jvm.clone_instance(&i_arg)?;
382        // Use the clones as arguments
383        let invocation_res = jvm.create_instance(
384            "org.astonbitecode.j4rs.tests.MyTest",
385            &[InvocationArg::from(i1)],
386        );
387        assert!(invocation_res.is_ok());
388        let invocation_res = jvm.create_instance(
389            "org.astonbitecode.j4rs.tests.MyTest",
390            &[InvocationArg::from(i2)],
391        );
392        assert!(invocation_res.is_ok());
393
394        Ok(())
395    }
396
397    //    #[test]
398    //    #[ignore]
399    fn _memory_leaks_create_instances() -> errors::Result<()> {
400        let jvm = create_tests_jvm()?;
401
402        for i in 0..100000000 {
403            match jvm.create_instance(
404                "org.astonbitecode.j4rs.tests.MySecondTest",
405                InvocationArg::empty(),
406            ) {
407                Ok(instance) => {
408                    if i % 100000 == 0 {
409                        println!("{}: {}", i, instance.class_name());
410                    }
411                }
412                Err(error) => {
413                    panic!("ERROR when creating Instance: {:?}", error);
414                }
415            }
416        }
417        let thousand_millis = time::Duration::from_millis(1000);
418        thread::sleep(thousand_millis);
419
420        Ok(())
421    }
422
423    //        #[test]
424    //    #[ignore]
425    fn _memory_leaks_invoke_instances() -> errors::Result<()> {
426        let jvm = create_tests_jvm()?;
427        match jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty()) {
428            Ok(instance) => {
429                let inv_arg = InvocationArg::try_from("tests")?;
430                for i in 0..100000000 {
431                    if i % 100000 == 0 {
432                        println!("{}", i);
433                    }
434                    jvm.invoke(&instance, "getMyWithArgs", &[&inv_arg])?;
435                }
436            }
437            Err(error) => {
438                panic!("ERROR when creating Instance: {:?}", error);
439            }
440        }
441
442        let thousand_millis = time::Duration::from_millis(1000);
443        thread::sleep(thousand_millis);
444
445        Ok(())
446    }
447
448    // #[test]
449    // #[ignore]
450    fn _memory_leaks_invoke_instances_and_to_rust() -> errors::Result<()> {
451        let jvm = create_tests_jvm()?;
452        match jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty()) {
453            Ok(instance) => {
454                for i in 0..100000000 {
455                    let ret_instance = jvm
456                        .invoke(
457                            &instance,
458                            "echo",
459                            &[InvocationArg::try_from(33333333_i32)?],
460                        )?;
461                    let v: i32 = jvm.to_rust(ret_instance)?;
462                    if i % 100000 == 0 {
463                        println!("{}: {}", i, v);
464                    }
465                }
466            }
467            Err(error) => {
468                panic!("ERROR when creating Instance: {:?}", error);
469            }
470        }
471
472        let thousand_millis = time::Duration::from_millis(1000);
473        thread::sleep(thousand_millis);
474
475        Ok(())
476    }
477
478    //    #[test]
479    //    #[ignore]
480    fn _memory_leaks_invoke_instances_w_new_invarg() -> errors::Result<()> {
481        let jvm = create_tests_jvm()?;
482        let mut string_arg_rust = "".to_string();
483        for _ in 0..100 {
484            string_arg_rust = format!("{}{}", string_arg_rust, "astring")
485        }
486        match jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty()) {
487            Ok(instance) => {
488                for i in 0..100000000 {
489                    if i % 100000 == 0 {
490                        println!("{}", i);
491                    }
492                    let _ia = InvocationArg::try_from(&string_arg_rust)?;
493                    jvm.invoke(&instance, "getMyWithArgs", &[_ia])?;
494                }
495            }
496            Err(error) => {
497                panic!("ERROR when creating Instance: {:?}", error);
498            }
499        }
500
501        let thousand_millis = time::Duration::from_millis(1000);
502        thread::sleep(thousand_millis);
503
504        Ok(())
505    }
506
507    //    #[test]
508    //    #[ignore]
509    fn _memory_leaks_create_instances_in_different_threads() -> errors::Result<()> {
510        for i in 0..100000000 {
511            thread::spawn(move || {
512                let jvm = create_tests_jvm().unwrap();
513                match jvm.create_instance(
514                    "org.astonbitecode.j4rs.tests.MySecondTest",
515                    InvocationArg::empty(),
516                ) {
517                    Ok(_) => {
518                        if i % 100000 == 0 {
519                            println!("{}", i);
520                        }
521                    }
522                    Err(error) => {
523                        panic!("ERROR when creating Instance: {:?}", error);
524                    }
525                };
526            });
527
528            let millis = time::Duration::from_millis(10);
529            thread::sleep(millis);
530        }
531
532        Ok(())
533    }
534
535    #[test]
536    fn cast() -> errors::Result<()> {
537        let jvm = create_tests_jvm()?;
538
539        let instantiation_args = vec![InvocationArg::try_from("Hi")?];
540        let instance = jvm
541            .create_instance("java.lang.String", instantiation_args.as_ref())?;
542        jvm.cast(&instance, "java.lang.Object")?;
543
544        Ok(())
545    }
546
547    #[test]
548    fn invoke_vec() -> errors::Result<()> {
549        let jvm = create_tests_jvm()?;
550
551        match jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty()) {
552            Ok(i) => {
553                // Test using InvocationArgs
554                let invocation_args = vec![
555                    InvocationArg::try_from("arg1"),
556                    InvocationArg::try_from("arg2"),
557                    InvocationArg::try_from("arg3"),
558                    InvocationArg::try_from("arg33"),
559                ];
560                let list_instance = jvm.java_list("java.lang.String", invocation_args)?;
561                let res = jvm.invoke(&i, "list", &[InvocationArg::from(list_instance)]);
562                assert!(res.is_ok());
563                // Test using instances
564                let instance = jvm.create_instance(
565                    "java.lang.String",
566                    &[InvocationArg::try_from("astring")?],
567                );
568                let list_instance = jvm.java_list("java.lang.String", vec![instance])?;
569                let res = jvm.invoke(&i, "list", &[InvocationArg::from(list_instance)]);
570                assert!(res.is_ok());
571                // Test other types
572                let list_instance = jvm
573                    .java_list(JavaClass::String, vec!["arg1", "arg2", "arg3", "arg33"])?;
574                let res = jvm.invoke(&i, "list", &[InvocationArg::from(list_instance)]);
575                assert!(res.is_ok());
576            }
577            Err(error) => {
578                panic!("ERROR when creating Instance: {:?}", error);
579            }
580        }
581
582        Ok(())
583    }
584
585    #[test]
586    fn invoke_map() -> errors::Result<()> {
587        let jvm = create_tests_jvm()?;
588
589        match jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty()) {
590            Ok(i) => {
591                let map = HashMap::from([("Potatoes", 3), ("Tomatoes", 33), ("Carrotoes", 333)]);
592                let map_instance = jvm
593                    .java_map(JavaClass::String, JavaClass::Integer, map)?;
594                let res = jvm.invoke(&i, "map", &[InvocationArg::from(map_instance)]);
595                assert!(res.is_ok());
596            }
597            Err(error) => {
598                panic!("ERROR when creating Instance: {:?}", error);
599            }
600        }
601
602        Ok(())
603    }
604
605    #[test]
606    fn multithread() -> errors::Result<()> {
607        let v: Vec<JoinHandle<String>> = (0..10)
608            .map(|i: i8| {
609                let v = thread::spawn(move || {
610                    let jvm = create_tests_jvm().unwrap();
611                    let instantiation_args =
612                        vec![InvocationArg::try_from(format!("Thread{}", i)).unwrap()];
613                    let instance = jvm
614                        .create_instance("java.lang.String", instantiation_args.as_ref()).unwrap();
615                    let string: String = jvm.to_rust(instance).unwrap();
616                    string
617                });
618                v
619            })
620            .collect();
621
622        for jh in v {
623            let str = jh.join();
624            println!("{}", str.unwrap());
625        }
626
627        Ok(())
628    }
629
630    #[test]
631    fn use_a_java_instance_in_different_thread() -> errors::Result<()> {
632        let jvm = create_tests_jvm()?;
633        let instantiation_args = vec![InvocationArg::try_from("3")?];
634        let instance = jvm
635            .create_instance("java.lang.String", instantiation_args.as_ref())
636            ?;
637
638        let jh = thread::spawn(move || {
639            let jvm = create_tests_jvm()?;
640            let res = jvm.invoke(&instance, "isEmpty", InvocationArg::empty());
641            res
642        });
643
644        let join_res = jh.join();
645        assert!(join_res.is_ok());
646        assert!(join_res.unwrap().is_ok());
647
648        Ok(())
649    }
650
651    #[test]
652    fn drop_and_attach_main_thread() -> errors::Result<()> {
653        let tid = format!("{:?}", thread::current().id());
654        {
655            let jvm = create_tests_jvm()?;
656            let instantiation_args = vec![InvocationArg::try_from(tid.clone())?];
657            let instance = jvm
658                .create_instance("java.lang.String", instantiation_args.as_ref())
659                ?;
660            let tid_from_java: &String = &(jvm.to_rust(instance)?);
661            assert!(&tid == tid_from_java);
662        }
663        {
664            let jvm = create_tests_jvm()?;
665            let instantiation_args = vec![InvocationArg::try_from(tid.clone())?];
666            let instance = jvm
667                .create_instance("java.lang.String", instantiation_args.as_ref())
668                ?;
669            let tid_from_java: &String = &(jvm.to_rust(instance)?);
670            assert!(&tid == tid_from_java);
671        }
672
673        Ok(())
674    }
675
676    #[test]
677    fn drop_and_attach_other_thread() -> errors::Result<()> {
678        let _: Jvm = super::new_jvm(Vec::new(), Vec::new())?;
679        let jh = thread::spawn(move || {
680            let tid = format!("{:?}", thread::current().id());
681            {
682                let jvm = create_tests_jvm().unwrap();
683                let instantiation_args = vec![InvocationArg::try_from(tid.clone()).unwrap()];
684                let instance = jvm
685                    .create_instance("java.lang.String", instantiation_args.as_ref())
686                    .unwrap();
687                let tid_from_java: &String = &jvm.to_rust(instance).unwrap();
688                assert!(&tid == tid_from_java);
689            }
690            {
691                let jvm = create_tests_jvm().unwrap();
692                let instantiation_args = vec![InvocationArg::try_from(tid.clone()).unwrap()];
693                let instance = jvm
694                    .create_instance("java.lang.String", instantiation_args.as_ref())
695                    .unwrap();
696                let tid_from_java: &String = &jvm.to_rust(instance).unwrap();
697                assert!(&tid == tid_from_java);
698            }
699            true
700        });
701
702        assert!(jh.join().unwrap());
703
704        Ok(())
705    }
706
707    #[test]
708    fn deploy_maven_artifact() -> errors::Result<()> {
709        let jvm = create_tests_jvm()?;
710        assert!(jvm
711            .deploy_artifact(&MavenArtifact::from("io.github.astonbitecode:j4rs:0.22.0"))
712            .is_ok());
713        let to_remove = format!(
714            "{}{}j4rs-0.22.0.jar",
715            jassets_path().unwrap().to_str().unwrap(),
716            MAIN_SEPARATOR
717        );
718        let _ = std::fs::remove_file(to_remove);
719
720        assert!(jvm.deploy_artifact(&UnknownArtifact {}).is_err());
721
722        Ok(())
723    }
724
725    #[test]
726    fn deploy_maven_artifact_with_transitive_deps() -> errors::Result<()> {
727        let jvm = create_tests_jvm()?;
728        assert!(jvm
729            .deploy_artifact_and_deps(&MavenArtifact::from("org.openjfx:javafx-graphics:21.0.9"))
730            .is_ok());
731
732        let to_remove1 = format!(
733            "{}{}javafx-graphics-21.0.9.jar",
734            jassets_path().unwrap().to_str().unwrap(),
735            MAIN_SEPARATOR
736        );
737        std::fs::remove_file(to_remove1)?;
738
739        let to_remove2 = format!(
740            "{}{}javafx-base-21.0.9.jar",
741            jassets_path().unwrap().to_str().unwrap(),
742            MAIN_SEPARATOR
743        );
744        std::fs::remove_file(to_remove2)?;
745
746        Ok(())
747    }
748
749    #[test]
750    fn deploy_maven_artifact_from_more_artifactories() -> errors::Result<()> {
751        let jvm: Jvm = JvmBuilder::new()
752            .with_maven_settings(MavenSettings::new(vec![
753                MavenArtifactRepo::from("myrepo1::https://my.repo.io/artifacts"),
754                MavenArtifactRepo::from("myrepo2::https://my.other.repo.io/artifacts"),
755            ]))
756            .build()?;
757        assert!(jvm
758            .deploy_artifact(&MavenArtifact::from("io.github.astonbitecode:j4rs:0.22.0"))
759            .is_ok());
760        let to_remove = format!(
761            "{}{}j4rs-0.22.0.jar",
762            jassets_path().unwrap().to_str().unwrap(),
763            MAIN_SEPARATOR
764        );
765        let _ = std::fs::remove_dir_all(to_remove);
766
767        Ok(())
768    }
769
770    #[test]
771    fn deploy_local_artifact() -> errors::Result<()> {
772        let jvm: Jvm = super::new_jvm(Vec::new(), Vec::new())?;
773        assert!(jvm
774            .deploy_artifact(&LocalJarArtifact::from("./non_existing.jar"))
775            .is_err());
776
777        Ok(())
778    }
779
780    struct UnknownArtifact {}
781
782    impl JavaArtifact for UnknownArtifact {}
783
784    #[test]
785    fn variadic_constructor() -> errors::Result<()> {
786        let jvm = create_tests_jvm()?;
787
788        let s1 = InvocationArg::try_from("abc")?;
789        let s2 = InvocationArg::try_from("def")?;
790        let s3 = InvocationArg::try_from("ghi")?;
791
792        let arr_instance = jvm
793            .create_java_array("java.lang.String", &vec![s1, s2, s3])
794            ?;
795
796        let test_instance = jvm
797            .create_instance(
798                "org.astonbitecode.j4rs.tests.MyTest",
799                &[InvocationArg::from(arr_instance)],
800            )
801            ?;
802
803        let i = jvm.invoke(&test_instance, "getMyString", InvocationArg::empty())?;
804
805        let s: String = jvm.to_rust(i)?;
806        assert!(s == "abc, def, ghi");
807
808        Ok(())
809    }
810
811    #[test]
812    fn variadic_string_method() -> errors::Result<()> {
813        let jvm = create_tests_jvm()?;
814        let test_instance = jvm
815            .create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())
816            ?;
817
818        let s1 = InvocationArg::try_from("abc")?;
819        let s2 = InvocationArg::try_from("def")?;
820        let s3 = InvocationArg::try_from("ghi")?;
821
822        let arr_instance = jvm
823            .create_java_array("java.lang.String", &vec![s1, s2, s3])
824            ?;
825
826        let i = jvm
827            .invoke(
828                &test_instance,
829                "getMyWithArgsList",
830                &[InvocationArg::from(arr_instance)],
831            )
832            ?;
833
834        let s: String = jvm.to_rust(i)?;
835        assert!(s == "abcdefghi");
836
837        Ok(())
838    }
839
840    #[test]
841    fn variadic_int_method() -> errors::Result<()> {
842        let jvm = create_tests_jvm()?;
843        let test_instance = jvm
844            .create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())
845            ?;
846
847        let s1 = InvocationArg::try_from(1)?;
848        let s2 = InvocationArg::try_from(2)?;
849        let s3 = InvocationArg::try_from(3)?;
850
851        let arr_instance = jvm
852            .create_java_array("java.lang.Integer", &vec![s1, s2, s3])
853            ?;
854
855        let i = jvm
856            .invoke(
857                &test_instance,
858                "addInts",
859                &[InvocationArg::from(arr_instance)],
860            )
861            ?;
862
863        let num: i32 = jvm.to_rust(i)?;
864        assert!(num == 6);
865
866        Ok(())
867    }
868
869    #[test]
870    fn variadic_long_primitive_method() -> errors::Result<()> {
871        let jvm = create_tests_jvm()?;
872        let values: Vec<i64> = vec![1, 2, 3];
873        let jargs: Vec<_> = values
874            .into_iter()
875            .map(|v| {
876                InvocationArg::try_from(v)
877                    .unwrap()
878                    .into_primitive()
879                    .unwrap()
880            })
881            .collect();
882
883        let arr_instance = jvm.create_java_array("long", &jargs)?;
884
885        let _ = jvm
886            .invoke_static(
887                "org.astonbitecode.j4rs.tests.MyTest",
888                "useLongPrimitivesArray",
889                &[InvocationArg::from(arr_instance)],
890            )
891            ?;
892
893        Ok(())
894    }
895
896    #[test]
897    fn instance_invocation_chain_and_collect() -> errors::Result<()> {
898        let jvm = create_tests_jvm()?;
899        let instance = jvm
900            .create_instance(
901                "org.astonbitecode.j4rs.tests.MyTest",
902                &[InvocationArg::try_from("string")?],
903            )
904            ?;
905
906        let i1 = jvm
907            .chain(&instance)
908            ?
909            .invoke(
910                "appendToMyString",
911                &[InvocationArg::try_from("_is_appended")?],
912            )
913            ?
914            .invoke("length", InvocationArg::empty())
915            ?
916            .collect();
917
918        let product: isize = jvm.to_rust(i1)?;
919
920        assert!(product == 18);
921
922        Ok(())
923    }
924
925    #[test]
926    fn instance_invocation_chain_and_to_rust() -> errors::Result<()> {
927        let jvm = create_tests_jvm()?;
928        let instance = jvm
929            .create_instance(
930                "org.astonbitecode.j4rs.tests.MyTest",
931                &[InvocationArg::try_from("string")?],
932            )
933            ?;
934
935        let product: isize = jvm
936            .into_chain(instance)
937            .invoke(
938                "appendToMyString",
939                &[InvocationArg::try_from("_is_appended")?],
940            )
941            ?
942            .invoke("length", InvocationArg::empty())
943            ?
944            .to_rust()
945            ?;
946
947        assert!(product == 18);
948
949        Ok(())
950    }
951
952    #[test]
953    fn static_invocation_chain_and_to_rust() -> errors::Result<()> {
954        let jvm = create_tests_jvm()?;
955
956        let static_invocation = jvm.static_class("java.lang.System")?;
957
958        let _: isize = jvm
959            .into_chain(static_invocation)
960            .invoke("currentTimeMillis", InvocationArg::empty())
961            ?
962            .to_rust()
963            ?;
964
965        Ok(())
966    }
967
968    #[test]
969    fn access_class_field_and_enum() -> errors::Result<()> {
970        let jvm = create_tests_jvm()?;
971
972        let static_invocation = jvm.static_class("java.lang.System")?;
973        let field_instance_res = jvm.field(&static_invocation, "out");
974        assert!(field_instance_res.is_ok());
975
976        let access_mode_enum = jvm.static_class("java.nio.file.AccessMode")?;
977        let access_mode_write = jvm.field(&access_mode_enum, "WRITE");
978        assert!(access_mode_write.is_ok());
979
980        Ok(())
981    }
982
983    #[test]
984    fn java_hello_world() -> errors::Result<()> {
985        let jvm = create_tests_jvm()?;
986
987        let system = jvm.static_class("java.lang.System")?;
988        let _ = jvm
989            .into_chain(system)
990            .field("out")
991            ?
992            .invoke(
993                "println",
994                &[InvocationArg::try_from("Hello World")?],
995            )
996            ?
997            .collect();
998
999        Ok(())
1000    }
1001
1002    #[test]
1003    fn parent_interface_method() -> errors::Result<()> {
1004        let jvm = create_tests_jvm()?;
1005        let instance = jvm
1006            .create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())
1007            ?;
1008
1009        let size: isize = jvm
1010            .into_chain(instance)
1011            .invoke("getMap", InvocationArg::empty())
1012            ?
1013            .cast("java.util.Map")
1014            ?
1015            .invoke("size", InvocationArg::empty())
1016            ?
1017            .to_rust()
1018            ?;
1019
1020        assert!(size == 2);
1021
1022        Ok(())
1023    }
1024
1025    #[test]
1026    fn invoke_generic_method() -> errors::Result<()> {
1027        let jvm = create_tests_jvm()?;
1028
1029        // Create the MyTest instance
1030        let instance = jvm
1031            .create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())
1032            ?;
1033
1034        // Retrieve the annotated Map
1035        let dummy_map = jvm.invoke(&instance, "getMap", InvocationArg::empty())?;
1036
1037        // Put a new Map entry
1038        let _ = jvm
1039            .invoke(
1040                &dummy_map,
1041                "put",
1042                &[InvocationArg::try_from("three")?,
1043                    InvocationArg::try_from(3)?],
1044            )
1045            ?;
1046
1047        // Get the size of the new map and assert
1048        let size: isize = jvm
1049            .into_chain(dummy_map)
1050            .invoke("size", InvocationArg::empty())
1051            ?
1052            .to_rust()
1053            ?;
1054
1055        assert!(size == 3);
1056
1057        Ok(())
1058    }
1059
1060    #[test]
1061    fn invoke_method_with_primitive_args() -> errors::Result<()> {
1062        let jvm = create_tests_jvm()?;
1063
1064        // Test the primitives in constructors.
1065        // The constructor of Integer takes a primitive int as an argument.
1066        let ia = InvocationArg::try_from(1_i32)
1067            ?
1068            .into_primitive()
1069            ?;
1070        let res1 = jvm.create_instance("java.lang.Integer", &[ia]);
1071        assert!(res1.is_ok());
1072
1073        // Test the primitives in invocations.
1074        let ia1 = InvocationArg::try_from(1_i32)?;
1075        let ia2 = InvocationArg::try_from(1_i32)?;
1076        let test_instance = jvm
1077            .create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())
1078            ?;
1079        let res2 = jvm.invoke(
1080            &test_instance,
1081            "addInts",
1082            &[ia1.into_primitive()?, ia2.into_primitive()?],
1083        );
1084        assert!(res2.is_ok());
1085
1086        Ok(())
1087    }
1088
1089    #[test]
1090    fn to_tust_returns_list() -> errors::Result<()> {
1091        let jvm = create_tests_jvm()?;
1092        let test_instance = jvm
1093            .create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())
1094            ?;
1095        let list_instance = jvm
1096            .invoke(
1097                &test_instance,
1098                "getNumbersUntil",
1099                &[InvocationArg::try_from(10_i32)?],
1100            )
1101            ?;
1102        let vec: Vec<i32> = jvm.to_rust(list_instance)?;
1103        assert!(vec.len() == 10);
1104
1105        Ok(())
1106    }
1107
1108    #[test]
1109    fn basic_types() -> errors::Result<()> {
1110        let jvm = create_tests_jvm()?;
1111        let test_instance = jvm
1112            .create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())
1113            ?;
1114
1115        // By values
1116        let arg = InvocationArg::try_from(33_i8)?;
1117        let i = jvm.invoke(&test_instance, "echo", &[arg])?;
1118        let ret: i8 = jvm.to_rust(i)?;
1119        assert!(ret == 33_i8);
1120
1121        let arg = InvocationArg::try_from(33_i16)?;
1122        let i = jvm.invoke(&test_instance, "echo", &[arg])?;
1123        let ret: i16 = jvm.to_rust(i)?;
1124        assert!(ret == 33_i16);
1125
1126        let arg = InvocationArg::try_from(33_i32)?;
1127        let i = jvm.invoke(&test_instance, "echo", &[arg])?;
1128        let ret: i32 = jvm.to_rust(i)?;
1129        assert!(ret == 33_i32);
1130
1131        let arg = InvocationArg::try_from(33_i64)?;
1132        let i = jvm.invoke(&test_instance, "echo", &[arg])?;
1133        let ret: i64 = jvm.to_rust(i)?;
1134        assert!(ret == 33_i64);
1135
1136        let arg = InvocationArg::try_from(33.33_f32)?;
1137        let i = jvm.invoke(&test_instance, "echo", &[arg])?;
1138        let ret: f32 = jvm.to_rust(i)?;
1139        assert!(ret == 33.33_f32);
1140
1141        let arg = InvocationArg::try_from(33.33_f64)?;
1142        let i = jvm.invoke(&test_instance, "echo", &[arg])?;
1143        let ret: f64 = jvm.to_rust(i)?;
1144        assert!(ret == 33.33_f64);
1145
1146        // By reference
1147        let arg = InvocationArg::try_from(&33_i8)?;
1148        let i = jvm.invoke(&test_instance, "echo", &[arg])?;
1149        let ret: i8 = jvm.to_rust(i)?;
1150        assert!(ret == 33_i8);
1151
1152        let arg = InvocationArg::try_from(&33_i16)?;
1153        let i = jvm.invoke(&test_instance, "echo", &[arg])?;
1154        let ret: i16 = jvm.to_rust(i)?;
1155        assert!(ret == 33_i16);
1156
1157        let arg = InvocationArg::try_from(&33_i32)?;
1158        let i = jvm.invoke(&test_instance, "echo", &[arg])?;
1159        let ret: i32 = jvm.to_rust(i)?;
1160        assert!(ret == 33_i32);
1161
1162        let arg = InvocationArg::try_from(&33_i64)?;
1163        let i = jvm.invoke(&test_instance, "echo", &[arg])?;
1164        let ret: i64 = jvm.to_rust(i)?;
1165        assert!(ret == 33_i64);
1166
1167        let arg = InvocationArg::try_from(&33.33_f32)?;
1168        let i = jvm.invoke(&test_instance, "echo", &[arg])?;
1169        let ret: f32 = jvm.to_rust(i)?;
1170        assert!(ret == 33.33_f32);
1171
1172        let arg = InvocationArg::try_from(&33.33_f64)?;
1173        let i = jvm.invoke(&test_instance, "echo", &[arg])?;
1174        let ret: f64 = jvm.to_rust(i)?;
1175        assert!(ret == 33.33_f64);
1176
1177        Ok(())
1178    }
1179
1180    #[test]
1181    fn vecs_arrays() -> errors::Result<()> {
1182        let jvm = create_tests_jvm()?;
1183        let test_instance = jvm
1184            .create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())
1185            ?;
1186
1187        let arg = InvocationArg::try_from([33_i8, 34_i8].as_slice())?;
1188        let i = jvm.invoke(&test_instance, "echo", &[arg])?;
1189        let ret: Vec<i8> = jvm.to_rust(i)?;
1190        assert!(ret == vec![33_i8, 34_i8]);
1191
1192        let arg = InvocationArg::try_from([33_i16, 34_i16].as_slice())?;
1193        let i = jvm.invoke(&test_instance, "echo", &[arg])?;
1194        let ret: Vec<i16> = jvm.to_rust(i)?;
1195        assert!(ret == vec![33_i16, 34_i16]);
1196
1197        let arg = InvocationArg::try_from([33_i32, 34_i32].as_slice())?;
1198        let i = jvm.invoke(&test_instance, "echo", &[arg])?;
1199        let ret: Vec<i32> = jvm.to_rust(i)?;
1200        assert!(ret == vec![33_i32, 34_i32]);
1201
1202        let arg = InvocationArg::try_from([33_i64, 34_i64].as_slice())?;
1203        let i = jvm.invoke(&test_instance, "echo", &[arg])?;
1204        let ret: Vec<i64> = jvm.to_rust(i)?;
1205        assert!(ret == vec![33_i64, 34_i64]);
1206
1207        let arg = InvocationArg::try_from([33_f32, 34_f32].as_slice())?;
1208        let i = jvm.invoke(&test_instance, "echo", &[arg])?;
1209        let ret: Vec<f32> = jvm.to_rust(i)?;
1210        assert!(ret == vec![33_f32, 34_f32]);
1211
1212        let arg = InvocationArg::try_from([33_f64, 34_f64].as_slice())?;
1213        let i = jvm.invoke(&test_instance, "echo", &[arg])?;
1214        let ret: Vec<f64> = jvm.to_rust(i)?;
1215        assert!(ret == vec![33_f64, 34_f64]);
1216
1217        Ok(())
1218    }
1219
1220    #[test]
1221    fn null_handling() -> errors::Result<()> {
1222        let jvm = create_tests_jvm()?;
1223        let test_instance = jvm
1224            .create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())
1225            ?;
1226        let null = jvm.invoke(&test_instance, "getNullInteger", InvocationArg::empty())?;
1227        let list_instance = jvm
1228            .invoke(
1229                &test_instance,
1230                "getNumbersUntil",
1231                &[InvocationArg::from(null)],
1232            )
1233            ?;
1234        let vec: Vec<i32> = jvm.to_rust(list_instance)?;
1235        assert!(vec.is_empty());
1236
1237        Ok(())
1238    }
1239
1240    #[test]
1241    fn null_creation() -> errors::Result<()> {
1242        let jvm = create_tests_jvm()?;
1243        let test_instance = jvm
1244            .create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())
1245            ?;
1246        let null = InvocationArg::try_from(Null::Of("java.lang.Integer"))?;
1247        let list_instance = jvm
1248            .invoke(&test_instance, "getNumbersUntil", &[null])
1249            ?;
1250        let vec: Vec<i32> = jvm.to_rust(list_instance)?;
1251        assert!(vec.is_empty());
1252
1253        let null = InvocationArg::try_from(Null::Integer)?;
1254        let list_instance = jvm
1255            .invoke(&test_instance, "getNumbersUntil", &[null])
1256            ?;
1257        let vec: Vec<i32> = jvm.to_rust(list_instance)?;
1258        assert!(vec.is_empty());
1259
1260        Ok(())
1261    }
1262
1263    #[test]
1264    fn to_rust_boxed() -> errors::Result<()> {
1265        let jvm = create_tests_jvm()?;
1266        let test_instance = jvm
1267            .create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())
1268            ?;
1269
1270        let i = jvm
1271            .invoke(
1272                &test_instance,
1273                "echo",
1274                &[InvocationArg::try_from(true)?],
1275            )
1276            ?;
1277        let _: Box<bool> = jvm.to_rust_boxed(i)?;
1278        let i = jvm
1279            .invoke(
1280                &test_instance,
1281                "echo",
1282                &[InvocationArg::try_from(33_i8)?],
1283            )
1284            ?;
1285        let _: Box<i8> = jvm.to_rust_boxed(i)?;
1286        let i = jvm
1287            .invoke(
1288                &test_instance,
1289                "echo",
1290                &[InvocationArg::try_from(33_i16)?],
1291            )
1292            ?;
1293        let _: Box<i16> = jvm.to_rust_boxed(i)?;
1294        let i = jvm
1295            .invoke(
1296                &test_instance,
1297                "echo",
1298                &[InvocationArg::try_from(33_i32)?],
1299            )
1300            ?;
1301        let _: Box<i32> = jvm.to_rust_boxed(i)?;
1302        let i = jvm
1303            .invoke(
1304                &test_instance,
1305                "echo",
1306                &[InvocationArg::try_from(33_i64)?],
1307            )
1308            ?;
1309        let _: Box<i64> = jvm.to_rust_boxed(i)?;
1310
1311        Ok(())
1312    }
1313
1314    #[test]
1315    fn check_equals() -> errors::Result<()> {
1316        let jvm = create_tests_jvm()?;
1317        let test_instance = jvm
1318            .create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())
1319            ?;
1320        let integer_instance = jvm.create_instance("java.lang.Integer", &[InvocationArg::try_from(3_i32)?.into_primitive()?])?;
1321        let ia1 = InvocationArg::try_from(3)?;
1322        assert!(jvm.check_equals(&integer_instance, &ia1)?);
1323
1324        let null_instance = jvm.invoke(&test_instance, "getNullInteger", InvocationArg::empty())?;
1325        assert!(jvm.check_equals(&null_instance, InvocationArg::try_from(Null::Integer)?)?);
1326        Ok(())
1327    }
1328
1329    #[test]
1330    fn instance_into_raw_object() -> errors::Result<()> {
1331        let jvm = create_tests_jvm()?;
1332        let test_instance = jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())?;
1333        let null_instance = jvm.invoke(&test_instance, "getNullInteger", InvocationArg::empty())?;
1334        let jobj = jvm.instance_into_raw_object(null_instance)?;
1335        assert!(jobj == null_mut());
1336        Ok(())
1337    }
1338
1339    #[test]
1340    fn jvm_into_raw_object() -> errors::Result<()> {
1341        let jvm = create_tests_jvm()?;
1342        let jobj = jvm.into_raw();
1343        assert!(jobj != null_mut());
1344        Ok(())
1345    }
1346}