Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b141712
feat(binding/java): add Entry for list
G-XD Oct 16, 2023
a9bb79e
feat(binding/java): support list for blocking
G-XD Oct 16, 2023
d3036ae
feat(binding/java): support list & remove_all
G-XD Oct 16, 2023
fb960f5
test(binding/java): add list test
G-XD Oct 16, 2023
7662b9c
feat(binding/java): add OpList args
G-XD Oct 17, 2023
deae37d
feat(binding/java): support metakey args
G-XD Oct 17, 2023
b72ccc1
chore(binding/java): use Set for Metakey args
G-XD Oct 18, 2023
9893be8
chore(binding/java): fix code
G-XD Oct 18, 2023
b7e0d85
Merge branch 'main' into java_binding_list
G-XD Oct 18, 2023
9df26e9
chore(binding/java): fix code
G-XD Oct 18, 2023
dc6f0d3
chore(binding/java): use Data for OpList
G-XD Oct 18, 2023
6b45bb1
Merge branch 'main' into java_binding_list
G-XD Oct 18, 2023
e788c68
test(binding/java): add test for list
G-XD Oct 18, 2023
4507714
refactor(binding/java): update list api
G-XD Oct 19, 2023
e6fb207
ci(binding/java): update root path of fs test
G-XD Oct 19, 2023
145ff8d
refactor(binding/java): refactor Metakey name
G-XD Oct 20, 2023
00dd58c
refactor(binding/java): remove list with args support
G-XD Oct 23, 2023
30941bc
refactor(binding/java): align the pattern of convert list
G-XD Oct 23, 2023
8e0374c
refactor(binding/java): align the pattern of convert list
G-XD Oct 23, 2023
38301ce
Merge branch 'main' into java_binding_list
G-XD Oct 25, 2023
8c43cc1
refactor(binding/java): use Metadata::metakey() api for make entry
G-XD Oct 25, 2023
5872f4d
ci(binding/java): remove behavior tests
G-XD Oct 25, 2023
6718148
refactor(binding/java): update code style
G-XD Oct 25, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 0 additions & 12 deletions .github/workflows/bindings_java.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,3 @@ jobs:
run: |
./mvnw clean install -DskipTests
./mvnw verify artifact:compare
- name: Behavior tests
working-directory: bindings/java
shell: bash
run: |
export OPENDAL_TEST=memory
export OPENDAL_MEMORY_ROOT=/opendal
./mvnw test -Dtest="behavior.*Test"

export OPENDAL_TEST=fs
export OPENDAL_FS_ROOT=/tmp
./mvnw test -Dtest="behavior.*Test"

58 changes: 58 additions & 0 deletions bindings/java/src/blocking_operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@ use jni::objects::JClass;
use jni::objects::JObject;
use jni::objects::JString;
use jni::sys::jobject;
use jni::sys::jobjectArray;
use jni::sys::jsize;
use jni::sys::{jbyteArray, jlong};
use jni::JNIEnv;

use opendal::BlockingOperator;

use crate::jstring_to_string;
use crate::make_entry;
use crate::make_metadata;
use crate::Result;

Expand Down Expand Up @@ -221,3 +224,58 @@ fn intern_rename(

Ok(op.rename(&source_path, &target_path)?)
}

/// # Safety
///
/// This function should not be called before the Operator are ready.
#[no_mangle]
pub unsafe extern "system" fn Java_org_apache_opendal_BlockingOperator_removeAll(
mut env: JNIEnv,
_: JClass,
op: *mut BlockingOperator,
path: JString,
) {
intern_remove_all(&mut env, &mut *op, path).unwrap_or_else(|e| {
e.throw(&mut env);
})
}

fn intern_remove_all(env: &mut JNIEnv, op: &mut BlockingOperator, path: JString) -> Result<()> {
let path = jstring_to_string(env, &path)?;

Ok(op.remove_all(&path)?)
}

/// # Safety
///
/// This function should not be called before the Operator are ready.
#[no_mangle]
pub unsafe extern "system" fn Java_org_apache_opendal_BlockingOperator_list(
mut env: JNIEnv,
_: JClass,
op: *mut BlockingOperator,
path: JString,
) -> jobjectArray {
intern_list(&mut env, &mut *op, path).unwrap_or_else(|e| {
e.throw(&mut env);
JObject::default().into_raw()
})
}

