toad_jni/
lib.rs

1//! High-level wrapper of [`jni`], making Java & Rust FFI easy & fun
2//!
3//! ## Globals
4//! [`toad_jni::global`](https://docs.rs/toad-jni/latest/toad_jni/global/index.html) offers the option to use a global JVM handle .
5//!
6//! [`toad_jni::global::jvm()`](https://docs.rs/toad-jni/latest/toad_jni/global/fn.jvm.html) must be initialized with
7//! [`toad_jni::global::init()`](https://docs.rs/toad-jni/latest/toad_jni/global/fn.init.html) in order to use Rust trait
8//! impls on java types. Some examples include:
9//!
10//! - [`Iterator`] on [`toad_jni::java::util::ArrayList`](https://docs.rs/toad-jni/latest/toad_jni/java/util/struct.ArrayList.html)
11//! - [`core::fmt::Debug`] on [`toad_jni::java::lang::Throwable`](https://docs.rs/toad-jni/latest/toad_jni/java/lang/struct.Throwable.html)
12//! - [`toad::net::Socket`] on [`toad_jni::java::nio::channels::DatagramChannel`](https://docs.rs/toad-jni/latest/toad_jni/java/nio/channels/struct.DatagramChannel.html)
13//!
14//! ## Types
15//! All java type signatures can be represented by rust types
16//! that implement the [`toad_jni::java::Type`](https://docs.rs/toad-jni/latest/toad_jni/java/trait.Type.html) trait, which is automatically
17//! implemented for all [`toad_jni::java::Class`](https://docs.rs/toad-jni/latest/toad_jni/java/trait.Class.html)es.
18//!
19//! ## Classes
20//! Classes are represented in `toad_jni` by implementing 2 traits:
21//! * [`toad_jni::java::Class`](https://docs.rs/toad-jni/latest/toad_jni/java/trait.Class.html)
22//! * [`toad_jni::java::Object`](https://docs.rs/toad-jni/latest/toad_jni/java/trait.Object.html) (see also [`toad_jni::java::object_newtype`](https://docs.rs/toad-jni/latest/toad_jni/java/macro.object_newtype.html))
23//!
24//! ### Fields and Methods
25//! There are several high-level lens-style structs for interacting with fields, methods and constructors:
26//! * [`toad_jni::java::Constructor`](https://docs.rs/toad-jni/latest/toad_jni/java/struct.Constructor.html)
27//! * [`toad_jni::java::StaticField`](https://docs.rs/toad-jni/latest/toad_jni/java/struct.StaticField.html)
28//! * [`toad_jni::java::StaticMethod`](https://docs.rs/toad-jni/latest/toad_jni/java/struct.StaticMethod.html)
29//! * [`toad_jni::java::Field`](https://docs.rs/toad-jni/latest/toad_jni/java/struct.Field.html)
30//! * [`toad_jni::java::Method`](https://docs.rs/toad-jni/latest/toad_jni/java/struct.Method.html)
31//!
32//! All of these types use [`toad_jni::java::Type`](https://docs.rs/toad-jni/latest/toad_jni/java/trait.Type.html) to transform nice Rust types into the corresponding
33//! JVM type signatures.
34//!
35//! For example, the `StaticMethod` representation of [`java.lang.String.format(String, ..Object)`](https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/String.html#format(java.lang.String,java.lang.Object...))
36//! would be:
37//! ```rust,no_run
38//! use toad_jni::java::lang::Object;
39//! use toad_jni::java::StaticMethod;
40//!
41//! static STRING_FORMAT: StaticMethod<String, fn(String, Vec<Object>) -> String> =
42//!   StaticMethod::new("format");
43//! ```
44//!
45//! It is recommended that these structs are stored in local `static` variables so that they can cache
46//! the internal JNI IDs of the class and methods, but this is not required.
47//!
48//! ### Example
49//! Consider the following java class:
50//! ```java
51//! package com.foo.bar;
52//!
53//! public class Foo {
54//!   public final static long NUMBER = 123;
55//!   public String bingus = "bingus";
56//!
57//!   public Foo() { }
58//!
59//!   public static String bar() {
60//!     return "bar";
61//!   }
62//!
63//!   public void setBingus(String newBingus) {
64//!     this.bingus = newBingus;
65//!   }
66//! }
67//! ```
68//!
69//! A Rust API to this class would look like:
70//! ```rust,no_run
71//! use toad_jni::java;
72//!
73//! pub struct Foo(java::lang::Object);
74//!
75//! java::object_newtype!(Foo);
76//!
77//! impl java::Class for Foo {
78//!   const PATH: &'static str = "com/foo/bar/Foo";
79//! }
80//!
81//! impl Foo {
82//!   pub fn new(e: &mut java::Env) -> Self {
83//!     static CTOR: java::Constructor<Foo, fn()> = java::Constructor::new();
84//!     CTOR.invoke(e)
85//!   }
86//!
87//!   pub fn number(e: &mut java::Env) -> i64 {
88//!     static NUMBER: java::StaticField<Foo, i64> = java::StaticField::new("NUMBER");
89//!     NUMBER.get(e)
90//!   }
91//!
92//!   pub fn bar(e: &mut java::Env) -> String {
93//!     static BAR: java::StaticMethod<Foo, fn() -> String> = java::StaticMethod::new("bar");
94//!     BAR.invoke(e)
95//!   }
96//!
97//!   pub fn bingus(&self, e: &mut java::Env) -> String {
98//!     static BINGUS: java::Field<Foo, String> = java::Field::new("bingus");
99//!     BINGUS.get(e, self)
100//!   }
101//!
102//!   pub fn set_bingus(&self, e: &mut java::Env, s: String) {
103//!     static SET_BINGUS: java::Method<Foo, fn(String)> = java::Method::new("setBingus");
104//!     SET_BINGUS.invoke(e, self, s)
105//!   }
106//! }
107//! ```
108
109// docs
110#![doc(html_root_url = "https://docs.rs/toad-jni/0.4.1")]
111#![cfg_attr(any(docsrs, feature = "docs"), feature(doc_cfg))]
112// -
113// style
114#![allow(clippy::unused_unit)]
115// -
116// deny
117#![deny(missing_docs)]
118#![deny(missing_copy_implementations)]
119// -
120// warnings
121#![cfg_attr(not(test), warn(unreachable_pub))]
122// -
123// features
124
125/// java language features and class shims
126pub mod java;
127
128/// Global JVM handles
129pub mod global {
130  use jni::{InitArgsBuilder, JavaVM};
131
132  static mut JVM: Option<JavaVM> = None;
133
134  /// Initialize the global jvm handle with an existing handle
135  pub fn init_with(jvm: JavaVM) {
136    unsafe {
137      JVM = Some(jvm);
138    }
139  }
140
141  /// Initialize the global jvm handle by creating a new handle
142  pub fn init() {
143    unsafe {
144      let args = InitArgsBuilder::new().build().unwrap();
145      JVM = Some(JavaVM::new(args).unwrap());
146    }
147
148    jvm().attach_current_thread_permanently().unwrap();
149  }
150
151  /// Get a reference to the global jvm handle
152  pub fn jvm() -> &'static mut JavaVM {
153    unsafe { JVM.as_mut().unwrap() }
154  }
155}
156
157#[cfg(test)]
158mod test {
159  use std::sync::Once;
160
161  use java::Primitive;
162  use jni::{InitArgsBuilder, JavaVM};
163  use toad_jni::java;
164
165  pub use crate as toad_jni;
166  use crate::java::Object;
167
168  pub fn init<'a>() -> java::Env<'a> {
169    static INIT: Once = Once::new();
170    INIT.call_once(|| {
171          std::env::set_var("FOO", "bar");
172          let args = InitArgsBuilder::new().build().unwrap();
173          toad_jni::global::init_with(JavaVM::new(args).unwrap());
174        });
175
176    let jvm = toad_jni::global::jvm();
177    jvm.attach_current_thread_permanently().unwrap()
178  }
179
180  #[test]
181  fn init_works() {
182    init();
183  }
184
185  #[test]
186  fn prim_wrappers() {
187    init();
188    let mut e = java::env();
189    let e = &mut e;
190
191    let i = (32i8).to_primitive_wrapper(e);
192    assert_eq!(i8::from_primitive_wrapper(e, i), 32i8);
193  }
194
195  #[test]
196  fn test_arraylist() {
197    init();
198    assert_eq!(vec![1i8, 2, 3, 4].into_iter()
199                                 .collect::<java::util::ArrayList<i8>>()
200                                 .into_iter()
201                                 .collect::<Vec<i8>>(),
202               vec![1, 2, 3, 4])
203  }
204
205  #[test]
206  fn test_optional() {
207    init();
208
209    let mut e = java::env();
210
211    let o = java::util::Optional::of(&mut e, 12i32);
212    assert_eq!(o.to_option(&mut e).unwrap(), 12);
213
214    let o = java::util::Optional::<i32>::empty(&mut e);
215    assert!(o.is_empty(&mut e));
216  }
217
218  #[test]
219  fn test_time() {
220    init();
221
222    let mut e = java::env();
223
224    let o = java::time::Duration::of_millis(&mut e, 1000);
225    assert_eq!(o.to_millis(&mut e), 1000);
226  }
227
228  #[test]
229  fn test_system() {
230    let mut e = init();
231    let e = &mut e;
232
233    type System = java::lang::System;
234    assert_eq!(System::get_env(e, "FOO"), Some("bar".to_string()));
235    assert_eq!(System::get_env(e, "BAR"), None);
236
237    assert_eq!(System::get_property(e, "foo.bar"), None);
238    assert_eq!(System::set_property(e, "foo.bar", "baz"), None);
239    assert_eq!(System::get_property(e, "foo.bar"), Some("baz".to_string()));
240
241    let args = vec![8329i32, 3281, 8329 + 3281].into_iter()
242                                               .map(|i| i.to_primitive_wrapper(e).downcast(e))
243                                               .collect();
244    match System::console(e).into_option(e) {
245      | Some(c) => {
246        c.printf(e, "%d + %d = %d", args);
247      },
248      | None => println!("no console"),
249    }
250  }
251
252  #[test]
253  fn test_inet() {
254    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
255
256    use java::net::*;
257
258    let mut env = init();
259    let e = &mut env;
260
261    assert_eq!(InetSocketAddress::new_wildcard_address(e, 1234).to_std(e),
262               "0.0.0.0:1234".parse().unwrap());
263
264    let local = InetAddress::from_std(e, IpAddr::from(Ipv4Addr::LOCALHOST));
265    assert_eq!(local.to_std(e), "127.0.0.1".parse::<IpAddr>().unwrap());
266    assert_eq!(InetSocketAddress::new(e, local, 1234).to_std(e),
267               "127.0.0.1:1234".parse::<SocketAddr>().unwrap());
268
269    let local = InetAddress::from_std(e, IpAddr::from(Ipv6Addr::LOCALHOST));
270    assert_eq!(local.to_std(e), "::1".parse::<IpAddr>().unwrap());
271    assert_eq!(InetSocketAddress::new(e, local, 1234).to_std(e),
272               "[::1]:1234".parse::<SocketAddr>().unwrap());
273  }
274
275  #[test]
276  fn test_bigint() {
277    init();
278
279    let mut e = java::env();
280    let e = &mut e;
281
282    type BigInt = java::math::BigInteger;
283
284    let bi = BigInt::from_be_bytes(e, &i128::MAX.to_be_bytes());
285    assert_eq!(bi.to_i128(e), i128::MAX);
286
287    let bi = BigInt::from_be_bytes(e, &i8::MAX.to_be_bytes());
288    assert_eq!(bi.to_i128(e), i8::MAX.into());
289
290    let bi = BigInt::from_be_bytes(e, &1i8.to_be_bytes());
291    assert_eq!(bi.to_i8(e), 1);
292
293    let bi = BigInt::from_be_bytes(e, &(-1i8).to_be_bytes());
294    assert_eq!(bi.to_i8(e), -1);
295
296    let bi = BigInt::from_be_bytes(e, &0i128.to_be_bytes());
297    assert_eq!(bi.to_i8(e), 0);
298    assert_eq!(bi.to_i16(e), 0);
299    assert_eq!(bi.to_i32(e), 0);
300    assert_eq!(bi.to_i64(e), 0);
301    assert_eq!(bi.to_i128(e), 0);
302  }
303}