You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@opendal.apache.org by ti...@apache.org on 2026/06/18 07:10:41 UTC
(opendal) branch main updated: chore(bindings/java): migrate to jni 0.22 (#7777)
This is an automated email from the ASF dual-hosted git repository.
tisonkun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/opendal.git
The following commit(s) were added to refs/heads/main by this push:
new 5fe1c549e chore(bindings/java): migrate to jni 0.22 (#7777)
5fe1c549e is described below
commit 5fe1c549e6d58cf7b4197315b28273a80aead1d5
Author: Xuanwo <gi...@xuanwo.io>
AuthorDate: Thu Jun 18 15:10:36 2026 +0800
chore(bindings/java): migrate to jni 0.22 (#7777)
Co-authored-by: tison <wa...@gmail.com>
---
bindings/java/Cargo.toml | 2 +-
bindings/java/src/async_operator.rs | 524 +++++++++------------
bindings/java/src/convert.rs | 98 ++--
bindings/java/src/error.rs | 72 ++-
bindings/java/src/executor.rs | 128 ++---
bindings/java/src/layer.rs | 34 +-
bindings/java/src/lib.rs | 135 +++---
.../src/main/java/org/apache/opendal/Metadata.java | 4 +-
bindings/java/src/operator.rs | 264 ++++++-----
bindings/java/src/operator_input_stream.rs | 62 +--
bindings/java/src/operator_output_stream.rs | 56 ++-
bindings/java/src/utility.rs | 33 +-
12 files changed, 715 insertions(+), 697 deletions(-)
diff --git a/bindings/java/Cargo.toml b/bindings/java/Cargo.toml
index deeda9cb0..e88f5a348 100644
--- a/bindings/java/Cargo.toml
+++ b/bindings/java/Cargo.toml
@@ -31,7 +31,7 @@ crate-type = ["cdylib", "staticlib"]
doc = false
[dependencies]
-jni = { version = "0.21.1" }
+jni = { version = "0.22.4" }
# opendal-java won't be published to crates.io, we always use the local version
opendal = { version = ">=0", path = "../../core", default-features = false, features = [
"blocking",
diff --git a/bindings/java/src/async_operator.rs b/bindings/java/src/async_operator.rs
index 8e3852439..5d9417277 100644
--- a/bindings/java/src/async_operator.rs
+++ b/bindings/java/src/async_operator.rs
@@ -17,15 +17,17 @@
use std::time::Duration;
-use jni::JNIEnv;
+use jni::Env;
+use jni::EnvUnowned;
+use jni::JavaVM;
+use jni::jni_sig;
+use jni::jni_str;
use jni::objects::JByteArray;
use jni::objects::JClass;
use jni::objects::JObject;
use jni::objects::JString;
use jni::objects::JValue;
-use jni::objects::JValueOwned;
use jni::sys::jlong;
-use jni::sys::jobject;
use jni::sys::jsize;
use opendal::Entry;
use opendal::Operator;
@@ -36,28 +38,26 @@ use crate::convert::{
bytes_to_jbytearray, jmap_to_hashmap, jstring_to_string, offset_length_to_range,
read_int64_field,
};
+use crate::error::ThrowException;
use crate::executor::Executor;
use crate::executor::executor_or_default;
-use crate::executor::get_current_env;
use crate::make_metadata;
use crate::make_operator_info;
use crate::make_presigned_request;
use crate::{make_entry, make_list_options, make_stat_options, make_write_options};
#[unsafe(no_mangle)]
-pub extern "system" fn Java_org_apache_opendal_AsyncOperator_constructor(
- mut env: JNIEnv,
- _: JClass,
- scheme: JString,
- map: JObject,
+pub extern "system" fn Java_org_apache_opendal_AsyncOperator_constructor<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
+ scheme: JString<'local>,
+ map: JObject<'local>,
) -> jlong {
- intern_constructor(&mut env, scheme, map).unwrap_or_else(|e| {
- e.throw(&mut env);
- 0
- })
+ env.with_env(|env| intern_constructor(env, scheme, map))
+ .resolve::<ThrowException>()
}
-fn intern_constructor(env: &mut JNIEnv, scheme: JString, map: JObject) -> Result<jlong> {
+fn intern_constructor(env: &mut Env, scheme: JString, map: JObject) -> Result<jlong> {
let scheme = jstring_to_string(env, &scheme)?;
let map = jmap_to_hashmap(env, &map)?;
let op = Operator::via_iter(scheme, map)?;
@@ -68,9 +68,9 @@ fn intern_constructor(env: &mut JNIEnv, scheme: JString, map: JObject) -> Result
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_duplicate(
- _: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_duplicate<'local>(
+ _: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut Operator,
) -> jlong {
let op = unsafe { &mut *op };
@@ -81,9 +81,9 @@ pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_duplicate(
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_disposeInternal(
- _: JNIEnv,
- _: JObject,
+pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_disposeInternal<'local>(
+ _: EnvUnowned<'local>,
+ _: JObject<'local>,
op: *mut Operator,
) {
unsafe {
@@ -95,23 +95,21 @@ pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_disposeInter
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_write(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_write<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut Operator,
executor: *const Executor,
- path: JString,
- content: JByteArray,
- write_options: JObject,
+ path: JString<'local>,
+ content: JByteArray<'local>,
+ write_options: JObject<'local>,
) -> jlong {
- intern_write(&mut env, op, executor, path, content, write_options).unwrap_or_else(|e| {
- e.throw(&mut env);
- 0
- })
+ env.with_env(|env| intern_write(env, op, executor, path, content, write_options))
+ .resolve::<ThrowException>()
}
fn intern_write(
- env: &mut JNIEnv,
+ env: &mut Env,
op: *mut Operator,
executor: *const Executor,
path: JString,
@@ -125,13 +123,12 @@ fn intern_write(
let path = jstring_to_string(env, &path)?;
let content = env.convert_byte_array(content)?;
- executor_or_default(env, executor)?.spawn(async move {
- let result = op
- .write_options(&path, content, write_opts)
- .await
- .map(|_| JValueOwned::Void)
- .map_err(Into::into);
- complete_future(id, result)
+ executor_or_default(executor)?.spawn(async move {
+ let result = op.write_options(&path, content, write_opts).await;
+ complete_future(id, move |_env| {
+ result?;
+ Ok(JObject::null())
+ });
});
Ok(id)
@@ -141,22 +138,20 @@ fn intern_write(
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_stat(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_stat<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut Operator,
executor: *const Executor,
- path: JString,
- stat_options: JObject,
+ path: JString<'local>,
+ stat_options: JObject<'local>,
) -> jlong {
- intern_stat(&mut env, op, executor, path, stat_options).unwrap_or_else(|e| {
- e.throw(&mut env);
- 0
- })
+ env.with_env(|env| intern_stat(env, op, executor, path, stat_options))
+ .resolve::<ThrowException>()
}
fn intern_stat(
- env: &mut JNIEnv,
+ env: &mut Env,
op: *mut Operator,
executor: *const Executor,
path: JString,
@@ -168,11 +163,9 @@ fn intern_stat(
let path = jstring_to_string(env, &path)?;
let stat_opts = make_stat_options(env, &options)?;
- executor_or_default(env, executor)?.spawn(async move {
- let metadata = op.stat_options(&path, stat_opts).await.map_err(Into::into);
- let mut env = unsafe { get_current_env() };
- let result = metadata.and_then(|metadata| make_metadata(&mut env, metadata));
- complete_future(id, result.map(JValueOwned::Object))
+ executor_or_default(executor)?.spawn(async move {
+ let result = op.stat_options(&path, stat_opts).await;
+ complete_future(id, move |env| make_metadata(env, result?));
});
Ok(id)
@@ -182,22 +175,20 @@ fn intern_stat(
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_read(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_read<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut Operator,
executor: *const Executor,
- path: JString,
- read_options: JObject,
+ path: JString<'local>,
+ read_options: JObject<'local>,
) -> jlong {
- intern_read(&mut env, op, executor, path, read_options).unwrap_or_else(|e| {
- e.throw(&mut env);
- 0
- })
+ env.with_env(|env| intern_read(env, op, executor, path, read_options))
+ .resolve::<ThrowException>()
}
fn intern_read(
- env: &mut JNIEnv,
+ env: &mut Env,
op: *mut Operator,
executor: *const Executor,
path: JString,
@@ -213,14 +204,14 @@ fn intern_read(
// Clone operator handle to move into the task
let op_cloned = unsafe { &*op }.clone();
- executor_or_default(env, executor)?.spawn(async move {
+ executor_or_default(executor)?.spawn(async move {
let mut read_op = op_cloned.read_with(&path_str);
read_op = read_op.range(range);
- let result = read_op.await.map_err(Into::into);
-
- let mut env = unsafe { get_current_env() };
- let result = result.and_then(|bs| bytes_to_jbytearray(&mut env, bs.to_bytes()));
- complete_future(id, result.map(|bs| JValueOwned::Object(bs.into())))
+ let result = read_op.await;
+ complete_future(id, move |env| {
+ let buffer = result?;
+ Ok(bytes_to_jbytearray(env, buffer.to_bytes())?.into())
+ });
});
Ok(id)
@@ -230,21 +221,19 @@ fn intern_read(
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_delete(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_delete<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut Operator,
executor: *const Executor,
- path: JString,
+ path: JString<'local>,
) -> jlong {
- intern_delete(&mut env, op, executor, path).unwrap_or_else(|e| {
- e.throw(&mut env);
- 0
- })
+ env.with_env(|env| intern_delete(env, op, executor, path))
+ .resolve::<ThrowException>()
}
fn intern_delete(
- env: &mut JNIEnv,
+ env: &mut Env,
op: *mut Operator,
executor: *const Executor,
path: JString,
@@ -254,13 +243,12 @@ fn intern_delete(
let path = jstring_to_string(env, &path)?;
- executor_or_default(env, executor)?.spawn(async move {
- let result = op
- .delete(&path)
- .await
- .map(|_| JValueOwned::Void)
- .map_err(Into::into);
- complete_future(id, result)
+ executor_or_default(executor)?.spawn(async move {
+ let result = op.delete(&path).await;
+ complete_future(id, move |_env| {
+ result?;
+ Ok(JObject::null())
+ });
});
Ok(id)
@@ -270,26 +258,24 @@ fn intern_delete(
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_makeBlockingOp(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_makeBlockingOp<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut Operator,
executor: *const Executor,
) -> jlong {
- intern_make_blocking_op(&mut env, op, executor).unwrap_or_else(|e| {
- e.throw(&mut env);
- 0
- })
+ env.with_env(|env| intern_make_blocking_op(env, op, executor))
+ .resolve::<ThrowException>()
}
fn intern_make_blocking_op(
- env: &mut JNIEnv,
+ _env: &mut Env,
op: *mut Operator,
executor: *const Executor,
) -> Result<jlong> {
let op = unsafe { &mut *op };
- let op = executor_or_default(env, executor)?
- .enter_with(move || blocking::Operator::new(op.clone()))?;
+ let op =
+ executor_or_default(executor)?.enter_with(move || blocking::Operator::new(op.clone()))?;
Ok(Box::into_raw(Box::new(op)) as jlong)
}
@@ -297,41 +283,40 @@ fn intern_make_blocking_op(
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_makeOperatorInfo(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_makeOperatorInfo<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut Operator,
-) -> jobject {
- intern_make_operator_info(&mut env, op).unwrap_or_else(|e| {
- e.throw(&mut env);
- JObject::default().into_raw()
- })
+) -> JObject<'local> {
+ env.with_env(|env| intern_make_operator_info(env, op))
+ .resolve::<ThrowException>()
}
-fn intern_make_operator_info(env: &mut JNIEnv, op: *mut Operator) -> Result<jobject> {
+fn intern_make_operator_info<'local>(
+ env: &mut Env<'local>,
+ op: *mut Operator,
+) -> Result<JObject<'local>> {
let op = unsafe { &mut *op };
- Ok(make_operator_info(env, op.info())?.into_raw())
+ make_operator_info(env, op.info())
}
/// # Safety
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_createDir(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_createDir<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut Operator,
executor: *const Executor,
- path: JString,
+ path: JString<'local>,
) -> jlong {
- intern_create_dir(&mut env, op, executor, path).unwrap_or_else(|e| {
- e.throw(&mut env);
- 0
- })
+ env.with_env(|env| intern_create_dir(env, op, executor, path))
+ .resolve::<ThrowException>()
}
fn intern_create_dir(
- env: &mut JNIEnv,
+ env: &mut Env,
op: *mut Operator,
executor: *const Executor,
path: JString,
@@ -341,13 +326,12 @@ fn intern_create_dir(
let path = jstring_to_string(env, &path)?;
- executor_or_default(env, executor)?.spawn(async move {
- let result = op
- .create_dir(&path)
- .await
- .map(|_| JValueOwned::Void)
- .map_err(Into::into);
- complete_future(id, result)
+ executor_or_default(executor)?.spawn(async move {
+ let result = op.create_dir(&path).await;
+ complete_future(id, move |_env| {
+ result?;
+ Ok(JObject::null())
+ });
});
Ok(id)
@@ -357,22 +341,20 @@ fn intern_create_dir(
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_copy(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_copy<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut Operator,
executor: *const Executor,
- source_path: JString,
- target_path: JString,
+ source_path: JString<'local>,
+ target_path: JString<'local>,
) -> jlong {
- intern_copy(&mut env, op, executor, source_path, target_path).unwrap_or_else(|e| {
- e.throw(&mut env);
- 0
- })
+ env.with_env(|env| intern_copy(env, op, executor, source_path, target_path))
+ .resolve::<ThrowException>()
}
fn intern_copy(
- env: &mut JNIEnv,
+ env: &mut Env,
op: *mut Operator,
executor: *const Executor,
source_path: JString,
@@ -384,13 +366,12 @@ fn intern_copy(
let source_path = jstring_to_string(env, &source_path)?;
let target_path = jstring_to_string(env, &target_path)?;
- executor_or_default(env, executor)?.spawn(async move {
- let result = op
- .copy(&source_path, &target_path)
- .await
- .map(|_| JValueOwned::Void)
- .map_err(Into::into);
- complete_future(id, result)
+ executor_or_default(executor)?.spawn(async move {
+ let result = op.copy(&source_path, &target_path).await;
+ complete_future(id, move |_env| {
+ result?;
+ Ok(JObject::null())
+ });
});
Ok(id)
@@ -400,22 +381,20 @@ fn intern_copy(
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_rename(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_rename<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut Operator,
executor: *const Executor,
- source_path: JString,
- target_path: JString,
+ source_path: JString<'local>,
+ target_path: JString<'local>,
) -> jlong {
- intern_rename(&mut env, op, executor, source_path, target_path).unwrap_or_else(|e| {
- e.throw(&mut env);
- 0
- })
+ env.with_env(|env| intern_rename(env, op, executor, source_path, target_path))
+ .resolve::<ThrowException>()
}
fn intern_rename(
- env: &mut JNIEnv,
+ env: &mut Env,
op: *mut Operator,
executor: *const Executor,
source_path: JString,
@@ -427,13 +406,12 @@ fn intern_rename(
let source_path = jstring_to_string(env, &source_path)?;
let target_path = jstring_to_string(env, &target_path)?;
- executor_or_default(env, executor)?.spawn(async move {
- let result = op
- .rename(&source_path, &target_path)
- .await
- .map(|_| JValueOwned::Void)
- .map_err(Into::into);
- complete_future(id, result)
+ executor_or_default(executor)?.spawn(async move {
+ let result = op.rename(&source_path, &target_path).await;
+ complete_future(id, move |_env| {
+ result?;
+ Ok(JObject::null())
+ });
});
Ok(id)
@@ -443,21 +421,19 @@ fn intern_rename(
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_removeAll(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_removeAll<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut Operator,
executor: *const Executor,
- path: JString,
+ path: JString<'local>,
) -> jlong {
- intern_remove_all(&mut env, op, executor, path).unwrap_or_else(|e| {
- e.throw(&mut env);
- 0
- })
+ env.with_env(|env| intern_remove_all(env, op, executor, path))
+ .resolve::<ThrowException>()
}
fn intern_remove_all(
- env: &mut JNIEnv,
+ env: &mut Env,
op: *mut Operator,
executor: *const Executor,
path: JString,
@@ -467,14 +443,12 @@ fn intern_remove_all(
let path = jstring_to_string(env, &path)?;
- executor_or_default(env, executor)?.spawn(async move {
- let result = op
- .delete_with(&path)
- .recursive(true)
- .await
- .map(|_| JValueOwned::Void)
- .map_err(Into::into);
- complete_future(id, result)
+ executor_or_default(executor)?.spawn(async move {
+ let result = op.delete_with(&path).recursive(true).await;
+ complete_future(id, move |_env| {
+ result?;
+ Ok(JObject::null())
+ });
});
Ok(id)
@@ -484,22 +458,20 @@ fn intern_remove_all(
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_list(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_list<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut Operator,
executor: *const Executor,
- path: JString,
- options: JObject,
+ path: JString<'local>,
+ options: JObject<'local>,
) -> jlong {
- intern_list(&mut env, op, executor, path, options).unwrap_or_else(|e| {
- e.throw(&mut env);
- 0
- })
+ env.with_env(|env| intern_list(env, op, executor, path, options))
+ .resolve::<ThrowException>()
}
fn intern_list(
- env: &mut JNIEnv,
+ env: &mut Env,
op: *mut Operator,
executor: *const Executor,
path: JString,
@@ -510,28 +482,24 @@ fn intern_list(
let path = jstring_to_string(env, &path)?;
let list_opts = make_list_options(env, &options)?;
- executor_or_default(env, executor)?.spawn(async move {
- let entries = op.list_options(&path, list_opts).await.map_err(Into::into);
- let result = make_entries(entries);
- complete_future(id, result.map(JValueOwned::Object))
+ executor_or_default(executor)?.spawn(async move {
+ let result = op.list_options(&path, list_opts).await;
+ complete_future(id, move |env| make_entries(env, result?));
});
Ok(id)
}
-fn make_entries<'local>(entries: Result<Vec<Entry>>) -> Result<JObject<'local>> {
- let entries = entries?;
-
- let mut env = unsafe { get_current_env() };
+fn make_entries<'local>(env: &mut Env<'local>, entries: Vec<Entry>) -> Result<JObject<'local>> {
let jarray = env.new_object_array(
entries.len() as jsize,
- "org/apache/opendal/Entry",
+ jni_str!("org/apache/opendal/Entry"),
JObject::null(),
)?;
for (idx, entry) in entries.into_iter().enumerate() {
- let entry = make_entry(&mut env, entry)?;
- env.set_object_array_element(&jarray, idx as jsize, entry)?;
+ let entry = make_entry(env, entry)?;
+ jarray.set_element(env, idx, &entry)?;
}
Ok(jarray.into())
@@ -541,22 +509,20 @@ fn make_entries<'local>(entries: Result<Vec<Entry>>) -> Result<JObject<'local>>
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_presignRead(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_presignRead<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut Operator,
executor: *const Executor,
- path: JString,
+ path: JString<'local>,
expire: jlong,
) -> jlong {
- intern_presign_read(&mut env, op, executor, path, expire).unwrap_or_else(|e| {
- e.throw(&mut env);
- 0
- })
+ env.with_env(|env| intern_presign_read(env, op, executor, path, expire))
+ .resolve::<ThrowException>()
}
fn intern_presign_read(
- env: &mut JNIEnv,
+ env: &mut Env,
op: *mut Operator,
executor: *const Executor,
path: JString,
@@ -568,11 +534,9 @@ fn intern_presign_read(
let path = jstring_to_string(env, &path)?;
let expire = Duration::from_nanos(expire as u64);
- executor_or_default(env, executor)?.spawn(async move {
- let result = op.presign_read(&path, expire).await.map_err(Into::into);
- let mut env = unsafe { get_current_env() };
- let result = result.and_then(|req| make_presigned_request(&mut env, req));
- complete_future(id, result.map(JValueOwned::Object))
+ executor_or_default(executor)?.spawn(async move {
+ let result = op.presign_read(&path, expire).await;
+ complete_future(id, move |env| make_presigned_request(env, result?));
});
Ok(id)
@@ -581,22 +545,20 @@ fn intern_presign_read(
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_presignWrite(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_presignWrite<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut Operator,
executor: *const Executor,
- path: JString,
+ path: JString<'local>,
expire: jlong,
) -> jlong {
- intern_presign_write(&mut env, op, executor, path, expire).unwrap_or_else(|e| {
- e.throw(&mut env);
- 0
- })
+ env.with_env(|env| intern_presign_write(env, op, executor, path, expire))
+ .resolve::<ThrowException>()
}
fn intern_presign_write(
- env: &mut JNIEnv,
+ env: &mut Env,
op: *mut Operator,
executor: *const Executor,
path: JString,
@@ -608,11 +570,9 @@ fn intern_presign_write(
let path = jstring_to_string(env, &path)?;
let expire = Duration::from_nanos(expire as u64);
- executor_or_default(env, executor)?.spawn(async move {
- let result = op.presign_write(&path, expire).await.map_err(Into::into);
- let mut env = unsafe { get_current_env() };
- let result = result.and_then(|req| make_presigned_request(&mut env, req));
- complete_future(id, result.map(JValueOwned::Object))
+ executor_or_default(executor)?.spawn(async move {
+ let result = op.presign_write(&path, expire).await;
+ complete_future(id, move |env| make_presigned_request(env, result?));
});
Ok(id)
@@ -622,22 +582,20 @@ fn intern_presign_write(
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_presignStat(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_presignStat<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut Operator,
executor: *const Executor,
- path: JString,
+ path: JString<'local>,
expire: jlong,
) -> jlong {
- intern_presign_stat(&mut env, op, executor, path, expire).unwrap_or_else(|e| {
- e.throw(&mut env);
- 0
- })
+ env.with_env(|env| intern_presign_stat(env, op, executor, path, expire))
+ .resolve::<ThrowException>()
}
fn intern_presign_stat(
- env: &mut JNIEnv,
+ env: &mut Env,
op: *mut Operator,
executor: *const Executor,
path: JString,
@@ -649,82 +607,70 @@ fn intern_presign_stat(
let path = jstring_to_string(env, &path)?;
let expire = Duration::from_nanos(expire as u64);
- executor_or_default(env, executor)?.spawn(async move {
- let result = op.presign_stat(&path, expire).await.map_err(Into::into);
- let mut env = unsafe { get_current_env() };
- let result = result.and_then(|req| make_presigned_request(&mut env, req));
- complete_future(id, result.map(JValueOwned::Object))
+ executor_or_default(executor)?.spawn(async move {
+ let result = op.presign_stat(&path, expire).await;
+ complete_future(id, move |env| make_presigned_request(env, result?));
});
Ok(id)
}
-fn make_object<'local>(
- env: &mut JNIEnv<'local>,
- value: JValueOwned<'local>,
-) -> Result<JObject<'local>> {
- let o = match value {
- JValueOwned::Object(o) => o,
- JValueOwned::Byte(_) => env.new_object("java/lang/Long", "(B)V", &[value.borrow()])?,
- JValueOwned::Char(_) => env.new_object("java/lang/Char", "(C)V", &[value.borrow()])?,
- JValueOwned::Short(_) => env.new_object("java/lang/Short", "(S)V", &[value.borrow()])?,
- JValueOwned::Int(_) => env.new_object("java/lang/Integer", "(I)V", &[value.borrow()])?,
- JValueOwned::Long(_) => env.new_object("java/lang/Long", "(J)V", &[value.borrow()])?,
- JValueOwned::Bool(_) => env.new_object("java/lang/Boolean", "(Z)V", &[value.borrow()])?,
- JValueOwned::Float(_) => env.new_object("java/lang/Float", "(F)V", &[value.borrow()])?,
- JValueOwned::Double(_) => env.new_object("java/lang/Double", "(D)V", &[value.borrow()])?,
- JValueOwned::Void => JObject::null(),
- };
- Ok(o)
-}
-
-fn complete_future(id: jlong, result: Result<JValueOwned>) {
- try_complete_future(id, result).expect("complete future must succeed");
-}
-
-fn try_complete_future(id: jlong, result: Result<JValueOwned>) -> Result<()> {
- let mut env = unsafe { get_current_env() };
- let future = get_future(&mut env, id)?;
- match result {
- Ok(result) => {
- let result = make_object(&mut env, result)?;
- env.call_method(
- future,
- "complete",
- "(Ljava/lang/Object;)Z",
- &[JValue::Object(&result)],
- )?
- }
- Err(err) => {
- let exception = err.to_exception(&mut env)?;
- env.call_method(
- future,
- "completeExceptionally",
- "(Ljava/lang/Throwable;)Z",
- &[JValue::Object(&exception)],
- )?
+/// Complete the Java `CompletableFuture` identified by `id`.
+///
+/// This runs on a `tokio` worker thread that was permanently attached to the
+/// JVM in `on_thread_start`, so `attach_current_thread` only pushes a scoped
+/// JNI frame here. The `build` closure turns the awaited operation result into a
+/// Java object within that frame; on error the future is completed
+/// exceptionally. All local references created during completion live and die
+/// within the frame.
+fn complete_future<F>(id: jlong, build: F)
+where
+ F: for<'a> FnOnce(&mut Env<'a>) -> Result<JObject<'a>>,
+{
+ let vm = JavaVM::singleton().expect("JavaVM singleton must be initialized");
+ vm.attach_current_thread(|env| -> Result<()> {
+ let future = get_future(env, id)?;
+ match build(env) {
+ Ok(object) => {
+ env.call_method(
+ &future,
+ jni_str!("complete"),
+ jni_sig!("(Ljava/lang/Object;)Z"),
+ &[JValue::Object(&object)],
+ )?;
+ }
+ Err(err) => {
+ let exception = err.to_exception(env)?;
+ env.call_method(
+ &future,
+ jni_str!("completeExceptionally"),
+ jni_sig!("(Ljava/lang/Throwable;)Z"),
+ &[JValue::Object(&exception)],
+ )?;
+ }
}
- };
- Ok(())
+ Ok(())
+ })
+ .expect("complete future must succeed");
}
-fn request_id(env: &mut JNIEnv) -> Result<jlong> {
+fn request_id(env: &mut Env) -> Result<jlong> {
Ok(env
.call_static_method(
- "org/apache/opendal/AsyncOperator$AsyncRegistry",
- "requestId",
- "()J",
+ jni_str!("org/apache/opendal/AsyncOperator$AsyncRegistry"),
+ jni_str!("requestId"),
+ jni_sig!("()J"),
&[],
)?
.j()?)
}
-fn get_future<'local>(env: &mut JNIEnv<'local>, id: jlong) -> Result<JObject<'local>> {
+fn get_future<'local>(env: &mut Env<'local>, id: jlong) -> Result<JObject<'local>> {
Ok(env
.call_static_method(
- "org/apache/opendal/AsyncOperator$AsyncRegistry",
- "get",
- "(J)Ljava/util/concurrent/CompletableFuture;",
+ jni_str!("org/apache/opendal/AsyncOperator$AsyncRegistry"),
+ jni_str!("get"),
+ jni_sig!("(J)Ljava/util/concurrent/CompletableFuture;"),
&[JValue::Long(id)],
)?
.l()?)
diff --git a/bindings/java/src/convert.rs b/bindings/java/src/convert.rs
index fc5bdc73a..f9182eba9 100644
--- a/bindings/java/src/convert.rs
+++ b/bindings/java/src/convert.rs
@@ -16,10 +16,14 @@
// under the License.
use crate::Result;
-use jni::JNIEnv;
+use jni::Env;
+use jni::jni_sig;
+use jni::jni_str;
+use jni::objects::JByteArray;
+use jni::objects::JMap;
use jni::objects::JObject;
use jni::objects::JString;
-use jni::objects::{JByteArray, JMap};
+use jni::strings::JNIString;
use jni::sys::jlong;
use opendal::{Error, ErrorKind};
use std::collections::HashMap;
@@ -30,29 +34,29 @@ pub(crate) fn usize_to_jlong(n: Option<usize>) -> jlong {
n.map_or(-1, |v| v as jlong)
}
-pub(crate) fn jmap_to_hashmap(
- env: &mut JNIEnv,
- params: &JObject,
-) -> Result<HashMap<String, String>> {
- let map = JMap::from_env(env, params)?;
+pub(crate) fn jmap_to_hashmap(env: &mut Env, params: &JObject) -> Result<HashMap<String, String>> {
+ let map = env.new_cast_local_ref::<JMap>(params)?;
let mut iter = map.iter(env)?;
let mut result: HashMap<String, String> = HashMap::new();
- while let Some(e) = iter.next(env)? {
- let k = JString::from(e.0);
- let v = JString::from(e.1);
- result.insert(env.get_string(&k)?.into(), env.get_string(&v)?.into());
+ while let Some(entry) = iter.next(env)? {
+ let k = entry.key(env)?;
+ let v = entry.value(env)?;
+ // SAFETY: a `java.util.Map<String, String>` only yields String keys and values.
+ let k = unsafe { JString::from_raw(env, k.into_raw()) };
+ let v = unsafe { JString::from_raw(env, v.into_raw()) };
+ result.insert(k.mutf8_chars(env)?.into(), v.mutf8_chars(env)?.into());
}
Ok(result)
}
pub(crate) fn hashmap_to_jmap<'a>(
- env: &mut JNIEnv<'a>,
+ env: &mut Env<'a>,
map: &HashMap<String, String>,
) -> Result<JObject<'a>> {
- let map_object = env.new_object("java/util/HashMap", "()V", &[])?;
- let jmap = env.get_map(&map_object)?;
+ let map_object = env.new_object(jni_str!("java/util/HashMap"), jni_sig!("()V"), &[])?;
+ let jmap = env.new_cast_local_ref::<JMap>(&map_object)?;
for (k, v) in map {
let key = env.new_string(k)?;
let value = env.new_string(v)?;
@@ -61,44 +65,53 @@ pub(crate) fn hashmap_to_jmap<'a>(
Ok(map_object)
}
-pub(crate) fn string_to_jstring<'a>(env: &mut JNIEnv<'a>, s: Option<&str>) -> Result<JObject<'a>> {
- s.map_or_else(
- || Ok(JObject::null()),
- |v| Ok(env.new_string(v.to_string())?.into()),
- )
+pub(crate) fn string_to_jstring<'a>(env: &mut Env<'a>, s: Option<&str>) -> Result<JObject<'a>> {
+ s.map_or_else(|| Ok(JObject::null()), |v| Ok(env.new_string(v)?.into()))
}
-pub(crate) fn read_bool_field(env: &mut JNIEnv<'_>, obj: &JObject, field: &str) -> Result<bool> {
- Ok(env.get_field(obj, field, "Z")?.z()?)
+pub(crate) fn read_bool_field(env: &mut Env<'_>, obj: &JObject, field: &str) -> Result<bool> {
+ Ok(env
+ .get_field(obj, JNIString::new(field), jni_sig!("Z"))?
+ .z()?)
}
-pub(crate) fn read_int64_field(env: &mut JNIEnv<'_>, obj: &JObject, field: &str) -> Result<i64> {
- Ok(env.get_field(obj, field, "J")?.j()?)
+pub(crate) fn read_int64_field(env: &mut Env<'_>, obj: &JObject, field: &str) -> Result<i64> {
+ Ok(env
+ .get_field(obj, JNIString::new(field), jni_sig!("J"))?
+ .j()?)
}
-pub(crate) fn read_int_field(env: &mut JNIEnv<'_>, obj: &JObject, field: &str) -> Result<i32> {
- Ok(env.get_field(obj, field, "I")?.i()?)
+pub(crate) fn read_int_field(env: &mut Env<'_>, obj: &JObject, field: &str) -> Result<i32> {
+ Ok(env
+ .get_field(obj, JNIString::new(field), jni_sig!("I"))?
+ .i()?)
}
pub(crate) fn read_string_field(
- env: &mut JNIEnv<'_>,
+ env: &mut Env<'_>,
obj: &JObject,
field: &str,
) -> Result<Option<String>> {
- let result = env.get_field(obj, field, "Ljava/lang/String;")?.l()?;
+ let result = env
+ .get_field(obj, JNIString::new(field), jni_sig!("Ljava/lang/String;"))?
+ .l()?;
if result.is_null() {
Ok(None)
} else {
- Ok(Some(jstring_to_string(env, &JString::from(result))?))
+ // SAFETY: the field is declared as `java.lang.String`.
+ let result = unsafe { JString::from_raw(env, result.into_raw()) };
+ Ok(Some(jstring_to_string(env, &result)?))
}
}
pub(crate) fn read_map_field(
- env: &mut JNIEnv<'_>,
+ env: &mut Env<'_>,
obj: &JObject,
field: &str,
) -> Result<Option<HashMap<String, String>>> {
- let result = env.get_field(obj, field, "Ljava/util/Map;")?.l()?;
+ let result = env
+ .get_field(obj, JNIString::new(field), jni_sig!("Ljava/util/Map;"))?
+ .l()?;
if result.is_null() {
Ok(None)
} else {
@@ -107,7 +120,7 @@ pub(crate) fn read_map_field(
}
pub(crate) fn read_jlong_field_to_usize(
- env: &mut JNIEnv,
+ env: &mut Env,
options: &JObject,
field_name: &str,
) -> Result<Option<usize>> {
@@ -123,19 +136,23 @@ pub(crate) fn read_jlong_field_to_usize(
}
pub(crate) fn read_instant_field_to_timestamp(
- env: &mut JNIEnv<'_>,
+ env: &mut Env<'_>,
obj: &JObject,
field: &str,
) -> Result<Option<opendal::raw::Timestamp>> {
- let result = env.get_field(obj, field, "Ljava/time/Instant;")?.l()?;
+ let result = env
+ .get_field(obj, JNIString::new(field), jni_sig!("Ljava/time/Instant;"))?
+ .l()?;
if result.is_null() {
return Ok(None);
}
let epoch_second = env
- .call_method(&result, "getEpochSecond", "()J", &[])?
+ .call_method(&result, jni_str!("getEpochSecond"), jni_sig!("()J"), &[])?
.j()?;
- let nano = env.call_method(&result, "getNano", "()I", &[])?.i()?;
+ let nano = env
+ .call_method(&result, jni_str!("getNano"), jni_sig!("()I"), &[])?
+ .i()?;
match opendal::raw::Timestamp::new(epoch_second, nano) {
Ok(ts) => Ok(Some(ts)),
Err(err) => Err(Error::new(
@@ -170,7 +187,7 @@ pub(crate) fn offset_length_to_range(offset: i64, length: i64) -> Result<(Bound<
}
pub(crate) fn bytes_to_jbytearray<'a>(
- env: &mut JNIEnv<'a>,
+ env: &mut Env<'a>,
bytes: impl AsRef<[u8]>,
) -> Result<JByteArray<'a>> {
let bytes = bytes.as_ref();
@@ -178,11 +195,6 @@ pub(crate) fn bytes_to_jbytearray<'a>(
Ok(res)
}
-/// # Safety
-///
-/// The caller must guarantee that the Object passed in is an instance
-/// of `java.lang.String`, passing in anything else will lead to undefined behavior.
-pub(crate) fn jstring_to_string(env: &mut JNIEnv, s: &JString) -> Result<String> {
- let res = unsafe { env.get_string_unchecked(s)? };
- Ok(res.into())
+pub(crate) fn jstring_to_string(env: &mut Env, s: &JString) -> Result<String> {
+ Ok(s.mutf8_chars(env)?.into())
}
diff --git a/bindings/java/src/error.rs b/bindings/java/src/error.rs
index 99f06fbab..a59185c36 100644
--- a/bindings/java/src/error.rs
+++ b/bindings/java/src/error.rs
@@ -19,9 +19,13 @@ use std::fmt::Debug;
use std::fmt::Display;
use std::fmt::Formatter;
-use jni::JNIEnv;
+use jni::Env;
+use jni::errors::ErrorPolicy;
+use jni::jni_sig;
+use jni::jni_str;
use jni::objects::JThrowable;
use jni::objects::JValue;
+use jni::strings::JNIString;
use opendal::ErrorKind;
pub(crate) struct Error {
@@ -29,22 +33,21 @@ pub(crate) struct Error {
}
impl Error {
- pub(crate) fn throw(&self, env: &mut JNIEnv) {
+ pub(crate) fn throw(&self, env: &mut Env) {
if let Err(err) = self.do_throw(env) {
match err {
jni::errors::Error::JavaException => {
- // other calls throws exception; safely ignored
+ // The exception has been thrown; it will propagate to the JVM.
}
- _ => env.fatal_error(err.to_string()),
+ _ => env.fatal_error(JNIString::new(err.to_string()).as_ref()),
}
}
}
pub(crate) fn to_exception<'local>(
&self,
- env: &mut JNIEnv<'local>,
+ env: &mut Env<'local>,
) -> jni::errors::Result<JThrowable<'local>> {
- let class = env.find_class("org/apache/opendal/OpenDALException")?;
let code = env.new_string(match self.inner.kind() {
ErrorKind::Unexpected => "Unexpected",
ErrorKind::Unsupported => "Unsupported",
@@ -62,19 +65,68 @@ impl Error {
})?;
let message = env.new_string(format!("{:?}", self.inner))?;
let exception = env.new_object(
- class,
- "(Ljava/lang/String;Ljava/lang/String;)V",
+ jni_str!("org/apache/opendal/OpenDALException"),
+ jni_sig!("(Ljava/lang/String;Ljava/lang/String;)V"),
&[JValue::Object(&code), JValue::Object(&message)],
)?;
- Ok(JThrowable::from(exception))
+ // SAFETY: `exception` was just constructed as an `OpenDALException`, a
+ // subclass of `java.lang.Throwable`.
+ Ok(unsafe { JThrowable::from_raw(env, exception.into_raw()) })
}
- fn do_throw(&self, env: &mut JNIEnv) -> jni::errors::Result<()> {
+ fn do_throw(&self, env: &mut Env) -> jni::errors::Result<()> {
let exception = self.to_exception(env)?;
env.throw(exception)
}
}
+/// A native-method [`ErrorPolicy`] that converts a Rust [`Error`] into an
+/// `OpenDALException` and a panic into a `RuntimeException`, returning the
+/// default value to the JVM in both cases.
+pub(crate) enum ThrowException {}
+
+impl<T: Default> ErrorPolicy<T, Error> for ThrowException {
+ type Captures<'unowned_env_local: 'native_method, 'native_method> = ();
+
+ fn on_error<'unowned_env_local: 'native_method, 'native_method>(
+ env: &mut Env<'unowned_env_local>,
+ _cap: &mut Self::Captures<'unowned_env_local, 'native_method>,
+ err: Error,
+ ) -> jni::errors::Result<T> {
+ if !env.exception_check() {
+ err.throw(env);
+ }
+ Ok(T::default())
+ }
+
+ fn on_panic<'unowned_env_local: 'native_method, 'native_method>(
+ env: &mut Env<'unowned_env_local>,
+ _cap: &mut Self::Captures<'unowned_env_local, 'native_method>,
+ payload: Box<dyn std::any::Any + Send + 'static>,
+ ) -> jni::errors::Result<T> {
+ if !env.exception_check() {
+ fn downcast_payload(payload: Box<dyn std::any::Any + Send + 'static>) -> String {
+ let payload = match payload.downcast::<&'static str>() {
+ Ok(payload) => return payload.to_string(),
+ Err(payload) => payload,
+ };
+
+ let payload = match payload.downcast::<String>() {
+ Ok(payload) => return *payload,
+ Err(payload) => payload,
+ };
+
+ let _ = payload;
+ "native method panicked".to_string()
+ }
+
+ let payload = downcast_payload(payload);
+ env.fatal_error(JNIString::new(payload).as_ref());
+ }
+ Ok(T::default())
+ }
+}
+
impl From<opendal::Error> for Error {
fn from(err: opendal::Error) -> Self {
Self { inner: err }
diff --git a/bindings/java/src/executor.rs b/bindings/java/src/executor.rs
index e6e305609..3f15688e7 100644
--- a/bindings/java/src/executor.rs
+++ b/bindings/java/src/executor.rs
@@ -15,16 +15,18 @@
// specific language governing permissions and limitations
// under the License.
-use std::cell::RefCell;
use std::ffi::c_void;
use std::future::Future;
use std::num::NonZeroUsize;
+use std::sync::OnceLock;
use std::sync::atomic::{AtomicUsize, Ordering};
-use std::sync::{Arc, OnceLock};
use std::thread::available_parallelism;
-use jni::JNIEnv;
+use jni::Env;
+use jni::EnvUnowned;
use jni::JavaVM;
+use jni::jni_sig;
+use jni::jni_str;
use jni::objects::JClass;
use jni::objects::JObject;
use jni::objects::JValue;
@@ -32,14 +34,15 @@ use jni::sys::{jint, jlong};
use tokio::task::JoinHandle;
use crate::Result;
+use crate::error::ThrowException;
static mut RUNTIME: OnceLock<Executor> = OnceLock::new();
-thread_local! {
- static ENV: RefCell<Option<*mut jni::sys::JNIEnv>> = const { RefCell::new(None) };
-}
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn JNI_OnLoad(_: JavaVM, _: *mut c_void) -> jint {
+pub unsafe extern "system" fn JNI_OnLoad(vm: *mut jni::sys::JavaVM, _: *mut c_void) -> jint {
+ // Register the JavaVM singleton so worker threads can attach to the JVM
+ // later via `JavaVM::singleton()`.
+ let _ = unsafe { JavaVM::from_raw(vm) };
opendal::init_default_registry();
jni::sys::JNI_VERSION_1_8
}
@@ -49,22 +52,12 @@ pub unsafe extern "system" fn JNI_OnLoad(_: JavaVM, _: *mut c_void) -> jint {
/// This function could be only called by java vm when unload this lib.
#[allow(static_mut_refs)]
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn JNI_OnUnload(_: JavaVM, _: *mut c_void) {
+pub unsafe extern "system" fn JNI_OnUnload(_: *mut jni::sys::JavaVM, _: *mut c_void) {
unsafe {
RUNTIME.take();
}
}
-/// # Safety
-///
-/// This function could be only called when the lib is loaded and within an executor thread.
-pub(crate) unsafe fn get_current_env<'local>() -> JNIEnv<'local> {
- let env = ENV
- .with(|cell| *cell.borrow_mut())
- .expect("env must be available");
- unsafe { JNIEnv::from_raw(env) }.expect("env must be valid")
-}
-
pub enum Executor {
Tokio(tokio::runtime::Runtime),
}
@@ -94,26 +87,25 @@ impl Executor {
}
#[unsafe(no_mangle)]
-pub extern "system" fn Java_org_apache_opendal_AsyncExecutor_makeTokioExecutor(
- mut env: JNIEnv,
- _: JClass,
+pub extern "system" fn Java_org_apache_opendal_AsyncExecutor_makeTokioExecutor<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
cores: usize,
) -> jlong {
- make_tokio_executor(&mut env, cores)
- .map(|executor| Box::into_raw(Box::new(executor)) as jlong)
- .unwrap_or_else(|e| {
- e.throw(&mut env);
- 0
- })
+ env.with_env(|_env| -> Result<jlong> {
+ let executor = make_tokio_executor(cores)?;
+ Ok(Box::into_raw(Box::new(executor)) as jlong)
+ })
+ .resolve::<ThrowException>()
}
/// # Safety
///
/// This function should not be called before the AsyncExecutor is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_AsyncExecutor_disposeInternal(
- _: JNIEnv,
- _: JObject,
+pub unsafe extern "system" fn Java_org_apache_opendal_AsyncExecutor_disposeInternal<'local>(
+ _: EnvUnowned<'local>,
+ _: JObject<'local>,
executor: *mut Executor,
) {
unsafe {
@@ -121,8 +113,7 @@ pub unsafe extern "system" fn Java_org_apache_opendal_AsyncExecutor_disposeInter
}
}
-pub(crate) fn make_tokio_executor(env: &mut JNIEnv, cores: usize) -> Result<Executor> {
- let vm = Arc::new(env.get_java_vm().expect("JavaVM must be available"));
+pub(crate) fn make_tokio_executor(cores: usize) -> Result<Executor> {
let counter = AtomicUsize::new(0);
let executor = tokio::runtime::Builder::new_multi_thread()
.worker_threads(cores)
@@ -130,37 +121,23 @@ pub(crate) fn make_tokio_executor(env: &mut JNIEnv, cores: usize) -> Result<Exec
let id = counter.fetch_add(1, Ordering::SeqCst);
format!("opendal-tokio-worker-{id}")
})
- .on_thread_start({
- let vm = vm.clone();
- move || {
- ENV.with(|cell| {
- let mut env = vm
- .attach_current_thread_as_daemon()
- .expect("attach thread must succeed");
-
- set_current_thread_name(&mut env)
- .expect("current thread name has been set above");
-
- *cell.borrow_mut() = Some(env.get_raw());
- })
- }
+ .on_thread_start(|| {
+ // `attach_current_thread` creates a permanent attachment; the thread
+ // is detached automatically when it exits.
+ let vm = JavaVM::singleton().expect("JavaVM singleton must be initialized");
+ vm.attach_current_thread(set_current_thread_name)
+ .expect("attach current thread must succeed");
})
- .on_thread_stop(move || {
- // Typically, the thread attached to the JVM will be detached automatically when the thread exits
- // and the corresponding thread-local AttachGuard is dropped.
- //
- // However, there are some edge cases on Windows that may lead to deadlocks. To mitigate this,
- // we explicitly detach the thread here.
+ .on_thread_stop(|| {
+ // Typically, the thread attached to the JVM will be detached automatically
+ // when the thread exits. However, there are some edge cases on Windows that
+ // may lead to deadlocks. To mitigate this, we explicitly detach the thread here.
//
- // See https://github.com/apache/opendal/issues/6869 and https://github.com/jni-rs/jni-rs/issues/701
- // for more details.
-
- ENV.with(|cell| {
- *cell.borrow_mut() = None;
- });
-
- // SAFETY: JNIEnv is unset above and we do not use AttachGuard anywhere.
- unsafe { vm.detach_current_thread() };
+ // See https://github.com/apache/opendal/issues/6869 and
+ // https://github.com/jni-rs/jni-rs/issues/701 for more details.
+ if let Ok(vm) = JavaVM::singleton() {
+ let _ = vm.detach_current_thread();
+ }
})
.enable_all()
.build()
@@ -174,12 +151,12 @@ pub(crate) fn make_tokio_executor(env: &mut JNIEnv, cores: usize) -> Result<Exec
Ok(Executor::Tokio(executor))
}
-fn set_current_thread_name(env: &mut JNIEnv) -> Result<()> {
+fn set_current_thread_name(env: &mut Env) -> Result<()> {
let current_thread = env
.call_static_method(
- "java/lang/Thread",
- "currentThread",
- "()Ljava/lang/Thread;",
+ jni_str!("java/lang/Thread"),
+ jni_str!("currentThread"),
+ jni_sig!("()Ljava/lang/Thread;"),
&[],
)?
.l()?;
@@ -188,9 +165,9 @@ fn set_current_thread_name(env: &mut JNIEnv) -> Result<()> {
None => unreachable!("thread name must be set"),
};
env.call_method(
- current_thread,
- "setName",
- "(Ljava/lang/String;)V",
+ ¤t_thread,
+ jni_str!("setName"),
+ jni_sig!("(Ljava/lang/String;)V"),
&[JValue::Object(&thread_name)],
)?;
Ok(())
@@ -200,13 +177,10 @@ fn set_current_thread_name(env: &mut JNIEnv) -> Result<()> {
///
/// Crash if the executor is disposed.
#[inline]
-pub(crate) fn executor_or_default<'a>(
- env: &mut JNIEnv<'a>,
- executor: *const Executor,
-) -> Result<&'a Executor> {
+pub(crate) fn executor_or_default(executor: *const Executor) -> Result<&'static Executor> {
unsafe {
if executor.is_null() {
- default_executor(env)
+ default_executor()
} else {
// SAFETY: executor must be valid
Ok(&*executor)
@@ -218,17 +192,15 @@ pub(crate) fn executor_or_default<'a>(
///
/// This function could be only when the lib is loaded.
#[allow(static_mut_refs)]
-unsafe fn default_executor<'a>(env: &mut JNIEnv<'a>) -> Result<&'a Executor> {
+unsafe fn default_executor() -> Result<&'static Executor> {
// Return the executor if it's already initialized
if let Some(runtime) = unsafe { RUNTIME.get() } {
return Ok(runtime);
}
// Try to initialize the executor
- let executor = make_tokio_executor(
- env,
- available_parallelism().map(NonZeroUsize::get).unwrap_or(1),
- )?;
+ let executor =
+ make_tokio_executor(available_parallelism().map(NonZeroUsize::get).unwrap_or(1))?;
Ok(unsafe { RUNTIME.get_or_init(|| executor) })
}
diff --git a/bindings/java/src/layer.rs b/bindings/java/src/layer.rs
index 5f41567c8..b41a15c18 100644
--- a/bindings/java/src/layer.rs
+++ b/bindings/java/src/layer.rs
@@ -19,7 +19,9 @@ use std::time::Duration;
use crate::Result;
use crate::convert::jstring_to_string;
-use jni::JNIEnv;
+use crate::error::ThrowException;
+use jni::Env;
+use jni::EnvUnowned;
use jni::objects::JClass;
use jni::objects::JString;
use jni::sys::jboolean;
@@ -31,9 +33,9 @@ use opendal::layers::ConcurrentLimitLayer;
use opendal::layers::RetryLayer;
#[unsafe(no_mangle)]
-pub extern "system" fn Java_org_apache_opendal_layer_RetryLayer_doLayer(
- _: JNIEnv,
- _: JClass,
+pub extern "system" fn Java_org_apache_opendal_layer_RetryLayer_doLayer<'local>(
+ _: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut Operator,
jitter: jboolean,
factor: jfloat,
@@ -46,7 +48,7 @@ pub extern "system" fn Java_org_apache_opendal_layer_RetryLayer_doLayer(
retry = retry.with_factor(factor);
retry = retry.with_min_delay(Duration::from_nanos(min_delay as u64));
retry = retry.with_max_delay(Duration::from_nanos(max_delay as u64));
- if jitter != 0 {
+ if jitter {
retry = retry.with_jitter()
}
if max_times >= 0 {
@@ -56,20 +58,18 @@ pub extern "system" fn Java_org_apache_opendal_layer_RetryLayer_doLayer(
}
#[unsafe(no_mangle)]
-pub extern "system" fn Java_org_apache_opendal_layer_CapabilityOverrideLayer_doLayer(
- mut env: JNIEnv,
- _: JClass,
+pub extern "system" fn Java_org_apache_opendal_layer_CapabilityOverrideLayer_doLayer<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut Operator,
- overrides: JString,
+ overrides: JString<'local>,
) -> jlong {
- intern_capability_override_layer(&mut env, op, overrides).unwrap_or_else(|e| {
- e.throw(&mut env);
- 0
- })
+ env.with_env(|env| intern_capability_override_layer(env, op, overrides))
+ .resolve::<ThrowException>()
}
fn intern_capability_override_layer(
- env: &mut JNIEnv,
+ env: &mut Env,
op: *mut Operator,
overrides: JString,
) -> Result<jlong> {
@@ -80,9 +80,9 @@ fn intern_capability_override_layer(
}
#[unsafe(no_mangle)]
-pub extern "system" fn Java_org_apache_opendal_layer_ConcurrentLimitLayer_doLayer(
- _: JNIEnv,
- _: JClass,
+pub extern "system" fn Java_org_apache_opendal_layer_ConcurrentLimitLayer_doLayer<'local>(
+ _: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut Operator,
permits: jlong,
) -> jlong {
diff --git a/bindings/java/src/lib.rs b/bindings/java/src/lib.rs
index c7e806ae1..7b6952ca9 100644
--- a/bindings/java/src/lib.rs
+++ b/bindings/java/src/lib.rs
@@ -17,10 +17,11 @@
use std::collections::HashMap;
-use jni::JNIEnv;
+use jni::Env;
+use jni::jni_sig;
+use jni::jni_str;
use jni::objects::JObject;
use jni::objects::JValue;
-use jni::sys::jboolean;
use jni::sys::jint;
use jni::sys::jlong;
use opendal::Entry;
@@ -42,7 +43,7 @@ mod utility;
pub(crate) type Result<T> = std::result::Result<T, error::Error>;
-fn make_presigned_request<'a>(env: &mut JNIEnv<'a>, req: PresignedRequest) -> Result<JObject<'a>> {
+fn make_presigned_request<'a>(env: &mut Env<'a>, req: PresignedRequest) -> Result<JObject<'a>> {
let method = env.new_string(req.method().as_str())?;
let uri = env.new_string(req.uri().to_string())?;
let headers = {
@@ -58,8 +59,8 @@ fn make_presigned_request<'a>(env: &mut JNIEnv<'a>, req: PresignedRequest) -> Re
};
let headers = convert::hashmap_to_jmap(env, &headers)?;
let result = env.new_object(
- "org/apache/opendal/PresignedRequest",
- "(Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;)V",
+ jni_str!("org/apache/opendal/PresignedRequest"),
+ jni_sig!("(Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;)V"),
&[
JValue::Object(&method),
JValue::Object(&uri),
@@ -69,15 +70,17 @@ fn make_presigned_request<'a>(env: &mut JNIEnv<'a>, req: PresignedRequest) -> Re
Ok(result)
}
-fn make_operator_info<'a>(env: &mut JNIEnv<'a>, info: OperatorInfo) -> Result<JObject<'a>> {
- let scheme = env.new_string(info.scheme().to_string())?;
- let root = env.new_string(info.root().to_string())?;
- let name = env.new_string(info.name().to_string())?;
+fn make_operator_info<'a>(env: &mut Env<'a>, info: OperatorInfo) -> Result<JObject<'a>> {
+ let scheme = env.new_string(info.scheme())?;
+ let root = env.new_string(info.root())?;
+ let name = env.new_string(info.name())?;
let capability_obj = make_capability(env, info.capability())?;
let result = env.new_object(
- "org/apache/opendal/OperatorInfo",
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/apache/opendal/Capability;)V",
+ jni_str!("org/apache/opendal/OperatorInfo"),
+ jni_sig!(
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/apache/opendal/Capability;)V"
+ ),
&[
JValue::Object(&scheme),
JValue::Object(&root),
@@ -88,56 +91,56 @@ fn make_operator_info<'a>(env: &mut JNIEnv<'a>, info: OperatorInfo) -> Result<JO
Ok(result)
}
-fn make_capability<'a>(env: &mut JNIEnv<'a>, cap: Capability) -> Result<JObject<'a>> {
+fn make_capability<'a>(env: &mut Env<'a>, cap: Capability) -> Result<JObject<'a>> {
let capability = env.new_object(
- "org/apache/opendal/Capability",
- "(ZZZZZZZZZZZZZZZZZZZZZZJJZZZZZZZZZZZZZZZ)V",
+ jni_str!("org/apache/opendal/Capability"),
+ jni_sig!("(ZZZZZZZZZZZZZZZZZZZZZZJJZZZZZZZZZZZZZZZ)V"),
&[
- JValue::Bool(cap.stat as jboolean),
- JValue::Bool(cap.stat_with_if_match as jboolean),
- JValue::Bool(cap.stat_with_if_none_match as jboolean),
- JValue::Bool(cap.stat_with_if_modified_since as jboolean),
- JValue::Bool(cap.stat_with_if_unmodified_since as jboolean),
- JValue::Bool(cap.stat_with_version as jboolean),
- JValue::Bool(cap.read as jboolean),
- JValue::Bool(cap.read_with_if_match as jboolean),
- JValue::Bool(cap.read_with_if_none_match as jboolean),
- JValue::Bool(cap.read_with_override_cache_control as jboolean),
- JValue::Bool(cap.read_with_override_content_disposition as jboolean),
- JValue::Bool(cap.read_with_override_content_type as jboolean),
- JValue::Bool(cap.write as jboolean),
- JValue::Bool(cap.write_can_multi as jboolean),
- JValue::Bool(cap.write_can_append as jboolean),
- JValue::Bool(cap.write_with_content_type as jboolean),
- JValue::Bool(cap.write_with_content_disposition as jboolean),
- JValue::Bool(cap.write_with_cache_control as jboolean),
- JValue::Bool(cap.write_with_if_match as jboolean),
- JValue::Bool(cap.write_with_if_none_match as jboolean),
- JValue::Bool(cap.write_with_if_not_exists as jboolean),
- JValue::Bool(cap.write_with_user_metadata as jboolean),
+ JValue::Bool(cap.stat),
+ JValue::Bool(cap.stat_with_if_match),
+ JValue::Bool(cap.stat_with_if_none_match),
+ JValue::Bool(cap.stat_with_if_modified_since),
+ JValue::Bool(cap.stat_with_if_unmodified_since),
+ JValue::Bool(cap.stat_with_version),
+ JValue::Bool(cap.read),
+ JValue::Bool(cap.read_with_if_match),
+ JValue::Bool(cap.read_with_if_none_match),
+ JValue::Bool(cap.read_with_override_cache_control),
+ JValue::Bool(cap.read_with_override_content_disposition),
+ JValue::Bool(cap.read_with_override_content_type),
+ JValue::Bool(cap.write),
+ JValue::Bool(cap.write_can_multi),
+ JValue::Bool(cap.write_can_append),
+ JValue::Bool(cap.write_with_content_type),
+ JValue::Bool(cap.write_with_content_disposition),
+ JValue::Bool(cap.write_with_cache_control),
+ JValue::Bool(cap.write_with_if_match),
+ JValue::Bool(cap.write_with_if_none_match),
+ JValue::Bool(cap.write_with_if_not_exists),
+ JValue::Bool(cap.write_with_user_metadata),
JValue::Long(convert::usize_to_jlong(cap.write_multi_max_size)),
JValue::Long(convert::usize_to_jlong(cap.write_multi_min_size)),
- JValue::Bool(cap.create_dir as jboolean),
- JValue::Bool(cap.delete as jboolean),
- JValue::Bool(cap.copy as jboolean),
- JValue::Bool(cap.rename as jboolean),
- JValue::Bool(cap.list as jboolean),
- JValue::Bool(cap.list_with_limit as jboolean),
- JValue::Bool(cap.list_with_start_after as jboolean),
- JValue::Bool(cap.list_with_recursive as jboolean),
- JValue::Bool(cap.list_with_versions as jboolean),
- JValue::Bool(cap.list_with_deleted as jboolean),
- JValue::Bool(cap.presign as jboolean),
- JValue::Bool(cap.presign_read as jboolean),
- JValue::Bool(cap.presign_stat as jboolean),
- JValue::Bool(cap.presign_write as jboolean),
- JValue::Bool(cap.shared as jboolean),
+ JValue::Bool(cap.create_dir),
+ JValue::Bool(cap.delete),
+ JValue::Bool(cap.copy),
+ JValue::Bool(cap.rename),
+ JValue::Bool(cap.list),
+ JValue::Bool(cap.list_with_limit),
+ JValue::Bool(cap.list_with_start_after),
+ JValue::Bool(cap.list_with_recursive),
+ JValue::Bool(cap.list_with_versions),
+ JValue::Bool(cap.list_with_deleted),
+ JValue::Bool(cap.presign),
+ JValue::Bool(cap.presign_read),
+ JValue::Bool(cap.presign_stat),
+ JValue::Bool(cap.presign_write),
+ JValue::Bool(cap.shared),
],
)?;
Ok(capability)
}
-fn make_metadata<'a>(env: &mut JNIEnv<'a>, metadata: Metadata) -> Result<JObject<'a>> {
+fn make_metadata<'a>(env: &mut Env<'a>, metadata: Metadata) -> Result<JObject<'a>> {
let mode = match metadata.mode() {
EntryMode::FILE => 0,
EntryMode::DIR => 1,
@@ -149,9 +152,9 @@ fn make_metadata<'a>(env: &mut JNIEnv<'a>, metadata: Metadata) -> Result<JObject
|v| {
Ok(env
.call_static_method(
- "java/time/Instant",
- "ofEpochSecond",
- "(JJ)Ljava/time/Instant;",
+ jni_str!("java/time/Instant"),
+ jni_str!("ofEpochSecond"),
+ jni_sig!("(JJ)Ljava/time/Instant;"),
&[
JValue::Long(v.into_inner().as_second()),
JValue::Long(v.into_inner().subsec_nanosecond() as jlong),
@@ -177,8 +180,10 @@ fn make_metadata<'a>(env: &mut JNIEnv<'a>, metadata: Metadata) -> Result<JObject
let result = env
.new_object(
- "org/apache/opendal/Metadata",
- "(IJLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/time/Instant;Ljava/lang/String;)V",
+ jni_str!("org/apache/opendal/Metadata"),
+ jni_sig!(
+ "(IJLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/time/Instant;Ljava/lang/String;)V"
+ ),
&[
JValue::Int(mode as jint),
JValue::Long(content_length),
@@ -194,19 +199,19 @@ fn make_metadata<'a>(env: &mut JNIEnv<'a>, metadata: Metadata) -> Result<JObject
Ok(result)
}
-fn make_entry<'a>(env: &mut JNIEnv<'a>, entry: Entry) -> Result<JObject<'a>> {
+fn make_entry<'a>(env: &mut Env<'a>, entry: Entry) -> Result<JObject<'a>> {
let path = env.new_string(entry.path())?;
let metadata = make_metadata(env, entry.metadata().to_owned())?;
Ok(env.new_object(
- "org/apache/opendal/Entry",
- "(Ljava/lang/String;Lorg/apache/opendal/Metadata;)V",
+ jni_str!("org/apache/opendal/Entry"),
+ jni_sig!("(Ljava/lang/String;Lorg/apache/opendal/Metadata;)V"),
&[JValue::Object(&path), JValue::Object(&metadata)],
)?)
}
fn make_write_options<'a>(
- env: &mut JNIEnv<'a>,
+ env: &mut Env<'a>,
options: &JObject,
) -> Result<opendal::options::WriteOptions> {
let concurrent = match convert::read_int_field(env, options, "concurrent")? {
@@ -235,7 +240,7 @@ fn make_write_options<'a>(
}
fn make_list_options<'a>(
- env: &mut JNIEnv<'a>,
+ env: &mut Env<'a>,
options: &JObject,
) -> Result<opendal::options::ListOptions> {
Ok(opendal::options::ListOptions {
@@ -247,7 +252,7 @@ fn make_list_options<'a>(
})
}
-fn make_stat_options(env: &mut JNIEnv, options: &JObject) -> Result<opendal::options::StatOptions> {
+fn make_stat_options(env: &mut Env, options: &JObject) -> Result<opendal::options::StatOptions> {
Ok(opendal::options::StatOptions {
if_match: convert::read_string_field(env, options, "ifMatch")?,
if_none_match: convert::read_string_field(env, options, "ifNoneMatch")?,
@@ -273,7 +278,7 @@ fn make_stat_options(env: &mut JNIEnv, options: &JObject) -> Result<opendal::opt
}
fn make_read_options<'a>(
- env: &mut JNIEnv<'a>,
+ env: &mut Env<'a>,
options: &JObject,
) -> Result<opendal::options::ReadOptions> {
let offset = convert::read_int64_field(env, options, "offset")?;
@@ -286,7 +291,7 @@ fn make_read_options<'a>(
}
fn make_reader_options<'a>(
- _: &mut JNIEnv<'a>,
+ _: &mut Env<'a>,
_: &JObject,
) -> Result<opendal::options::ReaderOptions> {
Ok(opendal::options::ReaderOptions::default())
diff --git a/bindings/java/src/main/java/org/apache/opendal/Metadata.java b/bindings/java/src/main/java/org/apache/opendal/Metadata.java
index 19f2f91be..47052979d 100644
--- a/bindings/java/src/main/java/org/apache/opendal/Metadata.java
+++ b/bindings/java/src/main/java/org/apache/opendal/Metadata.java
@@ -140,8 +140,10 @@ public class Metadata {
return EntryMode.FILE;
case 1:
return EntryMode.DIR;
- default:
+ case 2:
return EntryMode.UNKNOWN;
+ default:
+ throw new IllegalArgumentException("invalid EntryMode: " + mode);
}
}
}
diff --git a/bindings/java/src/operator.rs b/bindings/java/src/operator.rs
index c91824d43..8cc8aac66 100644
--- a/bindings/java/src/operator.rs
+++ b/bindings/java/src/operator.rs
@@ -15,20 +15,21 @@
// specific language governing permissions and limitations
// under the License.
-use jni::JNIEnv;
+use jni::Env;
+use jni::EnvUnowned;
+use jni::jni_str;
use jni::objects::JByteArray;
use jni::objects::JClass;
use jni::objects::JObject;
+use jni::objects::JObjectArray;
use jni::objects::JString;
-use jni::sys::jbyteArray;
use jni::sys::jlong;
-use jni::sys::jobject;
-use jni::sys::jobjectArray;
use jni::sys::jsize;
use opendal::blocking;
use crate::Result;
use crate::convert::{bytes_to_jbytearray, jstring_to_string};
+use crate::error::ThrowException;
use crate::make_metadata;
use crate::{make_entry, make_list_options, make_stat_options, make_write_options};
@@ -36,9 +37,9 @@ use crate::{make_entry, make_list_options, make_stat_options, make_write_options
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_Operator_disposeInternal(
- _: JNIEnv,
- _: JObject,
+pub unsafe extern "system" fn Java_org_apache_opendal_Operator_disposeInternal<'local>(
+ _: EnvUnowned<'local>,
+ _: JObject<'local>,
op: *mut blocking::Operator,
) {
unsafe {
@@ -50,9 +51,9 @@ pub unsafe extern "system" fn Java_org_apache_opendal_Operator_disposeInternal(
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_Operator_duplicate(
- _: JNIEnv,
- _: JObject,
+pub unsafe extern "system" fn Java_org_apache_opendal_Operator_duplicate<'local>(
+ _: EnvUnowned<'local>,
+ _: JObject<'local>,
op: *mut blocking::Operator,
) -> jlong {
let op = unsafe { &mut *op };
@@ -63,60 +64,60 @@ pub unsafe extern "system" fn Java_org_apache_opendal_Operator_duplicate(
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_Operator_read(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_Operator_read<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut blocking::Operator,
- path: JString,
- read_options: JObject,
-) -> jbyteArray {
- let op_ref = unsafe { &mut *op };
- intern_read(&mut env, op_ref, path, read_options).unwrap_or_else(|e| {
- e.throw(&mut env);
- JByteArray::default().into_raw()
+ path: JString<'local>,
+ read_options: JObject<'local>,
+) -> JByteArray<'local> {
+ env.with_env(|env| {
+ let op_ref = unsafe { &mut *op };
+ intern_read(env, op_ref, path, read_options)
})
+ .resolve::<ThrowException>()
}
-fn intern_read(
- env: &mut JNIEnv,
+fn intern_read<'local>(
+ env: &mut Env<'local>,
op: &mut blocking::Operator,
- path: JString,
- options: JObject,
-) -> Result<jbyteArray> {
+ path: JString<'local>,
+ options: JObject<'local>,
+) -> Result<JByteArray<'local>> {
use crate::make_read_options;
let path = jstring_to_string(env, &path)?;
let options = make_read_options(env, &options)?;
let content = op.read_options(&path, options)?;
- let result = bytes_to_jbytearray(env, content.to_bytes())?;
- Ok(result.into_raw())
+ bytes_to_jbytearray(env, content.to_bytes())
}
/// # Safety
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_Operator_write(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_Operator_write<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut blocking::Operator,
- path: JString,
- content: JByteArray,
- write_options: JObject,
+ path: JString<'local>,
+ content: JByteArray<'local>,
+ write_options: JObject<'local>,
) {
- let op_ref = unsafe { &mut *op };
- intern_write(&mut env, op_ref, path, content, write_options).unwrap_or_else(|e| {
- e.throw(&mut env);
+ env.with_env(|env| {
+ let op_ref = unsafe { &mut *op };
+ intern_write(env, op_ref, path, content, write_options)
})
+ .resolve::<ThrowException>()
}
-fn intern_write(
- env: &mut JNIEnv,
+fn intern_write<'local>(
+ env: &mut Env<'local>,
op: &mut blocking::Operator,
- path: JString,
- content: JByteArray,
- options: JObject,
+ path: JString<'local>,
+ content: JByteArray<'local>,
+ options: JObject<'local>,
) -> Result<()> {
let path = jstring_to_string(env, &path)?;
let content = env.convert_byte_array(content)?;
@@ -130,49 +131,54 @@ fn intern_write(
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_Operator_stat(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_Operator_stat<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut blocking::Operator,
- path: JString,
- stat_options: JObject,
-) -> jobject {
- let op_ref = unsafe { &mut *op };
- intern_stat(&mut env, op_ref, path, stat_options).unwrap_or_else(|e| {
- e.throw(&mut env);
- JObject::default().into_raw()
+ path: JString<'local>,
+ stat_options: JObject<'local>,
+) -> JObject<'local> {
+ env.with_env(|env| {
+ let op_ref = unsafe { &mut *op };
+ intern_stat(env, op_ref, path, stat_options)
})
+ .resolve::<ThrowException>()
}
-fn intern_stat(
- env: &mut JNIEnv,
+fn intern_stat<'local>(
+ env: &mut Env<'local>,
op: &mut blocking::Operator,
- path: JString,
- options: JObject,
-) -> Result<jobject> {
+ path: JString<'local>,
+ options: JObject<'local>,
+) -> Result<JObject<'local>> {
let path = jstring_to_string(env, &path)?;
let stat_opts = make_stat_options(env, &options)?;
let metadata = op.stat_options(&path, stat_opts)?;
- Ok(make_metadata(env, metadata)?.into_raw())
+ make_metadata(env, metadata)
}
/// # Safety
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_Operator_delete(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_Operator_delete<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut blocking::Operator,
- path: JString,
+ path: JString<'local>,
) {
- let op_ref = unsafe { &mut *op };
- intern_delete(&mut env, op_ref, path).unwrap_or_else(|e| {
- e.throw(&mut env);
+ env.with_env(|env| {
+ let op_ref = unsafe { &mut *op };
+ intern_delete(env, op_ref, path)
})
+ .resolve::<ThrowException>()
}
-fn intern_delete(env: &mut JNIEnv, op: &mut blocking::Operator, path: JString) -> Result<()> {
+fn intern_delete<'local>(
+ env: &mut Env<'local>,
+ op: &mut blocking::Operator,
+ path: JString<'local>,
+) -> Result<()> {
let path = jstring_to_string(env, &path)?;
Ok(op.delete(&path)?)
}
@@ -181,19 +187,24 @@ fn intern_delete(env: &mut JNIEnv, op: &mut blocking::Operator, path: JString) -
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_Operator_createDir(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_Operator_createDir<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut blocking::Operator,
- path: JString,
+ path: JString<'local>,
) {
- let op_ref = unsafe { &mut *op };
- intern_create_dir(&mut env, op_ref, path).unwrap_or_else(|e| {
- e.throw(&mut env);
+ env.with_env(|env| {
+ let op_ref = unsafe { &mut *op };
+ intern_create_dir(env, op_ref, path)
})
+ .resolve::<ThrowException>()
}
-fn intern_create_dir(env: &mut JNIEnv, op: &mut blocking::Operator, path: JString) -> Result<()> {
+fn intern_create_dir<'local>(
+ env: &mut Env<'local>,
+ op: &mut blocking::Operator,
+ path: JString<'local>,
+) -> Result<()> {
let path = jstring_to_string(env, &path)?;
Ok(op.create_dir(&path)?)
}
@@ -202,24 +213,25 @@ fn intern_create_dir(env: &mut JNIEnv, op: &mut blocking::Operator, path: JStrin
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_Operator_copy(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_Operator_copy<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut blocking::Operator,
- source_path: JString,
- target_path: JString,
+ source_path: JString<'local>,
+ target_path: JString<'local>,
) {
- let op_ref = unsafe { &mut *op };
- intern_copy(&mut env, op_ref, source_path, target_path).unwrap_or_else(|e| {
- e.throw(&mut env);
+ env.with_env(|env| {
+ let op_ref = unsafe { &mut *op };
+ intern_copy(env, op_ref, source_path, target_path)
})
+ .resolve::<ThrowException>()
}
-fn intern_copy(
- env: &mut JNIEnv,
+fn intern_copy<'local>(
+ env: &mut Env<'local>,
op: &mut blocking::Operator,
- source_path: JString,
- target_path: JString,
+ source_path: JString<'local>,
+ target_path: JString<'local>,
) -> Result<()> {
let source_path = jstring_to_string(env, &source_path)?;
let target_path = jstring_to_string(env, &target_path)?;
@@ -232,24 +244,25 @@ fn intern_copy(
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_Operator_rename(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_Operator_rename<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut blocking::Operator,
- source_path: JString,
- target_path: JString,
+ source_path: JString<'local>,
+ target_path: JString<'local>,
) {
- let op_ref = unsafe { &mut *op };
- intern_rename(&mut env, op_ref, source_path, target_path).unwrap_or_else(|e| {
- e.throw(&mut env);
+ env.with_env(|env| {
+ let op_ref = unsafe { &mut *op };
+ intern_rename(env, op_ref, source_path, target_path)
})
+ .resolve::<ThrowException>()
}
-fn intern_rename(
- env: &mut JNIEnv,
+fn intern_rename<'local>(
+ env: &mut Env<'local>,
op: &mut blocking::Operator,
- source_path: JString,
- target_path: JString,
+ source_path: JString<'local>,
+ target_path: JString<'local>,
) -> Result<()> {
let source_path = jstring_to_string(env, &source_path)?;
let target_path = jstring_to_string(env, &target_path)?;
@@ -261,19 +274,24 @@ fn intern_rename(
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_Operator_removeAll(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_Operator_removeAll<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut blocking::Operator,
- path: JString,
+ path: JString<'local>,
) {
- let op_ref = unsafe { &mut *op };
- intern_remove_all(&mut env, op_ref, path).unwrap_or_else(|e| {
- e.throw(&mut env);
+ env.with_env(|env| {
+ let op_ref = unsafe { &mut *op };
+ intern_remove_all(env, op_ref, path)
})
+ .resolve::<ThrowException>()
}
-fn intern_remove_all(env: &mut JNIEnv, op: &mut blocking::Operator, path: JString) -> Result<()> {
+fn intern_remove_all<'local>(
+ env: &mut Env<'local>,
+ op: &mut blocking::Operator,
+ path: JString<'local>,
+) -> Result<()> {
use opendal::options::DeleteOptions;
let path = jstring_to_string(env, &path)?;
@@ -290,40 +308,40 @@ fn intern_remove_all(env: &mut JNIEnv, op: &mut blocking::Operator, path: JStrin
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_Operator_list(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_Operator_list<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut blocking::Operator,
- path: JString,
- options: JObject,
-) -> jobjectArray {
- let op_ref = unsafe { &mut *op };
- intern_list(&mut env, op_ref, path, options).unwrap_or_else(|e| {
- e.throw(&mut env);
- JObject::default().into_raw()
+ path: JString<'local>,
+ options: JObject<'local>,
+) -> JObjectArray<'local> {
+ env.with_env(|env| {
+ let op_ref = unsafe { &mut *op };
+ intern_list(env, op_ref, path, options)
})
+ .resolve::<ThrowException>()
}
-fn intern_list(
- env: &mut JNIEnv,
+fn intern_list<'local>(
+ env: &mut Env<'local>,
op: &mut blocking::Operator,
- path: JString,
- options: JObject,
-) -> Result<jobjectArray> {
+ path: JString<'local>,
+ options: JObject<'local>,
+) -> Result<JObjectArray<'local>> {
let path = jstring_to_string(env, &path)?;
let list_opts = make_list_options(env, &options)?;
let entries = op.list_options(&path, list_opts)?;
let jarray = env.new_object_array(
entries.len() as jsize,
- "org/apache/opendal/Entry",
+ jni_str!("org/apache/opendal/Entry"),
JObject::null(),
)?;
for (idx, entry) in entries.into_iter().enumerate() {
let entry = make_entry(env, entry)?;
- env.set_object_array_element(&jarray, idx as jsize, entry)?;
+ jarray.set_element(env, idx, &entry)?;
}
- Ok(jarray.into_raw())
+ Ok(jarray)
}
diff --git a/bindings/java/src/operator_input_stream.rs b/bindings/java/src/operator_input_stream.rs
index f79abdb77..57a729979 100644
--- a/bindings/java/src/operator_input_stream.rs
+++ b/bindings/java/src/operator_input_stream.rs
@@ -15,38 +15,41 @@
// specific language governing permissions and limitations
// under the License.
-use jni::JNIEnv;
+use jni::Env;
+use jni::EnvUnowned;
use jni::objects::JByteArray;
use jni::objects::JClass;
use jni::objects::JObject;
use jni::objects::JString;
-use jni::sys::jbyteArray;
use jni::sys::jlong;
use opendal::blocking;
use opendal::blocking::StdBytesIterator;
use crate::convert::jstring_to_string;
+use crate::error::ThrowException;
/// # Safety
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_OperatorInputStream_constructReader(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_OperatorInputStream_constructReader<
+ 'local,
+>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut blocking::Operator,
- path: JString,
- options: JObject,
+ path: JString<'local>,
+ options: JObject<'local>,
) -> jlong {
- let op_ref = unsafe { &mut *op };
- intern_construct_reader(&mut env, op_ref, path, options).unwrap_or_else(|e| {
- e.throw(&mut env);
- 0
+ env.with_env(|env| {
+ let op_ref = unsafe { &mut *op };
+ intern_construct_reader(env, op_ref, path, options)
})
+ .resolve::<ThrowException>()
}
fn intern_construct_reader(
- env: &mut JNIEnv,
+ env: &mut Env,
op: &mut blocking::Operator,
path: JString,
options: JObject,
@@ -71,9 +74,9 @@ fn intern_construct_reader(
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_OperatorInputStream_disposeReader(
- _: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_OperatorInputStream_disposeReader<'local>(
+ _: EnvUnowned<'local>,
+ _: JClass<'local>,
reader: *mut StdBytesIterator,
) {
unsafe {
@@ -85,31 +88,28 @@ pub unsafe extern "system" fn Java_org_apache_opendal_OperatorInputStream_dispos
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_OperatorInputStream_readNextBytes(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_OperatorInputStream_readNextBytes<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
reader: *mut StdBytesIterator,
-) -> jbyteArray {
- let reader_ref = unsafe { &mut *reader };
- intern_read_next_bytes(&mut env, reader_ref).unwrap_or_else(|e| {
- e.throw(&mut env);
- JByteArray::default().into_raw()
+) -> JByteArray<'local> {
+ env.with_env(|env| {
+ let reader_ref = unsafe { &mut *reader };
+ intern_read_next_bytes(env, reader_ref)
})
+ .resolve::<ThrowException>()
}
-fn intern_read_next_bytes(
- env: &mut JNIEnv,
+fn intern_read_next_bytes<'local>(
+ env: &mut Env<'local>,
reader: &mut StdBytesIterator,
-) -> crate::Result<jbyteArray> {
+) -> crate::Result<JByteArray<'local>> {
match reader
.next()
.transpose()
.map_err(|err| opendal::Error::new(opendal::ErrorKind::Unexpected, err.to_string()))?
{
- None => Ok(JObject::null().into_raw()),
- Some(content) => {
- let result = env.byte_array_from_slice(&content)?;
- Ok(result.into_raw())
- }
+ None => Ok(JByteArray::default()),
+ Some(content) => Ok(env.byte_array_from_slice(&content)?),
}
}
diff --git a/bindings/java/src/operator_output_stream.rs b/bindings/java/src/operator_output_stream.rs
index 4c3c9e3bb..c7ae2fc1e 100644
--- a/bindings/java/src/operator_output_stream.rs
+++ b/bindings/java/src/operator_output_stream.rs
@@ -15,7 +15,8 @@
// specific language governing permissions and limitations
// under the License.
-use jni::JNIEnv;
+use jni::Env;
+use jni::EnvUnowned;
use jni::objects::JByteArray;
use jni::objects::JClass;
use jni::objects::JObject;
@@ -24,27 +25,30 @@ use jni::sys::jlong;
use opendal::blocking;
use crate::convert::jstring_to_string;
+use crate::error::ThrowException;
/// # Safety
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_OperatorOutputStream_constructWriter(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_OperatorOutputStream_constructWriter<
+ 'local,
+>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
op: *mut blocking::Operator,
- path: JString,
- options: JObject,
+ path: JString<'local>,
+ options: JObject<'local>,
) -> jlong {
- let op_ref = unsafe { &mut *op };
- intern_construct_write(&mut env, op_ref, path, options).unwrap_or_else(|e| {
- e.throw(&mut env);
- 0
+ env.with_env(|env| {
+ let op_ref = unsafe { &mut *op };
+ intern_construct_write(env, op_ref, path, options)
})
+ .resolve::<ThrowException>()
}
fn intern_construct_write(
- env: &mut JNIEnv,
+ env: &mut Env,
op: &mut blocking::Operator,
path: JString,
options: JObject,
@@ -61,15 +65,16 @@ fn intern_construct_write(
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_OperatorOutputStream_disposeWriter(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_OperatorOutputStream_disposeWriter<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
writer: *mut blocking::Writer,
) {
- let mut writer = unsafe { Box::from_raw(writer) };
- intern_dispose_write(&mut writer).unwrap_or_else(|e| {
- e.throw(&mut env);
+ env.with_env(|_env| {
+ let mut writer = unsafe { Box::from_raw(writer) };
+ intern_dispose_write(&mut writer)
})
+ .resolve::<ThrowException>()
}
fn intern_dispose_write(writer: &mut blocking::Writer) -> crate::Result<()> {
@@ -81,20 +86,21 @@ fn intern_dispose_write(writer: &mut blocking::Writer) -> crate::Result<()> {
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_OperatorOutputStream_writeBytes(
- mut env: JNIEnv,
- _: JClass,
+pub unsafe extern "system" fn Java_org_apache_opendal_OperatorOutputStream_writeBytes<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
writer: *mut blocking::Writer,
- content: JByteArray,
+ content: JByteArray<'local>,
) {
- let writer_ref = unsafe { &mut *writer };
- intern_write_bytes(&mut env, writer_ref, content).unwrap_or_else(|e| {
- e.throw(&mut env);
+ env.with_env(|env| {
+ let writer_ref = unsafe { &mut *writer };
+ intern_write_bytes(env, writer_ref, content)
})
+ .resolve::<ThrowException>()
}
fn intern_write_bytes(
- env: &mut JNIEnv,
+ env: &mut Env,
writer: &mut blocking::Writer,
content: JByteArray,
) -> crate::Result<()> {
diff --git a/bindings/java/src/utility.rs b/bindings/java/src/utility.rs
index 5524c328c..3a8cbc764 100644
--- a/bindings/java/src/utility.rs
+++ b/bindings/java/src/utility.rs
@@ -17,30 +17,31 @@
use std::collections::HashSet;
-use jni::JNIEnv;
+use jni::Env;
+use jni::EnvUnowned;
+use jni::jni_str;
use jni::objects::JClass;
use jni::objects::JObject;
-use jni::sys::jobjectArray;
+use jni::objects::JObjectArray;
use jni::sys::jsize;
use crate::Result;
use crate::convert::string_to_jstring;
+use crate::error::ThrowException;
/// # Safety
///
/// This function should not be called before the Operator is ready.
#[unsafe(no_mangle)]
-pub unsafe extern "system" fn Java_org_apache_opendal_OpenDAL_loadEnabledServices(
- mut env: JNIEnv,
- _: JClass,
-) -> jobjectArray {
- intern_load_enabled_services(&mut env).unwrap_or_else(|e| {
- e.throw(&mut env);
- JObject::default().into_raw()
- })
+pub unsafe extern "system" fn Java_org_apache_opendal_OpenDAL_loadEnabledServices<'local>(
+ mut env: EnvUnowned<'local>,
+ _: JClass<'local>,
+) -> JObjectArray<'local> {
+ env.with_env(|env| intern_load_enabled_services(env))
+ .resolve::<ThrowException>()
}
-fn intern_load_enabled_services(env: &mut JNIEnv) -> Result<jobjectArray> {
+fn intern_load_enabled_services<'local>(env: &mut Env<'local>) -> Result<JObjectArray<'local>> {
let services = HashSet::from([
opendal::services::ALIYUN_DRIVE_SCHEME,
opendal::services::ALLUXIO_SCHEME,
@@ -92,12 +93,16 @@ fn intern_load_enabled_services(env: &mut JNIEnv) -> Result<jobjectArray> {
opendal::services::YANDEX_DISK_SCHEME,
]);
- let res = env.new_object_array(services.len() as jsize, "java/lang/String", JObject::null())?;
+ let res = env.new_object_array(
+ services.len() as jsize,
+ jni_str!("java/lang/String"),
+ JObject::null(),
+ )?;
for (idx, service) in services.into_iter().enumerate() {
let srv = string_to_jstring(env, Some(service))?;
- env.set_object_array_element(&res, idx as jsize, srv)?;
+ res.set_element(env, idx, &srv)?;
}
- Ok(res.into_raw())
+ Ok(res)
}