This is an automated email from the ASF dual-hosted git repository.
xuanwo 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 c866d4bba feat: generate java configs (#5503)
c866d4bba is described below
commit c866d4bbae50f863b22ec2728011e80b633177a8
Author: tison <[email protected]>
AuthorDate: Fri Jan 3 21:37:41 2025 +0800
feat: generate java configs (#5503)
---
bindings/java/src/lib.rs | 4 +-
.../java/org/apache/opendal/AsyncOperator.java | 34 +-
.../src/main/java/org/apache/opendal/Operator.java | 17 +-
.../java/org/apache/opendal/ServiceConfig.java | 3572 ++++++++++++++++++++
.../org/apache/opendal/test/AsyncExecutorTest.java | 9 +-
.../java/org/apache/opendal/test/LayerTest.java | 15 +-
.../java/org/apache/opendal/test/MetadataTest.java | 15 +-
.../org/apache/opendal/test/OperatorInfoTest.java | 15 +-
.../test/OperatorInputOutputStreamTest.java | 9 +-
.../opendal/test/OperatorUtf8DecodeTest.java | 9 +-
dev/Cargo.lock | 1 +
dev/Cargo.toml | 1 +
dev/src/generate/java.j2 | 72 +
dev/src/generate/java.rs | 122 +
dev/src/generate/mod.rs | 2 +
dev/src/generate/parser.rs | 15 +
dev/src/generate/python.j2 | 2 +-
dev/src/generate/python.rs | 21 +-
18 files changed, 3866 insertions(+), 69 deletions(-)
diff --git a/bindings/java/src/lib.rs b/bindings/java/src/lib.rs
index eb317f264..45a0554ed 100644
--- a/bindings/java/src/lib.rs
+++ b/bindings/java/src/lib.rs
@@ -70,7 +70,7 @@ fn make_presigned_request<'a>(env: &mut JNIEnv<'a>, req:
PresignedRequest) -> Re
}
fn make_operator_info<'a>(env: &mut JNIEnv<'a>, info: OperatorInfo) ->
Result<JObject<'a>> {
- let schema = env.new_string(info.scheme().to_string())?;
+ let scheme = env.new_string(info.scheme().to_string())?;
let root = env.new_string(info.root().to_string())?;
let name = env.new_string(info.name().to_string())?;
let full_capability_obj = make_capability(env, info.full_capability())?;
@@ -81,7 +81,7 @@ fn make_operator_info<'a>(env: &mut JNIEnv<'a>, info:
OperatorInfo) -> Result<JO
"org/apache/opendal/OperatorInfo",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/apache/opendal/Capability;Lorg/apache/opendal/Capability;)V",
&[
- JValue::Object(&schema),
+ JValue::Object(&scheme),
JValue::Object(&root),
JValue::Object(&name),
JValue::Object(&full_capability_obj),
diff --git a/bindings/java/src/main/java/org/apache/opendal/AsyncOperator.java
b/bindings/java/src/main/java/org/apache/opendal/AsyncOperator.java
index 06d3fa72a..cd6b67236 100644
--- a/bindings/java/src/main/java/org/apache/opendal/AsyncOperator.java
+++ b/bindings/java/src/main/java/org/apache/opendal/AsyncOperator.java
@@ -106,6 +106,26 @@ public class AsyncOperator extends NativeObject {
private final long executorHandle;
+ /**
+ * Construct an OpenDAL operator.
+ *
+ * @param config the config of the underneath service to access data from.
+ */
+ public static AsyncOperator of(ServiceConfig config) {
+ return of(config, null);
+ }
+
+ /**
+ * Construct an OpenDAL operator.
+ *
+ * @param executor the underneath executor to run async operations; {@code
null} to use a default global executor.
+ */
+ public static AsyncOperator of(ServiceConfig config, AsyncExecutor
executor) {
+ final String scheme = config.scheme();
+ final Map<String, String> map = config.configMap();
+ return of(scheme, map, executor);
+ }
+
/**
* Construct an OpenDAL operator:
*
@@ -113,11 +133,11 @@ public class AsyncOperator extends NativeObject {
* 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 schema the name of the underneath service to access data from.
+ * @param scheme the name of the underneath service to access data from.
* @param map a map of properties to construct the underneath operator.
*/
- public static AsyncOperator of(String schema, Map<String, String> map) {
- return of(schema, map, null);
+ public static AsyncOperator of(String scheme, Map<String, String> map) {
+ return of(scheme, map, null);
}
/**
@@ -127,13 +147,13 @@ public class AsyncOperator extends NativeObject {
* 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 schema the name of the underneath service to access data from.
+ * @param scheme the name of the underneath service to access data from.
* @param map a map of properties to construct the underneath
operator.
* @param executor the underneath executor to run async operations; {@code
null} to use a default global executor.
*/
- public static AsyncOperator of(String schema, Map<String, String> map,
AsyncExecutor executor) {
+ public static AsyncOperator of(String scheme, Map<String, String> map,
AsyncExecutor executor) {
final long executorHandle = executor != null ? executor.nativeHandle :
0;
- final long nativeHandle = constructor(executorHandle, schema, map);
+ final long nativeHandle = constructor(executorHandle, scheme, map);
final OperatorInfo info = makeOperatorInfo(nativeHandle);
return new AsyncOperator(nativeHandle, executorHandle, info);
}
@@ -248,7 +268,7 @@ public class AsyncOperator extends NativeObject {
private static native long duplicate(long nativeHandle);
- private static native long constructor(long executorHandle, String schema,
Map<String, String> map);
+ private static native long constructor(long executorHandle, String scheme,
Map<String, String> map);
private static native long read(long nativeHandle, long executorHandle,
String path);
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 bb08c87a9..be496d2cb 100644
--- a/bindings/java/src/main/java/org/apache/opendal/Operator.java
+++ b/bindings/java/src/main/java/org/apache/opendal/Operator.java
@@ -30,6 +30,17 @@ import java.util.Map;
public class Operator extends NativeObject {
public final OperatorInfo info;
+ /**
+ * Construct an OpenDAL blocking operator.
+ *
+ * @param config the config of the underneath service to access data from.
+ */
+ public static Operator of(ServiceConfig config) {
+ try (final AsyncOperator operator = AsyncOperator.of(config)) {
+ return operator.blocking();
+ }
+ }
+
/**
* Construct an OpenDAL blocking operator:
*
@@ -37,11 +48,11 @@ public class Operator extends NativeObject {
* 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 schema the name of the underneath service to access data from.
+ * @param scheme the name of the underneath service to access data from.
* @param map a map of properties to construct the underneath operator.
*/
- public static Operator of(String schema, Map<String, String> map) {
- try (final AsyncOperator operator = AsyncOperator.of(schema, map)) {
+ public static Operator of(String scheme, Map<String, String> map) {
+ try (final AsyncOperator operator = AsyncOperator.of(scheme, map)) {
return operator.blocking();
}
}
diff --git a/bindings/java/src/main/java/org/apache/opendal/ServiceConfig.java
b/bindings/java/src/main/java/org/apache/opendal/ServiceConfig.java
new file mode 100644
index 000000000..0c56d9acc
--- /dev/null
+++ b/bindings/java/src/main/java/org/apache/opendal/ServiceConfig.java
@@ -0,0 +1,3572 @@
+/*
+ * 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.
+ */
+
+// DO NOT EDIT IT MANUALLY. This file is generated by
opendal/dev/generate/java.rs.
+
+package org.apache.opendal;
+
+import java.time.Duration;
+import java.util.HashMap;
+import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * Service configurations that are mapped from
+ * <a
href="https://docs.rs/opendal/latest/opendal/services/index.html">OpenDAL's
services</a>.
+ */
+@SuppressWarnings("unused") // intended to be used by users
+public interface ServiceConfig {
+ String scheme();
+
+ HashMap<String, String> configMap();
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class AliyunDrive implements ServiceConfig {
+ /**
+ * The drive_type of this backend.
+ * <p>
+ * All operations will happen under this type of drive.
+ * <p>
+ * Available values are `default`, `backup` and `resource`.
+ * <p>
+ * Fallback to default if not set or no other drives can be found.
+ */
+ private final @NonNull String driveType;
+ /**
+ * The Root of this backend.
+ * <p>
+ * All operations will happen under this root.
+ * <p>
+ * Default to `/` if not set.
+ */
+ private final String root;
+ /**
+ * The access_token of this backend.
+ * <p>
+ * Solution for client-only purpose. #4733
+ * <p>
+ * Required if no client_id, client_secret and refresh_token are
provided.
+ */
+ private final String accessToken;
+ /**
+ * The client_id of this backend.
+ * <p>
+ * Required if no access_token is provided.
+ */
+ private final String clientId;
+ /**
+ * The client_secret of this backend.
+ * <p>
+ * Required if no access_token is provided.
+ */
+ private final String clientSecret;
+ /**
+ * The refresh_token of this backend.
+ * <p>
+ * Required if no access_token is provided.
+ */
+ private final String refreshToken;
+
+ @Override
+ public String scheme() {
+ return "aliyun_drive";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ map.put("drive_type", driveType);
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (accessToken != null) {
+ map.put("access_token", accessToken);
+ }
+ if (clientId != null) {
+ map.put("client_id", clientId);
+ }
+ if (clientSecret != null) {
+ map.put("client_secret", clientSecret);
+ }
+ if (refreshToken != null) {
+ map.put("refresh_token", refreshToken);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Alluxio implements ServiceConfig {
+ /**
+ * root of this backend.
+ * <p>
+ * All operations will happen under this root.
+ * <p>
+ * default to `/` if not set.
+ */
+ private final String root;
+ /**
+ * endpoint of this backend.
+ * <p>
+ * Endpoint must be full uri, mostly like `http://127.0.0.1:39999`.
+ */
+ private final String endpoint;
+
+ @Override
+ public String scheme() {
+ return "alluxio";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (endpoint != null) {
+ map.put("endpoint", endpoint);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Atomicserver implements ServiceConfig {
+ /**
+ * work dir of this backend
+ */
+ private final String root;
+ /**
+ * endpoint of this backend
+ */
+ private final String endpoint;
+ /**
+ * private_key of this backend
+ */
+ private final String privateKey;
+ /**
+ * public_key of this backend
+ */
+ private final String publicKey;
+ /**
+ * parent_resource_id of this backend
+ */
+ private final String parentResourceId;
+
+ @Override
+ public String scheme() {
+ return "atomicserver";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (endpoint != null) {
+ map.put("endpoint", endpoint);
+ }
+ if (privateKey != null) {
+ map.put("private_key", privateKey);
+ }
+ if (publicKey != null) {
+ map.put("public_key", publicKey);
+ }
+ if (parentResourceId != null) {
+ map.put("parent_resource_id", parentResourceId);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Azblob implements ServiceConfig {
+ /**
+ * The container name of Azblob service backend.
+ */
+ private final @NonNull String container;
+ /**
+ * The root of Azblob service backend.
+ * <p>
+ * All operations will happen under this root.
+ */
+ private final String root;
+ /**
+ * The endpoint of Azblob service backend.
+ * <p>
+ * Endpoint must be full uri, e.g.
+ * <p>
+ * - Azblob: `https://accountname.blob.core.windows.net`
+ * - Azurite: `http://127.0.0.1:10000/devstoreaccount1`
+ */
+ private final String endpoint;
+ /**
+ * The account name of Azblob service backend.
+ */
+ private final String accountName;
+ /**
+ * The account key of Azblob service backend.
+ */
+ private final String accountKey;
+ /**
+ * The encryption key of Azblob service backend.
+ */
+ private final String encryptionKey;
+ /**
+ * The encryption key sha256 of Azblob service backend.
+ */
+ private final String encryptionKeySha256;
+ /**
+ * The encryption algorithm of Azblob service backend.
+ */
+ private final String encryptionAlgorithm;
+ /**
+ * The sas token of Azblob service backend.
+ */
+ private final String sasToken;
+ /**
+ * The maximum batch operations of Azblob service backend.
+ */
+ private final Long batchMaxOperations;
+
+ @Override
+ public String scheme() {
+ return "azblob";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ map.put("container", container);
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (endpoint != null) {
+ map.put("endpoint", endpoint);
+ }
+ if (accountName != null) {
+ map.put("account_name", accountName);
+ }
+ if (accountKey != null) {
+ map.put("account_key", accountKey);
+ }
+ if (encryptionKey != null) {
+ map.put("encryption_key", encryptionKey);
+ }
+ if (encryptionKeySha256 != null) {
+ map.put("encryption_key_sha256", encryptionKeySha256);
+ }
+ if (encryptionAlgorithm != null) {
+ map.put("encryption_algorithm", encryptionAlgorithm);
+ }
+ if (sasToken != null) {
+ map.put("sas_token", sasToken);
+ }
+ if (batchMaxOperations != null) {
+ map.put("batch_max_operations",
String.valueOf(batchMaxOperations));
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Azdls implements ServiceConfig {
+ /**
+ * Filesystem name of this backend.
+ */
+ private final @NonNull String filesystem;
+ /**
+ * Root of this backend.
+ */
+ private final String root;
+ /**
+ * Endpoint of this backend.
+ */
+ private final String endpoint;
+ /**
+ * Account name of this backend.
+ */
+ private final String accountName;
+ /**
+ * Account key of this backend.
+ */
+ private final String accountKey;
+
+ @Override
+ public String scheme() {
+ return "azdls";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ map.put("filesystem", filesystem);
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (endpoint != null) {
+ map.put("endpoint", endpoint);
+ }
+ if (accountName != null) {
+ map.put("account_name", accountName);
+ }
+ if (accountKey != null) {
+ map.put("account_key", accountKey);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Azfile implements ServiceConfig {
+ /**
+ * The share name for azfile.
+ */
+ private final @NonNull String shareName;
+ /**
+ * The root path for azfile.
+ */
+ private final String root;
+ /**
+ * The endpoint for azfile.
+ */
+ private final String endpoint;
+ /**
+ * The account name for azfile.
+ */
+ private final String accountName;
+ /**
+ * The account key for azfile.
+ */
+ private final String accountKey;
+ /**
+ * The sas token for azfile.
+ */
+ private final String sasToken;
+
+ @Override
+ public String scheme() {
+ return "azfile";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ map.put("share_name", shareName);
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (endpoint != null) {
+ map.put("endpoint", endpoint);
+ }
+ if (accountName != null) {
+ map.put("account_name", accountName);
+ }
+ if (accountKey != null) {
+ map.put("account_key", accountKey);
+ }
+ if (sasToken != null) {
+ map.put("sas_token", sasToken);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class B2 implements ServiceConfig {
+ /**
+ * bucket of this backend.
+ * <p>
+ * required.
+ */
+ private final @NonNull String bucket;
+ /**
+ * bucket id of this backend.
+ * <p>
+ * required.
+ */
+ private final @NonNull String bucketId;
+ /**
+ * root of this backend.
+ * <p>
+ * All operations will happen under this root.
+ */
+ private final String root;
+ /**
+ * keyID of this backend.
+ * <p>
+ * - If application_key_id is set, we will take user's input first.
+ * - If not, we will try to load it from environment.
+ */
+ private final String applicationKeyId;
+ /**
+ * applicationKey of this backend.
+ * <p>
+ * - If application_key is set, we will take user's input first.
+ * - If not, we will try to load it from environment.
+ */
+ private final String applicationKey;
+
+ @Override
+ public String scheme() {
+ return "b2";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ map.put("bucket", bucket);
+ map.put("bucket_id", bucketId);
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (applicationKeyId != null) {
+ map.put("application_key_id", applicationKeyId);
+ }
+ if (applicationKey != null) {
+ map.put("application_key", applicationKey);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Cacache implements ServiceConfig {
+ /**
+ * That path to the cacache data directory.
+ */
+ private final String datadir;
+
+ @Override
+ public String scheme() {
+ return "cacache";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (datadir != null) {
+ map.put("datadir", datadir);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Chainsafe implements ServiceConfig {
+ /**
+ * bucket_id of this backend.
+ * <p>
+ * required.
+ */
+ private final @NonNull String bucketId;
+ /**
+ * root of this backend.
+ * <p>
+ * All operations will happen under this root.
+ */
+ private final String root;
+ /**
+ * api_key of this backend.
+ */
+ private final String apiKey;
+
+ @Override
+ public String scheme() {
+ return "chainsafe";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ map.put("bucket_id", bucketId);
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (apiKey != null) {
+ map.put("api_key", apiKey);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class CloudflareKv implements ServiceConfig {
+ /**
+ * The token used to authenticate with CloudFlare.
+ */
+ private final String token;
+ /**
+ * The account ID used to authenticate with CloudFlare. Used as URI
path parameter.
+ */
+ private final String accountId;
+ /**
+ * The namespace ID. Used as URI path parameter.
+ */
+ private final String namespaceId;
+ /**
+ * Root within this backend.
+ */
+ private final String root;
+
+ @Override
+ public String scheme() {
+ return "cloudflare_kv";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (token != null) {
+ map.put("token", token);
+ }
+ if (accountId != null) {
+ map.put("account_id", accountId);
+ }
+ if (namespaceId != null) {
+ map.put("namespace_id", namespaceId);
+ }
+ if (root != null) {
+ map.put("root", root);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Compfs implements ServiceConfig {
+ /**
+ * root of this backend.
+ * <p>
+ * All operations will happen under this root.
+ */
+ private final String root;
+
+ @Override
+ public String scheme() {
+ return "compfs";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (root != null) {
+ map.put("root", root);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Cos implements ServiceConfig {
+ /**
+ * Root of this backend.
+ */
+ private final String root;
+ /**
+ * Endpoint of this backend.
+ */
+ private final String endpoint;
+ /**
+ * Secret ID of this backend.
+ */
+ private final String secretId;
+ /**
+ * Secret key of this backend.
+ */
+ private final String secretKey;
+ /**
+ * Bucket of this backend.
+ */
+ private final String bucket;
+ /**
+ * Disable config load so that opendal will not load config from
+ */
+ private final Boolean disableConfigLoad;
+
+ @Override
+ public String scheme() {
+ return "cos";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (endpoint != null) {
+ map.put("endpoint", endpoint);
+ }
+ if (secretId != null) {
+ map.put("secret_id", secretId);
+ }
+ if (secretKey != null) {
+ map.put("secret_key", secretKey);
+ }
+ if (bucket != null) {
+ map.put("bucket", bucket);
+ }
+ if (disableConfigLoad != null) {
+ map.put("disable_config_load",
String.valueOf(disableConfigLoad));
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class D1 implements ServiceConfig {
+ /**
+ * Set the token of cloudflare api.
+ */
+ private final String token;
+ /**
+ * Set the account id of cloudflare api.
+ */
+ private final String accountId;
+ /**
+ * Set the database id of cloudflare api.
+ */
+ private final String databaseId;
+ /**
+ * Set the working directory of OpenDAL.
+ */
+ private final String root;
+ /**
+ * Set the table of D1 Database.
+ */
+ private final String table;
+ /**
+ * Set the key field of D1 Database.
+ */
+ private final String keyField;
+ /**
+ * Set the value field of D1 Database.
+ */
+ private final String valueField;
+
+ @Override
+ public String scheme() {
+ return "d1";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (token != null) {
+ map.put("token", token);
+ }
+ if (accountId != null) {
+ map.put("account_id", accountId);
+ }
+ if (databaseId != null) {
+ map.put("database_id", databaseId);
+ }
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (table != null) {
+ map.put("table", table);
+ }
+ if (keyField != null) {
+ map.put("key_field", keyField);
+ }
+ if (valueField != null) {
+ map.put("value_field", valueField);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Dashmap implements ServiceConfig {
+ /**
+ * The root path for dashmap.
+ */
+ private final String root;
+
+ @Override
+ public String scheme() {
+ return "dashmap";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (root != null) {
+ map.put("root", root);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Dbfs implements ServiceConfig {
+ /**
+ * The root for dbfs.
+ */
+ private final String root;
+ /**
+ * The endpoint for dbfs.
+ */
+ private final String endpoint;
+ /**
+ * The token for dbfs.
+ */
+ private final String token;
+
+ @Override
+ public String scheme() {
+ return "dbfs";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (endpoint != null) {
+ map.put("endpoint", endpoint);
+ }
+ if (token != null) {
+ map.put("token", token);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Dropbox implements ServiceConfig {
+ /**
+ * root path for dropbox.
+ */
+ private final String root;
+ /**
+ * access token for dropbox.
+ */
+ private final String accessToken;
+ /**
+ * refresh_token for dropbox.
+ */
+ private final String refreshToken;
+ /**
+ * client_id for dropbox.
+ */
+ private final String clientId;
+ /**
+ * client_secret for dropbox.
+ */
+ private final String clientSecret;
+
+ @Override
+ public String scheme() {
+ return "dropbox";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (accessToken != null) {
+ map.put("access_token", accessToken);
+ }
+ if (refreshToken != null) {
+ map.put("refresh_token", refreshToken);
+ }
+ if (clientId != null) {
+ map.put("client_id", clientId);
+ }
+ if (clientSecret != null) {
+ map.put("client_secret", clientSecret);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Etcd implements ServiceConfig {
+ /**
+ * network address of the Etcd services.
+ * If use https, must set TLS options: `ca_path`, `cert_path`,
`key_path`.
+ * e.g. "127.0.0.1:23790,127.0.0.1:23791,127.0.0.1:23792" or
"http://127.0.0.1:23790,http://127.0.0.1:23791,http://127.0.0.1:23792" or
"https://127.0.0.1:23790,https://127.0.0.1:23791,https://127.0.0.1:23792"
+ * <p>
+ * default is "http://127.0.0.1:2379"
+ */
+ private final String endpoints;
+ /**
+ * the username to connect etcd service.
+ * <p>
+ * default is None
+ */
+ private final String username;
+ /**
+ * the password for authentication
+ * <p>
+ * default is None
+ */
+ private final String password;
+ /**
+ * the working directory of the etcd service. Can be "/path/to/dir"
+ * <p>
+ * default is "/"
+ */
+ private final String root;
+ /**
+ * certificate authority file path
+ * <p>
+ * default is None
+ */
+ private final String caPath;
+ /**
+ * cert path
+ * <p>
+ * default is None
+ */
+ private final String certPath;
+ /**
+ * key path
+ * <p>
+ * default is None
+ */
+ private final String keyPath;
+
+ @Override
+ public String scheme() {
+ return "etcd";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (endpoints != null) {
+ map.put("endpoints", endpoints);
+ }
+ if (username != null) {
+ map.put("username", username);
+ }
+ if (password != null) {
+ map.put("password", password);
+ }
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (caPath != null) {
+ map.put("ca_path", caPath);
+ }
+ if (certPath != null) {
+ map.put("cert_path", certPath);
+ }
+ if (keyPath != null) {
+ map.put("key_path", keyPath);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Fs implements ServiceConfig {
+ /**
+ * root dir for backend
+ */
+ private final String root;
+ /**
+ * tmp dir for atomic write
+ */
+ private final String atomicWriteDir;
+
+ @Override
+ public String scheme() {
+ return "fs";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (atomicWriteDir != null) {
+ map.put("atomic_write_dir", atomicWriteDir);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Gcs implements ServiceConfig {
+ /**
+ * bucket name
+ */
+ private final @NonNull String bucket;
+ /**
+ * root URI, all operations happens under `root`
+ */
+ private final String root;
+ /**
+ * endpoint URI of GCS service,
+ * default is `https://storage.googleapis.com`
+ */
+ private final String endpoint;
+ /**
+ * Scope for gcs.
+ */
+ private final String scope;
+ /**
+ * Service Account for gcs.
+ */
+ private final String serviceAccount;
+ /**
+ * Credentials string for GCS service OAuth2 authentication.
+ */
+ private final String credential;
+ /**
+ * Local path to credentials file for GCS service OAuth2
authentication.
+ */
+ private final String credentialPath;
+ /**
+ * The predefined acl for GCS.
+ */
+ private final String predefinedAcl;
+ /**
+ * The default storage class used by gcs.
+ */
+ private final String defaultStorageClass;
+ /**
+ * Allow opendal to send requests without signing when credentials are
not
+ * loaded.
+ */
+ private final Boolean allowAnonymous;
+ /**
+ * Disable attempting to load credentials from the GCE metadata server
when
+ * running within Google Cloud.
+ */
+ private final Boolean disableVmMetadata;
+ /**
+ * Disable loading configuration from the environment.
+ */
+ private final Boolean disableConfigLoad;
+ /**
+ * A Google Cloud OAuth2 token.
+ * <p>
+ * Takes precedence over `credential` and `credential_path`.
+ */
+ private final String token;
+
+ @Override
+ public String scheme() {
+ return "gcs";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ map.put("bucket", bucket);
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (endpoint != null) {
+ map.put("endpoint", endpoint);
+ }
+ if (scope != null) {
+ map.put("scope", scope);
+ }
+ if (serviceAccount != null) {
+ map.put("service_account", serviceAccount);
+ }
+ if (credential != null) {
+ map.put("credential", credential);
+ }
+ if (credentialPath != null) {
+ map.put("credential_path", credentialPath);
+ }
+ if (predefinedAcl != null) {
+ map.put("predefined_acl", predefinedAcl);
+ }
+ if (defaultStorageClass != null) {
+ map.put("default_storage_class", defaultStorageClass);
+ }
+ if (allowAnonymous != null) {
+ map.put("allow_anonymous", String.valueOf(allowAnonymous));
+ }
+ if (disableVmMetadata != null) {
+ map.put("disable_vm_metadata",
String.valueOf(disableVmMetadata));
+ }
+ if (disableConfigLoad != null) {
+ map.put("disable_config_load",
String.valueOf(disableConfigLoad));
+ }
+ if (token != null) {
+ map.put("token", token);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Gdrive implements ServiceConfig {
+ /**
+ * The root for gdrive
+ */
+ private final String root;
+ /**
+ * Access token for gdrive.
+ */
+ private final String accessToken;
+ /**
+ * Refresh token for gdrive.
+ */
+ private final String refreshToken;
+ /**
+ * Client id for gdrive.
+ */
+ private final String clientId;
+ /**
+ * Client secret for gdrive.
+ */
+ private final String clientSecret;
+
+ @Override
+ public String scheme() {
+ return "gdrive";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (accessToken != null) {
+ map.put("access_token", accessToken);
+ }
+ if (refreshToken != null) {
+ map.put("refresh_token", refreshToken);
+ }
+ if (clientId != null) {
+ map.put("client_id", clientId);
+ }
+ if (clientSecret != null) {
+ map.put("client_secret", clientSecret);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Ghac implements ServiceConfig {
+ /**
+ * The root path for ghac.
+ */
+ private final String root;
+ /**
+ * The version that used by cache.
+ */
+ private final String version;
+ /**
+ * The endpoint for ghac service.
+ */
+ private final String endpoint;
+ /**
+ * The runtime token for ghac service.
+ */
+ private final String runtimeToken;
+
+ @Override
+ public String scheme() {
+ return "ghac";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (version != null) {
+ map.put("version", version);
+ }
+ if (endpoint != null) {
+ map.put("endpoint", endpoint);
+ }
+ if (runtimeToken != null) {
+ map.put("runtime_token", runtimeToken);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Github implements ServiceConfig {
+ /**
+ * GitHub repo owner.
+ * <p>
+ * required.
+ */
+ private final @NonNull String owner;
+ /**
+ * GitHub repo name.
+ * <p>
+ * required.
+ */
+ private final @NonNull String repo;
+ /**
+ * root of this backend.
+ * <p>
+ * All operations will happen under this root.
+ */
+ private final String root;
+ /**
+ * GitHub access_token.
+ * <p>
+ * optional.
+ * If not provided, the backend will only support read operations for
public repositories.
+ * And rate limit will be limited to 60 requests per hour.
+ */
+ private final String token;
+
+ @Override
+ public String scheme() {
+ return "github";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ map.put("owner", owner);
+ map.put("repo", repo);
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (token != null) {
+ map.put("token", token);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Gridfs implements ServiceConfig {
+ /**
+ * The connection string of the MongoDB service.
+ */
+ private final String connectionString;
+ /**
+ * The database name of the MongoDB GridFs service to read/write.
+ */
+ private final String database;
+ /**
+ * The bucket name of the MongoDB GridFs service to read/write.
+ */
+ private final String bucket;
+ /**
+ * The chunk size of the MongoDB GridFs service used to break the user
file into chunks.
+ */
+ private final Integer chunkSize;
+ /**
+ * The working directory, all operations will be performed under it.
+ */
+ private final String root;
+
+ @Override
+ public String scheme() {
+ return "gridfs";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (connectionString != null) {
+ map.put("connection_string", connectionString);
+ }
+ if (database != null) {
+ map.put("database", database);
+ }
+ if (bucket != null) {
+ map.put("bucket", bucket);
+ }
+ if (chunkSize != null) {
+ map.put("chunk_size", String.valueOf(chunkSize));
+ }
+ if (root != null) {
+ map.put("root", root);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class HdfsNative implements ServiceConfig {
+ /**
+ * work dir of this backend
+ */
+ private final String root;
+ /**
+ * url of this backend
+ */
+ private final String url;
+ /**
+ * enable the append capacity
+ */
+ private final Boolean enableAppend;
+
+ @Override
+ public String scheme() {
+ return "hdfs_native";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (url != null) {
+ map.put("url", url);
+ }
+ if (enableAppend != null) {
+ map.put("enable_append", String.valueOf(enableAppend));
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Http implements ServiceConfig {
+ /**
+ * endpoint of this backend
+ */
+ private final String endpoint;
+ /**
+ * username of this backend
+ */
+ private final String username;
+ /**
+ * password of this backend
+ */
+ private final String password;
+ /**
+ * token of this backend
+ */
+ private final String token;
+ /**
+ * root of this backend
+ */
+ private final String root;
+
+ @Override
+ public String scheme() {
+ return "http";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (endpoint != null) {
+ map.put("endpoint", endpoint);
+ }
+ if (username != null) {
+ map.put("username", username);
+ }
+ if (password != null) {
+ map.put("password", password);
+ }
+ if (token != null) {
+ map.put("token", token);
+ }
+ if (root != null) {
+ map.put("root", root);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Huggingface implements ServiceConfig {
+ /**
+ * Repo type of this backend. Default is model.
+ * <p>
+ * Available values:
+ * - model
+ * - dataset
+ */
+ private final String repoType;
+ /**
+ * Repo id of this backend.
+ * <p>
+ * This is required.
+ */
+ private final String repoId;
+ /**
+ * Revision of this backend.
+ * <p>
+ * Default is main.
+ */
+ private final String revision;
+ /**
+ * Root of this backend. Can be "/path/to/dir".
+ * <p>
+ * Default is "/".
+ */
+ private final String root;
+ /**
+ * Token of this backend.
+ * <p>
+ * This is optional.
+ */
+ private final String token;
+
+ @Override
+ public String scheme() {
+ return "huggingface";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (repoType != null) {
+ map.put("repo_type", repoType);
+ }
+ if (repoId != null) {
+ map.put("repo_id", repoId);
+ }
+ if (revision != null) {
+ map.put("revision", revision);
+ }
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (token != null) {
+ map.put("token", token);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Icloud implements ServiceConfig {
+ /**
+ * root of this backend.
+ * <p>
+ * All operations will happen under this root.
+ * <p>
+ * default to `/` if not set.
+ */
+ private final String root;
+ /**
+ * apple_id of this backend.
+ * <p>
+ * apple_id must be full, mostly like `[email protected]`.
+ */
+ private final String appleId;
+ /**
+ * password of this backend.
+ * <p>
+ * password must be full.
+ */
+ private final String password;
+ /**
+ * Session
+ * <p>
+ * token must be valid.
+ */
+ private final String trustToken;
+ /**
+ * ds_web_auth_token must be set in Session
+ */
+ private final String dsWebAuthToken;
+ /**
+ * enable the china origin
+ * China region `origin` Header needs to be set to
"https://www.icloud.com.cn".
+ * <p>
+ * otherwise Apple server will return 302.
+ */
+ private final Boolean isChinaMainland;
+
+ @Override
+ public String scheme() {
+ return "icloud";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (appleId != null) {
+ map.put("apple_id", appleId);
+ }
+ if (password != null) {
+ map.put("password", password);
+ }
+ if (trustToken != null) {
+ map.put("trust_token", trustToken);
+ }
+ if (dsWebAuthToken != null) {
+ map.put("ds_web_auth_token", dsWebAuthToken);
+ }
+ if (isChinaMainland != null) {
+ map.put("is_china_mainland", String.valueOf(isChinaMainland));
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Ipfs implements ServiceConfig {
+ /**
+ * IPFS gateway endpoint.
+ */
+ private final String endpoint;
+ /**
+ * IPFS root.
+ */
+ private final String root;
+
+ @Override
+ public String scheme() {
+ return "ipfs";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (endpoint != null) {
+ map.put("endpoint", endpoint);
+ }
+ if (root != null) {
+ map.put("root", root);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Ipmfs implements ServiceConfig {
+ /**
+ * Root for ipfs.
+ */
+ private final String root;
+ /**
+ * Endpoint for ipfs.
+ */
+ private final String endpoint;
+
+ @Override
+ public String scheme() {
+ return "ipmfs";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (endpoint != null) {
+ map.put("endpoint", endpoint);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Koofr implements ServiceConfig {
+ /**
+ * Koofr endpoint.
+ */
+ private final @NonNull String endpoint;
+ /**
+ * Koofr email.
+ */
+ private final @NonNull String email;
+ /**
+ * root of this backend.
+ * <p>
+ * All operations will happen under this root.
+ */
+ private final String root;
+ /**
+ * password of this backend. (Must be the application password)
+ */
+ private final String password;
+
+ @Override
+ public String scheme() {
+ return "koofr";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ map.put("endpoint", endpoint);
+ map.put("email", email);
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (password != null) {
+ map.put("password", password);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Lakefs implements ServiceConfig {
+ /**
+ * Base url.
+ * <p>
+ * This is required.
+ */
+ private final String endpoint;
+ /**
+ * Username for Lakefs basic authentication.
+ * <p>
+ * This is required.
+ */
+ private final String username;
+ /**
+ * Password for Lakefs basic authentication.
+ * <p>
+ * This is required.
+ */
+ private final String password;
+ /**
+ * Root of this backend. Can be "/path/to/dir".
+ * <p>
+ * Default is "/".
+ */
+ private final String root;
+ /**
+ * The repository name
+ * <p>
+ * This is required.
+ */
+ private final String repository;
+ /**
+ * Name of the branch or a commit ID. Default is main.
+ * <p>
+ * This is optional.
+ */
+ private final String branch;
+
+ @Override
+ public String scheme() {
+ return "lakefs";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (endpoint != null) {
+ map.put("endpoint", endpoint);
+ }
+ if (username != null) {
+ map.put("username", username);
+ }
+ if (password != null) {
+ map.put("password", password);
+ }
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (repository != null) {
+ map.put("repository", repository);
+ }
+ if (branch != null) {
+ map.put("branch", branch);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Libsql implements ServiceConfig {
+ /**
+ * Connection string for libsql service.
+ */
+ private final String connectionString;
+ /**
+ * Authentication token for libsql service.
+ */
+ private final String authToken;
+ /**
+ * Table name for libsql service.
+ */
+ private final String table;
+ /**
+ * Key field name for libsql service.
+ */
+ private final String keyField;
+ /**
+ * Value field name for libsql service.
+ */
+ private final String valueField;
+ /**
+ * Root for libsql service.
+ */
+ private final String root;
+
+ @Override
+ public String scheme() {
+ return "libsql";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (connectionString != null) {
+ map.put("connection_string", connectionString);
+ }
+ if (authToken != null) {
+ map.put("auth_token", authToken);
+ }
+ if (table != null) {
+ map.put("table", table);
+ }
+ if (keyField != null) {
+ map.put("key_field", keyField);
+ }
+ if (valueField != null) {
+ map.put("value_field", valueField);
+ }
+ if (root != null) {
+ map.put("root", root);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Memcached implements ServiceConfig {
+ /**
+ * network address of the memcached service.
+ * <p>
+ * For example: "tcp://localhost:11211"
+ */
+ private final String endpoint;
+ /**
+ * the working directory of the service. Can be "/path/to/dir"
+ * <p>
+ * default is "/"
+ */
+ private final String root;
+ /**
+ * Memcached username, optional.
+ */
+ private final String username;
+ /**
+ * Memcached password, optional.
+ */
+ private final String password;
+ /**
+ * The default ttl for put operations.
+ */
+ private final Duration defaultTtl;
+
+ @Override
+ public String scheme() {
+ return "memcached";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (endpoint != null) {
+ map.put("endpoint", endpoint);
+ }
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (username != null) {
+ map.put("username", username);
+ }
+ if (password != null) {
+ map.put("password", password);
+ }
+ if (defaultTtl != null) {
+ map.put("default_ttl", defaultTtl.toString());
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Memory implements ServiceConfig {
+ /**
+ * root of the backend.
+ */
+ private final String root;
+
+ @Override
+ public String scheme() {
+ return "memory";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (root != null) {
+ map.put("root", root);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class MiniMoka implements ServiceConfig {
+ /**
+ * Sets the max capacity of the cache.
+ * <p>
+ * Refer to
[`mini-moka::sync::CacheBuilder::max_capacity`](https://docs.rs/mini-moka/latest/mini_moka/sync/struct.CacheBuilder.html#method.max_capacity)
+ */
+ private final Long maxCapacity;
+ /**
+ * Sets the time to live of the cache.
+ * <p>
+ * Refer to
[`mini-moka::sync::CacheBuilder::time_to_live`](https://docs.rs/mini-moka/latest/mini_moka/sync/struct.CacheBuilder.html#method.time_to_live)
+ */
+ private final Duration timeToLive;
+ /**
+ * Sets the time to idle of the cache.
+ * <p>
+ * Refer to
[`mini-moka::sync::CacheBuilder::time_to_idle`](https://docs.rs/mini-moka/latest/mini_moka/sync/struct.CacheBuilder.html#method.time_to_idle)
+ */
+ private final Duration timeToIdle;
+ /**
+ * root path of this backend
+ */
+ private final String root;
+
+ @Override
+ public String scheme() {
+ return "mini_moka";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (maxCapacity != null) {
+ map.put("max_capacity", String.valueOf(maxCapacity));
+ }
+ if (timeToLive != null) {
+ map.put("time_to_live", timeToLive.toString());
+ }
+ if (timeToIdle != null) {
+ map.put("time_to_idle", timeToIdle.toString());
+ }
+ if (root != null) {
+ map.put("root", root);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Moka implements ServiceConfig {
+ /**
+ * Name for this cache instance.
+ */
+ private final String name;
+ /**
+ * Sets the max capacity of the cache.
+ * <p>
+ * Refer to
[`moka::sync::CacheBuilder::max_capacity`](https://docs.rs/moka/latest/moka/sync/struct.CacheBuilder.html#method.max_capacity)
+ */
+ private final Long maxCapacity;
+ /**
+ * Sets the time to live of the cache.
+ * <p>
+ * Refer to
[`moka::sync::CacheBuilder::time_to_live`](https://docs.rs/moka/latest/moka/sync/struct.CacheBuilder.html#method.time_to_live)
+ */
+ private final Duration timeToLive;
+ /**
+ * Sets the time to idle of the cache.
+ * <p>
+ * Refer to
[`moka::sync::CacheBuilder::time_to_idle`](https://docs.rs/moka/latest/moka/sync/struct.CacheBuilder.html#method.time_to_idle)
+ */
+ private final Duration timeToIdle;
+ /**
+ * Sets the segments number of the cache.
+ * <p>
+ * Refer to
[`moka::sync::CacheBuilder::segments`](https://docs.rs/moka/latest/moka/sync/struct.CacheBuilder.html#method.segments)
+ */
+ private final Long numSegments;
+ /**
+ * root path of this backend
+ */
+ private final String root;
+
+ @Override
+ public String scheme() {
+ return "moka";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (name != null) {
+ map.put("name", name);
+ }
+ if (maxCapacity != null) {
+ map.put("max_capacity", String.valueOf(maxCapacity));
+ }
+ if (timeToLive != null) {
+ map.put("time_to_live", timeToLive.toString());
+ }
+ if (timeToIdle != null) {
+ map.put("time_to_idle", timeToIdle.toString());
+ }
+ if (numSegments != null) {
+ map.put("num_segments", String.valueOf(numSegments));
+ }
+ if (root != null) {
+ map.put("root", root);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Mongodb implements ServiceConfig {
+ /**
+ * connection string of this backend
+ */
+ private final String connectionString;
+ /**
+ * database of this backend
+ */
+ private final String database;
+ /**
+ * collection of this backend
+ */
+ private final String collection;
+ /**
+ * root of this backend
+ */
+ private final String root;
+ /**
+ * key field of this backend
+ */
+ private final String keyField;
+ /**
+ * value field of this backend
+ */
+ private final String valueField;
+
+ @Override
+ public String scheme() {
+ return "mongodb";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (connectionString != null) {
+ map.put("connection_string", connectionString);
+ }
+ if (database != null) {
+ map.put("database", database);
+ }
+ if (collection != null) {
+ map.put("collection", collection);
+ }
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (keyField != null) {
+ map.put("key_field", keyField);
+ }
+ if (valueField != null) {
+ map.put("value_field", valueField);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Monoiofs implements ServiceConfig {
+ /**
+ * The Root of this backend.
+ * <p>
+ * All operations will happen under this root.
+ * <p>
+ * Builder::build will return error if not set.
+ */
+ private final String root;
+
+ @Override
+ public String scheme() {
+ return "monoiofs";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (root != null) {
+ map.put("root", root);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Mysql implements ServiceConfig {
+ /**
+ * This connection string is used to connect to the mysql service.
There are url based formats:
+ * <p>
+ * ## Url
+ * <p>
+ * This format resembles the url format of the mysql client. The
format is:
`[scheme://][user[:[password]]@]host[:port][/schema][?attribute1=value1&attribute2=value2...`
+ * <p>
+ * - `mysql://user@localhost`
+ * - `mysql://user:password@localhost`
+ * - `mysql://user:password@localhost:3306`
+ * - `mysql://user:password@localhost:3306/db`
+ * <p>
+ * For more information, please refer to
<https://docs.rs/sqlx/latest/sqlx/mysql/struct.MySqlConnectOptions.html>.
+ */
+ private final String connectionString;
+ /**
+ * The table name for mysql.
+ */
+ private final String table;
+ /**
+ * The key field name for mysql.
+ */
+ private final String keyField;
+ /**
+ * The value field name for mysql.
+ */
+ private final String valueField;
+ /**
+ * The root for mysql.
+ */
+ private final String root;
+
+ @Override
+ public String scheme() {
+ return "mysql";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (connectionString != null) {
+ map.put("connection_string", connectionString);
+ }
+ if (table != null) {
+ map.put("table", table);
+ }
+ if (keyField != null) {
+ map.put("key_field", keyField);
+ }
+ if (valueField != null) {
+ map.put("value_field", valueField);
+ }
+ if (root != null) {
+ map.put("root", root);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class NebulaGraph implements ServiceConfig {
+ /**
+ * The host addr of nebulagraph's graphd server
+ */
+ private final String host;
+ /**
+ * The host port of nebulagraph's graphd server
+ */
+ private final Integer port;
+ /**
+ * The username of nebulagraph's graphd server
+ */
+ private final String username;
+ /**
+ * The password of nebulagraph's graphd server
+ */
+ private final String password;
+ /**
+ * The space name of nebulagraph's graphd server
+ */
+ private final String space;
+ /**
+ * The tag name of nebulagraph's graphd server
+ */
+ private final String tag;
+ /**
+ * The key field name of the NebulaGraph service to read/write.
+ */
+ private final String keyField;
+ /**
+ * The value field name of the NebulaGraph service to read/write.
+ */
+ private final String valueField;
+ /**
+ * The root for NebulaGraph
+ */
+ private final String root;
+
+ @Override
+ public String scheme() {
+ return "nebula_graph";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (host != null) {
+ map.put("host", host);
+ }
+ if (port != null) {
+ map.put("port", String.valueOf(port));
+ }
+ if (username != null) {
+ map.put("username", username);
+ }
+ if (password != null) {
+ map.put("password", password);
+ }
+ if (space != null) {
+ map.put("space", space);
+ }
+ if (tag != null) {
+ map.put("tag", tag);
+ }
+ if (keyField != null) {
+ map.put("key_field", keyField);
+ }
+ if (valueField != null) {
+ map.put("value_field", valueField);
+ }
+ if (root != null) {
+ map.put("root", root);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Obs implements ServiceConfig {
+ /**
+ * Root for obs.
+ */
+ private final String root;
+ /**
+ * Endpoint for obs.
+ */
+ private final String endpoint;
+ /**
+ * Access key id for obs.
+ */
+ private final String accessKeyId;
+ /**
+ * Secret access key for obs.
+ */
+ private final String secretAccessKey;
+ /**
+ * Bucket for obs.
+ */
+ private final String bucket;
+
+ @Override
+ public String scheme() {
+ return "obs";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (endpoint != null) {
+ map.put("endpoint", endpoint);
+ }
+ if (accessKeyId != null) {
+ map.put("access_key_id", accessKeyId);
+ }
+ if (secretAccessKey != null) {
+ map.put("secret_access_key", secretAccessKey);
+ }
+ if (bucket != null) {
+ map.put("bucket", bucket);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Onedrive implements ServiceConfig {
+ /**
+ * bearer access token for OneDrive
+ */
+ private final String accessToken;
+ /**
+ * root path of OneDrive folder.
+ */
+ private final String root;
+
+ @Override
+ public String scheme() {
+ return "onedrive";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (accessToken != null) {
+ map.put("access_token", accessToken);
+ }
+ if (root != null) {
+ map.put("root", root);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Oss implements ServiceConfig {
+ /**
+ * Bucket for oss.
+ */
+ private final @NonNull String bucket;
+ /**
+ * Root for oss.
+ */
+ private final String root;
+ /**
+ * Endpoint for oss.
+ */
+ private final String endpoint;
+ /**
+ * Presign endpoint for oss.
+ */
+ private final String presignEndpoint;
+ /**
+ * Server side encryption for oss.
+ */
+ private final String serverSideEncryption;
+ /**
+ * Server side encryption key id for oss.
+ */
+ private final String serverSideEncryptionKeyId;
+ /**
+ * Allow anonymous for oss.
+ */
+ private final Boolean allowAnonymous;
+ /**
+ * Access key id for oss.
+ */
+ private final String accessKeyId;
+ /**
+ * Access key secret for oss.
+ */
+ private final String accessKeySecret;
+ /**
+ * The size of max batch operations.
+ *
+ * @deprecated Please use `delete_max_size` instead of
`batch_max_operations`
+ */
+ private final Long batchMaxOperations;
+ /**
+ * The size of max delete operations.
+ */
+ private final Long deleteMaxSize;
+ /**
+ * If `role_arn` is set, we will use already known config as source
+ * credential to assume role with `role_arn`.
+ */
+ private final String roleArn;
+ /**
+ * role_session_name for this backend.
+ */
+ private final String roleSessionName;
+ /**
+ * `oidc_provider_arn` will be loaded from
+ * <p>
+ * - this field if it's `is_some`
+ * - env value: [`ALIBABA_CLOUD_OIDC_PROVIDER_ARN`]
+ */
+ private final String oidcProviderArn;
+ /**
+ * `oidc_token_file` will be loaded from
+ * <p>
+ * - this field if it's `is_some`
+ * - env value: [`ALIBABA_CLOUD_OIDC_TOKEN_FILE`]
+ */
+ private final String oidcTokenFile;
+ /**
+ * `sts_endpoint` will be loaded from
+ * <p>
+ * - this field if it's `is_some`
+ * - env value: [`ALIBABA_CLOUD_STS_ENDPOINT`]
+ */
+ private final String stsEndpoint;
+
+ @Override
+ public String scheme() {
+ return "oss";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ map.put("bucket", bucket);
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (endpoint != null) {
+ map.put("endpoint", endpoint);
+ }
+ if (presignEndpoint != null) {
+ map.put("presign_endpoint", presignEndpoint);
+ }
+ if (serverSideEncryption != null) {
+ map.put("server_side_encryption", serverSideEncryption);
+ }
+ if (serverSideEncryptionKeyId != null) {
+ map.put("server_side_encryption_key_id",
serverSideEncryptionKeyId);
+ }
+ if (allowAnonymous != null) {
+ map.put("allow_anonymous", String.valueOf(allowAnonymous));
+ }
+ if (accessKeyId != null) {
+ map.put("access_key_id", accessKeyId);
+ }
+ if (accessKeySecret != null) {
+ map.put("access_key_secret", accessKeySecret);
+ }
+ if (batchMaxOperations != null) {
+ map.put("batch_max_operations",
String.valueOf(batchMaxOperations));
+ }
+ if (deleteMaxSize != null) {
+ map.put("delete_max_size", String.valueOf(deleteMaxSize));
+ }
+ if (roleArn != null) {
+ map.put("role_arn", roleArn);
+ }
+ if (roleSessionName != null) {
+ map.put("role_session_name", roleSessionName);
+ }
+ if (oidcProviderArn != null) {
+ map.put("oidc_provider_arn", oidcProviderArn);
+ }
+ if (oidcTokenFile != null) {
+ map.put("oidc_token_file", oidcTokenFile);
+ }
+ if (stsEndpoint != null) {
+ map.put("sts_endpoint", stsEndpoint);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Pcloud implements ServiceConfig {
+ /**
+ * pCloud endpoint address.
+ */
+ private final @NonNull String endpoint;
+ /**
+ * root of this backend.
+ * <p>
+ * All operations will happen under this root.
+ */
+ private final String root;
+ /**
+ * pCloud username.
+ */
+ private final String username;
+ /**
+ * pCloud password.
+ */
+ private final String password;
+
+ @Override
+ public String scheme() {
+ return "pcloud";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ map.put("endpoint", endpoint);
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (username != null) {
+ map.put("username", username);
+ }
+ if (password != null) {
+ map.put("password", password);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Persy implements ServiceConfig {
+ /**
+ * That path to the persy data file. The directory in the path must
already exist.
+ */
+ private final String datafile;
+ /**
+ * That name of the persy segment.
+ */
+ private final String segment;
+ /**
+ * That name of the persy index.
+ */
+ private final String index;
+
+ @Override
+ public String scheme() {
+ return "persy";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (datafile != null) {
+ map.put("datafile", datafile);
+ }
+ if (segment != null) {
+ map.put("segment", segment);
+ }
+ if (index != null) {
+ map.put("index", index);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Postgresql implements ServiceConfig {
+ /**
+ * Root of this backend.
+ * <p>
+ * All operations will happen under this root.
+ * <p>
+ * Default to `/` if not set.
+ */
+ private final String root;
+ /**
+ * The URL should be with a scheme of either `postgres://` or
`postgresql://`.
+ * <p>
+ * - `postgresql://user@localhost`
+ * -
`postgresql://user:password@%2Fvar%2Flib%2Fpostgresql/mydb?connect_timeout=10`
+ * -
`postgresql://user@host1:1234,host2,host3:5678?target_session_attrs=read-write`
+ * - `postgresql:///mydb?user=user&host=/var/lib/postgresql`
+ * <p>
+ * For more information, please visit
<https://docs.rs/sqlx/latest/sqlx/postgres/struct.PgConnectOptions.html>.
+ */
+ private final String connectionString;
+ /**
+ * the table of postgresql
+ */
+ private final String table;
+ /**
+ * the key field of postgresql
+ */
+ private final String keyField;
+ /**
+ * the value field of postgresql
+ */
+ private final String valueField;
+
+ @Override
+ public String scheme() {
+ return "postgresql";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (connectionString != null) {
+ map.put("connection_string", connectionString);
+ }
+ if (table != null) {
+ map.put("table", table);
+ }
+ if (keyField != null) {
+ map.put("key_field", keyField);
+ }
+ if (valueField != null) {
+ map.put("value_field", valueField);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Redb implements ServiceConfig {
+ /**
+ * path to the redb data directory.
+ */
+ private final String datadir;
+ /**
+ * The root for redb.
+ */
+ private final String root;
+ /**
+ * The table name for redb.
+ */
+ private final String table;
+
+ @Override
+ public String scheme() {
+ return "redb";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (datadir != null) {
+ map.put("datadir", datadir);
+ }
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (table != null) {
+ map.put("table", table);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Redis implements ServiceConfig {
+ /**
+ * the number of DBs redis can take is unlimited
+ * <p>
+ * default is db 0
+ */
+ private final long db;
+ /**
+ * network address of the Redis service. Can be
"tcp://127.0.0.1:6379", e.g.
+ * <p>
+ * default is "tcp://127.0.0.1:6379"
+ */
+ private final String endpoint;
+ /**
+ * network address of the Redis cluster service. Can be
"tcp://127.0.0.1:6379,tcp://127.0.0.1:6380,tcp://127.0.0.1:6381", e.g.
+ * <p>
+ * default is None
+ */
+ private final String clusterEndpoints;
+ /**
+ * the username to connect redis service.
+ * <p>
+ * default is None
+ */
+ private final String username;
+ /**
+ * the password for authentication
+ * <p>
+ * default is None
+ */
+ private final String password;
+ /**
+ * the working directory of the Redis service. Can be "/path/to/dir"
+ * <p>
+ * default is "/"
+ */
+ private final String root;
+ /**
+ * The default ttl for put operations.
+ */
+ private final Duration defaultTtl;
+
+ @Override
+ public String scheme() {
+ return "redis";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ map.put("db", String.valueOf(db));
+ if (endpoint != null) {
+ map.put("endpoint", endpoint);
+ }
+ if (clusterEndpoints != null) {
+ map.put("cluster_endpoints", clusterEndpoints);
+ }
+ if (username != null) {
+ map.put("username", username);
+ }
+ if (password != null) {
+ map.put("password", password);
+ }
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (defaultTtl != null) {
+ map.put("default_ttl", defaultTtl.toString());
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class S3 implements ServiceConfig {
+ /**
+ * bucket name of this backend.
+ * <p>
+ * required.
+ */
+ private final @NonNull String bucket;
+ /**
+ * root of this backend.
+ * <p>
+ * All operations will happen under this root.
+ * <p>
+ * default to `/` if not set.
+ */
+ private final String root;
+ /**
+ * is bucket versioning enabled for this bucket
+ */
+ private final Boolean enableVersioning;
+ /**
+ * endpoint of this backend.
+ * <p>
+ * Endpoint must be full uri, e.g.
+ * <p>
+ * - AWS S3: `https://s3.amazonaws.com` or
`https://s3.{region}.amazonaws.com`
+ * - Cloudflare R2: `https://<ACCOUNT_ID>.r2.cloudflarestorage.com`
+ * - Aliyun OSS: `https://{region}.aliyuncs.com`
+ * - Tencent COS: `https://cos.{region}.myqcloud.com`
+ * - Minio: `http://127.0.0.1:9000`
+ * <p>
+ * If user inputs endpoint without scheme like "s3.amazonaws.com", we
+ * will prepend "https://" before it.
+ * <p>
+ * - If endpoint is set, we will take user's input first.
+ * - If not, we will try to load it from environment.
+ * - If still not set, default to `https://s3.amazonaws.com`.
+ */
+ private final String endpoint;
+ /**
+ * Region represent the signing region of this endpoint. This is
required
+ * if you are using the default AWS S3 endpoint.
+ * <p>
+ * If using a custom endpoint,
+ * - If region is set, we will take user's input first.
+ * - If not, we will try to load it from environment.
+ */
+ private final String region;
+ /**
+ * access_key_id of this backend.
+ * <p>
+ * - If access_key_id is set, we will take user's input first.
+ * - If not, we will try to load it from environment.
+ */
+ private final String accessKeyId;
+ /**
+ * secret_access_key of this backend.
+ * <p>
+ * - If secret_access_key is set, we will take user's input first.
+ * - If not, we will try to load it from environment.
+ */
+ private final String secretAccessKey;
+ /**
+ * session_token (aka, security token) of this backend.
+ * <p>
+ * This token will expire after sometime, it's recommended to set
session_token
+ * by hand.
+ */
+ private final String sessionToken;
+ /**
+ * role_arn for this backend.
+ * <p>
+ * If `role_arn` is set, we will use already known config as source
+ * credential to assume role with `role_arn`.
+ */
+ private final String roleArn;
+ /**
+ * external_id for this backend.
+ */
+ private final String externalId;
+ /**
+ * role_session_name for this backend.
+ */
+ private final String roleSessionName;
+ /**
+ * Disable config load so that opendal will not load config from
+ * environment.
+ * <p>
+ * For examples:
+ * <p>
+ * - envs like `AWS_ACCESS_KEY_ID`
+ * - files like `~/.aws/config`
+ */
+ private final Boolean disableConfigLoad;
+ /**
+ * Disable load credential from ec2 metadata.
+ * <p>
+ * This option is used to disable the default behavior of opendal
+ * to load credential from ec2 metadata, a.k.a, IMDSv2
+ */
+ private final Boolean disableEc2Metadata;
+ /**
+ * Allow anonymous will allow opendal to send request without signing
+ * when credential is not loaded.
+ */
+ private final Boolean allowAnonymous;
+ /**
+ * server_side_encryption for this backend.
+ * <p>
+ * Available values: `AES256`, `aws:kms`.
+ */
+ private final String serverSideEncryption;
+ /**
+ * server_side_encryption_aws_kms_key_id for this backend
+ * <p>
+ * - If `server_side_encryption` set to `aws:kms`, and
`server_side_encryption_aws_kms_key_id`
+ * is not set, S3 will use aws managed kms key to encrypt data.
+ * - If `server_side_encryption` set to `aws:kms`, and
`server_side_encryption_aws_kms_key_id`
+ * is a valid kms key id, S3 will use the provided kms key to encrypt
data.
+ * - If the `server_side_encryption_aws_kms_key_id` is invalid or not
found, an error will be
+ * returned.
+ * - If `server_side_encryption` is not `aws:kms`, setting
`server_side_encryption_aws_kms_key_id`
+ * is a noop.
+ */
+ private final String serverSideEncryptionAwsKmsKeyId;
+ /**
+ * server_side_encryption_customer_algorithm for this backend.
+ * <p>
+ * Available values: `AES256`.
+ */
+ private final String serverSideEncryptionCustomerAlgorithm;
+ /**
+ * server_side_encryption_customer_key for this backend.
+ * <p>
+ * # Value
+ * <p>
+ * base64 encoded key that matches algorithm specified in
+ * `server_side_encryption_customer_algorithm`.
+ */
+ private final String serverSideEncryptionCustomerKey;
+ /**
+ * Set server_side_encryption_customer_key_md5 for this backend.
+ * <p>
+ * # Value
+ * <p>
+ * MD5 digest of key specified in
`server_side_encryption_customer_key`.
+ */
+ private final String serverSideEncryptionCustomerKeyMd5;
+ /**
+ * default storage_class for this backend.
+ * <p>
+ * Available values:
+ * - `DEEP_ARCHIVE`
+ * - `GLACIER`
+ * - `GLACIER_IR`
+ * - `INTELLIGENT_TIERING`
+ * - `ONEZONE_IA`
+ * - `OUTPOSTS`
+ * - `REDUCED_REDUNDANCY`
+ * - `STANDARD`
+ * - `STANDARD_IA`
+ * <p>
+ * S3 compatible services don't support all of them
+ */
+ private final String defaultStorageClass;
+ /**
+ * Enable virtual host style so that opendal will send API requests
+ * in virtual host style instead of path style.
+ * <p>
+ * - By default, opendal will send API to
`https://s3.us-east-1.amazonaws.com/bucket_name`
+ * - Enabled, opendal will send API to
`https://bucket_name.s3.us-east-1.amazonaws.com`
+ */
+ private final Boolean enableVirtualHostStyle;
+ /**
+ * Set maximum batch operations of this backend.
+ * <p>
+ * Some compatible services have a limit on the number of operations
in a batch request.
+ * For example, R2 could return `Internal Error` while batch delete
1000 files.
+ * <p>
+ * Please tune this value based on services' document.
+ *
+ * @deprecated Please use `delete_max_size` instead of
`batch_max_operations`
+ */
+ private final Long batchMaxOperations;
+ /**
+ * Set the maximum delete size of this backend.
+ * <p>
+ * Some compatible services have a limit on the number of operations
in a batch request.
+ * For example, R2 could return `Internal Error` while batch delete
1000 files.
+ * <p>
+ * Please tune this value based on services' document.
+ */
+ private final Long deleteMaxSize;
+ /**
+ * Disable stat with override so that opendal will not send stat
request with override queries.
+ * <p>
+ * For example, R2 doesn't support stat with `response_content_type`
query.
+ */
+ private final Boolean disableStatWithOverride;
+ /**
+ * Checksum Algorithm to use when sending checksums in HTTP headers.
+ * This is necessary when writing to AWS S3 Buckets with Object Lock
enabled for example.
+ * <p>
+ * Available options:
+ * - "crc32c"
+ */
+ private final String checksumAlgorithm;
+ /**
+ * Disable write with if match so that opendal will not send write
request with if match headers.
+ * <p>
+ * For example, Ceph RADOS S3 doesn't support write with if match.
+ */
+ private final Boolean disableWriteWithIfMatch;
+
+ @Override
+ public String scheme() {
+ return "s3";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ map.put("bucket", bucket);
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (enableVersioning != null) {
+ map.put("enable_versioning", String.valueOf(enableVersioning));
+ }
+ if (endpoint != null) {
+ map.put("endpoint", endpoint);
+ }
+ if (region != null) {
+ map.put("region", region);
+ }
+ if (accessKeyId != null) {
+ map.put("access_key_id", accessKeyId);
+ }
+ if (secretAccessKey != null) {
+ map.put("secret_access_key", secretAccessKey);
+ }
+ if (sessionToken != null) {
+ map.put("session_token", sessionToken);
+ }
+ if (roleArn != null) {
+ map.put("role_arn", roleArn);
+ }
+ if (externalId != null) {
+ map.put("external_id", externalId);
+ }
+ if (roleSessionName != null) {
+ map.put("role_session_name", roleSessionName);
+ }
+ if (disableConfigLoad != null) {
+ map.put("disable_config_load",
String.valueOf(disableConfigLoad));
+ }
+ if (disableEc2Metadata != null) {
+ map.put("disable_ec2_metadata",
String.valueOf(disableEc2Metadata));
+ }
+ if (allowAnonymous != null) {
+ map.put("allow_anonymous", String.valueOf(allowAnonymous));
+ }
+ if (serverSideEncryption != null) {
+ map.put("server_side_encryption", serverSideEncryption);
+ }
+ if (serverSideEncryptionAwsKmsKeyId != null) {
+ map.put("server_side_encryption_aws_kms_key_id",
serverSideEncryptionAwsKmsKeyId);
+ }
+ if (serverSideEncryptionCustomerAlgorithm != null) {
+ map.put("server_side_encryption_customer_algorithm",
serverSideEncryptionCustomerAlgorithm);
+ }
+ if (serverSideEncryptionCustomerKey != null) {
+ map.put("server_side_encryption_customer_key",
serverSideEncryptionCustomerKey);
+ }
+ if (serverSideEncryptionCustomerKeyMd5 != null) {
+ map.put("server_side_encryption_customer_key_md5",
serverSideEncryptionCustomerKeyMd5);
+ }
+ if (defaultStorageClass != null) {
+ map.put("default_storage_class", defaultStorageClass);
+ }
+ if (enableVirtualHostStyle != null) {
+ map.put("enable_virtual_host_style",
String.valueOf(enableVirtualHostStyle));
+ }
+ if (batchMaxOperations != null) {
+ map.put("batch_max_operations",
String.valueOf(batchMaxOperations));
+ }
+ if (deleteMaxSize != null) {
+ map.put("delete_max_size", String.valueOf(deleteMaxSize));
+ }
+ if (disableStatWithOverride != null) {
+ map.put("disable_stat_with_override",
String.valueOf(disableStatWithOverride));
+ }
+ if (checksumAlgorithm != null) {
+ map.put("checksum_algorithm", checksumAlgorithm);
+ }
+ if (disableWriteWithIfMatch != null) {
+ map.put("disable_write_with_if_match",
String.valueOf(disableWriteWithIfMatch));
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Seafile implements ServiceConfig {
+ /**
+ * repo_name of this backend.
+ * <p>
+ * required.
+ */
+ private final @NonNull String repoName;
+ /**
+ * root of this backend.
+ * <p>
+ * All operations will happen under this root.
+ */
+ private final String root;
+ /**
+ * endpoint address of this backend.
+ */
+ private final String endpoint;
+ /**
+ * username of this backend.
+ */
+ private final String username;
+ /**
+ * password of this backend.
+ */
+ private final String password;
+
+ @Override
+ public String scheme() {
+ return "seafile";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ map.put("repo_name", repoName);
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (endpoint != null) {
+ map.put("endpoint", endpoint);
+ }
+ if (username != null) {
+ map.put("username", username);
+ }
+ if (password != null) {
+ map.put("password", password);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Sftp implements ServiceConfig {
+ /**
+ * endpoint of this backend
+ */
+ private final String endpoint;
+ /**
+ * root of this backend
+ */
+ private final String root;
+ /**
+ * user of this backend
+ */
+ private final String user;
+ /**
+ * key of this backend
+ */
+ private final String key;
+ /**
+ * known_hosts_strategy of this backend
+ */
+ private final String knownHostsStrategy;
+ /**
+ * enable_copy of this backend
+ */
+ private final Boolean enableCopy;
+
+ @Override
+ public String scheme() {
+ return "sftp";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (endpoint != null) {
+ map.put("endpoint", endpoint);
+ }
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (user != null) {
+ map.put("user", user);
+ }
+ if (key != null) {
+ map.put("key", key);
+ }
+ if (knownHostsStrategy != null) {
+ map.put("known_hosts_strategy", knownHostsStrategy);
+ }
+ if (enableCopy != null) {
+ map.put("enable_copy", String.valueOf(enableCopy));
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Sled implements ServiceConfig {
+ /**
+ * That path to the sled data directory.
+ */
+ private final String datadir;
+ /**
+ * The root for sled.
+ */
+ private final String root;
+ /**
+ * The tree for sled.
+ */
+ private final String tree;
+
+ @Override
+ public String scheme() {
+ return "sled";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (datadir != null) {
+ map.put("datadir", datadir);
+ }
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (tree != null) {
+ map.put("tree", tree);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Sqlite implements ServiceConfig {
+ /**
+ * Set the connection_string of the sqlite service.
+ * <p>
+ * This connection string is used to connect to the sqlite service.
There are url based formats:
+ * <p>
+ * ## Url
+ * <p>
+ * This format resembles the url format of the sqlite client:
+ * <p>
+ * - `sqlite::memory:`
+ * - `sqlite:data.db`
+ * - `sqlite://data.db`
+ * <p>
+ * For more information, please visit
<https://docs.rs/sqlx/latest/sqlx/sqlite/struct.SqliteConnectOptions.html>.
+ */
+ private final String connectionString;
+ /**
+ * Set the table name of the sqlite service to read/write.
+ */
+ private final String table;
+ /**
+ * Set the key field name of the sqlite service to read/write.
+ * <p>
+ * Default to `key` if not specified.
+ */
+ private final String keyField;
+ /**
+ * Set the value field name of the sqlite service to read/write.
+ * <p>
+ * Default to `value` if not specified.
+ */
+ private final String valueField;
+ /**
+ * set the working directory, all operations will be performed under
it.
+ * <p>
+ * default: "/"
+ */
+ private final String root;
+
+ @Override
+ public String scheme() {
+ return "sqlite";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (connectionString != null) {
+ map.put("connection_string", connectionString);
+ }
+ if (table != null) {
+ map.put("table", table);
+ }
+ if (keyField != null) {
+ map.put("key_field", keyField);
+ }
+ if (valueField != null) {
+ map.put("value_field", valueField);
+ }
+ if (root != null) {
+ map.put("root", root);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Supabase implements ServiceConfig {
+ /**
+ * The bucket for supabase service.
+ */
+ private final @NonNull String bucket;
+ /**
+ * The root for supabase service.
+ */
+ private final String root;
+ /**
+ * The endpoint for supabase service.
+ */
+ private final String endpoint;
+ /**
+ * The key for supabase service.
+ */
+ private final String key;
+
+ @Override
+ public String scheme() {
+ return "supabase";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ map.put("bucket", bucket);
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (endpoint != null) {
+ map.put("endpoint", endpoint);
+ }
+ if (key != null) {
+ map.put("key", key);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Surrealdb implements ServiceConfig {
+ /**
+ * The connection string for surrealdb.
+ */
+ private final String connectionString;
+ /**
+ * The username for surrealdb.
+ */
+ private final String username;
+ /**
+ * The password for surrealdb.
+ */
+ private final String password;
+ /**
+ * The namespace for surrealdb.
+ */
+ private final String namespace;
+ /**
+ * The database for surrealdb.
+ */
+ private final String database;
+ /**
+ * The table for surrealdb.
+ */
+ private final String table;
+ /**
+ * The key field for surrealdb.
+ */
+ private final String keyField;
+ /**
+ * The value field for surrealdb.
+ */
+ private final String valueField;
+ /**
+ * The root for surrealdb.
+ */
+ private final String root;
+
+ @Override
+ public String scheme() {
+ return "surrealdb";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (connectionString != null) {
+ map.put("connection_string", connectionString);
+ }
+ if (username != null) {
+ map.put("username", username);
+ }
+ if (password != null) {
+ map.put("password", password);
+ }
+ if (namespace != null) {
+ map.put("namespace", namespace);
+ }
+ if (database != null) {
+ map.put("database", database);
+ }
+ if (table != null) {
+ map.put("table", table);
+ }
+ if (keyField != null) {
+ map.put("key_field", keyField);
+ }
+ if (valueField != null) {
+ map.put("value_field", valueField);
+ }
+ if (root != null) {
+ map.put("root", root);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Swift implements ServiceConfig {
+ /**
+ * The endpoint for Swift.
+ */
+ private final String endpoint;
+ /**
+ * The container for Swift.
+ */
+ private final String container;
+ /**
+ * The root for Swift.
+ */
+ private final String root;
+ /**
+ * The token for Swift.
+ */
+ private final String token;
+
+ @Override
+ public String scheme() {
+ return "swift";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (endpoint != null) {
+ map.put("endpoint", endpoint);
+ }
+ if (container != null) {
+ map.put("container", container);
+ }
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (token != null) {
+ map.put("token", token);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Upyun implements ServiceConfig {
+ /**
+ * bucket address of this backend.
+ */
+ private final @NonNull String bucket;
+ /**
+ * root of this backend.
+ * <p>
+ * All operations will happen under this root.
+ */
+ private final String root;
+ /**
+ * username of this backend.
+ */
+ private final String operator;
+ /**
+ * password of this backend.
+ */
+ private final String password;
+
+ @Override
+ public String scheme() {
+ return "upyun";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ map.put("bucket", bucket);
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (operator != null) {
+ map.put("operator", operator);
+ }
+ if (password != null) {
+ map.put("password", password);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class VercelArtifacts implements ServiceConfig {
+ /**
+ * The access token for Vercel.
+ */
+ private final String accessToken;
+
+ @Override
+ public String scheme() {
+ return "vercel_artifacts";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (accessToken != null) {
+ map.put("access_token", accessToken);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class VercelBlob implements ServiceConfig {
+ /**
+ * root of this backend.
+ * <p>
+ * All operations will happen under this root.
+ */
+ private final String root;
+ /**
+ * vercel blob token.
+ */
+ private final String token;
+
+ @Override
+ public String scheme() {
+ return "vercel_blob";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (token != null) {
+ map.put("token", token);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Webdav implements ServiceConfig {
+ /**
+ * endpoint of this backend
+ */
+ private final String endpoint;
+ /**
+ * username of this backend
+ */
+ private final String username;
+ /**
+ * password of this backend
+ */
+ private final String password;
+ /**
+ * token of this backend
+ */
+ private final String token;
+ /**
+ * root of this backend
+ */
+ private final String root;
+ /**
+ * WebDAV Service doesn't support copy.
+ */
+ private final Boolean disableCopy;
+
+ @Override
+ public String scheme() {
+ return "webdav";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (endpoint != null) {
+ map.put("endpoint", endpoint);
+ }
+ if (username != null) {
+ map.put("username", username);
+ }
+ if (password != null) {
+ map.put("password", password);
+ }
+ if (token != null) {
+ map.put("token", token);
+ }
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (disableCopy != null) {
+ map.put("disable_copy", String.valueOf(disableCopy));
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class Webhdfs implements ServiceConfig {
+ /**
+ * Root for webhdfs.
+ */
+ private final String root;
+ /**
+ * Endpoint for webhdfs.
+ */
+ private final String endpoint;
+ /**
+ * Delegation token for webhdfs.
+ */
+ private final String delegation;
+ /**
+ * Disable batch listing
+ */
+ private final Boolean disableListBatch;
+ /**
+ * atomic_write_dir of this backend
+ */
+ private final String atomicWriteDir;
+
+ @Override
+ public String scheme() {
+ return "webhdfs";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ if (root != null) {
+ map.put("root", root);
+ }
+ if (endpoint != null) {
+ map.put("endpoint", endpoint);
+ }
+ if (delegation != null) {
+ map.put("delegation", delegation);
+ }
+ if (disableListBatch != null) {
+ map.put("disable_list_batch",
String.valueOf(disableListBatch));
+ }
+ if (atomicWriteDir != null) {
+ map.put("atomic_write_dir", atomicWriteDir);
+ }
+ return map;
+ }
+ }
+
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class YandexDisk implements ServiceConfig {
+ /**
+ * yandex disk oauth access_token.
+ */
+ private final @NonNull String accessToken;
+ /**
+ * root of this backend.
+ * <p>
+ * All operations will happen under this root.
+ */
+ private final String root;
+
+ @Override
+ public String scheme() {
+ return "yandex_disk";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ map.put("access_token", accessToken);
+ if (root != null) {
+ map.put("root", root);
+ }
+ return map;
+ }
+ }
+}
diff --git
a/bindings/java/src/test/java/org/apache/opendal/test/AsyncExecutorTest.java
b/bindings/java/src/test/java/org/apache/opendal/test/AsyncExecutorTest.java
index c776e8b75..23241c868 100644
--- a/bindings/java/src/test/java/org/apache/opendal/test/AsyncExecutorTest.java
+++ b/bindings/java/src/test/java/org/apache/opendal/test/AsyncExecutorTest.java
@@ -21,21 +21,20 @@ package org.apache.opendal.test;
import static org.assertj.core.api.Assertions.assertThat;
import java.nio.charset.StandardCharsets;
-import java.util.HashMap;
-import java.util.Map;
import lombok.Cleanup;
import org.apache.opendal.AsyncExecutor;
import org.apache.opendal.AsyncOperator;
+import org.apache.opendal.ServiceConfig;
import org.junit.jupiter.api.Test;
public class AsyncExecutorTest {
@Test
void testDedicatedTokioExecutor() {
- final Map<String, String> conf = new HashMap<>();
- conf.put("root", "/opendal/");
+ final ServiceConfig.Memory memory =
+ ServiceConfig.Memory.builder().root("/opendal/").build();
final int cores = Runtime.getRuntime().availableProcessors();
@Cleanup final AsyncExecutor executor =
AsyncExecutor.createTokioExecutor(cores);
- @Cleanup final AsyncOperator op = AsyncOperator.of("memory", conf,
executor);
+ @Cleanup final AsyncOperator op = AsyncOperator.of(memory, executor);
assertThat(op.info).isNotNull();
final String key = "key";
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 ed2ab4a1d..98b7ff1a9 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
@@ -20,11 +20,10 @@
package org.apache.opendal.test;
import static org.assertj.core.api.Assertions.assertThat;
-import java.util.HashMap;
-import java.util.Map;
import lombok.Cleanup;
import org.apache.opendal.AsyncOperator;
import org.apache.opendal.Layer;
+import org.apache.opendal.ServiceConfig;
import org.apache.opendal.layer.ConcurrentLimitLayer;
import org.apache.opendal.layer.RetryLayer;
import org.junit.jupiter.api.Test;
@@ -32,20 +31,20 @@ import org.junit.jupiter.api.Test;
public class LayerTest {
@Test
void testOperatorWithRetryLayer() {
- final Map<String, String> conf = new HashMap<>();
- conf.put("root", "/opendal/");
+ final ServiceConfig.Memory memory =
+ ServiceConfig.Memory.builder().root("/opendal/").build();
final Layer retryLayer = RetryLayer.builder().build();
- @Cleanup final AsyncOperator op = AsyncOperator.of("memory", conf);
+ @Cleanup final AsyncOperator op = AsyncOperator.of(memory);
@Cleanup final AsyncOperator layeredOp = op.layer(retryLayer);
assertThat(layeredOp.info).isNotNull();
}
@Test
void testOperatorWithConcurrentLimitLayer() {
- final Map<String, String> conf = new HashMap<>();
- conf.put("root", "/opendal/");
+ final ServiceConfig.Memory memory =
+ ServiceConfig.Memory.builder().root("/opendal/").build();
final Layer concurrentLimitLayer = new ConcurrentLimitLayer(1024);
- @Cleanup final AsyncOperator op = AsyncOperator.of("memory", conf);
+ @Cleanup final AsyncOperator op = AsyncOperator.of(memory);
@Cleanup final AsyncOperator layeredOp =
op.layer(concurrentLimitLayer);
assertThat(layeredOp.info).isNotNull();
}
diff --git
a/bindings/java/src/test/java/org/apache/opendal/test/MetadataTest.java
b/bindings/java/src/test/java/org/apache/opendal/test/MetadataTest.java
index 821390a12..06c0009cd 100644
--- a/bindings/java/src/test/java/org/apache/opendal/test/MetadataTest.java
+++ b/bindings/java/src/test/java/org/apache/opendal/test/MetadataTest.java
@@ -23,12 +23,11 @@ import static
org.apache.opendal.test.behavior.BehaviorTestBase.generateBytes;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.nio.file.Path;
-import java.util.HashMap;
-import java.util.Map;
import java.util.UUID;
import org.apache.opendal.AsyncOperator;
import org.apache.opendal.Metadata;
import org.apache.opendal.Operator;
+import org.apache.opendal.ServiceConfig;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
@@ -38,10 +37,10 @@ public class MetadataTest {
@Test
public void testAsyncMetadata() {
- final Map<String, String> conf = new HashMap<>();
- conf.put("root", tempDir.toString());
+ final ServiceConfig.Fs fs =
+ ServiceConfig.Fs.builder().root(tempDir.toString()).build();
- try (final AsyncOperator op = AsyncOperator.of("fs", conf)) {
+ try (final AsyncOperator op = AsyncOperator.of(fs)) {
final String dir = UUID.randomUUID() + "/";
op.createDir(dir).join();
final Metadata dirMetadata = op.stat(dir).join();
@@ -69,10 +68,10 @@ public class MetadataTest {
@Test
public void testBlockingMetadata() {
- final Map<String, String> conf = new HashMap<>();
- conf.put("root", tempDir.toString());
+ final ServiceConfig.Fs fs =
+ ServiceConfig.Fs.builder().root(tempDir.toString()).build();
- try (final Operator op = Operator.of("fs", conf)) {
+ try (final Operator op = Operator.of(fs)) {
final String dir = UUID.randomUUID() + "/";
op.createDir(dir);
final Metadata dirMetadata = op.stat(dir);
diff --git
a/bindings/java/src/test/java/org/apache/opendal/test/OperatorInfoTest.java
b/bindings/java/src/test/java/org/apache/opendal/test/OperatorInfoTest.java
index db48d22da..511c998b4 100644
--- a/bindings/java/src/test/java/org/apache/opendal/test/OperatorInfoTest.java
+++ b/bindings/java/src/test/java/org/apache/opendal/test/OperatorInfoTest.java
@@ -21,11 +21,10 @@ package org.apache.opendal.test;
import static org.assertj.core.api.Assertions.assertThat;
import java.nio.file.Path;
-import java.util.HashMap;
-import java.util.Map;
import org.apache.opendal.AsyncOperator;
import org.apache.opendal.Operator;
import org.apache.opendal.OperatorInfo;
+import org.apache.opendal.ServiceConfig;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
@@ -35,10 +34,10 @@ public class OperatorInfoTest {
@Test
public void testBlockingOperatorInfo() {
- final Map<String, String> conf = new HashMap<>();
- conf.put("root", tempDir.toString());
+ final ServiceConfig.Fs fs =
+ ServiceConfig.Fs.builder().root(tempDir.toString()).build();
- try (final Operator op = Operator.of("fs", conf)) {
+ try (final Operator op = Operator.of(fs)) {
final OperatorInfo info = op.info;
assertThat(info).isNotNull();
assertThat(info.scheme).isEqualTo("fs");
@@ -57,9 +56,9 @@ public class OperatorInfoTest {
@Test
public void testOperatorInfo() {
- final Map<String, String> conf = new HashMap<>();
- conf.put("root", "/opendal/");
- try (final AsyncOperator op = AsyncOperator.of("memory", conf)) {
+ final ServiceConfig.Memory memory =
+ ServiceConfig.Memory.builder().root("/opendal/").build();
+ try (final AsyncOperator op = AsyncOperator.of(memory)) {
final OperatorInfo info = op.info;
assertThat(info).isNotNull();
assertThat(info.scheme).isEqualTo("memory");
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 13de860ca..bfbbd2244 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
@@ -23,13 +23,12 @@ import static org.assertj.core.api.Assertions.assertThat;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.file.Path;
-import java.util.HashMap;
-import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Stream;
import org.apache.opendal.Operator;
import org.apache.opendal.OperatorInputStream;
import org.apache.opendal.OperatorOutputStream;
+import org.apache.opendal.ServiceConfig;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
@@ -39,10 +38,10 @@ public class OperatorInputOutputStreamTest {
@Test
void testReadWriteWithStream() throws Exception {
- final Map<String, String> conf = new HashMap<>();
- conf.put("root", tempDir.toString());
+ final ServiceConfig.Fs fs =
+ ServiceConfig.Fs.builder().root(tempDir.toString()).build();
- try (final Operator op = Operator.of("fs", conf)) {
+ try (final Operator op = Operator.of(fs)) {
final String path = "OperatorInputOutputStreamTest.txt";
final long multi = 1024 * 1024;
diff --git
a/bindings/java/src/test/java/org/apache/opendal/test/OperatorUtf8DecodeTest.java
b/bindings/java/src/test/java/org/apache/opendal/test/OperatorUtf8DecodeTest.java
index 3c7d6fbbf..031b411a7 100644
---
a/bindings/java/src/test/java/org/apache/opendal/test/OperatorUtf8DecodeTest.java
+++
b/bindings/java/src/test/java/org/apache/opendal/test/OperatorUtf8DecodeTest.java
@@ -21,10 +21,9 @@ package org.apache.opendal.test;
import static org.assertj.core.api.Assertions.assertThat;
import java.nio.file.Path;
-import java.util.HashMap;
-import java.util.Map;
import org.apache.opendal.Metadata;
import org.apache.opendal.Operator;
+import org.apache.opendal.ServiceConfig;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
@@ -39,10 +38,10 @@ public class OperatorUtf8DecodeTest {
*/
@Test
public void testWriteFileWithNonAsciiName() {
- final Map<String, String> conf = new HashMap<>();
- conf.put("root", tempDir.toString());
+ final ServiceConfig.Fs fs =
+ ServiceConfig.Fs.builder().root(tempDir.toString()).build();
- try (final Operator op = Operator.of("fs", conf)) {
+ try (final Operator op = Operator.of(fs)) {
final String path = "βπ±δΈζ.test";
final byte[] content = "βπ±δΈζ".getBytes();
op.write(path, content);
diff --git a/dev/Cargo.lock b/dev/Cargo.lock
index 9557672df..ec2ffb62d 100644
--- a/dev/Cargo.lock
+++ b/dev/Cargo.lock
@@ -187,6 +187,7 @@ dependencies = [
"anyhow",
"clap",
"env_logger",
+ "heck",
"log",
"minijinja",
"pretty_assertions",
diff --git a/dev/Cargo.toml b/dev/Cargo.toml
index 2acaeecae..1ff7ab62b 100644
--- a/dev/Cargo.toml
+++ b/dev/Cargo.toml
@@ -32,6 +32,7 @@ anyhow = { version = "1.0.95" }
clap = { version = "4.5.23", features = ["derive"] }
env_logger = { version = "0.11.5" }
log = { version = "0.4.22" }
+heck = { version = "0.5.0" }
minijinja = { version = "2.5.0" }
serde = { version = "1.0.216", features = ["derive"] }
syn = { version = "2.0.91", features = ["visit", "full", "extra-traits"] }
diff --git a/dev/src/generate/java.j2 b/dev/src/generate/java.j2
new file mode 100644
index 000000000..7c98469a2
--- /dev/null
+++ b/dev/src/generate/java.j2
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+// DO NOT EDIT IT MANUALLY. This file is generated by
opendal/dev/generate/java.rs.
+
+package org.apache.opendal;
+
+import java.time.Duration;
+import java.util.HashMap;
+import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * Service configurations that are mapped from
+ * <a
href="https://docs.rs/opendal/latest/opendal/services/index.html">OpenDAL's
services</a>.
+ */
+@SuppressWarnings("unused") // intended to be used by users
+public interface ServiceConfig {
+ String scheme();
+
+ HashMap<String, String> configMap();
+
+ {% for srv in srvs %}
+ @Builder
+ @Data
+ @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
+ class {{srv | case_java_class_name}} implements ServiceConfig {
+ {%- for field in srvs[srv].config %}
+ /**
+ * {{field.comments}}
+ {%- if field.deprecated %}
+ * @deprecated {{field.deprecated["note"]}}
+ {%- endif %}
+ */
+ {{make_field(field)}}
+ {%- endfor %}
+
+ @Override
+ public String scheme() {
+ return "{{srv}}";
+ }
+
+ @Override
+ public HashMap<String, String> configMap() {
+ final HashMap<String, String> map = new HashMap<>();
+ {% for field in srvs[srv].config -%}
+ {{make_populate_map(field)}}
+ {% endfor -%}
+ return map;
+ }
+ }
+ {% endfor %}
+}
diff --git a/dev/src/generate/java.rs b/dev/src/generate/java.rs
new file mode 100644
index 000000000..2abd51760
--- /dev/null
+++ b/dev/src/generate/java.rs
@@ -0,0 +1,122 @@
+// 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.
+
+use crate::generate::parser::{sorted_services, Config, ConfigType, Services};
+use anyhow::Result;
+use minijinja::value::ViaDeserialize;
+use minijinja::{context, Environment};
+use std::fs;
+use std::path::PathBuf;
+
+fn enabled_service(srv: &str) -> bool {
+ match srv {
+ // not enabled in bindings/java/Cargo.toml
+ "foundationdb" | "ftp" | "hdfs" | "rocksdb" | "tikv" => false,
+ _ => true,
+ }
+}
+
+pub fn generate(workspace_dir: PathBuf, services: Services) -> Result<()> {
+ let srvs = sorted_services(services, enabled_service);
+ let mut env = Environment::new();
+ env.add_template("java", include_str!("java.j2"))?;
+ env.add_function("make_field", make_field);
+ env.add_function("make_populate_map", make_populate_map);
+ env.add_filter("case_java_class_name", case_java_class_name);
+ let tmpl = env.get_template("java")?;
+
+ let output =
+
workspace_dir.join("bindings/java/src/main/java/org/apache/opendal/ServiceConfig.java");
+ fs::write(output, tmpl.render(context! { srvs => srvs })?)?;
+ Ok(())
+}
+
+fn case_java_class_name(s: String) -> String {
+ heck::AsUpperCamelCase(s).to_string()
+}
+
+fn case_java_field_name(s: String) -> String {
+ heck::AsLowerCamelCase(s).to_string()
+}
+
+fn make_field(field: ViaDeserialize<Config>) -> Result<String,
minijinja::Error> {
+ let field_type = if field.optional {
+ match field.value {
+ ConfigType::Bool => "Boolean",
+ ConfigType::String => "String",
+ ConfigType::Duration => "Duration",
+ ConfigType::Usize | ConfigType::U64 | ConfigType::I64 => "Long",
+ ConfigType::U32 | ConfigType::U16 => "Integer",
+ ConfigType::Vec => "List<String>",
+ }
+ } else {
+ match field.value {
+ ConfigType::Bool => "boolean",
+ ConfigType::String => "@NonNull String",
+ ConfigType::Duration => "@NonNull Duration",
+ ConfigType::Usize | ConfigType::U64 | ConfigType::I64 => "long",
+ ConfigType::U32 | ConfigType::U16 => "int",
+ ConfigType::Vec => "@NonNull List<String>",
+ }
+ };
+
+ Ok(format!(
+ "private final {} {};",
+ field_type,
+ case_java_field_name(field.name.clone())
+ ))
+}
+
+fn make_populate_map(field: ViaDeserialize<Config>) -> Result<String,
minijinja::Error> {
+ let populate = match field.value {
+ ConfigType::Usize
+ | ConfigType::U64
+ | ConfigType::I64
+ | ConfigType::Bool
+ | ConfigType::U32
+ | ConfigType::U16 => format!(
+ "map.put(\"{}\", String.valueOf({}));",
+ field.name,
+ case_java_field_name(field.name.clone())
+ ),
+ ConfigType::String => format!(
+ "map.put(\"{}\", {});",
+ field.name,
+ case_java_field_name(field.name.clone())
+ ),
+ ConfigType::Duration => format!(
+ "map.put(\"{}\", {}.toString());",
+ field.name,
+ case_java_field_name(field.name.clone())
+ ),
+ ConfigType::Vec => format!(
+ "map.put(\"{}\", String.join(\",\", {}));",
+ field.name,
+ case_java_field_name(field.name.clone())
+ ),
+ };
+
+ if field.optional {
+ Ok(format!(
+ "if ({} != null) {{\n {}\n}}",
+ case_java_field_name(field.name.clone()),
+ populate
+ ))
+ } else {
+ Ok(populate)
+ }
+}
diff --git a/dev/src/generate/mod.rs b/dev/src/generate/mod.rs
index 780f19a74..2a36ce765 100644
--- a/dev/src/generate/mod.rs
+++ b/dev/src/generate/mod.rs
@@ -15,6 +15,7 @@
// specific language governing permissions and limitations
// under the License.
+mod java;
mod parser;
mod python;
@@ -27,6 +28,7 @@ pub fn run(language: &str) -> Result<()> {
let services = parser::parse(&services_path.to_string_lossy())?;
match language {
+ "java" => java::generate(workspace_dir, services),
"python" | "py" => python::generate(workspace_dir, services),
_ => anyhow::bail!("unsupported language: {}", language),
}
diff --git a/dev/src/generate/parser.rs b/dev/src/generate/parser.rs
index 086d7be5a..ee4bc4d4c 100644
--- a/dev/src/generate/parser.rs
+++ b/dev/src/generate/parser.rs
@@ -29,6 +29,21 @@ use syn::{
pub type Services = HashMap<String, Service>;
+pub fn sorted_services(services: Services, test: fn(&str) -> bool) -> Services
{
+ let mut srvs = Services::new();
+ for (k, srv) in services.into_iter() {
+ if !test(k.as_str()) {
+ continue;
+ }
+
+ let mut sorted =
srv.config.into_iter().enumerate().collect::<Vec<_>>();
+ sorted.sort_by_key(|(i, v)| (v.optional, *i));
+ let config = sorted.into_iter().map(|(_, v)| v).collect();
+ srvs.insert(k, Service { config });
+ }
+ srvs
+}
+
/// Service represents a service supported by opendal core, like `s3` and `fs`
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
pub struct Service {
diff --git a/dev/src/generate/python.j2 b/dev/src/generate/python.j2
index 772407d52..49036d278 100644
--- a/dev/src/generate/python.j2
+++ b/dev/src/generate/python.j2
@@ -54,7 +54,7 @@ class _Base:
{% if field.deprecated %}
# deprecated: {{field.deprecated["note"]}}
{%- endif %}
- {{field.name}}: {{make_pytype(field.value)}}{% if field.optional %} =
...{% endif %},
+ {{field.name}}: {{make_python_type(field.value)}}{% if field.optional
%} = ...{% endif %},
{%- endfor %}
) -> None: ...
{% endfor %}
diff --git a/dev/src/generate/python.rs b/dev/src/generate/python.rs
index 268a6e89d..0e852ed0c 100644
--- a/dev/src/generate/python.rs
+++ b/dev/src/generate/python.rs
@@ -15,14 +15,13 @@
// specific language governing permissions and limitations
// under the License.
-use crate::generate::parser::{ConfigType, Service, Services};
+use crate::generate::parser::{sorted_services, ConfigType, Services};
use anyhow::Result;
use minijinja::value::ViaDeserialize;
use minijinja::{context, Environment};
use std::fs;
use std::path::PathBuf;
-/// TODO: add a common utils to parse enabled features from cargo.toml
fn enabled_service(srv: &str) -> bool {
match srv {
// not enabled in bindings/python/Cargo.toml
@@ -32,30 +31,18 @@ fn enabled_service(srv: &str) -> bool {
}
pub fn generate(workspace_dir: PathBuf, services: Services) -> Result<()> {
- let mut srvs = Services::new();
- for (k, srv) in services.into_iter() {
- if !enabled_service(k.as_str()) {
- continue;
- }
-
- let mut sorted =
srv.config.into_iter().enumerate().collect::<Vec<_>>();
- sorted.sort_by_key(|(i, v)| (v.optional, *i));
- let config = sorted.into_iter().map(|(_, v)| v).collect();
- srvs.insert(k, Service { config });
- }
-
+ let srvs = sorted_services(services, enabled_service);
let mut env = Environment::new();
env.add_template("python", include_str!("python.j2"))?;
- env.add_function("make_pytype", make_pytype);
+ env.add_function("make_python_type", make_python_type);
let tmpl = env.get_template("python")?;
let output =
workspace_dir.join("bindings/python/python/opendal/__base.pyi");
fs::write(output, tmpl.render(context! { srvs => srvs })?)?;
-
Ok(())
}
-fn make_pytype(ty: ViaDeserialize<ConfigType>) -> Result<String,
minijinja::Error> {
+fn make_python_type(ty: ViaDeserialize<ConfigType>) -> Result<String,
minijinja::Error> {
Ok(match ty.0 {
ConfigType::Bool => "_bool",
ConfigType::Duration => "_duration",