diff --git a/bindings/java/src/layer.rs b/bindings/java/src/layer.rs
index 7e3ba35fd17..acad24f02c1 100644
--- a/bindings/java/src/layer.rs
+++ b/bindings/java/src/layer.rs
@@ -22,6 +22,7 @@ use jni::sys::jboolean;
use jni::sys::jfloat;
use jni::sys::jlong;
use jni::JNIEnv;
+use opendal::layers::ConcurrentLimitLayer;
use opendal::layers::RetryLayer;
use opendal::Operator;
@@ -49,3 +50,15 @@ pub extern "system" fn Java_org_apache_opendal_layer_RetryLayer_doLayer(
}
Box::into_raw(Box::new(op.clone().layer(retry))) as jlong
}
+
+#[no_mangle]
+pub extern "system" fn Java_org_apache_opendal_layer_ConcurrentLimitLayer_doLayer(
+ _: JNIEnv,
+ _: JClass,
+ op: *mut Operator,
+ permits: jlong,
+) -> jlong {
+ let op = unsafe { &*op };
+ let concurrent_limit = ConcurrentLimitLayer::new(permits as usize);
+ Box::into_raw(Box::new(op.clone().layer(concurrent_limit))) as jlong
+}
diff --git a/bindings/java/src/main/java/org/apache/opendal/layer/ConcurrentLimitLayer.java b/bindings/java/src/main/java/org/apache/opendal/layer/ConcurrentLimitLayer.java
new file mode 100644
index 00000000000..8ec4988feb0
--- /dev/null
+++ b/bindings/java/src/main/java/org/apache/opendal/layer/ConcurrentLimitLayer.java
@@ -0,0 +1,52 @@
+/*
+ * 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;
+
+/**
+ * Users can control how many concurrent connections could be established between
+ * OpenDAL and underlying storage services.
+ *
+ * @see ConcurrentLimitLayer's rustdoc
+ */
+public class ConcurrentLimitLayer extends Layer {
+ private final long permits;
+
+ /**
+ * Create a new ConcurrentLimitLayer will specify permits.
+ *
+ * @param permits concurrent connections could be established
+ */
+ public ConcurrentLimitLayer(long permits) {
+ if (permits <= 0) {
+ throw new IllegalArgumentException("permits must be positive");
+ }
+
+ this.permits = permits;
+ }
+
+ @Override
+ protected long layer(long nativeOp) {
+ return doLayer(nativeOp, permits);
+ }
+
+ private static native long doLayer(long nativeHandle, long permits);
+}
diff --git a/bindings/java/src/main/java/org/apache/opendal/layer/RetryLayer.java b/bindings/java/src/main/java/org/apache/opendal/layer/RetryLayer.java
index 86c3bc66ee1..100a84fb6da 100644
--- a/bindings/java/src/main/java/org/apache/opendal/layer/RetryLayer.java
+++ b/bindings/java/src/main/java/org/apache/opendal/layer/RetryLayer.java
@@ -23,6 +23,12 @@
import lombok.Builder;
import org.apache.opendal.Layer;
+/**
+ * This layer will retry failed operations when {@code Error::is_temporary} returns {@code true}.
+ * If operation still failed, this layer will set error to Persistent which means error has been retried.
+ *
+ * @see RetryLayer's rustdoc
+ */
@Builder
public class RetryLayer 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 0300bb14199..ed2ab4a1d26 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,6 +25,7 @@
import lombok.Cleanup;
import org.apache.opendal.AsyncOperator;
import org.apache.opendal.Layer;
+import org.apache.opendal.layer.ConcurrentLimitLayer;
import org.apache.opendal.layer.RetryLayer;
import org.junit.jupiter.api.Test;
@@ -38,4 +39,14 @@ void testOperatorWithRetryLayer() {
@Cleanup final AsyncOperator layeredOp = op.layer(retryLayer);
assertThat(layeredOp.info).isNotNull();
}
+
+ @Test
+ void testOperatorWithConcurrentLimitLayer() {
+ final Map conf = new HashMap<>();
+ conf.put("root", "/opendal/");
+ final Layer concurrentLimitLayer = new ConcurrentLimitLayer(1024);
+ @Cleanup final AsyncOperator op = AsyncOperator.of("memory", conf);
+ @Cleanup final AsyncOperator layeredOp = op.layer(concurrentLimitLayer);
+ assertThat(layeredOp.info).isNotNull();
+ }
}