Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions bindings/java/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ opendal = { version = ">=0", path = "../../core", features = [
"blocking",
] }
tokio = { version = "1.28.1", features = ["full"] }
chrono = "0.4"

# This is not optimal. See also the Cargo issue:
# https://github.com/rust-lang/cargo/issues/1197#issuecomment-1641086954
Expand Down
9 changes: 6 additions & 3 deletions bindings/java/src/async_operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ use crate::make_metadata;
use crate::make_operator_info;
use crate::make_presigned_request;
use crate::Result;
use crate::{make_entry, make_list_options, make_write_options};
use crate::{make_entry, make_list_options, make_stat_options, make_write_options};

#[no_mangle]
pub extern "system" fn Java_org_apache_opendal_AsyncOperator_constructor(
Expand Down Expand Up @@ -147,8 +147,9 @@ pub unsafe extern "system" fn Java_org_apache_opendal_AsyncOperator_stat(
op: *mut Operator,
executor: *const Executor,
path: JString,
stat_options: JObject,
) -> jlong {
intern_stat(&mut env, op, executor, path).unwrap_or_else(|e| {
intern_stat(&mut env, op, executor, path, stat_options).unwrap_or_else(|e| {
e.throw(&mut env);
0
})
Expand All @@ -159,14 +160,16 @@ fn intern_stat(
op: *mut Operator,
executor: *const Executor,
path: JString,
options: JObject,
) -> Result<jlong> {
let op = unsafe { &mut *op };
let id = request_id(env)?;

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(&path).await.map_err(Into::into);
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))
Expand Down
29 changes: 29 additions & 0 deletions bindings/java/src/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
// under the License.

use crate::Result;
use chrono::{DateTime, Utc};
use jni::objects::JObject;
use jni::objects::JString;
use jni::objects::{JByteArray, JMap};
Expand Down Expand Up @@ -122,6 +123,34 @@ pub(crate) fn read_jlong_field_to_usize(
}
}

pub(crate) fn read_instant_field_to_date_time(
env: &mut JNIEnv<'_>,
obj: &JObject,
field: &str,
) -> Result<Option<DateTime<Utc>>> {
let result = env.get_field(obj, field, "Ljava/time/Instant;")?.l()?;
if result.is_null() {
return Ok(None);
}

let epoch_second = env
.call_method(&result, "getEpochSecond", "()J", &[])?
.j()?;
let nano = env.call_method(&result, "getNano", "()I", &[])?.i()?;
DateTime::from_timestamp(epoch_second, nano as u32)
.map(Some)
.ok_or_else(|| {
Error::new(
ErrorKind::Unexpected,
format!(
"Invalid timestamp: seconds={}, nanos={}",
epoch_second, nano
),
)
.into()
})
}

pub(crate) fn offset_length_to_range(offset: i64, length: i64) -> Result<(Bound<u64>, Bound<u64>)> {
let offset = u64::try_from(offset)
.map_err(|_| Error::new(ErrorKind::RangeNotSatisfied, "offset must be non-negative"))?;
Expand Down
30 changes: 29 additions & 1 deletion bindings/java/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,14 @@ fn make_operator_info<'a>(env: &mut JNIEnv<'a>, info: OperatorInfo) -> Result<JO
fn make_capability<'a>(env: &mut JNIEnv<'a>, cap: Capability) -> Result<JObject<'a>> {
let capability = env.new_object(
"org/apache/opendal/Capability",
"(ZZZZZZZZZZZZZZZZZZZJJZZZZZZZZZZZZZZZ)V",
"(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),
Expand Down Expand Up @@ -246,3 +249,28 @@ fn make_list_options<'a>(
deleted: convert::read_bool_field(env, options, "deleted").unwrap_or_default(),
})
}

