Add datasource base api and checkstyle (#3)
* Add datasource base api and checkstyle
* Update ThreadSafe.java
* Update pom.xml
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
index aa1e4df..9cf4836 100644
--- a/.mvn/wrapper/maven-wrapper.properties
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -17,3 +17,4 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar
+
diff --git a/datasource-client/pom.xml b/datasource-client/pom.xml
index 7eb4466..c621a0d 100644
--- a/datasource-client/pom.xml
+++ b/datasource-client/pom.xml
@@ -1,4 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
@@ -10,5 +24,27 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>datasource-client</artifactId>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>8</source>
+ <target>8</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>com.fasterxml.jackson.dataformat</groupId>
+ <artifactId>jackson-dataformat-properties</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ </dependency>
+ </dependencies>
-</project>
\ No newline at end of file
+</project>
diff --git a/datasource-client/src/main/java/org/apache/seatunnel/datasource/annotation/ThreadSafe.java b/datasource-client/src/main/java/org/apache/seatunnel/datasource/annotation/ThreadSafe.java
new file mode 100644
index 0000000..e7e9072
--- /dev/null
+++ b/datasource-client/src/main/java/org/apache/seatunnel/datasource/annotation/ThreadSafe.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.datasource.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.CLASS)
+public @interface ThreadSafe {
+}
diff --git a/datasource-client/src/main/java/org/apache/seatunnel/datasource/client/AbstractSeaTunnelDataSourceClient.java b/datasource-client/src/main/java/org/apache/seatunnel/datasource/client/AbstractSeaTunnelDataSourceClient.java
new file mode 100644
index 0000000..97a6a47
--- /dev/null
+++ b/datasource-client/src/main/java/org/apache/seatunnel/datasource/client/AbstractSeaTunnelDataSourceClient.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.datasource.client;
+
+import org.apache.seatunnel.datasource.service.SeaTunnelDataSourceService;
+
+import javax.sql.DataSource;
+
+public abstract class AbstractSeaTunnelDataSourceClient implements SeaTunnelDataSourceService {
+
+ protected DataSource dataSource;
+
+ public AbstractSeaTunnelDataSourceClient(DataSource dataSource) {
+ this.dataSource = dataSource;
+ // need init all plugins?
+ initPlugins();
+ }
+
+ private void initPlugins() {
+ // init all plugins
+ }
+
+}
diff --git a/datasource-client/src/main/java/org/apache/seatunnel/datasource/client/SeaTunnelDataSource.java b/datasource-client/src/main/java/org/apache/seatunnel/datasource/client/SeaTunnelDataSource.java
index 46123e4..278a26c 100644
--- a/datasource-client/src/main/java/org/apache/seatunnel/datasource/client/SeaTunnelDataSource.java
+++ b/datasource-client/src/main/java/org/apache/seatunnel/datasource/client/SeaTunnelDataSource.java
@@ -1,4 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package org.apache.seatunnel.datasource.client;
-public interface SeaTunnelDataSource {
+import org.apache.seatunnel.datasource.service.SeaTunnelAutoTableService;
+import org.apache.seatunnel.datasource.service.SeaTunnelDataSourceService;
+
+import javax.sql.DataSource;
+
+import java.util.concurrent.ConcurrentMap;
+
+public abstract class SeaTunnelDataSource implements SeaTunnelDataSourceService, SeaTunnelAutoTableService {
+
+ /**
+ * The data source. we use this to get the connection.
+ * if you want to use the connection pool, you can use the connection pool to get the connection.
+ */
+ protected DataSource dataSource;
+
+ // do not need to init all plugins?
+ protected ConcurrentMap<String, SeaTunnelAutoTableService> dataSourceMetaDataServicePluginMap;
+
}
diff --git a/datasource-client/src/main/java/org/apache/seatunnel/datasource/client/SeaTunnelDataSourceClient.java b/datasource-client/src/main/java/org/apache/seatunnel/datasource/client/SeaTunnelDataSourceClient.java
new file mode 100644
index 0000000..46dd654
--- /dev/null
+++ b/datasource-client/src/main/java/org/apache/seatunnel/datasource/client/SeaTunnelDataSourceClient.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.datasource.client;
+
+import org.apache.seatunnel.datasource.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.model.DataSource;
+import org.apache.seatunnel.datasource.model.DataSourcePluginInfo;
+import org.apache.seatunnel.datasource.model.Table;
+import org.apache.seatunnel.datasource.service.SeaTunnelAutoTableService;
+import org.apache.seatunnel.datasource.service.SeaTunnelTableService;
+
+import java.util.List;
+import java.util.Map;
+
+public class SeaTunnelDataSourceClient extends AbstractSeaTunnelDataSourceClient implements SeaTunnelTableService, SeaTunnelAutoTableService {
+
+ public SeaTunnelDataSourceClient(javax.sql.DataSource dataSource) {
+ super(dataSource);
+ }
+
+ @Override
+ public List<DataSourcePluginInfo> listAllDataSources() {
+ return null;
+ }
+
+ @Override
+ public OptionRule queryDataSourceFieldByName(String dataSourceName) {
+ return null;
+ }
+
+ @Override
+ public Boolean checkDataSourceFields(Map<String, Object> parameters) {
+ return null;
+ }
+
+ @Override
+ public DataSource queryDataSourceById(Long dataSourceId) {
+ return null;
+ }
+
+ @Override
+ public Boolean saveDataSource(String dataSourceName, String comment, String pluginName, Map<String, Object> parameters) {
+ return false;
+ }
+
+ @Override
+ public Boolean deleteDataSource(Long id) {
+ return false;
+ }
+
+ @Override
+ public Boolean updateDataSource(Long dataSourceId, String dataSourceName, String comment, Map<String, Object> parameters) {
+ return false;
+ }
+
+ @Override
+ public Boolean checkDataSourceConnectivity(Map<String, Object> dataSourceParams) {
+ return null;
+ }
+
+ @Override
+ public OptionRule getDataSourceMetadataFieldsByDataSourceName(String dataSourceName) {
+ return null;
+ }
+
+ @Override
+ public OptionRule getDataSourceMetadataFieldsByDataSourceId(String dataSourceId) {
+ return null;
+ }
+
+ @Override
+ public List<String> getTables(String dataSourceId, Map<String, Object> requestParams) {
+ return null;
+ }
+
+ @Override
+ public List<String> getDatabases(String dataSourceId, Map<String, Object> requestParams) {
+ return null;
+ }
+
+ @Override
+ public Table getTable(String dataSourceId, Map<String, Object> requestParams) {
+ return null;
+ }
+
+ @Override
+ public Boolean createTable(Long datasourceId, String databaseName, String tableName, Map<String, String> tableFields, String description) {
+ return null;
+ }
+
+ @Override
+ public Boolean dropSchema(String schemaId) {
+ return null;
+ }
+
+ @Override
+ public Table getTable(Long datasourceId, String databaseName, String tableName) {
+ return null;
+ }
+
+ @Override
+ public List<String> getTableNames(Long datasourceId, String databaseName) {
+ return null;
+ }
+
+ @Override
+ public List<String> getDatabaseNames(Long datasourceId) {
+ return null;
+ }
+
+ @Override
+ public List<Table> getTables(String datasourceId, String databaseName) {
+ return null;
+ }
+}
diff --git a/datasource-client/src/main/java/org/apache/seatunnel/datasource/client/SimpleSeaTunnelDataSourceClient.java b/datasource-client/src/main/java/org/apache/seatunnel/datasource/client/SimpleSeaTunnelDataSourceClient.java
new file mode 100644
index 0000000..6e1689f
--- /dev/null
+++ b/datasource-client/src/main/java/org/apache/seatunnel/datasource/client/SimpleSeaTunnelDataSourceClient.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.datasource.client;
+
+import org.apache.seatunnel.datasource.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.model.DataSource;
+import org.apache.seatunnel.datasource.model.DataSourcePluginInfo;
+
+import java.util.List;
+import java.util.Map;
+
+public class SimpleSeaTunnelDataSourceClient extends AbstractSeaTunnelDataSourceClient {
+
+ /**
+ * init datasource pool
+ */
+ public SimpleSeaTunnelDataSourceClient(javax.sql.DataSource dataSource) {
+ super(dataSource);
+ }
+
+ @Override
+ public List<DataSourcePluginInfo> listAllDataSources() {
+ return null;
+ }
+
+ @Override
+ public OptionRule queryDataSourceFieldByName(String dataSourceName) {
+ return null;
+ }
+
+ @Override
+ public Boolean checkDataSourceFields(Map<String, Object> parameters) {
+ return null;
+ }
+
+ @Override
+ public DataSource queryDataSourceById(Long dataSourceId) {
+ return null;
+ }
+
+ @Override
+ public Boolean saveDataSource(String dataSourceName, String comment, String pluginName, Map<String, Object> parameters) {
+ return false;
+ }
+
+ @Override
+ public Boolean deleteDataSource(Long id) {
+ return false;
+ }
+
+ @Override
+ public Boolean updateDataSource(Long dataSourceId, String dataSourceName, String comment, Map<String, Object> parameters) {
+ return false;
+ }
+
+ @Override
+ public Boolean checkDataSourceConnectivity(Map<String, Object> dataSourceParams) {
+ return null;
+ }
+}
diff --git a/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/Option.java b/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/Option.java
new file mode 100644
index 0000000..cfe22cb
--- /dev/null
+++ b/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/Option.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.datasource.configuration;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+
+import java.util.Objects;
+
+public class Option<T> {
+ /**
+ * The current key for that config option.
+ */
+ private final String key;
+
+ /**
+ * Type of the value that this Option describes.
+ */
+ private final TypeReference<T> typeReference;
+
+ /**
+ * The default value for this option.
+ */
+ private final T defaultValue;
+
+ /**
+ * The description for this option.
+ */
+ String description = "";
+
+ public Option(String key, TypeReference<T> typeReference, T defaultValue) {
+ this.key = key;
+ this.typeReference = typeReference;
+ this.defaultValue = defaultValue;
+ }
+
+ public String key() {
+ return key;
+ }
+
+ public TypeReference<T> typeReference() {
+ return typeReference;
+ }
+
+ public T defaultValue() {
+ return defaultValue;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public Option<T> withDescription(String description) {
+ this.description = description;
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof Option)) {
+ return false;
+ }
+ Option<?> that = (Option<?>) obj;
+ return Objects.equals(this.key, that.key) && Objects.equals(this.defaultValue, that.defaultValue);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(this.key, this.defaultValue);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Key: '%s', default: %s", key, defaultValue);
+ }
+}
diff --git a/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/Options.java b/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/Options.java
new file mode 100644
index 0000000..89e8cee
--- /dev/null
+++ b/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/Options.java
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.datasource.configuration;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import org.apache.commons.lang3.StringUtils;
+
+import java.lang.reflect.Type;
+import java.time.Duration;
+import java.util.List;
+import java.util.Map;
+
+public class Options {
+
+ /**
+ * Starts building a new {@link Option}.
+ *
+ * @param key The key for the config option.
+ * @return The builder for the config option with the given key.
+ */
+ public static OptionBuilder key(String key) {
+ checkArgument(StringUtils.isNotBlank(key), "Option's key not be null.");
+ return new OptionBuilder(key);
+ }
+
+ /**
+ * The option builder is used to create a {@link Option}. It is instantiated via {@link
+ * Options#key(String)}.
+ */
+ public static final class OptionBuilder {
+ private final String key;
+
+ /**
+ * Creates a new OptionBuilder.
+ *
+ * @param key The key for the config option
+ */
+ OptionBuilder(String key) {
+ this.key = key;
+ }
+
+ /**
+ * Defines that the value of the option should be of {@link Boolean} type.
+ */
+ public TypedOptionBuilder<Boolean> booleanType() {
+ return new TypedOptionBuilder<>(key, new TypeReference<Boolean>() {
+ });
+ }
+
+ /**
+ * Defines that the value of the option should be of {@link Integer} type.
+ */
+ public TypedOptionBuilder<Integer> intType() {
+ return new TypedOptionBuilder<>(key, new TypeReference<Integer>() {
+ });
+ }
+
+ /**
+ * Defines that the value of the option should be of {@link Long} type.
+ */
+ public TypedOptionBuilder<Long> longType() {
+ return new TypedOptionBuilder<>(key, new TypeReference<Long>() {
+ });
+ }
+
+ /**
+ * Defines that the value of the option should be of {@link Float} type.
+ */
+ public TypedOptionBuilder<Float> floatType() {
+ return new TypedOptionBuilder<>(key, new TypeReference<Float>() {
+ });
+ }
+
+ /**
+ * Defines that the value of the option should be of {@link Double} type.
+ */
+ public TypedOptionBuilder<Double> doubleType() {
+ return new TypedOptionBuilder<>(key, new TypeReference<Double>() {
+ });
+ }
+
+ /**
+ * Defines that the value of the option should be of {@link String} type.
+ */
+ public TypedOptionBuilder<String> stringType() {
+ return new TypedOptionBuilder<>(key, new TypeReference<String>() {
+ });
+ }
+
+ /**
+ * Defines that the value of the option should be of {@link Duration} type.
+ */
+ public TypedOptionBuilder<Duration> durationType() {
+ return new TypedOptionBuilder<>(key, new TypeReference<Duration>() {
+ });
+ }
+
+ /**
+ * Defines that the value of the option should be of {@link Enum} type.
+ *
+ * @param enumClass Concrete type of the expected enum.
+ */
+ public <T extends Enum<T>> TypedOptionBuilder<T> enumType(Class<T> enumClass) {
+ return new TypedOptionBuilder<>(key, new TypeReference<T>() {
+ @Override
+ public Type getType() {
+ return enumClass;
+ }
+ });
+ }
+
+ /**
+ * Defines that the value of the option should be a set of properties, which can be
+ * represented as {@code Map<String, String>}.
+ */
+ public TypedOptionBuilder<Map<String, String>> mapType() {
+ return new TypedOptionBuilder<>(key, new TypeReference<Map<String, String>>() {
+ });
+ }
+
+ /**
+ * Defines that the value of the option should be a list of properties, which can be
+ * represented as {@code List<String>}.
+ */
+ public TypedOptionBuilder<List<String>> listType() {
+ return new TypedOptionBuilder<>(key, new TypeReference<List<String>>() {
+ });
+ }
+
+ /**
+ * Defines that the value of the option should be a list of properties, which can be
+ * represented as {@code List<T>}.
+ */
+ public <T> TypedOptionBuilder<List<T>> listType(Class<T> option) {
+ return new TypedOptionBuilder<>(key, new TypeReference<List<T>>() {
+ });
+ }
+
+ public <T> TypedOptionBuilder<T> objectType(Class<T> option) {
+ return new TypedOptionBuilder<>(key, new TypeReference<T>() {
+ @Override
+ public Type getType() {
+ return option;
+ }
+ });
+ }
+
+ /**
+ * The value of the definition option should be represented as T.
+ *
+ * @param typeReference complex type reference
+ */
+ public <T> TypedOptionBuilder<T> type(TypeReference<T> typeReference) {
+ return new TypedOptionBuilder<>(key, typeReference);
+ }
+ }
+
+ /**
+ * Builder for {@link Option} with a defined atomic type.
+ *
+ * @param <T> atomic type of the option
+ */
+ public static class TypedOptionBuilder<T> {
+ private final String key;
+ private final TypeReference<T> typeReference;
+
+ TypedOptionBuilder(String key, TypeReference<T> typeReference) {
+ this.key = key;
+ this.typeReference = typeReference;
+ }
+
+ /**
+ * Creates a Option with the given default value.
+ *
+ * @param value The default value for the config option
+ * @return The config option with the default value.
+ */
+ public Option<T> defaultValue(T value) {
+ return new Option<>(key, typeReference, value);
+ }
+
+ /**
+ * Creates a Option without a default value.
+ *
+ * @return The config option without a default value.
+ */
+ public Option<T> noDefaultValue() {
+ return new Option<>(key, typeReference, null);
+ }
+ }
+}
diff --git a/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/ReadonlyConfig.java b/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/ReadonlyConfig.java
new file mode 100644
index 0000000..6d15148
--- /dev/null
+++ b/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/ReadonlyConfig.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.datasource.configuration;
+
+import static org.apache.seatunnel.datasource.configuration.util.ConfigUtil.convertToJsonString;
+import static org.apache.seatunnel.datasource.configuration.util.ConfigUtil.convertValue;
+import static org.apache.seatunnel.datasource.configuration.util.ConfigUtil.flatteningMap;
+import static org.apache.seatunnel.datasource.configuration.util.ConfigUtil.treeMap;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+public class ReadonlyConfig implements Serializable {
+ private static final long serialVersionUID = 1L;
+ private static final ObjectMapper JACKSON_MAPPER = new ObjectMapper();
+
+ /**
+ * Stores the concrete key/value pairs of this configuration object.
+ */
+ protected final Map<String, Object> confData;
+
+ private ReadonlyConfig(Map<String, Object> confData) {
+ this.confData = confData;
+ }
+
+ public static ReadonlyConfig fromMap(Map<String, Object> map) {
+ return new ReadonlyConfig(treeMap(map));
+ }
+
+ public <T> T get(Option<T> option) {
+ return getOptional(option).orElseGet(option::defaultValue);
+ }
+
+ public Map<String, String> toMap() {
+ if (confData.isEmpty()) {
+ return Collections.emptyMap();
+ }
+
+ Map<String, String> result = new HashMap<>();
+ toMap(result);
+ return result;
+ }
+
+ public void toMap(Map<String, String> result) {
+ if (confData.isEmpty()) {
+ return;
+ }
+ Map<String, Object> flatteningMap = flatteningMap(confData);
+ for (Map.Entry<String, Object> entry : flatteningMap.entrySet()) {
+ result.put(entry.getKey(), convertToJsonString(entry.getValue()));
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> Optional<T> getOptional(Option<T> option) {
+ if (option == null) {
+ throw new NullPointerException("Option not be null.");
+ }
+ String[] keys = option.key().split("\\.");
+ Map<String, Object> data = this.confData;
+ Object value = null;
+ for (int i = 0; i < keys.length; i++) {
+ value = data.get(keys[i]);
+ if (i < keys.length - 1) {
+ if (!(value instanceof Map)) {
+ return Optional.empty();
+ } else {
+ data = (Map<String, Object>) value;
+ }
+ }
+ }
+ if (value == null) {
+ return Optional.empty();
+ }
+ return Optional.of(convertValue(value, option.typeReference()));
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 0;
+ for (String s : this.confData.keySet()) {
+ hash ^= s.hashCode();
+ }
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof ReadonlyConfig)) {
+ return false;
+ }
+ Map<String, Object> otherConf = ((ReadonlyConfig) obj).confData;
+ return this.confData.equals(otherConf);
+ }
+
+ @Override
+ public String toString() {
+ return convertToJsonString(this.confData);
+ }
+}
diff --git a/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/util/Condition.java b/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/util/Condition.java
new file mode 100644
index 0000000..42832ea
--- /dev/null
+++ b/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/util/Condition.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.datasource.configuration.util;
+
+import org.apache.seatunnel.datasource.configuration.Option;
+
+import java.util.Objects;
+
+public class Condition<T> {
+ private final Option<T> option;
+ private final T expectValue;
+ private Boolean and = null;
+ private Condition<?> next = null;
+
+ Condition(Option<T> option, T expectValue) {
+ this.option = option;
+ this.expectValue = expectValue;
+ }
+
+ public static <T> Condition<T> of(Option<T> option, T expectValue) {
+ return new Condition<>(option, expectValue);
+ }
+
+ public <E> Condition<T> and(Option<E> option, E expectValue) {
+ return and(of(option, expectValue));
+ }
+
+ public <E> Condition<T> or(Option<E> option, E expectValue) {
+ return or(of(option, expectValue));
+ }
+
+ public Condition<T> and(Condition<?> next) {
+ addCondition(true, next);
+ return this;
+ }
+
+ public Condition<T> or(Condition<?> next) {
+ addCondition(false, next);
+ return this;
+ }
+
+ private void addCondition(boolean and, Condition<?> next) {
+ Condition<?> tail = getTailCondition();
+ tail.and = and;
+ tail.next = next;
+ }
+
+ protected int getCount() {
+ int i = 1;
+ Condition<?> cur = this;
+ while (cur.hasNext()) {
+ i++;
+ cur = cur.next;
+ }
+ return i;
+ }
+
+ Condition<?> getTailCondition() {
+ return hasNext() ? this.next.getTailCondition() : this;
+ }
+
+ public boolean hasNext() {
+ return this.next != null;
+ }
+
+ public Condition<?> getNext() {
+ return this.next;
+ }
+
+ public Option<T> getOption() {
+ return option;
+ }
+
+ public T getExpectValue() {
+ return expectValue;
+ }
+
+ public Boolean and() {
+ return this.and;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof Condition)) {
+ return false;
+ }
+ Condition<?> that = (Condition<?>) obj;
+ return Objects.equals(this.option, that.option)
+ && Objects.equals(this.expectValue, that.expectValue)
+ && Objects.equals(this.and, that.and)
+ && Objects.equals(this.next, that.next);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(this.option, this.expectValue, this.and, this.next);
+ }
+
+ @Override
+ public String toString() {
+ Condition<?> cur = this;
+ StringBuilder builder = new StringBuilder();
+ boolean bracket = false;
+ do {
+ builder.append("'")
+ .append(cur.option.key())
+ // TODO: support another condition
+ .append("' == ")
+ .append(cur.expectValue);
+ if (bracket) {
+ builder = new StringBuilder(String.format("(%s)", builder));
+ bracket = false;
+ }
+ if (cur.hasNext()) {
+ if (cur.next.hasNext() && !cur.and.equals(cur.next.and)) {
+ bracket = true;
+ }
+ builder.append(cur.and ? " && " : " || ");
+ }
+ cur = cur.next;
+ } while (cur != null);
+ return builder.toString();
+ }
+}
diff --git a/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/util/ConfigUtil.java b/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/util/ConfigUtil.java
new file mode 100644
index 0000000..cfbf331
--- /dev/null
+++ b/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/util/ConfigUtil.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.datasource.configuration.util;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.javaprop.JavaPropsMapper;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+public class ConfigUtil {
+ private static final JavaPropsMapper PROPERTIES_MAPPER = new JavaPropsMapper();
+ private static final ObjectMapper JACKSON_MAPPER = new ObjectMapper();
+
+ /**
+ * <pre>
+ * poll.timeout = 1000
+ * ==>> poll : {timeout = 1000, interval = 500}
+ * poll.interval = 500
+ * </pre>
+ */
+ public static Map<String, Object> treeMap(Object rawMap) {
+ try {
+ return PROPERTIES_MAPPER.readValue(PROPERTIES_MAPPER.writeValueAsString(rawMap), new TypeReference<Map<String, Object>>() {
+ });
+ } catch (JsonProcessingException e) {
+ throw new IllegalArgumentException("Json parsing exception.");
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ static Object flatteningMap(Object rawValue, Map<String, Object> newMap, List<String> keys, boolean nestedMap) {
+ if (rawValue == null) {
+ return null;
+ }
+ if (!(rawValue instanceof List) && !(rawValue instanceof Map)) {
+ if (newMap == null) {
+ return rawValue;
+ }
+ newMap.put(String.join(".", keys), rawValue);
+ return newMap;
+ }
+
+ if (rawValue instanceof List) {
+ List<Object> rawList = (List<Object>) rawValue;
+ rawList.replaceAll(value -> flatteningMap(value, null, null, false));
+ if (newMap != null) {
+ newMap.put(String.join(".", keys), rawList);
+ return newMap;
+ }
+ return rawList;
+ } else {
+ Map<String, Object> rawMap = (Map<String, Object>) rawValue;
+ if (!nestedMap) {
+ keys = new ArrayList<>();
+ newMap = new HashMap<>(rawMap.size());
+ }
+ for (Map.Entry<String, Object> entry : rawMap.entrySet()) {
+ keys.add(entry.getKey());
+ flatteningMap(entry.getValue(), newMap, keys, true);
+ keys.remove(keys.size() - 1);
+ }
+ return newMap;
+ }
+ }
+
+ /**
+ * <pre>
+ * poll.timeout = 1000
+ * poll : {timeout = 1000, interval = 500} ==>>
+ * poll.interval = 500
+ * </pre>
+ */
+ @SuppressWarnings("unchecked")
+ public static Map<String, Object> flatteningMap(Map<String, Object> treeMap) {
+ return (Map<String, Object>) flatteningMapWithObject(treeMap);
+ }
+
+ static Object flatteningMapWithObject(Object rawValue) {
+ return flatteningMap(rawValue, null, null, false);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <T> T convertValue(Object rawValue, TypeReference<T> typeReference) {
+ rawValue = flatteningMapWithObject(rawValue);
+ if (typeReference.getType() instanceof Class) {
+ // simple type
+ Class<T> clazz = (Class<T>) typeReference.getType();
+ if (clazz.equals(rawValue.getClass())) {
+ return (T) rawValue;
+ }
+ try {
+ return convertValue(rawValue, clazz);
+ } catch (IllegalArgumentException e) {
+ // Continue with Jackson parsing
+ }
+ }
+ try {
+ // complex type && untreated type
+ return JACKSON_MAPPER.readValue(convertToJsonString(rawValue), typeReference);
+ } catch (JsonProcessingException e) {
+ throw new IllegalArgumentException(String.format("Json parsing exception, value '%s', and expected type '%s'", rawValue, typeReference.getType().getTypeName()), e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ static <T> T convertValue(Object rawValue, Class<T> clazz) {
+ if (Boolean.class.equals(clazz)) {
+ return (T) convertToBoolean(rawValue);
+ } else if (clazz.isEnum()) {
+ return (T) convertToEnum(rawValue, (Class<? extends Enum<?>>) clazz);
+ } else if (String.class.equals(clazz)) {
+ return (T) convertToJsonString(rawValue);
+ }
+ throw new IllegalArgumentException("Unsupported type: " + clazz);
+ }
+
+ static Boolean convertToBoolean(Object o) {
+ switch (o.toString().toUpperCase()) {
+ case "TRUE":
+ return true;
+ case "FALSE":
+ return false;
+ default:
+ throw new IllegalArgumentException(
+ String.format(
+ "Unrecognized option for boolean: %s. Expected either true or false(case insensitive)",
+ o));
+ }
+ }
+
+ static <E extends Enum<?>> E convertToEnum(Object o, Class<E> clazz) {
+ return Arrays.stream(clazz.getEnumConstants())
+ .filter(e -> e.toString()
+ .toUpperCase(Locale.ROOT)
+ .equals(o.toString().toUpperCase(Locale.ROOT)))
+ .findAny()
+ .orElseThrow(() -> new IllegalArgumentException(String.format(
+ "Could not parse value for enum %s. Expected one of: [%s]",
+ clazz, Arrays.toString(clazz.getEnumConstants()))));
+ }
+
+ public static String convertToJsonString(Object o) {
+ if (o instanceof String) {
+ return (String) o;
+ }
+ try {
+ return JACKSON_MAPPER.writeValueAsString(o);
+ } catch (JsonProcessingException e) {
+ throw new IllegalArgumentException(String.format("Could not parse json, value: %s", o));
+ }
+ }
+}
diff --git a/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/util/Expression.java b/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/util/Expression.java
new file mode 100644
index 0000000..a735bf8
--- /dev/null
+++ b/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/util/Expression.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.datasource.configuration.util;
+
+import org.apache.seatunnel.datasource.configuration.Option;
+
+import java.util.Objects;
+
+public class Expression {
+ private final Condition<?> condition;
+ private Boolean and = null;
+ private Expression next = null;
+
+ Expression(Condition<?> condition) {
+ this.condition = condition;
+ }
+
+ public static <T> Expression of(Option<T> option, T expectValue) {
+ return new Expression(Condition.of(option, expectValue));
+ }
+
+ public static Expression of(Condition<?> condition) {
+ return new Expression(condition);
+ }
+
+ public Expression and(Expression next) {
+ addExpression(true, next);
+ return this;
+ }
+
+ public Expression or(Expression next) {
+ addExpression(false, next);
+ return this;
+ }
+
+ private void addExpression(boolean and, Expression next) {
+ Expression tail = getTailExpression();
+ tail.and = and;
+ tail.next = next;
+ }
+
+ private Expression getTailExpression() {
+ return hasNext() ? this.next.getTailExpression() : this;
+ }
+
+ public Condition<?> getCondition() {
+ return condition;
+ }
+
+ public boolean hasNext() {
+ return this.next != null;
+ }
+
+ public Expression getNext() {
+ return this.next;
+ }
+
+ public Boolean and() {
+ return this.and;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof Expression)) {
+ return false;
+ }
+ Expression that = (Expression) obj;
+ return Objects.equals(this.condition, that.condition)
+ && Objects.equals(this.and, that.and)
+ && Objects.equals(this.next, that.next);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(this.condition, this.and, this.next);
+ }
+
+ @Override
+ public String toString() {
+ Expression cur = this;
+ StringBuilder builder = new StringBuilder();
+ boolean bracket = false;
+ do {
+ if (cur.condition.getCount() > 1) {
+ builder.append("(")
+ .append(cur.condition)
+ .append(")");
+ } else {
+ builder.append(cur.condition);
+ }
+ if (bracket) {
+ builder = new StringBuilder(String.format("(%s)", builder));
+ bracket = false;
+ }
+ if (cur.hasNext()) {
+ if (cur.next.hasNext() && !cur.and.equals(cur.next.and)) {
+ bracket = true;
+ }
+ builder.append(cur.and ? " && " : " || ");
+ }
+ cur = cur.next;
+ } while (cur != null);
+ return builder.toString();
+ }
+}
diff --git a/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/util/OptionMark.java b/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/util/OptionMark.java
new file mode 100644
index 0000000..ff90309
--- /dev/null
+++ b/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/util/OptionMark.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.datasource.configuration.util;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Target(ElementType.FIELD)
+public @interface OptionMark {
+
+ /**
+ * The key of the option, if not configured, we will default convert `lowerCamelCase` to `under_score_case` and provide it to users
+ */
+ String name() default "";
+
+ /**
+ * The description of the option
+ */
+ String description() default "";
+}
diff --git a/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/util/OptionRule.java b/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/util/OptionRule.java
new file mode 100644
index 0000000..6ae5b3c
--- /dev/null
+++ b/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/util/OptionRule.java
@@ -0,0 +1,265 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.datasource.configuration.util;
+
+import org.apache.seatunnel.datasource.configuration.Option;
+
+import lombok.NonNull;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Validation rule for {@link Option}.
+ * <p>
+ * The option rule is typically built in one of the following pattern:
+ *
+ * <pre>{@code
+ * // simple rule
+ * OptionRule simpleRule = OptionRule.builder()
+ * .optional(POLL_TIMEOUT, POLL_INTERVAL)
+ * .required(CLIENT_SERVICE_URL)
+ * .build();
+ *
+ * // basic full rule
+ * OptionRule fullRule = OptionRule.builder()
+ * .optional(POLL_TIMEOUT, POLL_INTERVAL, CURSOR_STARTUP_MODE)
+ * .required(CLIENT_SERVICE_URL, ADMIN_SERVICE_URL)
+ * .exclusive(TOPIC_PATTERN, TOPIC)
+ * .conditional(CURSOR_STARTUP_MODE, StartMode.TIMESTAMP, CURSOR_STARTUP_TIMESTAMP)
+ * .build();
+ *
+ * // complex conditional rule
+ * // moot expression
+ * Expression expression = Expression.of(TOPIC_DISCOVERY_INTERVAL, 200)
+ * .and(Expression.of(Condition.of(CURSOR_STARTUP_MODE, StartMode.EARLIEST)
+ * .or(CURSOR_STARTUP_MODE, StartMode.LATEST)))
+ * .or(Expression.of(Condition.of(TOPIC_DISCOVERY_INTERVAL, 100)))
+ *
+ * OptionRule complexRule = OptionRule.builder()
+ * .optional(POLL_TIMEOUT, POLL_INTERVAL, CURSOR_STARTUP_MODE)
+ * .required(CLIENT_SERVICE_URL, ADMIN_SERVICE_URL)
+ * .exclusive(TOPIC_PATTERN, TOPIC)
+ * .conditional(expression, CURSOR_RESET_MODE)
+ * .build();
+ * }</pre>
+ */
+public class OptionRule {
+
+ /**
+ * Optional options with default value.
+ *
+ * <p> This options will not be validated.
+ * <p> This is used by the web-UI to show what options are available.
+ */
+ private final List<Option<?>> optionalOptions;
+
+ /**
+ * Required options with no default value.
+ *
+ * <p> Verify that the option is valid through the defined rules.
+ */
+ private final List<RequiredOption> requiredOptions;
+
+ OptionRule(List<Option<?>> optionalOptions, List<RequiredOption> requiredOptions) {
+ this.optionalOptions = optionalOptions;
+ this.requiredOptions = requiredOptions;
+ }
+
+ public List<Option<?>> getOptionalOptions() {
+ return optionalOptions;
+ }
+
+ public List<RequiredOption> getRequiredOptions() {
+ return requiredOptions;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof OptionRule)) {
+ return false;
+ }
+ OptionRule that = (OptionRule) o;
+ return Objects.equals(optionalOptions, that.optionalOptions)
+ && Objects.equals(requiredOptions, that.requiredOptions);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(optionalOptions, requiredOptions);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder for {@link OptionRule}.
+ */
+ public static class Builder {
+ private final List<Option<?>> optionalOptions = new ArrayList<>();
+ private final List<RequiredOption> requiredOptions = new ArrayList<>();
+
+ private Builder() {
+ }
+
+ /**
+ * Optional options
+ *
+ * <p> This options will not be validated.
+ * <p> This is used by the web-UI to show what options are available.
+ */
+ public Builder optional(@NonNull Option<?>... options) {
+ for (Option<?> option : options) {
+ verifyDuplicate(option, "OptionsOption");
+ }
+ this.optionalOptions.addAll(Arrays.asList(options));
+ return this;
+ }
+
+ /**
+ * Absolutely required options without any constraints.
+ */
+ public Builder required(@NonNull Option<?>... options) {
+ for (Option<?> option : options) {
+ verifyDuplicate(option, "RequiredOption");
+ verifyRequiredOptionDefaultValue(option);
+ }
+ this.requiredOptions.add(RequiredOption.AbsolutelyRequiredOptions.of(options));
+ return this;
+ }
+
+ /**
+ * Exclusive options, only one of the options needs to be configured.
+ */
+ public Builder exclusive(@NonNull Option<?>... options) {
+ if (options.length <= 1) {
+ throw new OptionValidationException("The number of exclusive options must be greater than 1.");
+ }
+ for (Option<?> option : options) {
+ verifyDuplicate(option, "ExclusiveOption");
+ verifyRequiredOptionDefaultValue(option);
+ }
+ this.requiredOptions.add(RequiredOption.ExclusiveRequiredOptions.of(options));
+ return this;
+ }
+
+ public <T> Builder conditional(@NonNull Option<T> conditionalOption, @NonNull List<T> expectValues, @NonNull Option<?>... requiredOptions) {
+ for (Option<?> o : requiredOptions) {
+ verifyDuplicate(o, "ConditionalOption");
+ verifyRequiredOptionDefaultValue(o);
+ }
+
+ verifyConditionalExists(conditionalOption);
+
+ if (expectValues.size() == 0) {
+ throw new OptionValidationException(
+ String.format("conditional option '%s' must have expect values .", conditionalOption.key()));
+ }
+
+ /**
+ * Each parameter can only be controlled by one other parameter
+ */
+ Expression expression = Expression.of(Condition.of(conditionalOption, expectValues.get(0)));
+ for (int i = 0; i < expectValues.size(); i++) {
+ if (i != 0) {
+ expression = expression.or(Expression.of(Condition.of(conditionalOption, expectValues.get(i))));
+ }
+ }
+
+ this.requiredOptions.add(RequiredOption.ConditionalRequiredOptions.of(expression,
+ new ArrayList<>(Arrays.asList(requiredOptions))));
+ return this;
+ }
+
+ public <T> Builder conditional(@NonNull Option<T> conditionalOption, @NonNull T expectValue, @NonNull Option<?>... requiredOptions) {
+ for (Option<?> o : requiredOptions) {
+ verifyDuplicate(o, "ConditionalOption");
+ verifyRequiredOptionDefaultValue(o);
+ }
+
+ verifyConditionalExists(conditionalOption);
+
+ /**
+ * Each parameter can only be controlled by one other parameter
+ */
+ Expression expression = Expression.of(Condition.of(conditionalOption, expectValue));
+
+ this.requiredOptions.add(RequiredOption.ConditionalRequiredOptions.of(expression,
+ new ArrayList<>(Arrays.asList(requiredOptions))));
+ return this;
+ }
+
+ /**
+ * Bundled options, must be present or absent together.
+ */
+ public Builder bundled(@NonNull Option<?>... requiredOptions) {
+ for (Option<?> option : requiredOptions) {
+ verifyDuplicate(option, "BundledOption");
+ }
+ this.requiredOptions.add(RequiredOption.BundledRequiredOptions.of(requiredOptions));
+ return this;
+ }
+
+ public OptionRule build() {
+ return new OptionRule(optionalOptions, requiredOptions);
+ }
+
+ private void verifyRequiredOptionDefaultValue(@NonNull Option<?> option) {
+ if (option.defaultValue() != null) {
+ throw new OptionValidationException(
+ String.format("Required option '%s' should have no default value.", option.key()));
+ }
+ }
+
+ private void verifyDuplicate(@NonNull Option<?> option, @NonNull String currentOptionType) {
+ if (optionalOptions.contains(option)) {
+ throw new OptionValidationException(
+ String.format("%s '%s' duplicate in option options.", currentOptionType, option.key()));
+ }
+
+ requiredOptions.forEach(requiredOption -> {
+ if (requiredOption.getOptions().contains(option)) {
+ throw new OptionValidationException(
+ String.format("%s '%s' duplicate in '%s'.", currentOptionType, option.key(), requiredOption.getClass().getName()));
+ }
+ });
+ }
+
+ private void verifyConditionalExists(@NonNull Option<?> option) {
+ boolean inOptions = optionalOptions.contains(option);
+ AtomicBoolean inRequired = new AtomicBoolean(false);
+ requiredOptions.forEach(requiredOption -> {
+ if (requiredOption.getOptions().contains(option)) {
+ inRequired.set(true);
+ }
+ });
+
+ if (!inOptions && !inRequired.get()) {
+ throw new OptionValidationException(
+ String.format("Conditional '%s' not found in options.", option.key()));
+ }
+ }
+ }
+}
diff --git a/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/util/OptionUtil.java b/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/util/OptionUtil.java
new file mode 100644
index 0000000..efb5c2f
--- /dev/null
+++ b/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/util/OptionUtil.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.datasource.configuration.util;
+
+import org.apache.seatunnel.datasource.configuration.Option;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import org.apache.commons.lang3.StringUtils;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class OptionUtil {
+
+ private OptionUtil() {
+ }
+
+ public static String getOptionKeys(List<Option<?>> options) {
+ StringBuilder builder = new StringBuilder();
+ boolean flag = false;
+ for (Option<?> option : options) {
+ if (flag) {
+ builder.append(", ");
+ }
+ builder.append("'")
+ .append(option.key())
+ .append("'");
+ flag = true;
+ }
+ return builder.toString();
+ }
+
+ public static String getOptionKeys(List<Option<?>> options,
+ List<RequiredOption.BundledRequiredOptions> bundledOptions) {
+ List<List<Option<?>>> optionList = new ArrayList<>();
+ for (Option<?> option : options) {
+ optionList.add(Collections.singletonList(option));
+ }
+ for (RequiredOption.BundledRequiredOptions bundledOption : bundledOptions) {
+ optionList.add(bundledOption.getRequiredOption());
+ }
+ boolean flag = false;
+ StringBuilder builder = new StringBuilder();
+ for (List<Option<?>> optionSet : optionList) {
+ if (flag) {
+ builder.append(", ");
+ }
+ builder.append("[")
+ .append(getOptionKeys(optionSet))
+ .append("]");
+ flag = true;
+ }
+ return builder.toString();
+ }
+
+ public static List<Option<?>> getOptions(Class<?> clazz) throws InstantiationException, IllegalAccessException {
+ Field[] fields = clazz.getDeclaredFields();
+ List<Option<?>> options = new ArrayList<>();
+ Object object = clazz.newInstance();
+ for (Field field : fields) {
+ field.setAccessible(true);
+ OptionMark option = field.getAnnotation(OptionMark.class);
+ if (option != null) {
+ options.add(new Option<>(
+ !StringUtils.isNotBlank(option.name()) ? formatUnderScoreCase(field.getName()) : option.name(),
+ new TypeReference<Object>() {
+ @Override
+ public Type getType() {
+ return field.getType();
+ }
+ }, field.get(object)).withDescription(option.description()));
+ }
+ }
+ return options;
+ }
+
+ private static String formatUnderScoreCase(String camel) {
+ StringBuilder underScore = new StringBuilder(String.valueOf(Character.toLowerCase(camel.charAt(0))));
+ for (int i = 1; i < camel.length(); i++) {
+ char c = camel.charAt(i);
+ underScore.append(Character.isLowerCase(c) ? c : "_" + Character.toLowerCase(c));
+ }
+ return underScore.toString();
+ }
+}
diff --git a/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/util/OptionValidationException.java b/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/util/OptionValidationException.java
new file mode 100644
index 0000000..13140b5
--- /dev/null
+++ b/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/util/OptionValidationException.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.datasource.configuration.util;
+
+/**
+ * Exception for all errors occurring during option validation phase.
+ */
+public class OptionValidationException extends RuntimeException {
+
+ public OptionValidationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public OptionValidationException(String message) {
+ super(message);
+ }
+
+ public OptionValidationException(String formatMessage, Object... args) {
+ super(String.format(formatMessage, args));
+ }
+}
diff --git a/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/util/RequiredOption.java b/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/util/RequiredOption.java
new file mode 100644
index 0000000..bc199f8
--- /dev/null
+++ b/datasource-client/src/main/java/org/apache/seatunnel/datasource/configuration/util/RequiredOption.java
@@ -0,0 +1,224 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.datasource.configuration.util;
+
+import static org.apache.seatunnel.datasource.configuration.util.OptionUtil.getOptionKeys;
+
+import org.apache.seatunnel.datasource.configuration.Option;
+
+import lombok.Getter;
+import lombok.NonNull;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+public interface RequiredOption {
+
+ List<Option<?>> getOptions();
+
+ /**
+ * These options are mutually exclusive, allowing only one set of options to be configured.
+ */
+ @Getter
+ class ExclusiveRequiredOptions implements RequiredOption {
+ private final List<Option<?>> exclusiveOptions;
+
+ public ExclusiveRequiredOptions(@NonNull List<Option<?>> exclusiveOptions) {
+ this.exclusiveOptions = exclusiveOptions;
+ }
+
+ public static ExclusiveRequiredOptions of(Option<?>... options) {
+ return new ExclusiveRequiredOptions(new ArrayList<>(Arrays.asList(options)));
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof ExclusiveRequiredOptions)) {
+ return false;
+ }
+ ExclusiveRequiredOptions that = (ExclusiveRequiredOptions) obj;
+ return Objects.equals(this.exclusiveOptions, that.exclusiveOptions);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(exclusiveOptions);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Exclusive required set options: %s", getOptionKeys(exclusiveOptions));
+ }
+
+ @Override
+ public List<Option<?>> getOptions() {
+ return exclusiveOptions;
+ }
+ }
+
+ /**
+ * The option is required.
+ */
+ class AbsolutelyRequiredOptions implements RequiredOption {
+ @Getter
+ private final List<Option<?>> requiredOption;
+
+ AbsolutelyRequiredOptions(List<Option<?>> requiredOption) {
+ this.requiredOption = requiredOption;
+ }
+
+ public static AbsolutelyRequiredOptions of(Option<?>... requiredOption) {
+ return new AbsolutelyRequiredOptions(new ArrayList<>(Arrays.asList(requiredOption)));
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof AbsolutelyRequiredOptions)) {
+ return false;
+ }
+ AbsolutelyRequiredOptions that = (AbsolutelyRequiredOptions) obj;
+ return Objects.equals(this.requiredOption, that.requiredOption);
+ }
+
+ @Override
+ public int hashCode() {
+ return this.requiredOption.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Absolutely required options: '%s'", getOptionKeys(requiredOption));
+ }
+
+ @Override
+ public List<Option<?>> getOptions() {
+ return requiredOption;
+ }
+ }
+
+ class ConditionalRequiredOptions implements RequiredOption {
+ private final Expression expression;
+ private final List<Option<?>> requiredOption;
+
+ ConditionalRequiredOptions(Expression expression, List<Option<?>> requiredOption) {
+ this.expression = expression;
+ this.requiredOption = requiredOption;
+ }
+
+ public static ConditionalRequiredOptions of(Expression expression, List<Option<?>> requiredOption) {
+ return new ConditionalRequiredOptions(expression, requiredOption);
+ }
+
+ public static ConditionalRequiredOptions of(Condition<?> condition, List<Option<?>> requiredOption) {
+ return new ConditionalRequiredOptions(Expression.of(condition), requiredOption);
+ }
+
+ public Expression getExpression() {
+ return expression;
+ }
+
+ public List<Option<?>> getRequiredOption() {
+ return requiredOption;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof ConditionalRequiredOptions)) {
+ return false;
+ }
+ ConditionalRequiredOptions that = (ConditionalRequiredOptions) obj;
+ return Objects.equals(this.expression, that.expression) && Objects.equals(this.requiredOption, that.requiredOption);
+ }
+
+ @Override
+ public int hashCode() {
+ return this.requiredOption.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Condition expression: %s, Required options: %s", expression, getOptionKeys(requiredOption));
+ }
+
+ @Override
+ public List<Option<?>> getOptions() {
+ return requiredOption;
+ }
+ }
+
+ /**
+ * These options are bundled, must be present or absent together.
+ */
+ class BundledRequiredOptions implements RequiredOption {
+ private final List<Option<?>> requiredOption;
+
+ BundledRequiredOptions(List<Option<?>> requiredOption) {
+ this.requiredOption = requiredOption;
+ }
+
+ public static BundledRequiredOptions of(Option<?>... requiredOption) {
+ return new BundledRequiredOptions(new ArrayList<>(Arrays.asList(requiredOption)));
+ }
+
+ public static BundledRequiredOptions of(List<Option<?>> requiredOption) {
+ return new BundledRequiredOptions(requiredOption);
+ }
+
+ public List<Option<?>> getRequiredOption() {
+ return requiredOption;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof BundledRequiredOptions)) {
+ return false;
+ }
+ BundledRequiredOptions that = (BundledRequiredOptions) obj;
+ return Objects.equals(this.requiredOption, that.requiredOption);
+ }
+
+ @Override
+ public int hashCode() {
+ return this.requiredOption.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Bundled Required options: %s", getOptionKeys(requiredOption));
+ }
+
+ @Override
+ public List<Option<?>> getOptions() {
+ return requiredOption;
+ }
+ }
+}
diff --git a/datasource-client/src/main/java/org/apache/seatunnel/datasource/model/BaseModel.java b/datasource-client/src/main/java/org/apache/seatunnel/datasource/model/BaseModel.java
new file mode 100644
index 0000000..09b8258
--- /dev/null
+++ b/datasource-client/src/main/java/org/apache/seatunnel/datasource/model/BaseModel.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.datasource.model;
+
+public class BaseModel {
+
+ private Long createdTime;
+
+ private Long updatedTime;
+
+ private Boolean deleted;
+}
diff --git a/datasource-client/src/main/java/org/apache/seatunnel/datasource/model/DataSource.java b/datasource-client/src/main/java/org/apache/seatunnel/datasource/model/DataSource.java
new file mode 100644
index 0000000..856f05a
--- /dev/null
+++ b/datasource-client/src/main/java/org/apache/seatunnel/datasource/model/DataSource.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.datasource.model;
+
+import java.util.Map;
+
+public class DataSource extends BaseModel {
+
+ private Long id;
+
+ private String dataSourceName;
+
+ private String pluginName;
+
+ private String comment;
+
+ private Map<String, Object> params;
+
+}
diff --git a/datasource-client/src/main/java/org/apache/seatunnel/datasource/model/DataSourcePluginInfo.java b/datasource-client/src/main/java/org/apache/seatunnel/datasource/model/DataSourcePluginInfo.java
new file mode 100644
index 0000000..db8abb9
--- /dev/null
+++ b/datasource-client/src/main/java/org/apache/seatunnel/datasource/model/DataSourcePluginInfo.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.datasource.model;
+
+public class DataSourcePluginInfo {
+
+ public String name;
+
+ public String icon;
+
+ public String version;
+
+}
diff --git a/datasource-client/src/main/java/org/apache/seatunnel/datasource/model/Table.java b/datasource-client/src/main/java/org/apache/seatunnel/datasource/model/Table.java
new file mode 100644
index 0000000..f6d8846
--- /dev/null
+++ b/datasource-client/src/main/java/org/apache/seatunnel/datasource/model/Table.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.datasource.model;
+
+import java.util.Set;
+
+public class Table {
+
+ private Long id;
+
+ private Long dataSourceId;
+
+ private String description;
+
+ private String type;
+
+ private String databaseName;
+
+ private String tableName;
+
+ private Set<TableField> fields;
+}
diff --git a/datasource-client/src/main/java/org/apache/seatunnel/datasource/model/TableField.java b/datasource-client/src/main/java/org/apache/seatunnel/datasource/model/TableField.java
new file mode 100644
index 0000000..ba04f71
--- /dev/null
+++ b/datasource-client/src/main/java/org/apache/seatunnel/datasource/model/TableField.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.datasource.model;
+
+import java.util.Set;
+
+public class TableField {
+ private String type;
+
+ private String name;
+
+ private String comment;
+
+ private Boolean primaryKey;
+
+ private String defaultValue;
+
+ private Boolean nullable;
+
+ private Set<String> labels;
+}
diff --git a/datasource-client/src/main/java/org/apache/seatunnel/datasource/plugin/DataSourceMetedata.java b/datasource-client/src/main/java/org/apache/seatunnel/datasource/plugin/DataSourceMetedata.java
new file mode 100644
index 0000000..ec2ab4e
--- /dev/null
+++ b/datasource-client/src/main/java/org/apache/seatunnel/datasource/plugin/DataSourceMetedata.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.datasource.plugin;
+
+import org.apache.seatunnel.datasource.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.model.Table;
+
+import java.util.List;
+import java.util.Map;
+
+public interface DataSourceMetedata {
+
+ Boolean checkDataSourceConnectivity(Map<String, Object> dataSourceParams);
+
+ default Boolean capableGetSchema() {
+ return false;
+ }
+
+ OptionRule getDataSourceFields();
+
+ Table getTable(Map<String, Object> requestParams);
+
+ List<String> getTables(Map<String, Object> requestParams);
+
+ List<String> getDatabases(Map<String, Object> requestParams);
+
+}
diff --git a/datasource-client/src/main/java/org/apache/seatunnel/datasource/service/SeaTunnelAutoTableService.java b/datasource-client/src/main/java/org/apache/seatunnel/datasource/service/SeaTunnelAutoTableService.java
new file mode 100644
index 0000000..dd0bc70
--- /dev/null
+++ b/datasource-client/src/main/java/org/apache/seatunnel/datasource/service/SeaTunnelAutoTableService.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.datasource.service;
+
+import org.apache.seatunnel.datasource.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.model.Table;
+
+import java.util.List;
+import java.util.Map;
+
+public interface SeaTunnelAutoTableService {
+
+ OptionRule getDataSourceMetadataFieldsByDataSourceName(String dataSourceName);
+
+ OptionRule getDataSourceMetadataFieldsByDataSourceId(String dataSourceId);
+
+ List<String> getTables(String dataSourceId, Map<String, Object> requestParams);
+
+ List<String> getDatabases(String dataSourceId, Map<String, Object> requestParams);
+
+ Table getTable(String dataSourceId, Map<String, Object> requestParams);
+
+}
diff --git a/datasource-client/src/main/java/org/apache/seatunnel/datasource/service/SeaTunnelDataSourceService.java b/datasource-client/src/main/java/org/apache/seatunnel/datasource/service/SeaTunnelDataSourceService.java
new file mode 100644
index 0000000..9b2db50
--- /dev/null
+++ b/datasource-client/src/main/java/org/apache/seatunnel/datasource/service/SeaTunnelDataSourceService.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.datasource.service;
+
+import org.apache.seatunnel.datasource.configuration.util.OptionRule;
+import org.apache.seatunnel.datasource.model.DataSource;
+import org.apache.seatunnel.datasource.model.DataSourcePluginInfo;
+
+import java.util.List;
+import java.util.Map;
+
+public interface SeaTunnelDataSourceService {
+
+ List<DataSourcePluginInfo> listAllDataSources();
+
+ OptionRule queryDataSourceFieldByName(String dataSourceName);
+
+ Boolean checkDataSourceFields(Map<String, Object> parameters);
+
+ DataSource queryDataSourceById(Long dataSourceId);
+
+ Boolean saveDataSource(String dataSourceName, String comment, String pluginName, Map<String, Object> parameters);
+
+ Boolean deleteDataSource(Long id);
+
+ Boolean updateDataSource(Long dataSourceId, String dataSourceName, String comment, Map<String, Object> parameters);
+
+ Boolean checkDataSourceConnectivity(Map<String, Object> dataSourceParams);
+}
diff --git a/datasource-client/src/main/java/org/apache/seatunnel/datasource/service/SeaTunnelTableService.java b/datasource-client/src/main/java/org/apache/seatunnel/datasource/service/SeaTunnelTableService.java
new file mode 100644
index 0000000..cccf0ca
--- /dev/null
+++ b/datasource-client/src/main/java/org/apache/seatunnel/datasource/service/SeaTunnelTableService.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.datasource.service;
+
+import org.apache.seatunnel.datasource.model.Table;
+
+import java.util.List;
+import java.util.Map;
+
+public interface SeaTunnelTableService {
+ Boolean createTable(Long datasourceId, String databaseName, String tableName, Map<String, String> tableFields, String description);
+
+ Boolean dropSchema(String schemaId);
+
+ Table getTable(Long datasourceId, String databaseName, String tableName);
+
+ List<String> getTableNames(Long datasourceId, String databaseName);
+
+ List<String> getDatabaseNames(Long datasourceId);
+
+ List<Table> getTables(String datasourceId, String databaseName);
+
+}
diff --git a/pom.xml b/pom.xml
index 3678bd9..5b2ca6d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -44,7 +44,8 @@
<scm>
<connection>scm:git:https://github.com/apache/incubator-seatunnel-datasource-sdk.git</connection>
- <developerConnection>scm:git:https://github.com/apache/incubator-seatunnel-datasource-sdk.git</developerConnection>
+ <developerConnection>scm:git:https://github.com/apache/incubator-seatunnel-datasource-sdk.git
+ </developerConnection>
<url>https://github.com/apache/incubator-seatunnel-datasource-sdk</url>
<tag>HEAD</tag>
</scm>
@@ -72,4 +73,95 @@
<modules>
<module>datasource-client</module>
</modules>
+
+ <properties>
+ <!--dependency version-->
+ <jackson.version>2.14.0</jackson.version>
+ <lombok.version>1.18.24</lombok.version>
+ <!--maven plugin version-->
+ <maven-checkstyle-plugin.version>3.2.0</maven-checkstyle-plugin.version>
+ <checkstyle.fails.on.error>true</checkstyle.fails.on.error>
+ </properties>
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>com.fasterxml.jackson.dataformat</groupId>
+ <artifactId>jackson-dataformat-properties</artifactId>
+ <version>${jackson.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.projectlombok</groupId>
+ <artifactId>lombok</artifactId>
+ <version>${lombok.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.12.0</version>
+ </dependency>
+
+ </dependencies>
+
+
+ </dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.projectlombok</groupId>
+ <artifactId>lombok</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <pluginManagement>
+ <plugins>
+ <!-- checkstyle (Start) -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <version>${maven-checkstyle-plugin.version}</version>
+ <configuration>
+ <!--suppress UnresolvedMavenProperty -->
+ <configLocation>${maven.multiModuleProjectDirectory}/tools/checkstyle/checkStyle.xml</configLocation>
+ <consoleOutput>true</consoleOutput>
+ <includeTestSourceDirectory>true</includeTestSourceDirectory>
+ <failOnViolation>${checkstyle.fails.on.error}</failOnViolation>
+ <sourceDirectories>
+ <sourceDirectory>${project.build.sourceDirectory}</sourceDirectory>
+ <sourceDirectory>${project.build.testSourceDirectory}</sourceDirectory>
+ </sourceDirectories>
+ <resourceIncludes>
+ **/*.properties,
+ **/*.sh,
+ **/*.bat,
+ **/*.yml,
+ **/*.yaml,
+ **/*.xml
+ </resourceIncludes>
+ <resourceExcludes>
+ **/.asf.yaml,
+ **/.github/**
+ </resourceExcludes>
+ </configuration>
+ <executions>
+ <execution>
+ <id>validate</id>
+ <phase>process-sources</phase>
+ <goals>
+ <goal>check</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <!-- checkstyle (End) -->
+ </plugins>
+ </pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
</project>
\ No newline at end of file
diff --git a/tools/checkstyle/checkStyle.xml b/tools/checkstyle/checkStyle.xml
new file mode 100755
index 0000000..56150c0
--- /dev/null
+++ b/tools/checkstyle/checkStyle.xml
@@ -0,0 +1,546 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ ~
+ -->
+
+<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
+ "http://checkstyle.org/dtds/configuration_1_3.dtd">
+<module name="Checker">
+
+ <property name="charset" value="UTF-8"/>
+
+ <property name="severity" value="error"/>
+
+ <property name="fileExtensions" value="java, properties, xml"/>
+
+ <module name="NewlineAtEndOfFile">
+ <!-- windows can use \r\n vs \n, so enforce the most used one ie UNIx style -->
+ <property name="lineSeparator" value="lf"/>
+ </module>
+
+ <property name="localeLanguage" value="en"/>
+
+ <module name="FileTabCharacter">
+ <property name="eachLine" value="true"/>
+ </module>
+
+ <module name="RegexpSingleline">
+ <property name="format" value="System\.out\.println"/>
+ <property name="message" value="Prohibit invoking System.out.println in source code !"/>
+ </module>
+
+ <module name="RegexpSingleline">
+ <property name="format" value="^\s*\*\s*@author"/>
+ <property name="minimum" value="0"/>
+ <property name="maximum" value="0"/>
+ <property name="message" value="ASF project doesn't allow @author copyright."/>
+ </module>
+
+ <module name="RegexpSingleline">
+ <property name="format"
+ value=".*[\u3400-\u4DB5\u4E00-\u9FA5\u9FA6-\u9FBB\uF900-\uFA2D\uFA30-\uFA6A\uFA70-\uFAD9\uFF00-\uFFEF\u2E80-\u2EFF\u3000-\u303F\u31C0-\u31EF]+.*"/>
+ <property name="message" value="Not allow chinese character !"/>
+ </module>
+
+ <module name="RegexpSingleline">
+ <!-- Checks that TODOs don't have stuff in parenthesis, e.g., username. -->
+ <property name="format" value="((//.*)|(\*.*))TODO\("/>
+ <property name="message" value="TODO comments must not include usernames."/>
+ <property name="severity" value="error"/>
+ </module>
+
+ <module name="RegexpSingleline">
+ <!-- \s matches whitespace character, $ matches end of line. -->
+ <property name="format" value="\s+$"/>
+ <property name="severity" value="error"/>
+ <property name="message" value="No trailing whitespace allowed."/>
+ </module>
+
+ <module name="RegexpSingleline">
+ <property name="format" value="Throwables.propagate\("/>
+ <property name="message" value="Throwables.propagate is deprecated"/>
+ <property name="severity" value="error"/>
+ </module>
+
+ <module name="FileLength">
+ <property name="max" value="3000"/>
+ </module>
+
+ <module name="LineLength">
+ <property name="max" value="500"/>
+ <property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
+ </module>
+
+ <module name="SuppressionSingleFilter">
+ <property name="files" value=".*(IT|Test).java"/>
+ <property name="checks" value="MagicNumber"/>
+ </module>
+
+ <module name="SuppressWarningsFilter"/>
+ <module name="TreeWalker">
+ <module name="SuppressWarningsHolder"/>
+ <module name="ImportOrder">
+ <property name="severity" value="error"/>
+ <property name="staticGroups" value="org.apache.seatunnel,org.apache.seatunnel.shade,*,javax,java,scala"/>
+ <property name="groups" value="org.apache.seatunnel,org.apache.seatunnel.shade,*,javax,java,scala"/>
+ <property name="separated" value="true"/>
+ <property name="sortStaticImportsAlphabetically" value="true"/>
+ <property name="option" value="top"/>
+ <property name="tokens" value="STATIC_IMPORT, IMPORT"/>
+ <message key="import.ordering" value="Import {0} appears after other imports that it should precede"/>
+ </module>
+
+ <module name="SuppressionCommentFilter"/>
+
+ <!-- Prohibit T.getT() methods for standard boxed types -->
+ <module name="Regexp">
+ <property name="format" value="Boolean\.getBoolean"/>
+ <property name="illegalPattern" value="true"/>
+ <property name="message" value="Use System.getProperties() to get system properties."/>
+ </module>
+
+ <module name="Regexp">
+ <property name="format" value="Integer\.getInteger"/>
+ <property name="illegalPattern" value="true"/>
+ <property name="message" value="Use System.getProperties() to get system properties."/>
+ </module>
+
+ <module name="Regexp">
+ <property name="format" value="Long\.getLong"/>
+ <property name="illegalPattern" value="true"/>
+ <property name="message" value="Use System.getProperties() to get system properties."/>
+ </module>
+
+ <!-- IllegalImport cannot blacklist classes so we have to fall back to Regexp. -->
+ <!-- forbid use of commons lang validate -->
+ <module name="Regexp">
+ <property name="format" value="org\.apache\.commons\.lang3\.Validate"/>
+ <property name="illegalPattern" value="true"/>
+ <property name="message"
+ value="Use Guava Checks instead of Commons Validate. Please refer to the coding guidelines."/>
+ </module>
+
+ <module name="Regexp">
+ <property name="format" value="org\.apache\.commons\.lang\."/>
+ <property name="illegalPattern" value="true"/>
+ <property name="message" value="Use commons-lang3 instead of commons-lang."/>
+ </module>
+
+ <module name="Regexp">
+ <property name="format" value="org\.codehaus\.jettison"/>
+ <property name="illegalPattern" value="true"/>
+ <property name="message" value="Use com.fasterxml.jackson instead of jettison."/>
+ </module>
+
+ <module name="IllegalImport">
+ <property name="regexp" value="true"/>
+ <property name="illegalPkgs"
+ value="^com\.google\.api\.client\.repackaged,
+ ^avro\.shaded, ^org\.apache\.hadoop\.hbase\.shaded,
+ ^org\.apache\.hadoop\.shaded,
+ ^javax\.ws\.rs\.ext,
+ ^cc\.concurrent\.mango\.util\.concurrent,
+ ^org\.apache\.curator-test\.shaded,
+ ^com\.sun\.istack,
+ ^org\.jetbrains\.annotations,
+ ^jline\.internal,
+ ^com\.cronutils\.utils,
+ ^javax\.ws\.rs\.ext,
+ ^org\.jboss\.netty\.util\.internal,
+ ^com\.sun\.javafx,
+ ^io\.reactivex\.annotations"/>
+ <property name="illegalClasses"
+ value="^java\.util\.logging\.Logging,
+ ^sun\.misc\.BASE64Encoder,
+ ^sun\.misc\.BASE64Decoder,
+ ^jdk\.internal\.jline\.internal\.Nullable,
+ ^org\.junit\.(?!(jupiter|platform)\.).+"/>
+ </module>
+
+ <module name="OuterTypeFilename">
+ <property name="severity" value="error"/>
+ </module>
+
+ <module name="OneTopLevelClass">
+ <property name="severity" value="error"/>
+ </module>
+
+ <module name="NoLineWrap">
+ <property name="severity" value="error"/>
+ </module>
+
+ <!-- Checks for braces around if and else blocks -->
+ <module name="NeedBraces">
+ <property name="severity" value="error"/>
+ <property name="tokens" value="LITERAL_IF, LITERAL_ELSE, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO"/>
+ </module>
+
+ <module name="LeftCurly">
+ <property name="severity" value="error"/>
+ </module>
+
+ <module name="InnerTypeLast"/>
+ <module name="IllegalTokenText">
+ <property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
+ <property name="format"
+ value="\\u00(08|09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
+ <property name="message"
+ value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
+ </module>
+
+ <module name="AvoidEscapedUnicodeCharacters">
+ <property name="allowEscapesForControlCharacters" value="true"/>
+ <property name="allowByTailComment" value="true"/>
+ <property name="allowNonPrintableEscapes" value="true"/>
+ </module>
+
+ <module name="EmptyBlock">
+ <property name="option" value="TEXT"/>
+ <property name="tokens" value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
+ </module>
+
+ <module name="OneStatementPerLine"/>
+
+ <module name="MagicNumber">
+ <property name="ignoreHashCodeMethod" value="true"/>
+ </module>
+
+ <module name="MultipleVariableDeclarations"/>
+
+ <module name="ArrayTypeStyle"/>
+
+ <module name="MissingSwitchDefault"/>
+
+ <module name="FallThrough">
+ <!-- Warn about falling through to the next case statement. Similar to
+ javac -Xlint:fallthrough, but the check is suppressed if a single-line comment
+ on the last non-blank line preceding the fallen-into case contains 'fall through' (or
+ some other variants that we don't publicized to promote consistency).
+ -->
+ <property name="reliefPattern"
+ value="fall through|Fall through|fallthru|Fallthru|falls through|Falls through|fallthrough|Fallthrough|No break|NO break|no break|continue on"/>
+ <property name="severity" value="error"/>
+ </module>
+
+ <!-- MODIFIERS CHECKS -->
+ <module name="ModifierOrder">
+ <!-- Warn if modifier order is inconsistent with JLS3 8.1.1, 8.3.1, and
+ 8.4.3. The prescribed order is:
+ public, protected, private, abstract, static, final, transient, volatile,
+ synchronized, native, strictfp
+ -->
+ <property name="severity" value="error"/>
+ </module>
+
+ <module name="SeparatorWrap">
+ <property name="tokens" value="DOT"/>
+ <property name="option" value="nl"/>
+ </module>
+
+ <module name="SeparatorWrap">
+ <property name="tokens" value="COMMA"/>
+ <property name="option" value="EOL"/>
+ </module>
+
+ <module name="UnusedImports">
+ <property name="severity" value="error"/>
+ <property name="processJavadoc" value="true"/>
+ <message key="import.unused" value="Unused import: {0}."/>
+ </module>
+
+ <module name="RedundantImport">
+ <!-- Checks for redundant import statements. -->
+ <property name="severity" value="error"/>
+ <message key="import.redundancy" value="Redundant import {0}."/>
+ </module>
+
+ <!-- Require static importing from Preconditions. -->
+ <module name="RegexpSinglelineJava">
+ <property name="format" value="^import com.google.common.base.Preconditions;$"/>
+ <property name="message" value="Static import functions from Guava Preconditions"/>
+ </module>
+
+ <!-- Detect multiple consecutive semicolons (e.g. ";;"). -->
+ <module name="RegexpSinglelineJava">
+ <property name="format" value=";{2,}"/>
+ <property name="message" value="Use one semicolon"/>
+ <property name="ignoreComments" value="true"/>
+ </module>
+
+ <module name="RegexpSinglelineJava">
+ <property name="format" value="throw new \w+Error\("/>
+ <property name="message" value="Avoid throwing error in application code."/>
+ </module>
+
+ <module name="RegexpSinglelineJava">
+ <property name="format" value="Objects\.toStringHelper"/>
+ <property name="message" value="Avoid using Object.toStringHelper. Use ToStringBuilder instead."/>
+ </module>
+
+ <module name="JavadocStyle">
+ <property name="severity" value="error"/>
+ <property name="checkHtml" value="true"/>
+ <property name="endOfSentenceFormat" value=""/>
+ </module>
+
+ <module name="RedundantModifier">
+ <!-- Checks for redundant modifiers on various symbol definitions.
+ See: http://checkstyle.sourceforge.net/config_modifier.html#RedundantModifier
+
+ We exclude METHOD_DEF to allow final methods in final classes to make them more future-proof.
+ -->
+ <property name="tokens" value="VARIABLE_DEF, ANNOTATION_FIELD_DEF, INTERFACE_DEF, CLASS_DEF, ENUM_DEF"/>
+ </module>
+
+ <module name="AvoidStarImport"/>
+
+ <module name="NonEmptyAtclauseDescription"/>
+
+ <!--Checks that classes that override equals() also override hashCode()-->
+ <module name="EqualsHashCode"/>
+ <!--Checks for over-complicated boolean expressions. Currently finds code like if (topic == true), topic || true, !false, etc.-->
+ <module name="SimplifyBooleanExpression"/>
+ <module name="OneStatementPerLine"/>
+ <module name="UnnecessaryParentheses"/>
+ <!--Checks for over-complicated boolean return statements. For example the following code-->
+ <module name="SimplifyBooleanReturn"/>
+
+ <!--Check that the default is after all the cases in producerGroup switch statement-->
+ <module name="DefaultComesLast"/>
+ <!--Detects empty statements (standalone ";" semicolon)-->
+ <module name="EmptyStatement">
+ <property name="severity" value="error"/>
+ </module>
+
+ <!--Checks that long constants are defined with an upper ell-->
+ <module name="UpperEll">
+ <!-- Checks that long constants are defined with an upper ell.-->
+ <property name="severity" value="error"/>
+ </module>
+
+ <module name="ConstantName">
+ <property name="format" value="(^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$)"/>
+ </module>
+ <!--Checks that local, non-final variable names conform to producerGroup format specified by the format property-->
+ <module name="LocalVariableName">
+ <!-- Validates identifiers for local variables against the expression "^[a-z][a-zA-Z0-9]*$". -->
+ <property name="tokens" value="VARIABLE_DEF"/>
+ <property name="format" value="^[a-z]([a-zA-Z0-9]*)?$"/>
+ <property name="severity" value="error"/>
+ <message key="name.invalidPattern" value="Local variable name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+
+ <!--Validates identifiers for local, final variables, including catch parameters-->
+ <module name="LocalFinalVariableName">
+ <!-- Validates identifiers for local final variables against the expression "^[a-z][a-zA-Z0-9]*$". -->
+ <property name="severity" value="error"/>
+ </module>
+
+ <!--Validates identifiers for non-static fields-->
+ <module name="MemberName">
+ <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
+ <message key="name.invalidPattern" value="Member name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+
+ <!--Validates identifiers for class type parameters-->
+ <module name="ClassTypeParameterName">
+ <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
+ <message key="name.invalidPattern" value="Class type name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+
+ <module name="PackageName">
+ <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]{1,})*$"/>
+ <property name="severity" value="error"/>
+ </module>
+
+ <module name="ConstantNameCheck">
+ <!-- Validates non-private, static, final fields against the supplied public/package final fields "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$". -->
+ <metadata name="altname" value="ConstantName"/>
+ <property name="applyToPublic" value="true"/>
+ <property name="applyToProtected" value="true"/>
+ <property name="applyToPackage" value="true"/>
+ <property name="applyToPrivate" value="false"/>
+ <property name="format" value="^([A-Z][A-Z0-9]*(_[A-Z0-9]+)*|FLAG_.*)$"/>
+ <message key="name.invalidPattern"
+ value="Variable ''{0}'' should be in ALL_CAPS (if it is a constant) or be private (otherwise)."/>
+ <property name="severity" value="error"/>
+ </module>
+
+ <module name="MemberNameCheck">
+ <!-- Validates non-static members against the supplied expression. -->
+ <metadata name="altname" value="MemberName"/>
+ <property name="applyToPublic" value="true"/>
+ <property name="applyToProtected" value="true"/>
+ <property name="applyToPackage" value="true"/>
+ <property name="applyToPrivate" value="true"/>
+ <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
+ <property name="severity" value="error"/>
+ </module>
+
+ <module name="MethodNameCheck">
+ <!-- Validates identifiers for method names. -->
+ <metadata name="altname" value="MethodName"/>
+ <property name="format" value="^[a-z][a-zA-Z0-9]*(_[a-zA-Z0-9]+)*$"/>
+ <property name="severity" value="error"/>
+ </module>
+
+ <module name="MethodName">
+ <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/>
+ <message key="name.invalidPattern" value="Method name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+
+ <module name="EmptyCatchBlock">
+ <property name="exceptionVariableName" value="expected"/>
+ </module>
+
+ <module name="CommentsIndentation"/>
+
+ <module name="ParameterName">
+ <!-- Validates identifiers for method parameters against the expression "^[a-z][a-zA-Z0-9]*$". -->
+ <property name="format" value="^[a-z]([a-zA-Z0-9]*)?$"/>
+ <property name="severity" value="error"/>
+ <message key="name.invalidPattern" value="Parameter name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+
+ <module name="CatchParameterName">
+ <property name="format" value="^[a-z]([a-zA-Z0-9]*)?$"/>
+ <message key="name.invalidPattern" value="Catch parameter name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+
+ <module name="StaticVariableNameCheck">
+ <!-- Validates static, non-final fields against the supplied expression "^[a-z][a-zA-Z0-9]*_?$". -->
+ <metadata name="altname" value="StaticVariableName"/>
+ <property name="applyToPublic" value="true"/>
+ <property name="applyToProtected" value="true"/>
+ <property name="applyToPackage" value="true"/>
+ <property name="applyToPrivate" value="true"/>
+ <property name="format" value="(^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$)"/>
+ <property name="severity" value="error"/>
+ </module>
+
+ <module name="TypeNameCheck">
+ <!-- Validates static, final fields against the expression "^[A-Z][a-zA-Z0-9]*$". -->
+ <metadata name="altname" value="TypeName"/>
+ <property name="severity" value="error"/>
+ </module>
+
+ <module name="TypeName">
+ <property name="format" value="(^[A-Z][a-zA-Z0-9]*$)|(^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$)"/>
+ <message key="name.invalidPattern" value="Type name ''{0}'' must match pattern ''{1}''."/>
+ </module>
+ <module name="MissingOverride"/>
+
+ <!--whitespace-->
+ <module name="GenericWhitespace">
+ <message key="ws.followed" value="GenericWhitespace ''{0}'' is followed by whitespace."/>
+ <message key="ws.preceded" value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
+ <message key="ws.illegalFollow" value="GenericWhitespace ''{0}'' should followed by whitespace."/>
+ <message key="ws.notPreceded" value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
+ </module>
+
+ <module name="Indentation">
+ <property name="basicOffset" value="4"/>
+ <property name="braceAdjustment" value="0"/>
+ <property name="caseIndent" value="4"/>
+ <property name="throwsIndent" value="2"/>
+ <property name="lineWrappingIndentation" value="4"/>
+ <property name="arrayInitIndent" value="4"/>
+ </module>
+
+ <module name="NoFinalizer"/>
+
+ <module name="NoWhitespaceAfter">
+ <!-- Checks that there is no whitespace after various unary operators.
+ Linebreaks are allowed.
+ -->
+ <property name="tokens" value="BNOT, DEC, DOT, INC, LNOT, UNARY_MINUS, UNARY_PLUS"/>
+ <property name="allowLineBreaks" value="true"/>
+ <property name="severity" value="error"/>
+ </module>
+
+ <module name="WhitespaceAfter">
+ <!-- Checks that commas, semicolons and typecasts are followed by
+ whitespace.
+ -->
+ <property name="tokens" value="COMMA, SEMI, TYPECAST"/>
+ </module>
+
+ <module name="WhitespaceAround">
+ <!-- Checks that various tokens are surrounded by whitespace.
+ This includes most binary operators and keywords followed
+ by regular or curly braces.
+ -->
+ <property name="allowEmptyConstructors" value="true"/>
+ <property name="allowEmptyMethods" value="true"/>
+ <property name="allowEmptyTypes" value="true"/>
+ <property name="allowEmptyLoops" value="true"/>
+ <property name="severity" value="error"/>
+ <property name="tokens" value="ASSIGN, BAND, BAND_ASSIGN, BOR,
+ BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR, BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN,
+ EQUAL, GE, GT, LAMBDA, LAND, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE,
+ LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_RETURN,
+ LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS,
+ MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION,
+ SL, SL_ASSIGN, SR_ASSIGN, STAR, STAR_ASSIGN, TYPE_EXTENSION_AND"/>
+ <message key="ws.notFollowed"
+ value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
+ <message key="ws.notPreceded" value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
+ </module>
+
+ <module name="MethodParamPad"/>
+ <module name="ParenPad">
+ <!-- Checks that there is no whitespace before close parens or after open parens. -->
+ <property name="severity" value="error"/>
+ </module>
+
+ <module name="NoWhitespaceBefore">
+ <!-- Checks that there is no whitespace before various unary operators. Linebreaks are allowed. -->
+ <property name="tokens" value="COMMA, SEMI, DOT, POST_DEC, POST_INC, ELLIPSIS, METHOD_REF"/>
+ <property name="allowLineBreaks" value="true"/>
+ <property name="severity" value="error"/>
+ </module>
+
+ <module name="OperatorWrap">
+ <!-- Checks that assignment operators are at the end of the line. -->
+ <property name="option" value="eol"/>
+ <property name="tokens"
+ value="ASSIGN, BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LE, LITERAL_INSTANCEOF, LT, MINUS, MOD, NOT_EQUAL, QUESTION, SL, SR, STAR, METHOD_REF"/>
+ </module>
+
+ <module name="AnnotationLocation">
+ <property name="allowSamelineMultipleAnnotations" value="false"/>
+ <property name="allowSamelineSingleParameterlessAnnotation" value="false"/>
+ <property name="allowSamelineParameterizedAnnotation" value="true"/>
+ <property name="tokens" value="METHOD_DEF, CTOR_DEF, VARIABLE_DEF, CLASS_DEF, INTERFACE_DEF, ENUM_DEF"/>
+ </module>
+
+ <module name="TypecastParenPad"/>
+
+ <module name="EmptyLineSeparator">
+ <!-- Checks for empty line separator between tokens. The only
+ excluded token is VARIABLE_DEF, allowing class fields to
+ be declared on consecutive lines.
+ -->
+ <property name="allowNoEmptyLineBetweenFields" value="true"/>
+ <property name="allowMultipleEmptyLines" value="false"/>
+ <property name="allowMultipleEmptyLinesInsideClassMembers" value="false"/>
+ <property name="tokens"
+ value="PACKAGE_DEF, IMPORT, STATIC_IMPORT, CLASS_DEF, INTERFACE_DEF, ENUM_DEF, STATIC_INIT, INSTANCE_INIT, METHOD_DEF, CTOR_DEF"/>
+ </module>
+ </module>
+
+</module>