From 657f8546d58e8ee4c3d645d0c1b8e90106d5bbd2 Mon Sep 17 00:00:00 2001 From: Kidy Lee Date: Mon, 10 Apr 2023 22:31:38 +0800 Subject: [PATCH] add Stat support --- bindings/java/src/lib.rs | 124 ++++++++++++++---- .../java/org/apache/opendal/Metadata.java | 61 +++++++++ .../java/org/apache/opendal/Operator.java | 9 +- .../java/org/apache/opendal/StepsTest.java | 13 +- 4 files changed, 174 insertions(+), 33 deletions(-) create mode 100644 bindings/java/src/main/java/org/apache/opendal/Metadata.java diff --git a/bindings/java/src/lib.rs b/bindings/java/src/lib.rs index 1ee69a23abd9..da456fd3d5ef 100644 --- a/bindings/java/src/lib.rs +++ b/bindings/java/src/lib.rs @@ -22,6 +22,7 @@ use jni::objects::JClass; use jni::objects::JMap; use jni::objects::JObject; use jni::objects::JString; +use jni::sys::{jboolean, jlong}; use jni::JNIEnv; use opendal::BlockingOperator; use opendal::Operator; @@ -33,7 +34,7 @@ pub extern "system" fn Java_org_apache_opendal_Operator_getOperator( _class: JClass, input: JString, params: JObject, -) -> *const i32 { +) -> jlong { let input: String = env .get_string(&input) .expect("Couldn't get java string!") @@ -43,7 +44,7 @@ pub extern "system" fn Java_org_apache_opendal_Operator_getOperator( let map = convert_map(&mut env, ¶ms); if let Ok(operator) = build_operator(scheme, map) { - Box::into_raw(Box::new(operator)) as *const i32 + Box::into_raw(Box::new(operator)) as jlong } else { env.exception_clear().expect("Couldn't clear exception"); env.throw_new( @@ -51,34 +52,10 @@ pub extern "system" fn Java_org_apache_opendal_Operator_getOperator( "Unsupported operator.", ) .expect("Couldn't throw exception"); - std::ptr::null() + 0 as jlong } } -fn convert_map(env: &mut JNIEnv, params: &JObject) -> HashMap { - let mut result: HashMap = HashMap::new(); - let _ = JMap::from_env(env, params) - .unwrap() - .iter(env) - .and_then(|mut iter| { - while let Some(e) = iter.next(env)? { - let key = JString::from(e.0); - let value = JString::from(e.1); - let key: String = env - .get_string(&key) - .expect("Couldn't get java string!") - .into(); - let value: String = env - .get_string(&value) - .expect("Couldn't get java string!") - .into(); - result.insert(key, value); - } - Ok(()) - }); - result -} - /// # Safety /// /// This function should not be called before the Operator are ready. @@ -88,6 +65,12 @@ pub unsafe extern "system" fn Java_org_apache_opendal_Operator_freeOperator( _class: JClass, ptr: *mut Operator, ) { + if ptr.is_null() { + // The pointer is null, nothing to free + return; + } + + // Take ownership of the pointer by wrapping it with a Box let _ = Box::from_raw(ptr); } @@ -137,6 +120,69 @@ pub unsafe extern "system" fn Java_org_apache_opendal_Operator_read<'local>( output } +/// # Safety +/// +/// This function should not be called before the Operator are ready. +#[no_mangle] +pub unsafe extern "system" fn Java_org_apache_opendal_Operator_stat( + mut env: JNIEnv, + _class: JClass, + ptr: *mut BlockingOperator, + file: JString, +) -> jlong { + let op = &mut *ptr; + let file: String = env + .get_string(&file) + .expect("Couldn't get java string!") + .into(); + let metadata = op.stat(&file).unwrap(); + Box::into_raw(Box::new(metadata)) as jlong +} + +/// # Safety +/// +/// This function should not be called before the Stat are ready. +#[no_mangle] +pub unsafe extern "system" fn Java_org_apache_opendal_Metadata_isFile( + mut _env: JNIEnv, + _class: JClass, + ptr: *mut opendal::Metadata, +) -> jboolean { + let metadata = &mut *ptr; + metadata.is_file() as jboolean +} + +/// # Safety +/// +/// This function should not be called before the Stat are ready. +#[no_mangle] +pub unsafe extern "system" fn Java_org_apache_opendal_Metadata_getContentLength( + mut _env: JNIEnv, + _class: JClass, + ptr: *mut opendal::Metadata, +) -> jlong { + let metadata = &mut *ptr; + metadata.content_length() as jlong +} + +/// # Safety +/// +/// This function should not be called before the Stat are ready. +#[no_mangle] +pub unsafe extern "system" fn Java_org_apache_opendal_Metadata_freeStat( + mut _env: JNIEnv, + _class: JClass, + ptr: *mut opendal::Metadata, +) { + if ptr.is_null() { + // The pointer is null, nothing to free + return; + } + + // Take ownership of the pointer by wrapping it with a Box + let _ = Box::from_raw(ptr); +} + /// # Safety /// /// This function should not be called before the Operator are ready. @@ -188,3 +234,27 @@ fn build_operator( Ok(op) } + +fn convert_map(env: &mut JNIEnv, params: &JObject) -> HashMap { + let mut result: HashMap = HashMap::new(); + let _ = JMap::from_env(env, params) + .unwrap() + .iter(env) + .and_then(|mut iter| { + while let Some(e) = iter.next(env)? { + let key = JString::from(e.0); + let value = JString::from(e.1); + let key: String = env + .get_string(&key) + .expect("Couldn't get java string!") + .into(); + let value: String = env + .get_string(&value) + .expect("Couldn't get java string!") + .into(); + result.insert(key, value); + } + Ok(()) + }); + result +} diff --git a/bindings/java/src/main/java/org/apache/opendal/Metadata.java b/bindings/java/src/main/java/org/apache/opendal/Metadata.java new file mode 100644 index 000000000000..31cb59e955a6 --- /dev/null +++ b/bindings/java/src/main/java/org/apache/opendal/Metadata.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.opendal; + +import io.questdb.jar.jni.JarJniLoader; + +public class Metadata { + + public static final String ORG_APACHE_OPENDAL_RUST_LIBS = "/org/apache/opendal/rust/libs"; + + public static final String OPENDAL_JAVA = "opendal_java"; + + long statPtr; + + private native void freeStat(long statPtr); + + private native boolean isFile(long statPtr); + + private native long getContentLength(long statPtr); + + static { + JarJniLoader.loadLib( + Operator.class, + ORG_APACHE_OPENDAL_RUST_LIBS, + OPENDAL_JAVA); + } + + public Metadata(long statPtr) { + this.statPtr = statPtr; + } + + public boolean isFile() { + return isFile(this.statPtr); + } + + @Override + protected void finalize() { + freeStat(this.statPtr); + } + + public long getContentLength() { + return getContentLength(this.statPtr); + } +} diff --git a/bindings/java/src/main/java/org/apache/opendal/Operator.java b/bindings/java/src/main/java/org/apache/opendal/Operator.java index 7a033bee892e..a51d71e60489 100644 --- a/bindings/java/src/main/java/org/apache/opendal/Operator.java +++ b/bindings/java/src/main/java/org/apache/opendal/Operator.java @@ -46,7 +46,7 @@ public Operator(String schema, Map params) { private native long getOperator(String type, Map params); - private native void freeOperator(long ptr); + protected native void freeOperator(long ptr); private native void write(long ptr, String fileName, String content); @@ -54,6 +54,8 @@ public Operator(String schema, Map params) { private native void delete(long ptr, String fileName); + private native long stat(long ptr, String file); + public void write(String fileName, String content) { write(this.ptr, fileName, content); @@ -67,6 +69,11 @@ public void delete(String s) { delete(this.ptr, s); } + public Metadata stat(String fileName) { + long statPtr = stat(this.ptr, fileName); + return new Metadata(statPtr); + } + @Override protected void finalize() throws Throwable { super.finalize(); diff --git a/bindings/java/src/test/java/org/apache/opendal/StepsTest.java b/bindings/java/src/test/java/org/apache/opendal/StepsTest.java index bca3c774d48c..a4f411f0ee39 100644 --- a/bindings/java/src/test/java/org/apache/opendal/StepsTest.java +++ b/bindings/java/src/test/java/org/apache/opendal/StepsTest.java @@ -26,7 +26,7 @@ import java.util.HashMap; import java.util.Map; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.*; public class StepsTest { @@ -46,21 +46,24 @@ public void blocking_write_path_test_with_content_hello_world(String fileName, S @Then("The blocking file {string} should exist") - public void the_blocking_file_test_should_exist(String content) { - + public void the_blocking_file_test_should_exist(String fileName) { + Metadata metadata = this.operator.stat(fileName); + assertNotNull(metadata); } @Then("The blocking file {string} entry mode must be file") public void the_blocking_file_test_entry_mode_must_be_file(String fileName) { + Metadata metadata = this.operator.stat(fileName); + assertTrue(metadata.isFile()); } @Then("The blocking file {string} content length must be {int}") public void the_blocking_file_test_content_length_must_be_13(String fileName, int length) { - String content = this.operator.read(fileName); + Metadata metadata = this.operator.stat(fileName); - assertEquals(content.length(), length); + assertEquals(metadata.getContentLength(), length); } @Then("The blocking file {string} must have content {string}")