fn make_stat_options(env: &mut JNIEnv, 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")?,
if_modified_since: convert::read_instant_field_to_date_time(
env,
options,
"ifModifiedSince",
)?,
if_unmodified_since: convert::read_instant_field_to_date_time(
env,
options,
"ifUnmodifiedSince",
)?,
version: convert::read_string_field(env, options, "version")?,
override_content_type: convert::read_string_field(env, options, "overrideContentType")?,
override_cache_control: convert::read_string_field(env, options, "overrideCacheControl")?,
override_content_disposition: convert::read_string_field(
env,
options,
"overrideContentDisposition",
)?,
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,11 @@ public CompletableFuture<Void> write(String path, byte[] content, WriteOptions o
}

public CompletableFuture<Metadata> stat(String path) {
final long requestId = stat(nativeHandle, executorHandle, path);
return stat(path, StatOptions.builder().build());
}

public CompletableFuture<Metadata> stat(String path, StatOptions options) {
final long requestId = stat(nativeHandle, executorHandle, path, options);
return AsyncRegistry.take(requestId);
}

Expand Down Expand Up @@ -311,7 +315,7 @@ private static native long write(

private static native long delete(long nativeHandle, long executorHandle, String path);

private static native long stat(long nativeHandle, long executorHandle, String path);
private static native long stat(long nativeHandle, long executorHandle, String path, StatOptions options);

private static native long presignRead(long nativeHandle, long executorHandle, String path, long duration);

Expand Down
21 changes: 21 additions & 0 deletions bindings/java/src/main/java/org/apache/opendal/Capability.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,21 @@ public class Capability {
*/
public final boolean statWithIfNoneMatch;

/**
* If operator supports stat with if modified since.
*/
public final boolean statWithIfModifiedSince;

/**
* If operator supports stat with if unmodified since.
*/
public final boolean statWithIfUnmodifiedSince;

/**
* If operator supports stat with versions.
*/
public final boolean statWithVersion;

/**
* If operator supports read.
*/
Expand Down Expand Up @@ -211,6 +226,9 @@ public Capability(
boolean stat,
boolean statWithIfMatch,
boolean statWithIfNoneMatch,
boolean statWithIfModifiedSince,
boolean statWithIfUnmodifiedSince,
boolean statWithVersion,
boolean read,
boolean readWithIfMatch,
boolean readWithIfNoneMatch,
Expand Down Expand Up @@ -247,6 +265,9 @@ public Capability(
this.stat = stat;
this.statWithIfMatch = statWithIfMatch;
this.statWithIfNoneMatch = statWithIfNoneMatch;
this.statWithIfModifiedSince = statWithIfModifiedSince;
this.statWithIfUnmodifiedSince = statWithIfUnmodifiedSince;
this.statWithVersion = statWithVersion;
this.read = read;
this.readWithIfMatch = readWithIfMatch;
this.readWithIfNoneMatch = readWithIfNoneMatch;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,11 @@ public void delete(String path) {
}

public Metadata stat(String path) {
return stat(nativeHandle, path);
return stat(nativeHandle, path, StatOptions.builder().build());
}

public Metadata stat(String path, StatOptions options) {
return stat(nativeHandle, path, options);
}

public void createDir(String path) {
Expand Down Expand Up @@ -154,7 +158,7 @@ public List<Entry> list(String path, ListOptions options) {

private static native void delete(long op, String path);

private static native Metadata stat(long op, String path);
private static native Metadata stat(long op, String path, StatOptions options);

private static native long createDir(long op, String path);

Expand Down
77 changes: 77 additions & 0 deletions bindings/java/src/main/java/org/apache/opendal/StatOptions.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* 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 java.time.Instant;
import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class StatOptions {

/**
* Sets if-match condition for this operation.
* If file exists and its etag doesn't match, an error will be returned.
*/
private String ifMatch;

/**
* Sets if-none-match condition for this operation.
* If file exists and its etag matches, an error will be returned.
*/
private String ifNoneMatch;

/**
* Sets if-modified-since condition for this operation.
* If file exists and hasn't been modified since the specified time, an error will be returned.
*/
private Instant ifModifiedSince;

/**
* Sets if-unmodified-since condition for this operation.
* If file exists and has been modified since the specified time, an error will be returned.
*/
private Instant ifUnmodifiedSince;

/**
* Sets version for this operation.
* Retrieves data of a specified version of the given path.
*/
private String version;

/**
* Specifies the content-type header for presigned operations.
* Only meaningful when used along with presign.
*/
private String overrideContentType;

/**
* Specifies the cache-control header for presigned operations.
* Only meaningful when used along with presign.
*/
private String overrideCacheControl;

/**
* Specifies the content-disposition header for presigned operations.
* Only meaningful when used along with presign.
*/
private String overrideContentDisposition;
}
15 changes: 11 additions & 4 deletions bindings/java/src/operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use crate::convert::{
};
use crate::make_metadata;
use crate::Result;
use crate::{make_entry, make_list_options, make_write_options};
use crate::{make_entry, make_list_options, make_stat_options, make_write_options};

/// # Safety
///
Expand Down Expand Up @@ -141,16 +141,23 @@ pub unsafe extern "system" fn Java_org_apache_opendal_Operator_stat(
_: JClass,
op: *mut blocking::Operator,
path: JString,
stat_options: JObject,
) -> jobject {
intern_stat(&mut env, &mut *op, path).unwrap_or_else(|e| {
intern_stat(&mut env, &mut *op, path, stat_options).unwrap_or_else(|e| {
e.throw(&mut env);
JObject::default().into_raw()
})
}

fn intern_stat(env: &mut JNIEnv, op: &mut blocking::Operator, path: JString) -> Result<jobject> {
fn intern_stat(
env: &mut JNIEnv,
op: &mut blocking::Operator,
path: JString,
options: JObject,
) -> Result<jobject> {
let path = jstring_to_string(env, &path)?;
let metadata = op.stat(&path)?;
let stat_opts = make_stat_options(env, &options)?;
let metadata = op.stat_options(&path, stat_opts)?;
Ok(make_metadata(env, metadata)?.into_raw())
}

Expand Down
Loading
Loading