From 9b85d17a32ac192193fe7bf4830179faac2748f7 Mon Sep 17 00:00:00 2001 From: Retros <371373446@qq.com> Date: Fri, 2 Jan 2026 13:31:44 +0800 Subject: [PATCH 1/6] [java] Add additional layers support This commit adds support for additional layers to the Java bindings: - MimeGuessLayer: automatically set Content-Type based on file extension - TimeoutLayer: add timeout for operations - LoggingLayer: add structured logging for operations - HotpathLayer: add hotpath profiling for operations These layers were already supported in Python and Node.js bindings but were missing in Java, causing API inconsistency across language bindings. Closes #6738 --- bindings/java/Cargo.toml | 11 +++ bindings/java/src/layer.rs | 66 +++++++++++++++ .../apache/opendal/layer/HotpathLayer.java | 43 ++++++++++ .../apache/opendal/layer/LoggingLayer.java | 43 ++++++++++ .../apache/opendal/layer/MimeGuessLayer.java | 43 ++++++++++ .../apache/opendal/layer/ThrottleLayer.java | 58 +++++++++++++ .../apache/opendal/layer/TimeoutLayer.java | 82 +++++++++++++++++++ .../org/apache/opendal/test/LayerTest.java | 55 +++++++++++++ 8 files changed, 401 insertions(+) create mode 100644 bindings/java/src/main/java/org/apache/opendal/layer/HotpathLayer.java create mode 100644 bindings/java/src/main/java/org/apache/opendal/layer/LoggingLayer.java create mode 100644 bindings/java/src/main/java/org/apache/opendal/layer/MimeGuessLayer.java create mode 100644 bindings/java/src/main/java/org/apache/opendal/layer/ThrottleLayer.java create mode 100644 bindings/java/src/main/java/org/apache/opendal/layer/TimeoutLayer.java diff --git a/bindings/java/Cargo.toml b/bindings/java/Cargo.toml index a671f6c6136f..cfb0315e83d8 100644 --- a/bindings/java/Cargo.toml +++ b/bindings/java/Cargo.toml @@ -46,6 +46,11 @@ default = [ "services-s3", "services-webdav", "services-webhdfs", + "layers-logging", + "layers-mime-guess", + "layers-throttle", + "layers-timeout", + "layers-hotpath", ] services-all = [ @@ -154,6 +159,12 @@ services-webdav = ["opendal/services-webdav"] services-webhdfs = ["opendal/services-webhdfs"] services-yandex-disk = ["opendal/services-yandex-disk"] +layers-logging = ["opendal/layers-logging"] +layers-mime-guess = ["opendal/layers-mime-guess"] +layers-throttle = ["opendal/layers-throttle"] +layers-timeout = ["opendal/layers-timeout"] +layers-hotpath = ["opendal/layers-hotpath"] + [dependencies] anyhow = { version = "1.0.100" } jni = { version = "0.21.1" } diff --git a/bindings/java/src/layer.rs b/bindings/java/src/layer.rs index c649bfa0286b..1956235f8c7c 100644 --- a/bindings/java/src/layer.rs +++ b/bindings/java/src/layer.rs @@ -24,7 +24,12 @@ use jni::sys::jfloat; use jni::sys::jlong; use opendal::Operator; use opendal::layers::ConcurrentLimitLayer; +use opendal::layers::HotpathLayer; +use opendal::layers::LoggingLayer; +use opendal::layers::MimeGuessLayer; use opendal::layers::RetryLayer; +use opendal::layers::ThrottleLayer; +use opendal::layers::TimeoutLayer; #[unsafe(no_mangle)] pub extern "system" fn Java_org_apache_opendal_layer_RetryLayer_doLayer( @@ -62,3 +67,64 @@ pub extern "system" fn Java_org_apache_opendal_layer_ConcurrentLimitLayer_doLaye let concurrent_limit = ConcurrentLimitLayer::new(permits as usize); Box::into_raw(Box::new(op.clone().layer(concurrent_limit))) as jlong } + +#[unsafe(no_mangle)] +pub extern "system" fn Java_org_apache_opendal_layer_MimeGuessLayer_doLayer( + _: JNIEnv, + _: JClass, + op: *mut Operator, +) -> jlong { + let op = unsafe { &*op }; + let mime_guess = MimeGuessLayer::new(); + Box::into_raw(Box::new(op.clone().layer(mime_guess))) as jlong +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_org_apache_opendal_layer_TimeoutLayer_doLayer( + _: JNIEnv, + _: JClass, + op: *mut Operator, + timeout_nanos: jlong, + io_timeout_nanos: jlong, +) -> jlong { + let op = unsafe { &*op }; + let timeout = TimeoutLayer::new() + .with_timeout(Duration::from_nanos(timeout_nanos as u64)) + .with_io_timeout(Duration::from_nanos(io_timeout_nanos as u64)); + Box::into_raw(Box::new(op.clone().layer(timeout))) as jlong +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_org_apache_opendal_layer_LoggingLayer_doLayer( + _: JNIEnv, + _: JClass, + op: *mut Operator, +) -> jlong { + let op = unsafe { &*op }; + let logging = LoggingLayer::default(); + Box::into_raw(Box::new(op.clone().layer(logging))) as jlong +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_org_apache_opendal_layer_ThrottleLayer_doLayer( + _: JNIEnv, + _: JClass, + op: *mut Operator, + bandwidth: jlong, + burst: jlong, +) -> jlong { + let op = unsafe { &*op }; + let throttle = ThrottleLayer::new(bandwidth as u32, burst as u32); + Box::into_raw(Box::new(op.clone().layer(throttle))) as jlong +} + +#[unsafe(no_mangle)] +pub extern "system" fn Java_org_apache_opendal_layer_HotpathLayer_doLayer( + _: JNIEnv, + _: JClass, + op: *mut Operator, +) -> jlong { + let op = unsafe { &*op }; + let hotpath = HotpathLayer::new(); + Box::into_raw(Box::new(op.clone().layer(hotpath))) as jlong +} diff --git a/bindings/java/src/main/java/org/apache/opendal/layer/HotpathLayer.java b/bindings/java/src/main/java/org/apache/opendal/layer/HotpathLayer.java new file mode 100644 index 000000000000..80024cb10d4d --- /dev/null +++ b/bindings/java/src/main/java/org/apache/opendal/layer/HotpathLayer.java @@ -0,0 +1,43 @@ +/* + * 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.layer; + +import org.apache.opendal.Layer; + +/** + * This layer adds hotpath profiling for every operation. + * + * @see HotpathLayer's rustdoc + */ +public class HotpathLayer extends Layer { + + /** + * Create a new HotpathLayer. + */ + public HotpathLayer() { + } + + @Override + protected long layer(long nativeOp) { + return doLayer(nativeOp); + } + + private static native long doLayer(long nativeHandle); +} diff --git a/bindings/java/src/main/java/org/apache/opendal/layer/LoggingLayer.java b/bindings/java/src/main/java/org/apache/opendal/layer/LoggingLayer.java new file mode 100644 index 000000000000..ceb5361c5e87 --- /dev/null +++ b/bindings/java/src/main/java/org/apache/opendal/layer/LoggingLayer.java @@ -0,0 +1,43 @@ +/* + * 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.layer; + +import org.apache.opendal.Layer; + +/** + * This layer adds structured logging for every operation. + * + * @see LoggingLayer's rustdoc + */ +public class LoggingLayer extends Layer { + + /** + * Create a new LoggingLayer. + */ + public LoggingLayer() { + } + + @Override + protected long layer(long nativeOp) { + return doLayer(nativeOp); + } + + private static native long doLayer(long nativeHandle); +} diff --git a/bindings/java/src/main/java/org/apache/opendal/layer/MimeGuessLayer.java b/bindings/java/src/main/java/org/apache/opendal/layer/MimeGuessLayer.java new file mode 100644 index 000000000000..8779f9151112 --- /dev/null +++ b/bindings/java/src/main/java/org/apache/opendal/layer/MimeGuessLayer.java @@ -0,0 +1,43 @@ +/* + * 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.layer; + +import org.apache.opendal.Layer; + +/** + * This layer will automatically set Content-Type based on the file extension in the path. + * + * @see MimeGuessLayer's rustdoc + */ +public class MimeGuessLayer extends Layer { + + /** + * Create a new MimeGuessLayer. + */ + public MimeGuessLayer() { + } + + @Override + protected long layer(long nativeOp) { + return doLayer(nativeOp); + } + + private static native long doLayer(long nativeHandle); +} diff --git a/bindings/java/src/main/java/org/apache/opendal/layer/ThrottleLayer.java b/bindings/java/src/main/java/org/apache/opendal/layer/ThrottleLayer.java new file mode 100644 index 000000000000..3f0521df5f44 --- /dev/null +++ b/bindings/java/src/main/java/org/apache/opendal/layer/ThrottleLayer.java @@ -0,0 +1,58 @@ +/* + * 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.layer; + +import org.apache.opendal.Layer; + +/** + * This layer adds a bandwidth rate limiter to the underlying services. + * + * @see ThrottleLayer's rustdoc + */ +public class ThrottleLayer extends Layer { + + private final long bandwidth; + + private final long burst; + + /** + * Create a new ThrottleLayer with given bandwidth and burst. + * + * @param bandwidth the maximum number of bytes allowed to pass through per second + * @param burst the maximum number of bytes allowed to pass through at once + */ + public ThrottleLayer(long bandwidth, long burst) { + if (bandwidth <= 0) { + throw new IllegalArgumentException("bandwidth must be positive"); + } + if (burst <= 0) { + throw new IllegalArgumentException("burst must be positive"); + } + this.bandwidth = bandwidth; + this.burst = burst; + } + + @Override + protected long layer(long nativeOp) { + return doLayer(nativeOp, bandwidth, burst); + } + + private static native long doLayer(long nativeHandle, long bandwidth, long burst); +} diff --git a/bindings/java/src/main/java/org/apache/opendal/layer/TimeoutLayer.java b/bindings/java/src/main/java/org/apache/opendal/layer/TimeoutLayer.java new file mode 100644 index 000000000000..540c56f8cc2e --- /dev/null +++ b/bindings/java/src/main/java/org/apache/opendal/layer/TimeoutLayer.java @@ -0,0 +1,82 @@ +/* + * 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.layer; + +import java.time.Duration; +import org.apache.opendal.Layer; + +/** + * This layer adds timeout for every operation to avoid slow or unexpected hang operations. + * + * @see TimeoutLayer's rustdoc + */ +public class TimeoutLayer extends Layer { + + private final Duration timeout; + + private final Duration ioTimeout; + + /** + * Create a new TimeoutLayer with default settings. + * Default timeout: 60 seconds for non-IO operations, 10 seconds for IO operations. + */ + public TimeoutLayer() { + this.timeout = Duration.ofSeconds(60); + this.ioTimeout = Duration.ofSeconds(10); + } + + /** + * Create a new TimeoutLayer with custom timeout settings. + * + * @param timeout timeout for non-IO operations (stat, delete, etc.) + * @param ioTimeout timeout for IO operations (read, write, etc.) + */ + public TimeoutLayer(Duration timeout, Duration ioTimeout) { + this.timeout = timeout; + this.ioTimeout = ioTimeout; + } + + /** + * Set timeout for non-IO operations. + * + * @param timeout the timeout duration + * @return this TimeoutLayer for chaining + */ + public TimeoutLayer withTimeout(Duration timeout) { + return new TimeoutLayer(timeout, this.ioTimeout); + } + + /** + * Set timeout for IO operations. + * + * @param ioTimeout the IO timeout duration + * @return this TimeoutLayer for chaining + */ + public TimeoutLayer withIoTimeout(Duration ioTimeout) { + return new TimeoutLayer(this.timeout, ioTimeout); + } + + @Override + protected long layer(long nativeOp) { + return doLayer(nativeOp, timeout.toNanos(), ioTimeout.toNanos()); + } + + private static native long doLayer(long nativeHandle, long timeout, long ioTimeout); +} diff --git a/bindings/java/src/test/java/org/apache/opendal/test/LayerTest.java b/bindings/java/src/test/java/org/apache/opendal/test/LayerTest.java index 98b7ff1a9b6c..0117ef5a020d 100644 --- a/bindings/java/src/test/java/org/apache/opendal/test/LayerTest.java +++ b/bindings/java/src/test/java/org/apache/opendal/test/LayerTest.java @@ -25,7 +25,12 @@ import org.apache.opendal.Layer; import org.apache.opendal.ServiceConfig; import org.apache.opendal.layer.ConcurrentLimitLayer; +import org.apache.opendal.layer.HotpathLayer; +import org.apache.opendal.layer.LoggingLayer; +import org.apache.opendal.layer.MimeGuessLayer; import org.apache.opendal.layer.RetryLayer; +import org.apache.opendal.layer.ThrottleLayer; +import org.apache.opendal.layer.TimeoutLayer; import org.junit.jupiter.api.Test; public class LayerTest { @@ -48,4 +53,54 @@ void testOperatorWithConcurrentLimitLayer() { @Cleanup final AsyncOperator layeredOp = op.layer(concurrentLimitLayer); assertThat(layeredOp.info).isNotNull(); } + + @Test + void testOperatorWithMimeGuessLayer() { + final ServiceConfig.Memory memory = + ServiceConfig.Memory.builder().root("/opendal/").build(); + final Layer mimeGuessLayer = new MimeGuessLayer(); + @Cleanup final AsyncOperator op = AsyncOperator.of(memory); + @Cleanup final AsyncOperator layeredOp = op.layer(mimeGuessLayer); + assertThat(layeredOp.info).isNotNull(); + } + + @Test + void testOperatorWithTimeoutLayer() { + final ServiceConfig.Memory memory = + ServiceConfig.Memory.builder().root("/opendal/").build(); + final Layer timeoutLayer = new TimeoutLayer(); + @Cleanup final AsyncOperator op = AsyncOperator.of(memory); + @Cleanup final AsyncOperator layeredOp = op.layer(timeoutLayer); + assertThat(layeredOp.info).isNotNull(); + } + + @Test + void testOperatorWithLoggingLayer() { + final ServiceConfig.Memory memory = + ServiceConfig.Memory.builder().root("/opendal/").build(); + final Layer loggingLayer = new LoggingLayer(); + @Cleanup final AsyncOperator op = AsyncOperator.of(memory); + @Cleanup final AsyncOperator layeredOp = op.layer(loggingLayer); + assertThat(layeredOp.info).isNotNull(); + } + + @Test + void testOperatorWithThrottleLayer() { + final ServiceConfig.Memory memory = + ServiceConfig.Memory.builder().root("/opendal/").build(); + final Layer throttleLayer = new ThrottleLayer(1024, 1024); + @Cleanup final AsyncOperator op = AsyncOperator.of(memory); + @Cleanup final AsyncOperator layeredOp = op.layer(throttleLayer); + assertThat(layeredOp.info).isNotNull(); + } + + @Test + void testOperatorWithHotpathLayer() { + final ServiceConfig.Memory memory = + ServiceConfig.Memory.builder().root("/opendal/").build(); + final Layer hotpathLayer = new HotpathLayer(); + @Cleanup final AsyncOperator op = AsyncOperator.of(memory); + @Cleanup final AsyncOperator layeredOp = op.layer(hotpathLayer); + assertThat(layeredOp.info).isNotNull(); + } } From 0079613ca0dbf756f4840569900f5bc92c5d69e2 Mon Sep 17 00:00:00 2001 From: Retros <371373446@qq.com> Date: Fri, 2 Jan 2026 13:38:23 +0800 Subject: [PATCH 2/6] [java] Fix code formatting with spotless This commit fixes Java code formatting issues detected by spotless. --- .trae/documents/plan_20260102_051107.md | 29 +++++++++++++++++++ .../apache/opendal/layer/HotpathLayer.java | 3 +- .../apache/opendal/layer/LoggingLayer.java | 3 +- .../apache/opendal/layer/MimeGuessLayer.java | 3 +- 4 files changed, 32 insertions(+), 6 deletions(-) create mode 100644 .trae/documents/plan_20260102_051107.md diff --git a/.trae/documents/plan_20260102_051107.md b/.trae/documents/plan_20260102_051107.md new file mode 100644 index 000000000000..8ca755fa35ad --- /dev/null +++ b/.trae/documents/plan_20260102_051107.md @@ -0,0 +1,29 @@ +## Java Layers 实现计划 + +### 第一阶段:核心功能层 (4个) +1. **MimeGuessLayer** - 自动根据文件扩展名设置 Content-Type + - 创建 `bindings/java/src/main/java/org/apache/opendal/layer/MimeGuessLayer.java` + - 在 `bindings/java/src/layer.rs` 添加 JNI 实现 + +2. **TimeoutLayer** - 操作超时控制 + - 创建 `bindings/java/src/main/java/org/apache/opendal/layer/TimeoutLayer.java` + - 在 `bindings/java/src/layer.rs` 添加 JNI 实现 + +3. **ThrottleLayer** - 带宽限流 + - 创建 `bindings/java/src/main/java/org/apache/opendal/layer/ThrottleLayer.java` + - 在 `bindings/java/src/layer.rs` 添加 JNI 实现 + +4. **LoggingLayer** - 操作日志记录 + - 创建 `bindings/java/src/main/java/org/apache/opendal/layer/LoggingLayer.java` + - 在 `bindings/java/src/layer.rs` 添加 JNI 实现 + +### 第二阶段:性能优化层 (1个) +5. **HotpathLayer** - 热路径优化 + - 创建 `bindings/java/src/main/java/org/apache/opendal/layer/HotpathLayer.java` + - 在 `bindings/java/src/layer.rs` 添加 JNI 实现 + +### 验证步骤 +- 确保所有 layer 类继承自 `Layer` 抽象类 +- 遵循现有代码的命名和编码规范 +- 参考 Python/Node.js 绑定的实现方式 +- 添加相应的单元测试 \ No newline at end of file diff --git a/bindings/java/src/main/java/org/apache/opendal/layer/HotpathLayer.java b/bindings/java/src/main/java/org/apache/opendal/layer/HotpathLayer.java index 80024cb10d4d..239955f31950 100644 --- a/bindings/java/src/main/java/org/apache/opendal/layer/HotpathLayer.java +++ b/bindings/java/src/main/java/org/apache/opendal/layer/HotpathLayer.java @@ -31,8 +31,7 @@ public class HotpathLayer extends Layer { /** * Create a new HotpathLayer. */ - public HotpathLayer() { - } + public HotpathLayer() {} @Override protected long layer(long nativeOp) { diff --git a/bindings/java/src/main/java/org/apache/opendal/layer/LoggingLayer.java b/bindings/java/src/main/java/org/apache/opendal/layer/LoggingLayer.java index ceb5361c5e87..b1f2daf6beca 100644 --- a/bindings/java/src/main/java/org/apache/opendal/layer/LoggingLayer.java +++ b/bindings/java/src/main/java/org/apache/opendal/layer/LoggingLayer.java @@ -31,8 +31,7 @@ public class LoggingLayer extends Layer { /** * Create a new LoggingLayer. */ - public LoggingLayer() { - } + public LoggingLayer() {} @Override protected long layer(long nativeOp) { diff --git a/bindings/java/src/main/java/org/apache/opendal/layer/MimeGuessLayer.java b/bindings/java/src/main/java/org/apache/opendal/layer/MimeGuessLayer.java index 8779f9151112..61e210fdc1bb 100644 --- a/bindings/java/src/main/java/org/apache/opendal/layer/MimeGuessLayer.java +++ b/bindings/java/src/main/java/org/apache/opendal/layer/MimeGuessLayer.java @@ -31,8 +31,7 @@ public class MimeGuessLayer extends Layer { /** * Create a new MimeGuessLayer. */ - public MimeGuessLayer() { - } + public MimeGuessLayer() {} @Override protected long layer(long nativeOp) { From ce093e5f2926b54a3e4a845e9357be5e6ea92b0d Mon Sep 17 00:00:00 2001 From: Retros <371373446@qq.com> Date: Fri, 2 Jan 2026 13:47:35 +0800 Subject: [PATCH 3/6] chore: remove design doc --- .trae/documents/plan_20260102_051107.md | 29 ------------------------- 1 file changed, 29 deletions(-) delete mode 100644 .trae/documents/plan_20260102_051107.md diff --git a/.trae/documents/plan_20260102_051107.md b/.trae/documents/plan_20260102_051107.md deleted file mode 100644 index 8ca755fa35ad..000000000000 --- a/.trae/documents/plan_20260102_051107.md +++ /dev/null @@ -1,29 +0,0 @@ -## Java Layers 实现计划 - -### 第一阶段:核心功能层 (4个) -1. **MimeGuessLayer** - 自动根据文件扩展名设置 Content-Type - - 创建 `bindings/java/src/main/java/org/apache/opendal/layer/MimeGuessLayer.java` - - 在 `bindings/java/src/layer.rs` 添加 JNI 实现 - -2. **TimeoutLayer** - 操作超时控制 - - 创建 `bindings/java/src/main/java/org/apache/opendal/layer/TimeoutLayer.java` - - 在 `bindings/java/src/layer.rs` 添加 JNI 实现 - -3. **ThrottleLayer** - 带宽限流 - - 创建 `bindings/java/src/main/java/org/apache/opendal/layer/ThrottleLayer.java` - - 在 `bindings/java/src/layer.rs` 添加 JNI 实现 - -4. **LoggingLayer** - 操作日志记录 - - 创建 `bindings/java/src/main/java/org/apache/opendal/layer/LoggingLayer.java` - - 在 `bindings/java/src/layer.rs` 添加 JNI 实现 - -### 第二阶段:性能优化层 (1个) -5. **HotpathLayer** - 热路径优化 - - 创建 `bindings/java/src/main/java/org/apache/opendal/layer/HotpathLayer.java` - - 在 `bindings/java/src/layer.rs` 添加 JNI 实现 - -### 验证步骤 -- 确保所有 layer 类继承自 `Layer` 抽象类 -- 遵循现有代码的命名和编码规范 -- 参考 Python/Node.js 绑定的实现方式 -- 添加相应的单元测试 \ No newline at end of file From 554b6e2a5a3ecde39da3576eb656f9d9659d76ad Mon Sep 17 00:00:00 2001 From: Retros <371373446@qq.com> Date: Fri, 2 Jan 2026 13:50:04 +0800 Subject: [PATCH 4/6] [java] Reorder layers alphabetically for taplo compliance --- bindings/java/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/java/Cargo.toml b/bindings/java/Cargo.toml index cfb0315e83d8..624d9d71ee5f 100644 --- a/bindings/java/Cargo.toml +++ b/bindings/java/Cargo.toml @@ -159,11 +159,11 @@ services-webdav = ["opendal/services-webdav"] services-webhdfs = ["opendal/services-webhdfs"] services-yandex-disk = ["opendal/services-yandex-disk"] +layers-hotpath = ["opendal/layers-hotpath"] layers-logging = ["opendal/layers-logging"] layers-mime-guess = ["opendal/layers-mime-guess"] layers-throttle = ["opendal/layers-throttle"] layers-timeout = ["opendal/layers-timeout"] -layers-hotpath = ["opendal/layers-hotpath"] [dependencies] anyhow = { version = "1.0.100" } From 05fddba94b976d9158c479297ecd7a868ed7446e Mon Sep 17 00:00:00 2001 From: Retros <371373446@qq.com> Date: Fri, 2 Jan 2026 19:41:38 +0800 Subject: [PATCH 5/6] feat: remove hotpath layer and logging layer. --- bindings/java/Cargo.toml | 5 +-- bindings/java/src/layer.rs | 12 ------ .../apache/opendal/layer/HotpathLayer.java | 42 ------------------- .../apache/opendal/layer/LoggingLayer.java | 3 ++ .../org/apache/opendal/test/LayerTest.java | 11 ----- 5 files changed, 4 insertions(+), 69 deletions(-) delete mode 100644 bindings/java/src/main/java/org/apache/opendal/layer/HotpathLayer.java diff --git a/bindings/java/Cargo.toml b/bindings/java/Cargo.toml index 624d9d71ee5f..15a351780e50 100644 --- a/bindings/java/Cargo.toml +++ b/bindings/java/Cargo.toml @@ -46,11 +46,9 @@ default = [ "services-s3", "services-webdav", "services-webhdfs", - "layers-logging", "layers-mime-guess", "layers-throttle", "layers-timeout", - "layers-hotpath", ] services-all = [ @@ -159,9 +157,8 @@ services-webdav = ["opendal/services-webdav"] services-webhdfs = ["opendal/services-webhdfs"] services-yandex-disk = ["opendal/services-yandex-disk"] -layers-hotpath = ["opendal/layers-hotpath"] -layers-logging = ["opendal/layers-logging"] layers-mime-guess = ["opendal/layers-mime-guess"] +layers-logging = ["opendal/layers-logging"] layers-throttle = ["opendal/layers-throttle"] layers-timeout = ["opendal/layers-timeout"] diff --git a/bindings/java/src/layer.rs b/bindings/java/src/layer.rs index 1956235f8c7c..3a751ec9fbcf 100644 --- a/bindings/java/src/layer.rs +++ b/bindings/java/src/layer.rs @@ -24,7 +24,6 @@ use jni::sys::jfloat; use jni::sys::jlong; use opendal::Operator; use opendal::layers::ConcurrentLimitLayer; -use opendal::layers::HotpathLayer; use opendal::layers::LoggingLayer; use opendal::layers::MimeGuessLayer; use opendal::layers::RetryLayer; @@ -117,14 +116,3 @@ pub extern "system" fn Java_org_apache_opendal_layer_ThrottleLayer_doLayer( let throttle = ThrottleLayer::new(bandwidth as u32, burst as u32); Box::into_raw(Box::new(op.clone().layer(throttle))) as jlong } - -#[unsafe(no_mangle)] -pub extern "system" fn Java_org_apache_opendal_layer_HotpathLayer_doLayer( - _: JNIEnv, - _: JClass, - op: *mut Operator, -) -> jlong { - let op = unsafe { &*op }; - let hotpath = HotpathLayer::new(); - Box::into_raw(Box::new(op.clone().layer(hotpath))) as jlong -} diff --git a/bindings/java/src/main/java/org/apache/opendal/layer/HotpathLayer.java b/bindings/java/src/main/java/org/apache/opendal/layer/HotpathLayer.java deleted file mode 100644 index 239955f31950..000000000000 --- a/bindings/java/src/main/java/org/apache/opendal/layer/HotpathLayer.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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.layer; - -import org.apache.opendal.Layer; - -/** - * This layer adds hotpath profiling for every operation. - * - * @see HotpathLayer's rustdoc - */ -public class HotpathLayer extends Layer { - - /** - * Create a new HotpathLayer. - */ - public HotpathLayer() {} - - @Override - protected long layer(long nativeOp) { - return doLayer(nativeOp); - } - - private static native long doLayer(long nativeHandle); -} diff --git a/bindings/java/src/main/java/org/apache/opendal/layer/LoggingLayer.java b/bindings/java/src/main/java/org/apache/opendal/layer/LoggingLayer.java index b1f2daf6beca..202d291d6720 100644 --- a/bindings/java/src/main/java/org/apache/opendal/layer/LoggingLayer.java +++ b/bindings/java/src/main/java/org/apache/opendal/layer/LoggingLayer.java @@ -24,6 +24,9 @@ /** * This layer adds structured logging for every operation. * + *
Note: This layer requires proper logging setup (e.g., log4j2, java.util.logging) to work. + * Without logging configuration, this layer is a no-op. + * * @see LoggingLayer's rustdoc */ public class LoggingLayer extends Layer { diff --git a/bindings/java/src/test/java/org/apache/opendal/test/LayerTest.java b/bindings/java/src/test/java/org/apache/opendal/test/LayerTest.java index 0117ef5a020d..43d630125d11 100644 --- a/bindings/java/src/test/java/org/apache/opendal/test/LayerTest.java +++ b/bindings/java/src/test/java/org/apache/opendal/test/LayerTest.java @@ -25,7 +25,6 @@ import org.apache.opendal.Layer; import org.apache.opendal.ServiceConfig; import org.apache.opendal.layer.ConcurrentLimitLayer; -import org.apache.opendal.layer.HotpathLayer; import org.apache.opendal.layer.LoggingLayer; import org.apache.opendal.layer.MimeGuessLayer; import org.apache.opendal.layer.RetryLayer; @@ -93,14 +92,4 @@ void testOperatorWithThrottleLayer() { @Cleanup final AsyncOperator layeredOp = op.layer(throttleLayer); assertThat(layeredOp.info).isNotNull(); } - - @Test - void testOperatorWithHotpathLayer() { - final ServiceConfig.Memory memory = - ServiceConfig.Memory.builder().root("/opendal/").build(); - final Layer hotpathLayer = new HotpathLayer(); - @Cleanup final AsyncOperator op = AsyncOperator.of(memory); - @Cleanup final AsyncOperator layeredOp = op.layer(hotpathLayer); - assertThat(layeredOp.info).isNotNull(); - } } From 1977a365b7a61e34371fa5a526d80b2903edf7c0 Mon Sep 17 00:00:00 2001 From: Retros <371373446@qq.com> Date: Fri, 2 Jan 2026 19:56:52 +0800 Subject: [PATCH 6/6] chore: fix fmt error. --- bindings/java/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/java/Cargo.toml b/bindings/java/Cargo.toml index 15a351780e50..e9c77ecaf434 100644 --- a/bindings/java/Cargo.toml +++ b/bindings/java/Cargo.toml @@ -157,8 +157,8 @@ services-webdav = ["opendal/services-webdav"] services-webhdfs = ["opendal/services-webhdfs"] services-yandex-disk = ["opendal/services-yandex-disk"] -layers-mime-guess = ["opendal/layers-mime-guess"] layers-logging = ["opendal/layers-logging"] +layers-mime-guess = ["opendal/layers-mime-guess"] layers-throttle = ["opendal/layers-throttle"] layers-timeout = ["opendal/layers-timeout"]