This is an automated email from the ASF dual-hosted git repository.
tison 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 a76040c6e feat: Binding Java's createInputStream and
createOutputStream to support options (#7097)
a76040c6e is described below
commit a76040c6ed986e04b57a5093729f5af3435394f1
Author: Sahil Shadwal <[email protected]>
AuthorDate: Thu Dec 25 13:58:29 2025 +0530
feat: Binding Java's createInputStream and createOutputStream to support
options (#7097)
* feat: Introduce `ReadOptions` and `WriteOptions` for Java binding stream
operations and `Operator.read` method.
* reformatted the code
---
bindings/java/src/lib.rs | 20 +++++++++++
.../src/main/java/org/apache/opendal/Operator.java | 21 ++++++++---
.../org/apache/opendal/OperatorInputStream.java | 6 ++--
.../org/apache/opendal/OperatorOutputStream.java | 10 +++---
bindings/java/src/operator.rs | 20 +++--------
bindings/java/src/operator_input_stream.rs | 17 +++++++--
bindings/java/src/operator_output_stream.rs | 10 ++++--
.../test/OperatorInputOutputStreamTest.java | 42 ++++++++++++++++++++++
8 files changed, 114 insertions(+), 32 deletions(-)
diff --git a/bindings/java/src/lib.rs b/bindings/java/src/lib.rs
index f1a2896da..802966808 100644
--- a/bindings/java/src/lib.rs
+++ b/bindings/java/src/lib.rs
@@ -274,3 +274,23 @@ fn make_stat_options(env: &mut JNIEnv, options: &JObject)
-> Result<opendal::opt
)?,
})
}
+
+fn make_read_options<'a>(
+ env: &mut JNIEnv<'a>,
+ options: &JObject,
+) -> Result<opendal::options::ReadOptions> {
+ let offset = convert::read_int64_field(env, options, "offset")?;
+ let length = convert::read_int64_field(env, options, "length")?;
+
+ Ok(opendal::options::ReadOptions {
+ range: convert::offset_length_to_range(offset, length)?.into(),
+ ..Default::default()
+ })
+}
+
+fn make_reader_options<'a>(
+ _: &mut JNIEnv<'a>,
+ _: &JObject,
+) -> Result<opendal::options::ReaderOptions> {
+ Ok(opendal::options::ReaderOptions::default())
+}
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 b25bb7824..8b4148d3e 100644
--- a/bindings/java/src/main/java/org/apache/opendal/Operator.java
+++ b/bindings/java/src/main/java/org/apache/opendal/Operator.java
@@ -25,7 +25,8 @@ import java.util.List;
import java.util.Map;
/**
- * Operator represents an underneath OpenDAL operator that accesses data
synchronously.
+ * Operator represents an underneath OpenDAL operator that accesses data
+ * synchronously.
*/
public class Operator extends NativeObject {
public final OperatorInfo info;
@@ -45,7 +46,8 @@ public class Operator extends NativeObject {
* Construct an OpenDAL blocking operator:
*
* <p>
- * You can find all possible schemes <a
href="https://docs.rs/opendal/latest/opendal/enum.Scheme.html">here</a>
+ * You can find all possible schemes
+ * <a
href="https://docs.rs/opendal/latest/opendal/enum.Scheme.html">here</a>
* and see what config options each service supports.
*
* @param scheme the name of the underneath service to access data from.
@@ -88,11 +90,16 @@ public class Operator extends NativeObject {
}
public OperatorOutputStream createOutputStream(String path) {
- return new OperatorOutputStream(this, path);
+ return new OperatorOutputStream(this, path,
WriteOptions.builder().build());
}
public OperatorOutputStream createOutputStream(String path, int maxBytes) {
- return new OperatorOutputStream(this, path, maxBytes);
+ return new OperatorOutputStream(
+ this, path, maxBytes, WriteOptions.builder().build());
+ }
+
+ public OperatorOutputStream createOutputStream(String path, WriteOptions
options) {
+ return new OperatorOutputStream(this, path, options);
}
public byte[] read(String path) {
@@ -108,7 +115,11 @@ public class Operator extends NativeObject {
}
public OperatorInputStream createInputStream(String path) {
- return new OperatorInputStream(this, path);
+ return new OperatorInputStream(this, path,
ReadOptions.builder().build());
+ }
+
+ public OperatorInputStream createInputStream(String path, ReadOptions
options) {
+ return new OperatorInputStream(this, path, options);
}
public void delete(String path) {
diff --git
a/bindings/java/src/main/java/org/apache/opendal/OperatorInputStream.java
b/bindings/java/src/main/java/org/apache/opendal/OperatorInputStream.java
index 870b73a03..edf2b5f3a 100644
--- a/bindings/java/src/main/java/org/apache/opendal/OperatorInputStream.java
+++ b/bindings/java/src/main/java/org/apache/opendal/OperatorInputStream.java
@@ -39,9 +39,9 @@ public class OperatorInputStream extends InputStream {
private int offset = 0;
private byte[] bytes = new byte[0];
- public OperatorInputStream(Operator operator, String path) {
+ public OperatorInputStream(Operator operator, String path, ReadOptions
options) {
final long op = operator.nativeHandle;
- this.reader = new Reader(constructReader(op, path));
+ this.reader = new Reader(constructReader(op, path, options));
}
@Override
@@ -99,7 +99,7 @@ public class OperatorInputStream extends InputStream {
reader.close();
}
- private static native long constructReader(long op, String path);
+ private static native long constructReader(long op, String path,
ReadOptions options);
private static native long disposeReader(long reader);
diff --git
a/bindings/java/src/main/java/org/apache/opendal/OperatorOutputStream.java
b/bindings/java/src/main/java/org/apache/opendal/OperatorOutputStream.java
index 6e2a53541..d7eec35e3 100644
--- a/bindings/java/src/main/java/org/apache/opendal/OperatorOutputStream.java
+++ b/bindings/java/src/main/java/org/apache/opendal/OperatorOutputStream.java
@@ -43,13 +43,13 @@ public class OperatorOutputStream extends OutputStream {
private int offset = 0;
- public OperatorOutputStream(Operator operator, String path) {
- this(operator, path, DEFAULT_MAX_BYTES);
+ public OperatorOutputStream(Operator operator, String path, WriteOptions
options) {
+ this(operator, path, DEFAULT_MAX_BYTES, options);
}
- public OperatorOutputStream(Operator operator, String path, int maxBytes) {
+ public OperatorOutputStream(Operator operator, String path, int maxBytes,
WriteOptions options) {
final long op = operator.nativeHandle;
- this.writer = new Writer(constructWriter(op, path));
+ this.writer = new Writer(constructWriter(op, path, options));
this.maxBytes = maxBytes;
this.bytes = new byte[maxBytes];
}
@@ -83,7 +83,7 @@ public class OperatorOutputStream extends OutputStream {
writer.close();
}
- private static native long constructWriter(long op, String path);
+ private static native long constructWriter(long op, String path,
WriteOptions options);
private static native long disposeWriter(long writer);
diff --git a/bindings/java/src/operator.rs b/bindings/java/src/operator.rs
index 08922dfdf..76b5ba5fe 100644
--- a/bindings/java/src/operator.rs
+++ b/bindings/java/src/operator.rs
@@ -26,12 +26,9 @@ use jni::sys::jobject;
use jni::sys::jobjectArray;
use jni::sys::jsize;
use opendal::blocking;
-use opendal::options;
use crate::Result;
-use crate::convert::{
- bytes_to_jbytearray, jstring_to_string, offset_length_to_range,
read_int64_field,
-};
+use crate::convert::{bytes_to_jbytearray, jstring_to_string};
use crate::make_metadata;
use crate::{make_entry, make_list_options, make_stat_options,
make_write_options};
@@ -86,18 +83,11 @@ fn intern_read(
path: JString,
options: JObject,
) -> Result<jbyteArray> {
- let path = jstring_to_string(env, &path)?;
-
- let offset = read_int64_field(env, &options, "offset")?;
- let length = read_int64_field(env, &options, "length")?;
+ use crate::make_read_options;
- let content = op.read_options(
- &path,
- options::ReadOptions {
- range: offset_length_to_range(offset, length)?.into(),
- ..Default::default()
- },
- )?;
+ 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())
diff --git a/bindings/java/src/operator_input_stream.rs
b/bindings/java/src/operator_input_stream.rs
index 917de087d..f79abdb77 100644
--- a/bindings/java/src/operator_input_stream.rs
+++ b/bindings/java/src/operator_input_stream.rs
@@ -36,9 +36,10 @@ pub unsafe extern "system" fn
Java_org_apache_opendal_OperatorInputStream_constr
_: JClass,
op: *mut blocking::Operator,
path: JString,
+ options: JObject,
) -> jlong {
let op_ref = unsafe { &mut *op };
- intern_construct_reader(&mut env, op_ref, path).unwrap_or_else(|e| {
+ intern_construct_reader(&mut env, op_ref, path,
options).unwrap_or_else(|e| {
e.throw(&mut env);
0
})
@@ -48,9 +49,21 @@ fn intern_construct_reader(
env: &mut JNIEnv,
op: &mut blocking::Operator,
path: JString,
+ options: JObject,
) -> crate::Result<jlong> {
+ use crate::convert;
+ use crate::make_reader_options;
+
let path = jstring_to_string(env, &path)?;
- let reader = op.reader(&path)?.into_bytes_iterator(..)?;
+ let reader_options = make_reader_options(env, &options)?;
+
+ let offset = convert::read_int64_field(env, &options, "offset")?;
+ let length = convert::read_int64_field(env, &options, "length")?;
+ let range = convert::offset_length_to_range(offset, length)?;
+
+ let reader = op
+ .reader_options(&path, reader_options)?
+ .into_bytes_iterator(range)?;
Ok(Box::into_raw(Box::new(reader)) as jlong)
}
diff --git a/bindings/java/src/operator_output_stream.rs
b/bindings/java/src/operator_output_stream.rs
index c07488d17..4c3c9e3bb 100644
--- a/bindings/java/src/operator_output_stream.rs
+++ b/bindings/java/src/operator_output_stream.rs
@@ -18,6 +18,7 @@
use jni::JNIEnv;
use jni::objects::JByteArray;
use jni::objects::JClass;
+use jni::objects::JObject;
use jni::objects::JString;
use jni::sys::jlong;
use opendal::blocking;
@@ -33,9 +34,10 @@ pub unsafe extern "system" fn
Java_org_apache_opendal_OperatorOutputStream_const
_: JClass,
op: *mut blocking::Operator,
path: JString,
+ options: JObject,
) -> jlong {
let op_ref = unsafe { &mut *op };
- intern_construct_write(&mut env, op_ref, path).unwrap_or_else(|e| {
+ intern_construct_write(&mut env, op_ref, path, options).unwrap_or_else(|e|
{
e.throw(&mut env);
0
})
@@ -45,9 +47,13 @@ fn intern_construct_write(
env: &mut JNIEnv,
op: &mut blocking::Operator,
path: JString,
+ options: JObject,
) -> crate::Result<jlong> {
+ use crate::make_write_options;
+
let path = jstring_to_string(env, &path)?;
- let writer = op.writer(&path)?;
+ let options = make_write_options(env, &options)?;
+ let writer = op.writer_options(&path, options)?;
Ok(Box::into_raw(Box::new(writer)) as jlong)
}
diff --git
a/bindings/java/src/test/java/org/apache/opendal/test/OperatorInputOutputStreamTest.java
b/bindings/java/src/test/java/org/apache/opendal/test/OperatorInputOutputStreamTest.java
index bfbbd2244..237179963 100644
---
a/bindings/java/src/test/java/org/apache/opendal/test/OperatorInputOutputStreamTest.java
+++
b/bindings/java/src/test/java/org/apache/opendal/test/OperatorInputOutputStreamTest.java
@@ -28,7 +28,9 @@ import java.util.stream.Stream;
import org.apache.opendal.Operator;
import org.apache.opendal.OperatorInputStream;
import org.apache.opendal.OperatorOutputStream;
+import org.apache.opendal.ReadOptions;
import org.apache.opendal.ServiceConfig;
+import org.apache.opendal.WriteOptions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
@@ -62,4 +64,44 @@ public class OperatorInputOutputStreamTest {
}
}
}
+
+ @Test
+ void testCreateInputStreamWithOptions() {
+ final ServiceConfig.Fs fs =
+ ServiceConfig.Fs.builder().root(tempDir.toString()).build();
+ try (final Operator op = Operator.of(fs)) {
+ final String path = "testCreateInputStreamWithOptions.txt";
+ final String content = "0123456789";
+ op.write(path, content);
+
+ try (final OperatorInputStream is = op.createInputStream(
+ path,
ReadOptions.builder().offset(4L).length(5L).build())) {
+ final byte[] buffer = new byte[5];
+ final int read = is.read(buffer, 0, 5);
+ assertThat(read).isEqualTo(5);
+ assertThat(new String(buffer)).isEqualTo("45678");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ @Test
+ void testCreateOutputStreamWithOptions() {
+ final ServiceConfig.Fs fs =
+ ServiceConfig.Fs.builder().root(tempDir.toString()).build();
+ try (final Operator op = Operator.of(fs)) {
+ final String path = "testCreateOutputStreamWithOptions.txt";
+ final String content = "0123456789";
+
+ try (final OperatorOutputStream os = op.createOutputStream(
+ path,
WriteOptions.builder().contentType("text/plain").build())) {
+ os.write(content.getBytes());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ assertThat(op.read(path)).isEqualTo(content.getBytes());
+ }
+ }
}