fn intern_list(env: &mut JNIEnv, op: &mut BlockingOperator, path: JString) -> Result<jobjectArray> {
let path = jstring_to_string(env, &path)?;
let obs = op.list(&path)?;

let jarray = env.new_object_array(
obs.len() as jsize,
"org/apache/opendal/Entry",
JObject::null(),
)?;

for (idx, entry) in obs.iter().enumerate() {
let entry = make_entry(env, entry.to_owned())?;
env.set_object_array_element(&jarray, idx as jsize, entry)?;
}

Ok(jarray.into_raw())
}
84 changes: 67 additions & 17 deletions bindings/java/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ use jni::JavaVM;
use once_cell::sync::OnceCell;
use opendal::raw::PresignedRequest;
use opendal::Capability;
use opendal::Entry;
use opendal::EntryMode;
use opendal::Metadata;
use opendal::Metakey;
use opendal::OperatorInfo;
use tokio::runtime::Builder;
use tokio::runtime::Runtime;
Expand Down Expand Up @@ -233,31 +235,68 @@ fn make_metadata<'a>(env: &mut JNIEnv<'a>, metadata: Metadata) -> Result<JObject
EntryMode::Unknown => 2,
};

let last_modified = metadata.last_modified().map_or_else(
|| Ok::<JObject<'_>, Error>(JObject::null()),
|v| {
Ok(env.new_object(
"java/util/Date",
"(J)V",
&[JValue::Long(v.timestamp_millis())],
)?)
},
)?;
let metakey = metadata.metakey();

let contains_metakey = |k| metakey.contains(k) || metakey.contains(Metakey::Complete);

let last_modified = if contains_metakey(Metakey::LastModified) {
metadata.last_modified().map_or_else(
|| Ok::<JObject<'_>, Error>(JObject::null()),
|v| {
Ok(env.new_object(
"java/util/Date",
"(J)V",
&[JValue::Long(v.timestamp_millis())],
)?)
},
)?
} else {
JObject::null()
};

let cache_control = string_to_jstring(env, metadata.cache_control())?;
let content_disposition = string_to_jstring(env, metadata.content_disposition())?;
let content_md5 = string_to_jstring(env, metadata.content_md5())?;
let content_type = string_to_jstring(env, metadata.content_type())?;
let etag = string_to_jstring(env, metadata.etag())?;
let version = string_to_jstring(env, metadata.version())?;
let cache_control = if contains_metakey(Metakey::CacheControl) {
string_to_jstring(env, metadata.cache_control())?
} else {
JObject::null()
};
let content_disposition = if contains_metakey(Metakey::ContentDisposition) {
string_to_jstring(env, metadata.content_disposition())?
} else {
JObject::null()
};
let content_md5 = if contains_metakey(Metakey::ContentMd5) {
string_to_jstring(env, metadata.content_md5())?
} else {
JObject::null()
};
let content_type = if contains_metakey(Metakey::ContentType) {
string_to_jstring(env, metadata.content_type())?
} else {
JObject::null()
};
let etag = if contains_metakey(Metakey::Etag) {
string_to_jstring(env, metadata.etag())?
} else {
JObject::null()
};
let version = if contains_metakey(Metakey::Version) {
string_to_jstring(env, metadata.version())?
} else {
JObject::null()
};
let content_length = if contains_metakey(Metakey::ContentLength) {
metadata.content_length() as jlong
} else {
-1
};

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/util/Date;Ljava/lang/String;)V",
&[
JValue::Int(mode as jint),
JValue::Long(metadata.content_length() as jlong),
JValue::Long(content_length),
JValue::Object(&content_disposition),
JValue::Object(&content_md5),
JValue::Object(&content_type),
Expand All @@ -270,6 +309,17 @@ 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>> {
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",
&[JValue::Object(&path), JValue::Object(&metadata)],
)?)
}

