Skip to content

Commit

Permalink
add Stat support
Browse files Browse the repository at this point in the history
  • Loading branch information
kidylee committed Apr 11, 2023
1 parent 8b9c4c2 commit 657f854
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 33 deletions.
124 changes: 97 additions & 27 deletions bindings/java/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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!")
Expand All @@ -43,42 +44,18 @@ pub extern "system" fn Java_org_apache_opendal_Operator_getOperator(

let map = convert_map(&mut env, &params);
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(
"java/lang/IllegalArgumentException",
"Unsupported operator.",
)
.expect("Couldn't throw exception");
std::ptr::null()
0 as jlong
}
}

fn convert_map(env: &mut JNIEnv, params: &JObject) -> HashMap<String, String> {
let mut result: HashMap<String, String> = 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.
Expand All @@ -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);
}

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -188,3 +234,27 @@ fn build_operator(

Ok(op)
}

fn convert_map(env: &mut JNIEnv, params: &JObject) -> HashMap<String, String> {
let mut result: HashMap<String, String> = 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
}
61 changes: 61 additions & 0 deletions bindings/java/src/main/java/org/apache/opendal/Metadata.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
9 changes: 8 additions & 1 deletion bindings/java/src/main/java/org/apache/opendal/Operator.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,16 @@ public Operator(String schema, Map<String, String> params) {

private native long getOperator(String type, Map<String, String> params);

private native void freeOperator(long ptr);
protected native void freeOperator(long ptr);

private native void write(long ptr, String fileName, String content);

private native String read(long ptr, String fileName);

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);
Expand All @@ -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();
Expand Down
13 changes: 8 additions & 5 deletions bindings/java/src/test/java/org/apache/opendal/StepsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -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}")
Expand Down

0 comments on commit 657f854

Please sign in to comment.