diff --git a/.gitignore b/.gitignore index 67f3374..49d133a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ mylib/target -**/*.class \ No newline at end of file +**/*.class +**/*.h \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..70a2ea2 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +java_run: lib + cd java_src && \ + javac me/ehlxr/HelloWorld.java && \ + java -Djava.library.path=../mylib/target/debug/ me.ehlxr.HelloWorld +.PHONY: lib + +javah: + cd java_src && \ + javah me.ehlxr.HelloWorld + +lib: + cd mylib && cargo build \ No newline at end of file diff --git a/java_src/me/ehlxr/HelloWorld.java b/java_src/me/ehlxr/HelloWorld.java index 9610079..6741274 100644 --- a/java_src/me/ehlxr/HelloWorld.java +++ b/java_src/me/ehlxr/HelloWorld.java @@ -1,15 +1,51 @@ package me.ehlxr; -public class HelloWorld { +class HelloWorld { + private static native String hello(String input); + private static native byte[] helloByte(byte[] input); + private static native void factAndCallMeBack(int n, HelloWorld callback); + + private static native long counterNew(HelloWorld callback); + private static native void counterIncrement(long counter_ptr); + private static native void counterDestroy(long counter_ptr); + + private static native void asyncComputation(HelloWorld callback); + static { // Linux: export LD_LIBRARY_PATH=/Users/ehlxr/Desktop/jni_rs/mylib/target/debug // Mac: export JAVA_LIBRARY_PATH=/Users/ehlxr/Desktop/jni_rs/mylib/target/debug System.loadLibrary("mylib"); } - private static native String hello(String input); public static void main(String[] args) { String output = HelloWorld.hello("Java"); System.out.println(output); + byte[] outputByte = HelloWorld.helloByte("byte".getBytes()); + System.out.println(outputByte); + + HelloWorld.factAndCallMeBack(6, new HelloWorld()); + + long counter_ptr = counterNew(new HelloWorld()); + + for (int i = 0; i < 5; i++) { + counterIncrement(counter_ptr); + } + + counterDestroy(counter_ptr); + + System.out.println("Invoking asyncComputation (thread id = " + Thread.currentThread().getId() + ")"); + asyncComputation(new HelloWorld()); } -} \ No newline at end of file + + public void factCallback(int res) { + System.out.println("factCallback: res = " + res); + } + + public void counterCallback(int count) { + System.out.println("counterCallback: count = " + count); + } + + public void asyncCallback(int progress) { + System.out.println("asyncCallback: thread id = " + Thread.currentThread().getId() + ", progress = " + progress + "%"); + } +} diff --git a/mylib/src/lib.rs b/mylib/src/lib.rs index 98e027e..7b4c527 100644 --- a/mylib/src/lib.rs +++ b/mylib/src/lib.rs @@ -1,7 +1,11 @@ -use jni::objects::{JClass, JString}; -use jni::sys::jstring; use jni::JNIEnv; +use jni::objects::{GlobalRef, JClass, JObject, JString}; + +use jni::sys::{jbyteArray, jint, jlong, jstring}; + +use std::{sync::mpsc, thread, time::Duration}; + #[no_mangle] pub extern "system" fn Java_me_ehlxr_HelloWorld_hello( env: JNIEnv, @@ -14,8 +18,125 @@ pub extern "system" fn Java_me_ehlxr_HelloWorld_hello( .into(); let output = env - .new_string(format!("Hello, {}! from Rust...111", input)) + .new_string(format!("Hello, {}! from Rust..", input)) .expect("Couldn't create java string!"); - output.into_inner() } + +#[no_mangle] +pub extern "system" fn Java_me_ehlxr_HelloWorld_helloByte( + env: JNIEnv, + _class: JClass, + input: jbyteArray, +) -> jbyteArray { + let _input = env.convert_byte_array(input).unwrap(); + + let buf = [1; 2000]; + let output = env.byte_array_from_slice(&buf).unwrap(); + output +} + +#[no_mangle] +pub extern "system" fn Java_me_ehlxr_HelloWorld_factAndCallMeBack( + env: JNIEnv, + _class: JClass, + n: jint, + callback: JObject, +) { + let i = n as i32; + let res: jint = (2..i + 1).product(); + + env.call_method(callback, "factCallback", "(I)V", &[res.into()]) + .unwrap(); +} + +struct Counter { + count: i32, + callback: GlobalRef, +} + +impl Counter { + pub fn new(callback: GlobalRef) -> Counter { + Counter { + count: 0, + callback: callback, + } + } + + pub fn increment(&mut self, env: JNIEnv) { + self.count = self.count + 1; + env.call_method( + &self.callback, + "counterCallback", + "(I)V", + &[self.count.into()], + ) + .unwrap(); + } +} + +#[no_mangle] +pub unsafe extern "system" fn Java_me_ehlxr_HelloWorld_counterNew( + env: JNIEnv, + _class: JClass, + callback: JObject, +) -> jlong { + let global_ref = env.new_global_ref(callback).unwrap(); + let counter = Counter::new(global_ref); + + Box::into_raw(Box::new(counter)) as jlong +} + +#[no_mangle] +pub unsafe extern "system" fn Java_me_ehlxr_HelloWorld_counterIncrement( + env: JNIEnv, + _class: JClass, + counter_ptr: jlong, +) { + let counter = &mut *(counter_ptr as *mut Counter); + + counter.increment(env); +} + +#[no_mangle] +pub unsafe extern "system" fn Java_me_ehlxr_HelloWorld_counterDestroy( + _env: JNIEnv, + _class: JClass, + counter_ptr: jlong, +) { + let _boxed_counter = Box::from_raw(counter_ptr as *mut Counter); +} + +#[no_mangle] +pub extern "system" fn Java_me_ehlxr_HelloWorld_asyncComputation( + env: JNIEnv, + _class: JClass, + callback: JObject, +) { + let jvm = env.get_java_vm().unwrap(); + + let callback = env.new_global_ref(callback).unwrap(); + + let (tx, rx) = mpsc::channel(); + + let _ = thread::spawn(move || { + tx.send(()).unwrap(); + + let env = jvm.attach_current_thread().unwrap(); + + for i in 0..11 { + let progress = (i * 10) as jint; + env.call_method(&callback, "asyncCallback", "(I)V", &[progress.into()]) + .unwrap(); + thread::sleep(Duration::from_millis(100)); + } + }); + + rx.recv().unwrap(); +} + +#[cfg(test)] +mod tests { + #[test] + fn it_works() {} +}