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());
+        }
+    }
 }

Reply via email to