fn string_to_jstring<'a>(env: &mut JNIEnv<'a>, s: Option<&str>) -> Result<JObject<'a>> {
s.map_or_else(
|| Ok(JObject::null()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
package org.apache.opendal;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
Expand Down Expand Up @@ -92,6 +94,14 @@ public void rename(String sourcePath, String targetPath) {
rename(nativeHandle, sourcePath, targetPath);
}

public void removeAll(String path) {
removeAll(nativeHandle, path);
}

public List<Entry> list(String path) {
return Arrays.asList(list(nativeHandle, path));
}

@Override
protected native void disposeInternal(long handle);

Expand All @@ -110,4 +120,8 @@ public void rename(String sourcePath, String targetPath) {
private static native long copy(long nativeHandle, String sourcePath, String targetPath);

private static native long rename(long nativeHandle, String sourcePath, String targetPath);

private static native void removeAll(long nativeHandle, String path);

private static native Entry[] list(long nativeHandle, String path);
}
33 changes: 33 additions & 0 deletions bindings/java/src/main/java/org/apache/opendal/Entry.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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 lombok.Data;

@Data
public class Entry {
public final String path;
public final Metadata metadata;

public Entry(String path, Metadata metadata) {
this.path = path;
this.metadata = metadata;
}
}
58 changes: 55 additions & 3 deletions bindings/java/src/main/java/org/apache/opendal/Metadata.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,60 @@
*/
@Data
public class Metadata {
/**
* Mode of the entry.
*/
public final EntryMode mode;
/**
* Content Length of the entry.
*
* Note: For now, this value is only available when calling on result of `stat`, otherwise it will be -1.
*/
public final long contentLength;
/**
* Content-Disposition of the entry.
*
* Note: For now, this value is only available when calling on result of `stat`, otherwise it will be null.
*/
public final String contentDisposition;
/**
* Content MD5 of the entry.
*
* Note: For now, this value is only available when calling on result of `stat`, otherwise it will be null.
*/
public final String contentMd5;
/**
* Content Type of the entry.
*
* Note: For now, this value is only available when calling on result of `stat`, otherwise it will be null.
*/
public final String contentType;
/**
* Cache Control of the entry.
*
* Note: For now, this value is only available when calling on result of `stat`, otherwise it will be null.
*/
public final String cacheControl;
/**
* Etag of the entry.
*
* Note: For now, this value is only available when calling on result of `stat`, otherwise it will be null.
*/
public final String etag;
/**
* Last Modified of the entry.
*
* Note: For now, this value is only available when calling on result of `stat`, otherwise it will be null.
*/
public final Date lastModified;
/**
* Version of the entry.
* Version is a string that can be used to identify the version of this entry.
* This field may come out from the version control system, like object
* versioning in AWS S3.
*
* Note: For now, this value is only available when calling on result of `stat`, otherwise it will be null.
*/
public final String version;

public Metadata(
Expand Down Expand Up @@ -67,11 +113,17 @@ public boolean isDir() {
}

public enum EntryMode {
/// FILE means the path has data to read.
/**
* FILE means the path has data to read.
*/
FILE,
/// DIR means the path can be listed.
/**
* DIR means the path can be listed.
*/
DIR,
/// Unknown means we don't know what we can do on this path.
/**
* Unknown means we don't know what we can do on this path.
*/
UNKNOWN;

public static EntryMode of(int mode) {
Expand Down
18 changes: 18 additions & 0 deletions bindings/java/src/main/java/org/apache/opendal/Operator.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@

import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
Expand Down Expand Up @@ -205,6 +208,17 @@ public CompletableFuture<Void> rename(String sourcePath, String targetPath) {
return AsyncRegistry.take(requestId);
}

public CompletableFuture<Void> removeAll(String path) {
final long requestId = removeAll(nativeHandle, path);
return AsyncRegistry.take(requestId);
}

public CompletableFuture<List<Entry>> list(String path) {
final long requestid = list(nativeHandle, path);
final CompletableFuture<Entry[]> result = AsyncRegistry.take(requestid);
return Objects.requireNonNull(result).thenApplyAsync(Arrays::asList);
}

@Override
protected native void disposeInternal(long handle);

Expand Down Expand Up @@ -237,4 +251,8 @@ public CompletableFuture<Void> rename(String sourcePath, String targetPath) {
private static native long copy(long nativeHandle, String sourcePath, String targetPath);

private static native long rename(long nativeHandle, String sourcePath, String targetPath);

private static native long removeAll(long nativeHandle, String path);

private static native long list(long nativeHandle, String path);
}
Loading