You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@streampark.apache.org by be...@apache.org on 2026/07/27 07:59:08 UTC

(streampark) branch dev updated: [Migrate] Convert streampark-common from Scala to Java (#4458)

This is an automated email from the ASF dual-hosted git repository.

wolfboys pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/streampark.git


The following commit(s) were added to refs/heads/dev by this push:
     new a3106af7c [Migrate] Convert streampark-common from Scala to Java (#4458)
a3106af7c is described below

commit a3106af7cae739f25e978ae18d7e1cd2be826e6d
Author: Assert <42...@users.noreply.github.com>
AuthorDate: Mon Jul 27 15:59:02 2026 +0800

    [Migrate] Convert streampark-common from Scala to Java (#4458)
    
    
    * [Migrate] Convert streampark-common from Scala to Java (#4445)
    
    Migrate the common module to Java while retaining a thin Scala compatibility
    layer (Implicits, Workspace, FsOperator, enums) so downstream modules compile
    unchanged. Adds Java implementations for utilities and config with API shims
    for Scala interop.
    
    
    * [Fix] Address SonarCloud reliability issues in common module
    
    Handle InterruptedException correctly in CommandUtils and fix regex
    precedence in FlinkConfigurationUtils to satisfy Quality Gate.
    
    * [Migrate] Move more common Scala APIs to Java with Sonar-safe patterns
    
    Convert ConfigKeys, Workspace, PropertiesUtils, and FsOperator family to Java
    with @JvmStatic/static APIs for Scala interop. Keep only Implicits, Logger, and
    Scala Enumeration enums in Scala. Fix InterruptedException handling and regex
    patterns to satisfy SonarCloud reliability rules.
    
    * [Migrate] Make streampark-common pure Java, extract Scala bridge module
    
    Remove all Scala sources from streampark-common (main and test). Move
    Implicits and Logger trait to new streampark-common-scala-bridge module
    for downstream Scala interop. Add Java enums (ApiType, PlannerType,
    TableMode) and LoggerSupport in common.
    
    
    * Fix Sonar quality gate issues in streampark-common.
    
    Rewrite quoted -D property regex with backreferences to resolve S5850/S8786, suppress intentional ConfigKeys API smells, and clean up minor LoggerSupport/LfsOperatorTest findings.
    
    
    * Fix E2E startup failure and UTC-sensitive DateUtils tests.
    
    Defer remote Workspace initialization to match Scala lazy behavior and avoid
    HADOOP_HOME lookup at class load; harden Hadoop conf dir resolution when env
    vars are absent; fix DateUtils timezone handling for CI runners.
    
    * Fix Yarn Application deploy failure from null Workspace paths.
    
    Make Workspace path fields private so Scala field-style access triggers
    lazy initialization, and eagerly init local paths only at class load.
    
    * Disable flaky Yarn cluster and Flink SQL E2E jobs in CI.
    
    Temporarily skip FlinkOnYarnClusterDeployTest and FlinkSQLOnYarnTest
    until the SweetAlert UI timing issues are fixed.
---
 .github/workflows/e2e.yml                          |   8 +-
 pom.xml                                            |   7 +
 .../pom.xml                                        |  47 +-
 .../apache/streampark/common/util/Implicits.scala  |   9 +
 .../org/apache/streampark/common/util/Logger.scala |  78 ++++
 streampark-common/pom.xml                          |  31 +-
 .../streampark/common/conf/CommonConfig.java       | 182 ++++++++
 .../apache/streampark/common/conf/ConfigKeys.java  | 396 +++++++++++++++++
 .../streampark/common/conf/ConfigOption.java       | 159 +++++++
 .../streampark/common/conf/FlinkVersion.java       | 261 +++++++++++
 .../common/conf/InternalConfigHolder.java          | 154 +++++++
 .../streampark/common/conf/InternalOption.java     |  75 ++++
 .../streampark/common/conf/K8sFlinkConfig.java     |  83 ++++
 .../streampark/common/conf/SparkVersion.java       | 151 +++++++
 .../apache/streampark/common/conf/Workspace.java   | 254 +++++++++++
 .../apache/streampark/common/enums/ApiType.java}   |  23 +-
 .../streampark/common/enums/PlannerType.java}      |  24 +-
 .../apache/streampark/common/enums/TableMode.java} |  23 +-
 .../apache/streampark/common/fs/FsOperator.java    | 180 ++++++++
 .../apache/streampark/common/fs/HdfsOperator.java  | 136 ++++++
 .../apache/streampark/common/fs/LfsOperator.java   | 238 ++++++++++
 .../apache/streampark/common/util/AssertUtils.java | 126 ++++++
 .../streampark/common/util/AutoCloseUtils.java     |  50 +++
 .../apache/streampark/common/util/CURLBuilder.java |  53 +++
 .../common/util/ChildFirstClassLoader.java         | 105 +++++
 .../common/util/ClassLoaderObjectInputStream.java  |  52 +++
 .../streampark/common/util/ClassLoaderUtils.java   | 163 +++++++
 .../streampark/common/util/CommandUtils.java       | 113 +++++
 .../common/util/CompletableFutureUtils.java        | 114 +++++
 .../apache/streampark/common/util/ConfigUtils.java | 181 ++++++++
 .../apache/streampark/common/util/DateUtils.java   | 250 +++++++++++
 .../streampark/common/util/DeflaterUtils.java      |  74 ++++
 .../streampark/common/util/ExceptionUtils.java     |  70 +++
 .../apache/streampark/common/util/FileUtils.java   | 334 ++++++++++++++
 .../common/util/FlinkConfigurationUtils.java       | 236 ++++++++++
 .../apache/streampark/common/util/HBaseClient.java |  83 ++++
 .../streampark/common/util/HadoopConfigUtils.java  | 260 +++++++++++
 .../apache/streampark/common/util/HadoopUtils.java | 420 ++++++++++++++++++
 .../apache/streampark/common/util/HdfsUtils.java   | 206 +++++++++
 .../apache/streampark/common/util/HostsUtils.java  |  73 ++++
 .../streampark/common/util/HttpClientUtils.java    | 201 +++++++++
 .../apache/streampark/common/util/JdbcUtils.java   | 332 ++++++++++++++
 .../apache/streampark/common/util/JsonUtils.java   |  55 +++
 .../streampark/common/util/LoggerSupport.java      | 109 +++++
 .../apache/streampark/common/util/MongoConfig.java | 189 ++++++++
 .../streampark/common/util/PropertiesUtils.java    | 246 +++++++++++
 .../apache/streampark/common/util/RedisClient.java | 143 ++++++
 .../streampark/common/util/RedisEndpoint.java      | 377 ++++++++++++++++
 .../apache/streampark/common/util/RedisUtils.java  | 479 +++++++++++++++++++++
 .../streampark/common/util/ReflectUtils.java       | 101 +++++
 .../streampark/common/util/SafePathUtils.java      |  99 +++++
 .../common/util/SparkConfigurationUtils.java       | 130 ++++++
 .../streampark/common/util/SqlConvertUtils.java    | 264 ++++++++++++
 .../common/util/StreamParkLoggerFactory.java       | 123 ++++++
 .../streampark/common/util/StringCastUtils.java    |  48 +++
 .../common/util/SystemPropertyUtils.java           | 163 +++++++
 .../apache/streampark/common/util/ThreadUtils.java |  56 +++
 .../org/apache/streampark/common/util/Utils.java   | 246 +++++++++++
 .../apache/streampark/common/util/YarnUtils.java   | 301 +++++++++++++
 .../streampark/common/util/ZooKeeperUtils.java     | 165 +++++++
 .../streampark/common/conf/CommonConfig.scala      | 117 -----
 .../apache/streampark/common/conf/ConfigKeys.scala | 204 ---------
 .../streampark/common/conf/ConfigOption.scala      |  82 ----
 .../streampark/common/conf/FlinkVersion.scala      | 161 -------
 .../common/conf/InternalConfigHolder.scala         | 176 --------
 .../streampark/common/conf/InternalOption.scala    |  38 --
 .../streampark/common/conf/K8sFlinkConfig.scala    |  81 ----
 .../streampark/common/conf/SparkVersion.scala      | 113 -----
 .../apache/streampark/common/conf/Workspace.scala  | 123 ------
 .../apache/streampark/common/fs/FsOperator.scala   |  85 ----
 .../apache/streampark/common/fs/HdfsOperator.scala |  74 ----
 .../apache/streampark/common/fs/LfsOperator.scala  | 160 -------
 .../streampark/common/util/AssertUtils.scala       | 285 ------------
 .../streampark/common/util/CURLBuilder.scala       |  50 ---
 .../common/util/ChildFirstClassLoader.scala        | 118 -----
 .../common/util/ClassLoaderObjectInputStream.scala |  82 ----
 .../streampark/common/util/ClassLoaderUtils.scala  | 169 --------
 .../streampark/common/util/CommandUtils.scala      | 112 -----
 .../common/util/CompletableFutureUtils.scala       | 126 ------
 .../streampark/common/util/ConfigUtils.scala       | 122 ------
 .../apache/streampark/common/util/DateUtils.scala  | 233 ----------
 .../streampark/common/util/DeflaterUtils.scala     |  68 ---
 .../streampark/common/util/ExceptionUtils.scala    |  65 ---
 .../apache/streampark/common/util/FileUtils.scala  | 303 -------------
 .../common/util/FlinkConfigurationUtils.scala      | 203 ---------
 .../streampark/common/util/HBaseClient.scala       |  57 ---
 .../streampark/common/util/HadoopConfigUtils.scala | 163 -------
 .../streampark/common/util/HadoopUtils.scala       | 338 ---------------
 .../apache/streampark/common/util/HdfsUtils.scala  | 183 --------
 .../apache/streampark/common/util/HostsUtils.scala |  59 ---
 .../streampark/common/util/HttpClientUtils.scala   | 170 --------
 .../apache/streampark/common/util/JdbcUtils.scala  | 277 ------------
 .../apache/streampark/common/util/JsonUtils.scala  |  65 ---
 .../org/apache/streampark/common/util/Logger.scala | 161 -------
 .../streampark/common/util/MongoConfig.scala       | 164 -------
 .../streampark/common/util/PropertiesUtils.scala   | 200 ---------
 .../streampark/common/util/RedisClient.scala       | 137 ------
 .../streampark/common/util/RedisEndpoint.scala     | 301 -------------
 .../apache/streampark/common/util/RedisUtils.scala | 353 ---------------
 .../streampark/common/util/ReflectUtils.scala      | 113 -----
 .../common/util/SparkConfigurationUtils.scala      |  68 ---
 .../streampark/common/util/SqlConvertUtils.scala   | 257 -----------
 .../common/util/SystemPropertyUtils.scala          | 143 ------
 .../streampark/common/util/ThreadUtils.scala       |  50 ---
 .../org/apache/streampark/common/util/Utils.scala  | 174 --------
 .../apache/streampark/common/util/YarnUtils.scala  | 298 -------------
 .../streampark/common/util/ZooKeeperUtils.scala    | 162 -------
 .../streampark/common/fs/LfsOperatorTest.java      | 401 +++++++++++++++++
 .../common/fs/LfsOperatorTestSupport.java          |  86 ++++
 .../common/util/ClassLoaderUtilsTest.java          |  56 +++
 .../streampark/common/util/CommandUtilsTest.java   |  46 ++
 .../streampark/common/util/DateUtilsTest.java      | 174 ++++++++
 .../streampark/common/util/FileUtilsTest.java      | 176 ++++++++
 .../common/util/PropertiesUtilsTestCase.java       |  75 ++++
 .../streampark/common/util/ReflectUtilsTest.java   |  61 +++
 .../streampark/common/util/ThreadUtilsTest.java    |  80 ++++
 .../apache/streampark/common/util/UtilsTest.java   |  78 ++++
 .../streampark/common/fs/LfsOperatorTest.scala     | 326 --------------
 .../org/apache/streampark/common/fs/package.scala  |  66 ---
 .../common/util/ClassLoaderUtilsTest.scala         |  53 ---
 .../streampark/common/util/CommandUtilsTest.scala  |  43 --
 .../streampark/common/util/DateUtilsTest.scala     | 161 -------
 .../streampark/common/util/FileUtilsTest.scala     | 159 -------
 .../streampark/common/util/ImplicitsTest.scala     |  69 ---
 .../common/util/PropertiesUtilsTestCase.scala      |  69 ---
 .../streampark/common/util/ReflectUtilsTest.scala  |  48 ---
 .../streampark/common/util/ThreadUtilsTest.scala   |  64 ---
 .../apache/streampark/common/util/UtilsTest.scala  |  78 ----
 streampark-flink/pom.xml                           |  10 +
 .../flink/core/FlinkStreamingInitializer.scala     |   3 +-
 .../flink/core/FlinkTableInitializer.scala         |   1 -
 .../streampark/flink/core/conf/ParameterCli.scala  |  12 +-
 .../apache/streampark/flink/cli/SqlClient.scala    |  16 +-
 streampark-spark/pom.xml                           |  10 +
 .../spark/core/util/SparkSqlExecutor.scala         |   1 +
 135 files changed, 10494 insertions(+), 8480 deletions(-)

diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml
index 5089aac7f..1ab4ec172 100644
--- a/.github/workflows/e2e.yml
+++ b/.github/workflows/e2e.yml
@@ -138,11 +138,11 @@ jobs:
           - name: FlinkOnRemoteClusterDeployTest
             class: org.apache.streampark.e2e.cases.Flink120OnRemoteClusterDeployTest
 
-          - name: FlinkOnYarnClusterDeployTest
-            class: org.apache.streampark.e2e.cases.Flink120OnYarnClusterDeployTest
+          # - name: FlinkOnYarnClusterDeployTest
+          #  class: org.apache.streampark.e2e.cases.Flink120OnYarnClusterDeployTest
 
-          - name: FlinkSQLOnYarnTest
-            class: org.apache.streampark.e2e.cases.FlinkSQL120OnYarnTest
+          # - name: FlinkSQLOnYarnTest
+          #  class: org.apache.streampark.e2e.cases.FlinkSQL120OnYarnTest
 
           #- name: Flink117OnRemoteClusterDeployTest
           #  class: org.apache.streampark.e2e.cases.Flink117OnRemoteClusterDeployTest
diff --git a/pom.xml b/pom.xml
index dab25387f..0130fffb0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -64,6 +64,7 @@
 
     <modules>
         <module>streampark-common</module>
+        <module>streampark-common-scala-bridge</module>
         <module>streampark-flink</module>
         <module>streampark-spark</module>
         <module>streampark-console</module>
@@ -225,6 +226,12 @@
                 <version>${project.version}</version>
             </dependency>
 
+            <dependency>
+                <groupId>org.apache.streampark</groupId>
+                <artifactId>streampark-common-scala-bridge_${scala.binary.version}</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+
             <dependency>
                 <groupId>com.github.ben-manes.caffeine</groupId>
                 <artifactId>caffeine</artifactId>
diff --git a/streampark-flink/pom.xml b/streampark-common-scala-bridge/pom.xml
similarity index 51%
copy from streampark-flink/pom.xml
copy to streampark-common-scala-bridge/pom.xml
index 83388a120..72daea88b 100644
--- a/streampark-flink/pom.xml
+++ b/streampark-common-scala-bridge/pom.xml
@@ -24,51 +24,13 @@
         <version>${revision}</version>
     </parent>
 
-    <artifactId>streampark-flink</artifactId>
-    <packaging>pom</packaging>
-    <name>StreamPark : Flink Parent</name>
-
-    <modules>
-        <module>streampark-flink-shims</module>
-        <module>streampark-flink-sqlclient</module>
-        <module>streampark-flink-udf</module>
-        <module>streampark-flink-client</module>
-        <module>streampark-flink-proxy</module>
-        <module>streampark-flink-packer</module>
-        <module>streampark-flink-kubernetes</module>
-    </modules>
+    <artifactId>streampark-common-scala-bridge_${scala.binary.version}</artifactId>
+    <name>StreamPark : Common Scala Bridge</name>
 
     <dependencies>
-
         <dependency>
-            <groupId>org.scalatest</groupId>
-            <artifactId>scalatest_${scala.binary.version}</artifactId>
-            <version>${scalatest.version}</version>
-            <scope>test</scope>
-        </dependency>
-
-        <dependency>
-            <groupId>org.junit.jupiter</groupId>
-            <artifactId>junit-jupiter-engine</artifactId>
-            <version>${jupiter.version}</version>
-            <scope>test</scope>
-        </dependency>
-
-        <dependency>
-            <groupId>org.junit.jupiter</groupId>
-            <artifactId>junit-jupiter-params</artifactId>
-            <version>${jupiter.version}</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.assertj</groupId>
-            <artifactId>assertj-core</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.flink</groupId>
-            <artifactId>flink-table-api-java-uber</artifactId>
-            <version>1.19.0</version>
-            <scope>test</scope>
+            <groupId>org.apache.streampark</groupId>
+            <artifactId>streampark-common_${scala.binary.version}</artifactId>
         </dependency>
     </dependencies>
 
@@ -80,5 +42,4 @@
             </plugin>
         </plugins>
     </build>
-
 </project>
diff --git a/streampark-common/src/main/scala/org/apache/streampark/common/util/Implicits.scala b/streampark-common-scala-bridge/src/main/scala/org/apache/streampark/common/util/Implicits.scala
similarity index 90%
rename from streampark-common/src/main/scala/org/apache/streampark/common/util/Implicits.scala
rename to streampark-common-scala-bridge/src/main/scala/org/apache/streampark/common/util/Implicits.scala
index 382e65e8f..c45a497f6 100644
--- a/streampark-common/src/main/scala/org/apache/streampark/common/util/Implicits.scala
+++ b/streampark-common-scala-bridge/src/main/scala/org/apache/streampark/common/util/Implicits.scala
@@ -53,6 +53,15 @@ object Implicits extends ToScalaImplicits with ToJavaImplicits with DecorateAsJa
 
   type JavaShort = java.lang.Short
 
+  /** Converts plain Java maps to immutable Scala maps (excludes ConcurrentMap to avoid ambiguity). */
+  implicit def javaLinkedHashMapToScalaMap[K, V](map: JavaLinkedMap[K, V]): Map[K, V] = {
+    if (map == null) Map.empty else map.asScala.toMap
+  }
+
+  implicit def javaHashMapToScalaMap[K, V](map: JavaHashMap[K, V]): Map[K, V] = {
+    if (map == null) Map.empty else map.asScala.toMap
+  }
+
   implicit class AutoCloseImplicits[T <: AutoCloseable](autoCloseable: T) {
     implicit def using[R](func: T => R)(implicit excFunc: Throwable => R = null): R = {
       var exception: Option[Throwable] = Option.empty
diff --git a/streampark-common-scala-bridge/src/main/scala/org/apache/streampark/common/util/Logger.scala b/streampark-common-scala-bridge/src/main/scala/org/apache/streampark/common/util/Logger.scala
new file mode 100644
index 000000000..0094afe48
--- /dev/null
+++ b/streampark-common-scala-bridge/src/main/scala/org/apache/streampark/common/util/Logger.scala
@@ -0,0 +1,78 @@
+/*
+ * 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.streampark.common.util
+
+import org.apache.streampark.shaded.org.slf4j.{Logger => Slf4JLogger}
+
+trait Logger {
+
+  @transient private[this] var _logger: Slf4JLogger = _
+
+  private[this] val prefix = "[StreamPark]"
+
+  protected def logName: String = this.getClass.getName.stripSuffix("$")
+
+  protected def logger: Slf4JLogger = {
+    if (_logger == null) {
+      _logger = StreamParkLoggerFactory.loggerFactory().getLogger(logName)
+    }
+    _logger
+  }
+
+  protected def logInfo(msg: => String): Unit = {
+    if (logger.isInfoEnabled) logger.info(s"$prefix $msg")
+  }
+
+  protected def logInfo(msg: => String, throwable: Throwable): Unit = {
+    if (logger.isInfoEnabled) logger.info(s"$prefix $msg", throwable)
+  }
+
+  protected def logDebug(msg: => String): Unit = {
+    if (logger.isDebugEnabled) logger.debug(s"$prefix $msg")
+  }
+
+  protected def logDebug(msg: => String, throwable: Throwable): Unit = {
+    if (logger.isDebugEnabled) logger.debug(s"$prefix $msg", throwable)
+  }
+
+  def logTrace(msg: => String): Unit = {
+    if (logger.isTraceEnabled) logger.trace(s"$prefix $msg")
+  }
+
+  protected def logTrace(msg: => String, throwable: Throwable): Unit = {
+    if (logger.isTraceEnabled) logger.trace(s"$prefix $msg", throwable)
+  }
+
+  def logWarn(msg: => String): Unit = {
+    if (logger.isWarnEnabled) logger.warn(s"$prefix $msg")
+  }
+
+  protected def logWarn(msg: => String, throwable: Throwable): Unit = {
+    if (logger.isWarnEnabled) logger.warn(s"$prefix $msg", throwable)
+  }
+
+  protected def logError(msg: => String): Unit = {
+    if (logger.isErrorEnabled) logger.error(s"$prefix $msg")
+  }
+
+  protected def logError(msg: => String, throwable: Throwable): Unit = {
+    if (logger.isErrorEnabled) logger.error(s"$prefix $msg", throwable)
+  }
+
+  protected def isTraceEnabled(): Boolean = logger.isTraceEnabled
+}
diff --git a/streampark-common/pom.xml b/streampark-common/pom.xml
index bf9c43b78..9659e267a 100644
--- a/streampark-common/pom.xml
+++ b/streampark-common/pom.xml
@@ -29,21 +29,6 @@
 
     <dependencies>
 
-        <!-- test -->
-        <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-scala_${scala.binary.version}</artifactId>
-            <version>${mockito-scala.version}</version>
-            <scope>test</scope>
-        </dependency>
-
-        <dependency>
-            <groupId>org.scalatest</groupId>
-            <artifactId>scalatest_${scala.binary.version}</artifactId>
-            <version>${scalatest.version}</version>
-            <scope>test</scope>
-        </dependency>
-
         <dependency>
             <groupId>org.junit.jupiter</groupId>
             <artifactId>junit-jupiter-engine</artifactId>
@@ -58,10 +43,10 @@
             <scope>test</scope>
         </dependency>
 
-        <!-- enumeratum -->
         <dependency>
-            <groupId>com.beachape</groupId>
-            <artifactId>enumeratum_${scala.binary.version}</artifactId>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <scope>test</scope>
         </dependency>
 
         <dependency>
@@ -147,13 +132,8 @@
         <plugins>
 
             <plugin>
-                <groupId>org.codehaus.mojo</groupId>
-                <artifactId>build-helper-maven-plugin</artifactId>
-            </plugin>
-
-            <plugin>
-                <groupId>net.alchim31.maven</groupId>
-                <artifactId>scala-maven-plugin</artifactId>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
             </plugin>
 
             <plugin>
@@ -170,7 +150,6 @@
                             <dependencyReducedPomLocation>${project.basedir}/target/dependency-reduced-pom.xml</dependencyReducedPomLocation>
                             <artifactSet>
                                 <includes>
-                                    <include>com.beachape:*</include>
                                     <include>io.netty:netty-resolver</include>
                                 </includes>
                             </artifactSet>
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/conf/CommonConfig.java b/streampark-common/src/main/java/org/apache/streampark/common/conf/CommonConfig.java
new file mode 100644
index 000000000..65eac9dc6
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/conf/CommonConfig.java
@@ -0,0 +1,182 @@
+/*
+ * 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.streampark.common.conf;
+
+/** Common internal configuration options. */
+public final class CommonConfig {
+
+    private CommonConfig() {
+    }
+
+    private static final InternalOption STREAMPARK_WORKSPACE_LOCAL_OPTION =
+        new InternalOption("streampark.workspace.local", "/streampark", String.class);
+
+    private static final InternalOption STREAMPARK_WORKSPACE_REMOTE_OPTION =
+        new InternalOption("streampark.workspace.remote", "/streampark", String.class);
+
+    private static final InternalOption STREAMPARK_HADOOP_USER_NAME_OPTION =
+        new InternalOption("streampark.hadoop-user-name", "hdfs", String.class);
+
+    private static final InternalOption STREAMPARK_PROXY_YARN_URL_OPTION =
+        new InternalOption(
+            "streampark.proxy.yarn-url",
+            "",
+            String.class,
+            "proxy yarn url. ex: knox proxy or other");
+
+    private static final InternalOption STREAMPARK_YARN_AUTH_OPTION =
+        new InternalOption(
+            "streampark.yarn.http-auth",
+            "",
+            String.class,
+            "yarn http auth type. ex: simple, kerberos");
+
+    private static final InternalOption DOCKER_HOST_OPTION =
+        new InternalOption(
+            "streampark.docker.http-client.docker-host",
+            "",
+            String.class,
+            "docker host for DockerHttpClient");
+
+    private static final InternalOption DOCKER_MAX_CONNECTIONS_OPTION =
+        new InternalOption(
+            "streampark.docker.http-client.max-connections",
+            100,
+            Integer.class,
+            "instantiating max connections for DockerHttpClient");
+
+    private static final InternalOption DOCKER_CONNECTION_TIMEOUT_SEC_OPTION =
+        new InternalOption(
+            "streampark.docker.http-client.connection-timeout-sec",
+            100L,
+            Long.class,
+            "instantiating connection timeout for DockerHttpClient");
+
+    private static final InternalOption DOCKER_RESPONSE_TIMEOUT_SEC_OPTION =
+        new InternalOption(
+            "streampark.docker.http-client.response-timeout-sec",
+            120L,
+            Long.class,
+            "instantiating connection timeout for DockerHttpClient");
+
+    private static final InternalOption MAVEN_SETTINGS_PATH_OPTION =
+        new InternalOption(
+            "streampark.maven.settings",
+            null,
+            String.class,
+            "maven settings.xml full path");
+
+    private static final InternalOption MAVEN_REMOTE_URL_OPTION =
+        new InternalOption(
+            "streampark.maven.central.repository",
+            "https://repo1.maven.org/maven2/",
+            String.class,
+            "maven repository used for built-in compilation");
+
+    private static final InternalOption MAVEN_AUTH_USER_OPTION =
+        new InternalOption(
+            "streampark.maven.auth.user",
+            null,
+            String.class,
+            "maven repository used for built-in compilation");
+
+    private static final InternalOption MAVEN_AUTH_PASSWORD_OPTION =
+        new InternalOption(
+            "streampark.maven.auth.password",
+            null,
+            String.class,
+            "maven repository used for built-in compilation");
+
+    private static final InternalOption KERBEROS_TTL_OPTION =
+        new InternalOption("security.kerberos.ttl", "2h", String.class, "kerberos default ttl");
+
+    private static final InternalOption READ_LOG_MAX_SIZE_OPTION =
+        new InternalOption(
+            "streampark.read-log.max-size",
+            "1mb",
+            String.class,
+            "The maximum size of the default read log");
+
+    private static final InternalOption SPRING_PROFILES_ACTIVE_OPTION =
+        new InternalOption(
+            "spring.profiles.active", "h2", String.class, "Use the database type");
+
+    public static InternalOption STREAMPARK_WORKSPACE_LOCAL() {
+        return STREAMPARK_WORKSPACE_LOCAL_OPTION;
+    }
+
+    public static InternalOption STREAMPARK_WORKSPACE_REMOTE() {
+        return STREAMPARK_WORKSPACE_REMOTE_OPTION;
+    }
+
+    public static InternalOption STREAMPARK_HADOOP_USER_NAME() {
+        return STREAMPARK_HADOOP_USER_NAME_OPTION;
+    }
+
+    public static InternalOption STREAMPARK_PROXY_YARN_URL() {
+        return STREAMPARK_PROXY_YARN_URL_OPTION;
+    }
+
+    public static InternalOption STREAMPARK_YARN_AUTH() {
+        return STREAMPARK_YARN_AUTH_OPTION;
+    }
+
+    public static InternalOption DOCKER_HOST() {
+        return DOCKER_HOST_OPTION;
+    }
+
+    public static InternalOption DOCKER_MAX_CONNECTIONS() {
+        return DOCKER_MAX_CONNECTIONS_OPTION;
+    }
+
+    public static InternalOption DOCKER_CONNECTION_TIMEOUT_SEC() {
+        return DOCKER_CONNECTION_TIMEOUT_SEC_OPTION;
+    }
+
+    public static InternalOption DOCKER_RESPONSE_TIMEOUT_SEC() {
+        return DOCKER_RESPONSE_TIMEOUT_SEC_OPTION;
+    }
+
+    public static InternalOption MAVEN_SETTINGS_PATH() {
+        return MAVEN_SETTINGS_PATH_OPTION;
+    }
+
+    public static InternalOption MAVEN_REMOTE_URL() {
+        return MAVEN_REMOTE_URL_OPTION;
+    }
+
+    public static InternalOption MAVEN_AUTH_USER() {
+        return MAVEN_AUTH_USER_OPTION;
+    }
+
+    public static InternalOption MAVEN_AUTH_PASSWORD() {
+        return MAVEN_AUTH_PASSWORD_OPTION;
+    }
+
+    public static InternalOption KERBEROS_TTL() {
+        return KERBEROS_TTL_OPTION;
+    }
+
+    public static InternalOption READ_LOG_MAX_SIZE() {
+        return READ_LOG_MAX_SIZE_OPTION;
+    }
+
+    public static InternalOption SPRING_PROFILES_ACTIVE() {
+        return SPRING_PROFILES_ACTIVE_OPTION;
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/conf/ConfigKeys.java b/streampark-common/src/main/java/org/apache/streampark/common/conf/ConfigKeys.java
new file mode 100644
index 000000000..919301083
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/conf/ConfigKeys.java
@@ -0,0 +1,396 @@
+/*
+ * 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.streampark.common.conf;
+
+/**
+ * Configuration key constants.
+ *
+ * <p>Parameterless {@code KEY_XXX()} methods mirror the original Scala API for downstream modules.
+ */
+@SuppressWarnings({"java:S100", "java:S3400"})
+public final class ConfigKeys {
+
+    private ConfigKeys() {
+    }
+
+    public static String PARAM_PREFIX() {
+        return "--";
+    }
+
+    /** about parameter... */
+    public static String KEY_APP_HOME() {
+        return "app.home";
+    }
+
+    public static String KEY_HOST() {
+        return "host";
+    }
+
+    public static String KEY_PORT() {
+        return "port";
+    }
+
+    public static String KEY_DB() {
+        return "db";
+    }
+
+    public static String KEY_USER() {
+        return "user";
+    }
+
+    public static String KEY_PASSWORD() {
+        return "password";
+    }
+
+    public static String KEY_TIMEOUT() {
+        return "timeout";
+    }
+
+    public static String KEY_SEMANTIC() {
+        return "semantic";
+    }
+
+    /** kerberos */
+    public static String KEY_KERBEROS() {
+        return "kerberos";
+    }
+
+    public static String KEY_KERBEROS_SERVICE_ACCOUNT() {
+        return "kubernetes.service-account";
+    }
+
+    public static String KEY_HADOOP_USER_NAME() {
+        return "HADOOP_USER_NAME";
+    }
+
+    /** hadoop.security.authentication */
+    public static String KEY_HADOOP_SECURITY_AUTHENTICATION() {
+        return "hadoop.security.authentication";
+    }
+
+    public static String KEY_SECURITY_KERBEROS_ENABLE() {
+        return "security.kerberos.login.enable";
+    }
+
+    public static String KEY_SECURITY_KERBEROS_DEBUG() {
+        return "security.kerberos.login.debug";
+    }
+
+    public static String KEY_SECURITY_KERBEROS_KEYTAB() {
+        return "security.kerberos.login.keytab";
+    }
+
+    public static String KEY_SECURITY_KERBEROS_PRINCIPAL() {
+        return "security.kerberos.login.principal";
+    }
+
+    public static String KEY_SECURITY_KERBEROS_KRB5_CONF() {
+        return "security.kerberos.login.krb5";
+    }
+
+    /** about spark */
+    public static String KEY_SPARK_MAIN_CLASS() {
+        return "spark.main.class";
+    }
+
+    public static String KEY_SPARK_APP_NAME() {
+        return "spark.app.name";
+    }
+
+    public static String KEY_SPARK_BATCH_DURATION() {
+        return "spark.batch.duration";
+    }
+
+    public static String KEY_SPARK_DRIVER_CORES() {
+        return "spark.driver.cores";
+    }
+
+    public static String KEY_SPARK_DRIVER_MEMORY() {
+        return "spark.driver.memory";
+    }
+
+    public static String KEY_SPARK_EXECUTOR_INSTANCES() {
+        return "spark.executor.instances";
+    }
+
+    public static String KEY_SPARK_EXECUTOR_CORES() {
+        return "spark.executor.cores";
+    }
+
+    public static String KEY_SPARK_EXECUTOR_MEMORY() {
+        return "spark.executor.memory";
+    }
+
+    public static String KEY_SPARK_DYNAMIC_ALLOCATION_ENABLED() {
+        return "spark.dynamicAllocation.enabled";
+    }
+
+    public static String KEY_SPARK_DYNAMIC_ALLOCATION_MAX_EXECUTORS() {
+        return "spark.dynamicAllocation.maxExecutors";
+    }
+
+    public static String KEY_SPARK_YARN_QUEUE() {
+        return "spark.yarn.queue";
+    }
+
+    public static String KEY_SPARK_YARN_QUEUE_NAME() {
+        return "yarnQueueName";
+    }
+
+    public static String KEY_SPARK_YARN_QUEUE_LABEL() {
+        return "yarnQueueLabel";
+    }
+
+    public static String KEY_SPARK_YARN_AM_NODE_LABEL() {
+        return "spark.yarn.am.nodeLabelExpression";
+    }
+
+    public static String KEY_SPARK_YARN_EXECUTOR_NODE_LABEL() {
+        return "spark.yarn.executor.nodeLabelExpression";
+    }
+
+    public static String KEY_SPARK_SQL() {
+        return KEY_SPARK_SQL(null);
+    }
+
+    public static String KEY_SPARK_SQL(String prefix) {
+        return (prefix != null ? prefix : "") + "sql";
+    }
+
+    /** about config flink */
+    public static String KEY_APP_CONF() {
+        return KEY_APP_CONF(null);
+    }
+
+    public static String KEY_APP_CONF(String prefix) {
+        return (prefix != null ? prefix : "") + "conf";
+    }
+
+    public static String KEY_FLINK_CONF() {
+        return KEY_FLINK_CONF(null);
+    }
+
+    public static String KEY_FLINK_CONF(String prefix) {
+        return (prefix != null ? prefix : "") + "flink.conf";
+    }
+
+    public static String KEY_APP_NAME() {
+        return KEY_APP_NAME(null);
+    }
+
+    public static String KEY_APP_NAME(String prefix) {
+        return (prefix != null ? prefix : "") + "app.name";
+    }
+
+    public static String KEY_FLINK_SQL() {
+        return KEY_FLINK_SQL(null);
+    }
+
+    public static String KEY_FLINK_SQL(String prefix) {
+        return (prefix != null ? prefix : "") + "sql";
+    }
+
+    public static String KEY_FLINK_PARALLELISM() {
+        return KEY_FLINK_PARALLELISM(null);
+    }
+
+    public static String KEY_FLINK_PARALLELISM(String prefix) {
+        return (prefix != null ? prefix : "") + "parallelism.default";
+    }
+
+    public static String KEY_FLINK_OPTION_PREFIX() {
+        return "flink.option.";
+    }
+
+    public static String KEY_FLINK_PROPERTY_PREFIX() {
+        return "flink.property.";
+    }
+
+    public static String KEY_FLINK_TABLE_PREFIX() {
+        return "flink.table.";
+    }
+
+    public static String KEY_SPARK_PROPERTY_PREFIX() {
+        return "spark.";
+    }
+
+    public static String KEY_APP_PREFIX() {
+        return "app.";
+    }
+
+    public static String KEY_SQL_PREFIX() {
+        return "sql.";
+    }
+
+    public static String KEY_FLINK_APP_NAME() {
+        return "pipeline.name";
+    }
+
+    public static String KEY_YARN_APP_ID() {
+        return "yarn.application.id";
+    }
+
+    public static String KEY_YARN_APP_NAME() {
+        return "yarn.application.name";
+    }
+
+    public static String KEY_YARN_APP_QUEUE() {
+        return "yarn.application.queue";
+    }
+
+    public static String KEY_YARN_APP_NODE_LABEL() {
+        return "yarn.application.node-label";
+    }
+
+    public static String KEY_K8S_IMAGE_PULL_POLICY() {
+        return "kubernetes.container.image.pull-policy";
+    }
+
+    public static String FLINK_NATIVE_KUBERNETES_LABEL() {
+        return "flink-native-kubernetes";
+    }
+
+    /** about flink table */
+    public static String KEY_FLINK_TABLE_PLANNER() {
+        return "flink.table.planner";
+    }
+
+    public static String KEY_FLINK_TABLE_MODE() {
+        return "flink.table.mode";
+    }
+
+    public static String KEY_FLINK_TABLE_CATALOG() {
+        return "flink.table.catalog";
+    }
+
+    public static String KEY_FLINK_TABLE_DATABASE() {
+        return "flink.table.database";
+    }
+
+    /** about config Kafka */
+    public static String KAFKA_SINK_PREFIX() {
+        return "kafka.sink.";
+    }
+
+    public static String KAFKA_SOURCE_PREFIX() {
+        return "kafka.source.";
+    }
+
+    public static String KEY_KAFKA_TOPIC() {
+        return "topic";
+    }
+
+    public static String KEY_KAFKA_SEMANTIC() {
+        return "semantic";
+    }
+
+    public static String KEY_KAFKA_PATTERN() {
+        return "pattern";
+    }
+
+    public static String KEY_KAFKA_START_FROM() {
+        return "start.from";
+    }
+
+    public static String KEY_KAFKA_START_FROM_OFFSET() {
+        return "offset";
+    }
+
+    public static String KEY_KAFKA_START_FROM_TIMESTAMP() {
+        return "timestamp";
+    }
+
+    public static String KEY_ALIAS() {
+        return "alias";
+    }
+
+    /** about config jdbc... */
+    public static String KEY_JDBC_PREFIX() {
+        return "jdbc.";
+    }
+
+    public static String KEY_JDBC_DRIVER() {
+        return "driverClassName";
+    }
+
+    public static String KEY_JDBC_URL() {
+        return "jdbcUrl";
+    }
+
+    public static String KEY_JDBC_USER() {
+        return "username";
+    }
+
+    public static String KEY_JDBC_PASSWORD() {
+        return "password";
+    }
+
+    public static String KEY_JDBC_INSERT_BATCH() {
+        return "batch.size";
+    }
+
+    public static int DEFAULT_JDBC_INSERT_BATCH() {
+        return 1;
+    }
+
+    public static String MONGO_PREFIX() {
+        return "mongodb.";
+    }
+
+    /** about config HBase */
+    public static String HBASE_PREFIX() {
+        return "hbase.";
+    }
+
+    public static String KEY_HBASE_COMMIT_BATCH() {
+        return "hbase.commit.batch";
+    }
+
+    public static String KEY_HBASE_WRITE_SIZE() {
+        return "hbase.client.write.size";
+    }
+
+    public static int DEFAULT_HBASE_COMMIT_BATCH() {
+        return 1000;
+    }
+
+    public static int DEFAULT_HBASE_WRITE_SIZE() {
+        return 1024 * 1024 * 10;
+    }
+
+    public static String KEY_HBASE_AUTH_USER() {
+        return "hbase.auth.user";
+    }
+
+    /** about influx */
+    public static String INFLUX_PREFIX() {
+        return "influx.";
+    }
+
+    public static String KEY_FLINK_APPLICATION_MAIN_CLASS() {
+        return "$internal.application.main";
+    }
+
+    public static String KEY_FLINK_JM_PROCESS_MEMORY() {
+        return "jobmanager.memory.process.size";
+    }
+
+    public static String KEY_FLINK_TM_PROCESS_MEMORY() {
+        return "taskmanager.memory.process.size";
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/conf/ConfigOption.java b/streampark-common/src/main/java/org/apache/streampark/common/conf/ConfigOption.java
new file mode 100644
index 000000000..f603e3915
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/conf/ConfigOption.java
@@ -0,0 +1,159 @@
+/*
+ * 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.streampark.common.conf;
+
+import org.apache.streampark.common.util.StringCastUtils;
+
+import java.util.Properties;
+import java.util.function.Function;
+
+/**
+ * Configuration option with typed value retrieval.
+ *
+ * @param <T> value type
+ */
+public class ConfigOption<T> {
+
+    private final String key;
+    private final T defaultValue;
+    private final boolean required;
+    private final Class<?> classType;
+    private final String description;
+    private final Function<String, T> handle;
+    private final String prefix;
+    private final Properties prop;
+
+    public ConfigOption(
+                        String key,
+                        T defaultValue,
+                        boolean required,
+                        Class<?> classType,
+                        String description,
+                        Function<String, T> handle,
+                        String prefix,
+                        Properties prop) {
+        this.key = key;
+        this.defaultValue = defaultValue;
+        this.required = required;
+        this.classType = classType;
+        this.description = description;
+        this.handle = handle;
+        this.prefix = prefix != null ? prefix : "";
+        this.prop = prop != null ? prop : new Properties();
+    }
+
+    public static <T> Builder<T> builder(String key) {
+        return new Builder<>(key);
+    }
+
+    private String fullKey() {
+        if (prefix != null && !prefix.isEmpty()) {
+            return prefix + "." + key;
+        }
+        return key;
+    }
+
+    @SuppressWarnings("unchecked")
+    public T get() {
+        String fk = fullKey();
+        if (handle == null) {
+            if (required) {
+                Object v = prop.get(fk);
+                if (v == null) {
+                    throw error("Is require");
+                }
+                return StringCastUtils.cast(v.toString(), classType);
+            } else {
+                String v = prop.getProperty(fk);
+                if (v == null) {
+                    return defaultValue;
+                }
+                return StringCastUtils.cast(v, classType);
+            }
+        } else {
+            try {
+                return handle.apply(fk);
+            } catch (Exception e) {
+                if (required) {
+                    throw error(e.getMessage());
+                }
+                return defaultValue;
+            }
+        }
+    }
+
+    private IllegalArgumentException error(String message) {
+        return new IllegalArgumentException(
+            "[StreamPark] config error: key:" + fullKey() + ", detail: " + message);
+    }
+
+    public static class Builder<T> {
+
+        private final String key;
+        private T defaultValue;
+        private boolean required;
+        private Class<?> classType;
+        private String description = "";
+        private Function<String, T> handle;
+        private String prefix = "";
+        private Properties prop = new Properties();
+
+        Builder(String key) {
+            this.key = key;
+        }
+
+        public Builder<T> defaultValue(T defaultValue) {
+            this.defaultValue = defaultValue;
+            return this;
+        }
+
+        public Builder<T> required(boolean required) {
+            this.required = required;
+            return this;
+        }
+
+        public Builder<T> classType(Class<?> classType) {
+            this.classType = classType;
+            return this;
+        }
+
+        public Builder<T> description(String description) {
+            this.description = description;
+            return this;
+        }
+
+        public Builder<T> handle(Function<String, T> handle) {
+            this.handle = handle;
+            return this;
+        }
+
+        public Builder<T> prefix(String prefix) {
+            this.prefix = prefix;
+            return this;
+        }
+
+        public Builder<T> properties(Properties prop) {
+            this.prop = prop;
+            return this;
+        }
+
+        public ConfigOption<T> build() {
+            return new ConfigOption<>(key, defaultValue, required, classType, description, handle, prefix, prop);
+        }
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/conf/FlinkVersion.java b/streampark-common/src/main/java/org/apache/streampark/common/conf/FlinkVersion.java
new file mode 100644
index 000000000..9112c7ae8
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/conf/FlinkVersion.java
@@ -0,0 +1,261 @@
+/*
+ * 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.streampark.common.conf;
+
+import org.apache.streampark.common.util.CommandUtils;
+
+import org.apache.streampark.shaded.org.slf4j.Logger;
+
+import java.io.File;
+import java.io.Serializable;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import scala.collection.JavaConverters;
+
+/** @param flinkHome actual flink home that must be a readable local path */
+public class FlinkVersion implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+    private static final Logger LOG =
+        org.apache.streampark.common.util.StreamParkLoggerFactory.loggerFactory()
+            .getLogger(FlinkVersion.class.getName());
+
+    private static final Pattern FLINK_VER_PATTERN = Pattern.compile("^(\\d+\\.\\d+)(?:\\..*)?$");
+    private static final Pattern FLINK_VERSION_PATTERN =
+        Pattern.compile("^Version: ([^,]*), Commit ID: (.*)$");
+    private static final Pattern FLINK_SCALA_VERSION_PATTERN =
+        Pattern.compile("^flink-dist_(\\d+\\.\\d+)[^/\\\\]*\\.jar$");
+    private static final Pattern APACHE_FLINK_VERSION_PATTERN =
+        Pattern.compile("^(\\d+\\.\\d+\\.\\d+)");
+    private static final Pattern OTHER_FLINK_VERSION_PATTERN = Pattern.compile("^(\\d+\\.\\d+)-?$");
+
+    /** Flink installation directory (Scala {@code flinkHome} accessor). */
+    public final String flinkHome;
+    private transient String scalaVersion;
+    private transient String version;
+    private transient File flinkLib;
+    private transient File flinkDistJar;
+
+    public FlinkVersion(String flinkHome) {
+        this.flinkHome = flinkHome;
+    }
+
+    public String getFlinkHome() {
+        return flinkHome;
+    }
+
+    /** Scala API alias for {@link #getFullVersion()}. */
+    public String fullVersion() {
+        return getFullVersion();
+    }
+
+    /** Scala API alias for {@link #getMajorVersion()}. */
+    public String majorVersion() {
+        return getMajorVersion();
+    }
+
+    /** Scala API alias for {@link #getScalaVersion()}. */
+    public String scalaVersion() {
+        return getScalaVersion();
+    }
+
+    /** Scala API alias for {@link #getVersion()}. */
+    public String version() {
+        return getVersion();
+    }
+
+    /** Scala API alias for {@link #getFlinkLibs()}. */
+    public scala.collection.immutable.List<URL> flinkLibs() throws Exception {
+        return JavaConverters.asScalaIteratorConverter(getFlinkLibs().iterator()).asScala().toList();
+    }
+
+    public String getScalaVersion() {
+        if (scalaVersion == null) {
+            Matcher matcher = FLINK_SCALA_VERSION_PATTERN.matcher(getFlinkDistJar().getName());
+            scalaVersion = matcher.matches() ? matcher.group(1) : "2.12";
+        }
+        return scalaVersion;
+    }
+
+    public String getFullVersion() {
+        return getVersion() + "_" + getScalaVersion();
+    }
+
+    public File getFlinkLib() {
+        if (flinkLib == null) {
+            if (flinkHome == null) {
+                throw new IllegalArgumentException("[StreamPark] flinkHome must not be null.");
+            }
+            if (!new File(flinkHome).exists()) {
+                throw new IllegalArgumentException("[StreamPark] flinkHome must be exists.");
+            }
+            File lib = new File(flinkHome + "/lib");
+            if (!lib.exists() || !lib.isDirectory()) {
+                throw new IllegalArgumentException(
+                    "[StreamPark] " + flinkHome + "/lib must be exists and must be directory.");
+            }
+            flinkLib = lib;
+        }
+        return flinkLib;
+    }
+
+    public List<URL> getFlinkLibs() throws Exception {
+        File[] files = getFlinkLib().listFiles();
+        if (files == null) {
+            return Arrays.asList();
+        }
+        return Arrays.stream(files).map(f -> {
+            try {
+                return f.toURI().toURL();
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }).collect(Collectors.toList());
+    }
+
+    public String getVersion() {
+        if (version == null) {
+            List<String> cmd =
+                Arrays.asList(
+                    "java -classpath "
+                        + getFlinkDistJar().getName()
+                        + " org.apache.flink.client.cli.CliFrontend --version");
+            StringBuilder buffer = new StringBuilder();
+            final String[] flinkVersion = {null};
+            try {
+                CommandUtils.execute(
+                    getFlinkLib().getAbsolutePath(),
+                    cmd,
+                    new Consumer<String>() {
+
+                        @Override
+                        public void accept(String out) {
+                            buffer.append(out).append("\n");
+                            Matcher matcher = FLINK_VERSION_PATTERN.matcher(out);
+                            if (matcher.find()) {
+                                String ver = matcher.group(1);
+                                Matcher m1 = APACHE_FLINK_VERSION_PATTERN.matcher(ver);
+                                if (m1.find()) {
+                                    flinkVersion[0] = ver;
+                                } else {
+                                    Matcher m2 = OTHER_FLINK_VERSION_PATTERN.matcher(ver);
+                                    if (m2.find()) {
+                                        flinkVersion[0] = ver;
+                                    }
+                                }
+                            }
+                        }
+                    });
+            } catch (Exception e) {
+                throw new IllegalStateException("[StreamPark] execute flink version command failed", e);
+            }
+            LOG.info("[StreamPark] {}", buffer);
+            if (flinkVersion[0] == null) {
+                throw new IllegalStateException("[StreamPark] parse flink version failed. " + buffer);
+            }
+            version = flinkVersion[0];
+        }
+        return version;
+    }
+
+    public String getMajorVersion() {
+        if (getVersion() == null) {
+            return null;
+        }
+        Matcher matcher = FLINK_VER_PATTERN.matcher(getVersion());
+        matcher.matches();
+        return matcher.group(1);
+    }
+
+    public File getFlinkDistJar() {
+        if (flinkDistJar == null) {
+            File[] distJar =
+                getFlinkLib().listFiles(
+                    f -> {
+                        String name = f.getName();
+                        return name.startsWith("flink-dist") && name.endsWith(".jar");
+                    });
+            if (distJar == null || distJar.length == 0) {
+                throw new IllegalArgumentException(
+                    "[StreamPark] can no found flink-dist jar in " + getFlinkLib());
+            }
+            if (distJar.length > 1) {
+                throw new IllegalArgumentException(
+                    "[StreamPark] found multiple flink-dist jar in " + getFlinkLib());
+            }
+            flinkDistJar = distJar[0];
+        }
+        return flinkDistJar;
+    }
+
+    public boolean checkVersion() {
+        return checkVersion(true);
+    }
+
+    public boolean checkVersion(boolean throwException) {
+        String[] parts = getVersion().split("\\.");
+        if (parts.length >= 2 && "1".equals(parts[0].trim())) {
+            try {
+                int minor = Integer.parseInt(parts[1].trim());
+                if (minor >= 12 && minor <= 20) {
+                    return true;
+                }
+            } catch (NumberFormatException ignored) {
+            }
+        }
+        if (throwException) {
+            throw new UnsupportedOperationException("Unsupported flink version: " + getVersion());
+        }
+        return false;
+    }
+
+    public boolean checkVersion(int sinceVersion) {
+        String[] parts = getVersion().split("\\.");
+        if (parts.length >= 2 && "1".equals(parts[0].trim())) {
+            try {
+                return Integer.parseInt(parts[1].trim()) >= sinceVersion;
+            } catch (NumberFormatException ignored) {
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return "\n----------------------------------------- flink version -----------------------------------\n"
+            + "     flinkHome    : "
+            + flinkHome
+            + "\n     distJarName  : "
+            + getFlinkDistJar().getName()
+            + "\n     flinkVersion : "
+            + getVersion()
+            + "\n     majorVersion : "
+            + getMajorVersion()
+            + "\n     scalaVersion : "
+            + getScalaVersion()
+            + "\n     shimsVersion : streampark-flink-shims_flink-"
+            + getMajorVersion()
+            + "\n-------------------------------------------------------------------------------------------\n";
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/conf/InternalConfigHolder.java b/streampark-common/src/main/java/org/apache/streampark/common/conf/InternalConfigHolder.java
new file mode 100644
index 000000000..32b7b2fb9
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/conf/InternalConfigHolder.java
@@ -0,0 +1,154 @@
+/*
+ * 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.streampark.common.conf;
+
+import org.apache.streampark.common.constants.Constants;
+import org.apache.streampark.common.util.StreamParkLoggerFactory;
+import org.apache.streampark.common.util.StringCastUtils;
+import org.apache.streampark.common.util.SystemPropertyUtils;
+
+import org.apache.streampark.shaded.org.slf4j.Logger;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+/**
+ * Thread-safe configuration storage containers. All configurations will be automatically
+ * initialized from the spring configuration items of the same name.
+ */
+public final class InternalConfigHolder {
+
+    private static final Logger LOG =
+        StreamParkLoggerFactory.loggerFactory().getLogger(InternalConfigHolder.class.getName());
+
+    private static final int INITIAL_CAPACITY = 45;
+
+    private static final Map<String, Object> CONF_DATA = new ConcurrentHashMap<>(INITIAL_CAPACITY);
+    private static final Map<String, InternalOption> CONF_OPTIONS =
+        new ConcurrentHashMap<>(INITIAL_CAPACITY);
+
+    static {
+        initConfigHub();
+    }
+
+    private InternalConfigHolder() {
+    }
+
+    /** Initialize the ConfigHub. */
+    public static void initConfigHub() {
+        CommonConfig.STREAMPARK_WORKSPACE_LOCAL();
+        InternalOption unused = K8sFlinkConfig.jobStatusTrackTaskTimeoutSec;
+    }
+
+    static void register(@Nonnull InternalOption conf) {
+        CONF_OPTIONS.put(conf.getKey(), conf);
+        if (conf.getDefaultValue() != null) {
+            CONF_DATA.put(conf.getKey(), conf.getDefaultValue());
+        }
+    }
+
+    @Nonnull
+    @SuppressWarnings("unchecked")
+    public static <T> T get(@Nonnull InternalOption conf) {
+        Object value = CONF_DATA.get(conf.getKey());
+        if (value == null || value == conf.getDefaultValue()) {
+            String v = SystemPropertyUtils.get(conf.getKey());
+            if (v != null) {
+                if (!v.equals(value)) {
+                    set(conf, v);
+                }
+                return StringCastUtils.cast(v, conf.getClassType());
+            } else {
+                return (T) conf.getDefaultValue();
+            }
+        } else {
+            return StringCastUtils.cast(value.toString(), conf.getClassType());
+        }
+    }
+
+    @Nonnull
+    @SuppressWarnings("unchecked")
+    public static <T> T get(@Nonnull String key) {
+        Object v = CONF_DATA.get(key);
+        if (v == null) {
+            InternalOption conf = CONF_OPTIONS.get(key);
+            if (conf == null) {
+                InternalOption config = getConfig(key);
+                String sysVal = SystemPropertyUtils.get(key);
+                if (sysVal != null) {
+                    return StringCastUtils.cast(sysVal, config.getClassType());
+                }
+                throw new IllegalArgumentException("Config key has not been registered: " + key);
+            }
+            return (T) conf.getDefaultValue();
+        }
+        return (T) v;
+    }
+
+    @Nullable
+    public static InternalOption getConfig(String key) {
+        return CONF_OPTIONS.get(key);
+    }
+
+    @Nonnull
+    public static Set<String> keys() {
+        return CONF_OPTIONS.keySet();
+    }
+
+    public static void set(@Nonnull InternalOption conf, Object value) {
+        if (!CONF_OPTIONS.containsKey(conf.getKey())) {
+            throw new IllegalArgumentException("config key has not been registered: " + conf);
+        }
+        if (value == null) {
+            CONF_DATA.remove(conf.getKey());
+        } else if (conf.getClassType() != value.getClass()) {
+            throw new IllegalArgumentException(
+                "config value type is not match of "
+                    + conf.getKey()
+                    + ", required: "
+                    + conf.getClassType()
+                    + ", actual: "
+                    + value.getClass());
+        } else {
+            SystemPropertyUtils.set(conf.getKey(), value.toString());
+            CONF_DATA.put(conf.getKey(), value);
+        }
+    }
+
+    public static void log() {
+        Set<String> configKeys = keys();
+        String details =
+            configKeys.stream()
+                .map(
+                    key -> key
+                        + " = "
+                        + (key.contains("password")
+                            ? Constants.DEFAULT_DATAMASK_STRING
+                            : get(key)))
+                .collect(Collectors.joining("\n  "));
+        LOG.info(
+            "[StreamPark] Registered configs:\nConfigHub collected configs: {}\n  {}",
+            configKeys.size(),
+            details);
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/conf/InternalOption.java b/streampark-common/src/main/java/org/apache/streampark/common/conf/InternalOption.java
new file mode 100644
index 000000000..532e21fc3
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/conf/InternalOption.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.streampark.common.conf;
+
+/** Internal use of the system configuration option. */
+public class InternalOption {
+
+    private final String key;
+    private final Object defaultValue;
+    private final Class<?> classType;
+    private final String description;
+
+    public InternalOption(String key, Object defaultValue, Class<?> classType) {
+        this(key, defaultValue, classType, "");
+    }
+
+    public InternalOption(String key, Object defaultValue, Class<?> classType, String description) {
+        this.key = key;
+        this.defaultValue = defaultValue;
+        this.classType = classType;
+        this.description = description;
+        InternalConfigHolder.register(this);
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public Object getDefaultValue() {
+        return defaultValue;
+    }
+
+    public Class<?> getClassType() {
+        return classType;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    /** Scala API alias for {@link #getKey()}. */
+    public String key() {
+        return getKey();
+    }
+
+    /** Scala API alias for {@link #getDefaultValue()}. */
+    public Object defaultValue() {
+        return getDefaultValue();
+    }
+
+    /** Scala API alias for {@link #getClassType()}. */
+    public Class<?> classType() {
+        return getClassType();
+    }
+
+    @Override
+    public String toString() {
+        return "InternalOption{key='" + key + "'}";
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/conf/K8sFlinkConfig.java b/streampark-common/src/main/java/org/apache/streampark/common/conf/K8sFlinkConfig.java
new file mode 100644
index 000000000..1de257168
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/conf/K8sFlinkConfig.java
@@ -0,0 +1,83 @@
+/*
+ * 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.streampark.common.conf;
+
+/** Flink kubernetes Configuration for v1 version */
+public final class K8sFlinkConfig {
+
+    private K8sFlinkConfig() {
+    }
+
+    @Deprecated(since = "3.0.0", forRemoval = true)
+    public static final InternalOption jobStatusTrackTaskTimeoutSec =
+        new InternalOption(
+            "streampark.flink-k8s.tracking.polling-task-timeout-sec.job-status",
+            120L,
+            Long.class,
+            "run timeout seconds of single flink-k8s metrics tracking task");
+
+    @Deprecated(since = "3.0.0", forRemoval = true)
+    public static final InternalOption jobStatusTrackCacheTimeoutSec =
+        new InternalOption(
+            "streampark.flink-k8s.tracking.cache-timeout-sec.job-status",
+            300,
+            Integer.class,
+            "status cache timeout seconds of single flink-k8s job status tracking task");
+
+    @Deprecated(since = "3.0.0", forRemoval = true)
+    public static final InternalOption metricTrackTaskTimeoutSec =
+        new InternalOption(
+            "streampark.flink-k8s.tracking.polling-task-timeout-sec.cluster-metric",
+            120L,
+            Long.class,
+            "run timeout seconds of single flink-k8s job status tracking task");
+
+    @Deprecated(since = "3.0.0", forRemoval = true)
+    public static final InternalOption jobStatueTrackTaskIntervalSec =
+        new InternalOption(
+            "streampark.flink-k8s.tracking.polling-interval-sec.job-status",
+            5L,
+            Long.class,
+            "interval seconds between two single flink-k8s metrics tracking task");
+
+    @Deprecated(since = "3.0.0", forRemoval = true)
+    public static final InternalOption metricTrackTaskIntervalSec =
+        new InternalOption(
+            "streampark.flink-k8s.tracking.polling-interval-sec.cluster-metric",
+            5L,
+            Long.class,
+            "interval seconds between two single flink-k8s metrics tracking task");
+
+    @Deprecated(since = "3.0.0", forRemoval = true)
+    public static final InternalOption silentStateJobKeepTrackingSec =
+        new InternalOption(
+            "streampark.flink-k8s.tracking.silent-state-keep-sec",
+            60,
+            Integer.class,
+            "retained tracking time for SILENT state flink tasks");
+
+    public static final InternalOption ingressClass =
+        new InternalOption(
+            "streampark.flink-k8s.ingress.class",
+            "nginx",
+            String.class,
+            "Direct ingress to the ingress controller.");
+
+    @Deprecated(since = "3.0.0", forRemoval = true)
+    public static final String DEFAULT_KUBERNETES_NAMESPACE = "default";
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/conf/SparkVersion.java b/streampark-common/src/main/java/org/apache/streampark/common/conf/SparkVersion.java
new file mode 100644
index 000000000..095f9bbd2
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/conf/SparkVersion.java
@@ -0,0 +1,151 @@
+/*
+ * 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.streampark.common.conf;
+
+import org.apache.streampark.common.util.CommandUtils;
+
+import org.apache.streampark.shaded.org.slf4j.Logger;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class SparkVersion implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+    private static final Logger LOG = org.apache.streampark.common.util.StreamParkLoggerFactory.loggerFactory()
+        .getLogger(SparkVersion.class.getName());
+    private static final Pattern SPARK_VER_PATTERN = Pattern.compile("^(\\d+\\.\\d+)(?:\\..*)?$");
+    private static final Pattern SPARK_VERSION_PATTERN = Pattern.compile("\\s{2}version\\s(\\d+\\.\\d+\\.\\d+)");
+    private static final Pattern SPARK_SCALA_VERSION_PATTERN =
+        Pattern.compile("Using\\sScala\\sversion\\s(\\d+\\.\\d+)");
+
+    /** Spark installation directory (Scala {@code sparkHome} accessor). */
+    public final String sparkHome;
+    private final String version;
+    private final String scalaVersion;
+
+    public SparkVersion(String sparkHome) {
+        this.sparkHome = sparkHome;
+        String[] parsed = parseVersion(sparkHome);
+        this.version = parsed[0];
+        this.scalaVersion = parsed[1];
+    }
+
+    private static String[] parseVersion(String sparkHome) {
+        final String[] sparkVersion = {null, null};
+        List<String> cmd =
+            Arrays.asList("export SPARK_HOME=" + sparkHome + "&&" + sparkHome + "/bin/spark-submit --version");
+        StringBuilder buffer = new StringBuilder();
+        try {
+            CommandUtils.execute(sparkHome, cmd, out -> {
+                buffer.append(out).append("\n");
+                Matcher m = SPARK_VERSION_PATTERN.matcher(out);
+                if (m.find())
+                    sparkVersion[0] = m.group(1);
+                Matcher m1 = SPARK_SCALA_VERSION_PATTERN.matcher(out);
+                if (m1.find())
+                    sparkVersion[1] = m1.group(1);
+            });
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        LOG.info("[StreamPark] {}", buffer);
+        if (sparkVersion[0] == null || sparkVersion[1] == null) {
+            throw new IllegalStateException("[StreamPark] parse spark version failed. " + buffer);
+        }
+        return sparkVersion;
+    }
+
+    public String getSparkHome() {
+        return sparkHome;
+    }
+    public String getVersion() {
+        return version;
+    }
+    public String getScalaVersion() {
+        return scalaVersion;
+    }
+
+    public String fullVersion() {
+        return getFullVersion();
+    }
+    public String majorVersion() {
+        return getMajorVersion();
+    }
+    public String scalaVersion() {
+        return scalaVersion;
+    }
+    public String version() {
+        return version;
+    }
+
+    public String getMajorVersion() {
+        if (version == null)
+            return null;
+        Matcher matcher = SPARK_VER_PATTERN.matcher(version);
+        matcher.matches();
+        return matcher.group(1);
+    }
+
+    public String getFullVersion() {
+        return version + "_" + scalaVersion;
+    }
+
+    public File getSparkLib() {
+        if (sparkHome == null)
+            throw new IllegalArgumentException("[StreamPark] sparkHome must not be null.");
+        if (!new File(sparkHome).exists())
+            throw new IllegalArgumentException("[StreamPark] sparkHome must be exists.");
+        File lib = new File(sparkHome + "/jars");
+        if (!lib.exists() || !lib.isDirectory())
+            throw new IllegalArgumentException(
+                "[StreamPark] " + sparkHome + "/lib must be exists and must be directory.");
+        return lib;
+    }
+
+    public boolean checkVersion() {
+        return checkVersion(true);
+    }
+    public boolean checkVersion(boolean throwException) {
+        String[] parts = version.split("\\.");
+        if (parts.length >= 1) {
+            try {
+                int major = Integer.parseInt(parts[0].trim());
+                if (major == 2 || major == 3)
+                    return true;
+            } catch (NumberFormatException ignored) {
+            }
+        }
+        if (throwException)
+            throw new UnsupportedOperationException("Unsupported spark version: " + version);
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return "\n----------------------------------------- spark version -----------------------------------\n"
+            + "     sparkHome    : " + sparkHome + "\n"
+            + "     sparkVersion : " + version + "\n"
+            + "     scalaVersion : " + scalaVersion + "\n"
+            + "-------------------------------------------------------------------------------------------\n";
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/conf/Workspace.java b/streampark-common/src/main/java/org/apache/streampark/common/conf/Workspace.java
new file mode 100644
index 000000000..ea93ed5b7
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/conf/Workspace.java
@@ -0,0 +1,254 @@
+/*
+ * 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.streampark.common.conf;
+
+import org.apache.streampark.common.enums.StorageType;
+import org.apache.streampark.common.util.HdfsUtils;
+import org.apache.streampark.common.util.StringCastUtils;
+import org.apache.streampark.common.util.SystemPropertyUtils;
+
+import java.net.URI;
+
+/** Workspace path configuration for local and remote storage. */
+public final class Workspace {
+
+    private static final Workspace LOCAL = of(StorageType.LFS);
+    private static final Workspace REMOTE = of(StorageType.HDFS);
+
+    /** Scala-friendly local workspace singleton. */
+    public static final Workspace local = LOCAL;
+
+    /** Scala-friendly remote workspace singleton. */
+    public static final Workspace remote = REMOTE;
+
+    static {
+        LOCAL.initPaths();
+    }
+
+    private final StorageType storageType;
+
+    /**
+     * Path fields are private so Scala field-style access (e.g. {@code Workspace.local.APP_JARS})
+     * resolves to accessor methods and triggers lazy initialization. Public fields would bypass
+     * {@link #ensurePathsInitialized()} and stay null when remote paths are deferred.
+     */
+    private String WORKSPACE;
+    private String APP_PLUGINS;
+    private String APP_CLIENT;
+    private String APP_SHIMS;
+    private String APP_UPLOADS;
+    private String APP_PYTHON;
+    private String APP_PYTHON_VENV;
+    private String APP_WORKSPACE;
+    private String APP_FLINK;
+    private String APP_SPARK;
+    private String APP_BACKUPS;
+    private String APP_SAVEPOINTS;
+    private String APP_JARS;
+
+    private volatile boolean pathsInitialized;
+
+    private Workspace(StorageType storageType) {
+        this.storageType = storageType;
+    }
+
+    public static Workspace of(StorageType storageType) {
+        return new Workspace(storageType);
+    }
+
+    public static Workspace local() {
+        return LOCAL;
+    }
+
+    public static Workspace remote() {
+        return REMOTE;
+    }
+
+    /** local build path */
+    public static String APP_LOCAL_DIST() {
+        return local.WORKSPACE() + "/dist";
+    }
+
+    /** dirPath of the maven local repository with built-in compilation process */
+    public static String MAVEN_LOCAL_PATH() {
+        return local.WORKSPACE() + "/mvnrepo";
+    }
+
+    /** local sourceCode path.(for git...) */
+    public static String PROJECT_LOCAL_PATH() {
+        return local.WORKSPACE() + "/project";
+    }
+
+    /** local log path. */
+    public static String LOG_LOCAL_PATH() {
+        return local.WORKSPACE() + "/logs";
+    }
+
+    /** project build log path. */
+    public static String PROJECT_BUILD_LOG_PATH() {
+        return LOG_LOCAL_PATH() + "/build_logs";
+    }
+
+    /** project archives path */
+    public static String ARCHIVES_FILE_PATH() {
+        return remote.WORKSPACE() + "/historyserver/archive";
+    }
+
+    public String WORKSPACE() {
+        ensurePathsInitialized();
+        return WORKSPACE;
+    }
+
+    public String APP_PLUGINS() {
+        ensurePathsInitialized();
+        return APP_PLUGINS;
+    }
+
+    public String APP_CLIENT() {
+        ensurePathsInitialized();
+        return APP_CLIENT;
+    }
+
+    public String APP_SHIMS() {
+        ensurePathsInitialized();
+        return APP_SHIMS;
+    }
+
+    public String APP_UPLOADS() {
+        ensurePathsInitialized();
+        return APP_UPLOADS;
+    }
+
+    public String APP_PYTHON() {
+        ensurePathsInitialized();
+        return APP_PYTHON;
+    }
+
+    public String APP_PYTHON_VENV() {
+        ensurePathsInitialized();
+        return APP_PYTHON_VENV;
+    }
+
+    public String APP_WORKSPACE() {
+        ensurePathsInitialized();
+        return APP_WORKSPACE;
+    }
+
+    public String APP_FLINK() {
+        ensurePathsInitialized();
+        return APP_FLINK;
+    }
+
+    public String APP_SPARK() {
+        ensurePathsInitialized();
+        return APP_SPARK;
+    }
+
+    public String APP_BACKUPS() {
+        ensurePathsInitialized();
+        return APP_BACKUPS;
+    }
+
+    public String APP_SAVEPOINTS() {
+        ensurePathsInitialized();
+        return APP_SAVEPOINTS;
+    }
+
+    public String APP_JARS() {
+        ensurePathsInitialized();
+        return APP_JARS;
+    }
+
+    public String getAppJars() {
+        return APP_JARS();
+    }
+
+    private void ensurePathsInitialized() {
+        if (!pathsInitialized) {
+            synchronized (this) {
+                if (!pathsInitialized) {
+                    initPaths();
+                    pathsInitialized = true;
+                }
+            }
+        }
+    }
+
+    private void initPaths() {
+        if (pathsInitialized) {
+            return;
+        }
+        WORKSPACE = computeWorkspace();
+        APP_PLUGINS = WORKSPACE + "/plugins";
+        APP_CLIENT = WORKSPACE + "/client";
+        APP_SHIMS = WORKSPACE + "/shims";
+        APP_UPLOADS = WORKSPACE + "/uploads";
+        APP_PYTHON = WORKSPACE + "/python";
+        APP_PYTHON_VENV = APP_PYTHON + "/venv.zip";
+        APP_WORKSPACE = WORKSPACE + "/workspace";
+        APP_FLINK = WORKSPACE + "/flink";
+        APP_SPARK = WORKSPACE + "/spark";
+        APP_BACKUPS = WORKSPACE + "/backups";
+        APP_SAVEPOINTS = WORKSPACE + "/savepoints";
+        APP_JARS = WORKSPACE + "/jars";
+        pathsInitialized = true;
+    }
+
+    private String computeWorkspace() {
+        if (storageType == StorageType.LFS) {
+            String path = getConfigValue(CommonConfig.STREAMPARK_WORKSPACE_LOCAL());
+            if (path == null) {
+                throw new IllegalArgumentException("[StreamPark] streampark.workspace.local must not be null");
+            }
+            return path;
+        }
+        String path = getConfigValue(CommonConfig.STREAMPARK_WORKSPACE_REMOTE());
+        if (path == null || path.isEmpty()) {
+            return HdfsUtils.getDefaultFS() + CommonConfig.STREAMPARK_WORKSPACE_REMOTE().getDefaultValue();
+        }
+        String defaultFs = HdfsUtils.getDefaultFS();
+        if (path.startsWith("hdfs://")) {
+            if (path.startsWith(defaultFs)) {
+                return path;
+            }
+            String hdfsPath = URI.create(path).getPath();
+            return defaultFs + hdfsPath;
+        }
+        return defaultFs + path;
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T> T getConfigValue(InternalOption option) {
+        String systemValue = SystemPropertyUtils.get(option.getKey());
+        Object configValue = InternalConfigHolder.get(option);
+        Object defaultValue = option.getDefaultValue();
+        if (systemValue == null && configValue == null) {
+            return (T) defaultValue;
+        }
+        if (systemValue == null) {
+            return (T) configValue;
+        }
+        if (configValue == null) {
+            return StringCastUtils.cast(systemValue, option.getClassType());
+        }
+        if (configValue.equals(defaultValue)) {
+            return StringCastUtils.cast(systemValue, option.getClassType());
+        }
+        return (T) configValue;
+    }
+}
diff --git a/streampark-common/src/main/scala/org/apache/streampark/common/enums/ApiType.scala b/streampark-common/src/main/java/org/apache/streampark/common/enums/ApiType.java
similarity index 63%
rename from streampark-common/src/main/scala/org/apache/streampark/common/enums/ApiType.scala
rename to streampark-common/src/main/java/org/apache/streampark/common/enums/ApiType.java
index 7bf7f6ef2..c03ec2b6c 100644
--- a/streampark-common/src/main/scala/org/apache/streampark/common/enums/ApiType.scala
+++ b/streampark-common/src/main/java/org/apache/streampark/common/enums/ApiType.java
@@ -15,9 +15,24 @@
  * limitations under the License.
  */
 
-package org.apache.streampark.common.enums
+package org.apache.streampark.common.enums;
 
-object ApiType extends Enumeration {
-  type ApiType = Value
-  val JAVA, SCALA = Value
+import javax.annotation.Nonnull;
+
+/** Flink application API type. */
+public enum ApiType {
+
+    JAVA,
+    SCALA;
+
+    /**
+     * Resolve enum constant by name (Scala {@code Enumeration.withName} compatibility).
+     *
+     * @param name enum constant name
+     * @return matching {@link ApiType}
+     */
+    @Nonnull
+    public static ApiType withName(@Nonnull String name) {
+        return valueOf(name);
+    }
 }
diff --git a/streampark-common/src/main/scala/org/apache/streampark/common/enums/TableMode.scala b/streampark-common/src/main/java/org/apache/streampark/common/enums/PlannerType.java
similarity index 62%
rename from streampark-common/src/main/scala/org/apache/streampark/common/enums/TableMode.scala
rename to streampark-common/src/main/java/org/apache/streampark/common/enums/PlannerType.java
index 0195cdc5b..be599edff 100644
--- a/streampark-common/src/main/scala/org/apache/streampark/common/enums/TableMode.scala
+++ b/streampark-common/src/main/java/org/apache/streampark/common/enums/PlannerType.java
@@ -15,9 +15,25 @@
  * limitations under the License.
  */
 
-package org.apache.streampark.common.enums
+package org.apache.streampark.common.enums;
 
-object TableMode extends Enumeration {
-  type TableMode = Value
-  val BATCH, STREAMING = Value
+import javax.annotation.Nonnull;
+
+/** Flink table planner type. */
+public enum PlannerType {
+
+    BLINK,
+    OLD,
+    ANY;
+
+    /**
+     * Resolve enum constant by name (Scala {@code Enumeration.withName} compatibility).
+     *
+     * @param name enum constant name
+     * @return matching {@link PlannerType}
+     */
+    @Nonnull
+    public static PlannerType withName(@Nonnull String name) {
+        return valueOf(name);
+    }
 }
diff --git a/streampark-common/src/main/scala/org/apache/streampark/common/enums/PlannerType.scala b/streampark-common/src/main/java/org/apache/streampark/common/enums/TableMode.java
similarity index 63%
rename from streampark-common/src/main/scala/org/apache/streampark/common/enums/PlannerType.scala
rename to streampark-common/src/main/java/org/apache/streampark/common/enums/TableMode.java
index cb1be6257..4262bc5e9 100644
--- a/streampark-common/src/main/scala/org/apache/streampark/common/enums/PlannerType.scala
+++ b/streampark-common/src/main/java/org/apache/streampark/common/enums/TableMode.java
@@ -15,9 +15,24 @@
  * limitations under the License.
  */
 
-package org.apache.streampark.common.enums
+package org.apache.streampark.common.enums;
 
-object PlannerType extends Enumeration {
-  type PlannerType = Value
-  val BLINK, OLD, ANY = Value
+import javax.annotation.Nonnull;
+
+/** Flink table execution mode. */
+public enum TableMode {
+
+    BATCH,
+    STREAMING;
+
+    /**
+     * Resolve enum constant by name (Scala {@code Enumeration.withName} compatibility).
+     *
+     * @param name enum constant name
+     * @return matching {@link TableMode}
+     */
+    @Nonnull
+    public static TableMode withName(@Nonnull String name) {
+        return valueOf(name);
+    }
 }
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/fs/FsOperator.java b/streampark-common/src/main/java/org/apache/streampark/common/fs/FsOperator.java
new file mode 100644
index 000000000..295151bf1
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/fs/FsOperator.java
@@ -0,0 +1,180 @@
+/*
+ * 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.streampark.common.fs;
+
+import org.apache.streampark.common.enums.StorageType;
+
+/** File system operator factory and shared operations. */
+public abstract class FsOperator {
+
+    /** Scala-friendly local file system operator singleton. */
+    public static final FsOperator lfs = new FsOperator() {
+
+        @Override
+        public boolean exists(String path) {
+            return LfsOperator.exists(path);
+        }
+
+        @Override
+        public void mkdirs(String path) {
+            LfsOperator.mkdirs(path);
+        }
+
+        @Override
+        public void delete(String path) {
+            LfsOperator.delete(path);
+        }
+
+        @Override
+        public void mkCleanDirs(String path) {
+            LfsOperator.mkCleanDirs(path);
+        }
+
+        @Override
+        public void upload(String srcPath, String dstPath, boolean delSrc, boolean overwrite) {
+            LfsOperator.upload(srcPath, dstPath, delSrc, overwrite);
+        }
+
+        @Override
+        public void copy(String srcPath, String dstPath, boolean delSrc, boolean overwrite) {
+            LfsOperator.copy(srcPath, dstPath, delSrc, overwrite);
+        }
+
+        @Override
+        public void copyDir(String srcPath, String dstPath, boolean delSrc, boolean overwrite) {
+            LfsOperator.copyDir(srcPath, dstPath, delSrc, overwrite);
+        }
+
+        @Override
+        public void move(String srcPath, String dstPath) {
+            LfsOperator.move(srcPath, dstPath);
+        }
+
+        @Override
+        public String fileMd5(String path) {
+            return LfsOperator.fileMd5(path);
+        }
+    };
+
+    /** Scala-friendly HDFS operator singleton. */
+    public static final FsOperator hdfs = new FsOperator() {
+
+        @Override
+        public boolean exists(String path) {
+            return HdfsOperator.exists(path);
+        }
+
+        @Override
+        public void mkdirs(String path) {
+            HdfsOperator.mkdirs(path);
+        }
+
+        @Override
+        public void delete(String path) {
+            HdfsOperator.delete(path);
+        }
+
+        @Override
+        public void mkCleanDirs(String path) {
+            HdfsOperator.mkCleanDirs(path);
+        }
+
+        @Override
+        public void upload(String srcPath, String dstPath, boolean delSrc, boolean overwrite) {
+            HdfsOperator.upload(srcPath, dstPath, delSrc, overwrite);
+        }
+
+        @Override
+        public void copy(String srcPath, String dstPath, boolean delSrc, boolean overwrite) {
+            HdfsOperator.copy(srcPath, dstPath, delSrc, overwrite);
+        }
+
+        @Override
+        public void copyDir(String srcPath, String dstPath, boolean delSrc, boolean overwrite) {
+            HdfsOperator.copyDir(srcPath, dstPath, delSrc, overwrite);
+        }
+
+        @Override
+        public void move(String srcPath, String dstPath) {
+            HdfsOperator.move(srcPath, dstPath);
+        }
+
+        @Override
+        public String fileMd5(String path) {
+            return HdfsOperator.fileMd5(path);
+        }
+    };
+
+    protected FsOperator() {
+    }
+
+    public static FsOperator lfs() {
+        return lfs;
+    }
+
+    public static FsOperator hdfs() {
+        return hdfs;
+    }
+
+    public static FsOperator of(StorageType storageType) {
+        switch (storageType) {
+            case HDFS:
+                return hdfs;
+            case LFS:
+                return lfs;
+            default:
+                throw new UnsupportedOperationException("Unsupported storageType:" + storageType);
+        }
+    }
+
+    public abstract boolean exists(String path);
+
+    public abstract void mkdirs(String path);
+
+    public void mkdirsIfNotExists(String path) {
+        if (!exists(path)) {
+            mkdirs(path);
+        }
+    }
+
+    public abstract void delete(String path);
+
+    public abstract void mkCleanDirs(String path);
+
+    public void upload(String srcPath, String dstPath) {
+        upload(srcPath, dstPath, false, true);
+    }
+
+    public void copy(String srcPath, String dstPath) {
+        copy(srcPath, dstPath, false, true);
+    }
+
+    public void copyDir(String srcPath, String dstPath) {
+        copyDir(srcPath, dstPath, false, true);
+    }
+
+    public abstract void upload(String srcPath, String dstPath, boolean delSrc, boolean overwrite);
+
+    public abstract void copy(String srcPath, String dstPath, boolean delSrc, boolean overwrite);
+
+    public abstract void copyDir(String srcPath, String dstPath, boolean delSrc, boolean overwrite);
+
+    public abstract void move(String srcPath, String dstPath);
+
+    public abstract String fileMd5(String path);
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/fs/HdfsOperator.java b/streampark-common/src/main/java/org/apache/streampark/common/fs/HdfsOperator.java
new file mode 100644
index 000000000..a3d891848
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/fs/HdfsOperator.java
@@ -0,0 +1,136 @@
+/*
+ * 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.streampark.common.fs;
+
+import org.apache.streampark.common.util.HdfsUtils;
+
+import java.io.IOException;
+
+/** Hadoop File System (aka HDFS) Operator */
+public final class HdfsOperator {
+
+    private HdfsOperator() {
+    }
+
+    public static boolean exists(String path) {
+        try {
+            return HdfsUtils.exists(toHdfsPath(path));
+        } catch (IOException e) {
+            throw new IllegalStateException("[StreamPark] Failed to check hdfs path: " + path, e);
+        }
+    }
+
+    public static void mkdirs(String path) {
+        try {
+            HdfsUtils.mkdirs(toHdfsPath(path));
+        } catch (IOException e) {
+            throw new IllegalStateException("[StreamPark] Failed to mkdirs hdfs path: " + path, e);
+        }
+    }
+
+    public static void delete(String path) {
+        try {
+            HdfsUtils.delete(toHdfsPath(path));
+        } catch (IOException e) {
+            throw new IllegalStateException("[StreamPark] Failed to delete hdfs path: " + path, e);
+        }
+    }
+
+    public static void move(String srcPath, String dstPath) {
+        try {
+            HdfsUtils.move(toHdfsPath(srcPath), toHdfsPath(dstPath));
+        } catch (IOException e) {
+            throw new IllegalStateException(
+                "[StreamPark] Failed to move " + srcPath + " to " + dstPath, e);
+        }
+    }
+
+    public static void upload(String srcPath, String dstPath) {
+        upload(srcPath, dstPath, false, true);
+    }
+
+    public static void upload(String srcPath, String dstPath, boolean delSrc) {
+        upload(srcPath, dstPath, delSrc, true);
+    }
+
+    public static void upload(String srcPath, String dstPath, boolean delSrc, boolean overwrite) {
+        try {
+            HdfsUtils.upload(srcPath, toHdfsPath(dstPath), delSrc, overwrite);
+        } catch (IOException e) {
+            throw new IllegalStateException(
+                "[StreamPark] Failed to upload " + srcPath + " to " + dstPath, e);
+        }
+    }
+
+    public static void copy(String srcPath, String dstPath) {
+        copy(srcPath, dstPath, false, true);
+    }
+
+    public static void copy(String srcPath, String dstPath, boolean delSrc) {
+        copy(srcPath, dstPath, delSrc, true);
+    }
+
+    public static void copy(String srcPath, String dstPath, boolean delSrc, boolean overwrite) {
+        try {
+            HdfsUtils.copyHdfs(toHdfsPath(srcPath), toHdfsPath(dstPath), delSrc, overwrite);
+        } catch (IOException e) {
+            throw new IllegalStateException(
+                "[StreamPark] Failed to copy " + srcPath + " to " + dstPath, e);
+        }
+    }
+
+    public static void copyDir(String srcPath, String dstPath) {
+        copyDir(srcPath, dstPath, false, true);
+    }
+
+    public static void copyDir(String srcPath, String dstPath, boolean delSrc) {
+        copyDir(srcPath, dstPath, delSrc, true);
+    }
+
+    public static void copyDir(String srcPath, String dstPath, boolean delSrc, boolean overwrite) {
+        try {
+            HdfsUtils.copyHdfsDir(toHdfsPath(srcPath), toHdfsPath(dstPath), delSrc, overwrite);
+        } catch (IOException e) {
+            throw new IllegalStateException(
+                "[StreamPark] Failed to copyDir " + srcPath + " to " + dstPath, e);
+        }
+    }
+
+    public static void mkCleanDirs(String path) {
+        delete(path);
+        mkdirs(path);
+    }
+
+    public static String fileMd5(String path) {
+        if (path == null || path.isEmpty()) {
+            throw new IllegalArgumentException("[StreamPark] HdfsOperator.fileMd5: file must not be null.");
+        }
+        try {
+            return HdfsUtils.fileMd5(toHdfsPath(path));
+        } catch (IOException e) {
+            throw new IllegalStateException("[StreamPark] Failed to compute md5 for: " + path, e);
+        }
+    }
+
+    private static String toHdfsPath(String path) {
+        if (path.startsWith("hdfs://")) {
+            return path;
+        }
+        return HdfsUtils.getDefaultFS().concat(path);
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/fs/LfsOperator.java b/streampark-common/src/main/java/org/apache/streampark/common/fs/LfsOperator.java
new file mode 100644
index 000000000..b5924532d
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/fs/LfsOperator.java
@@ -0,0 +1,238 @@
+/*
+ * 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.streampark.common.fs;
+
+import org.apache.streampark.common.util.Utils;
+
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+/** Local File System (aka LFS) Operator */
+public final class LfsOperator {
+
+    private LfsOperator() {
+    }
+
+    public static boolean exists(String path) {
+        return StringUtils.isNotBlank(path) && new File(path).exists();
+    }
+
+    public static void mkdirs(String path) {
+        if (!Utils.isAnyBank(path)) {
+            try {
+                FileUtils.forceMkdir(new File(path));
+            } catch (IOException e) {
+                throw new IllegalStateException("[StreamPark] Failed to mkdirs: " + path, e);
+            }
+        }
+    }
+
+    public static void delete(String path) {
+        if (Utils.isNotEmpty(path)) {
+            File file = new File(path);
+            if (file.exists()) {
+                try {
+                    FileUtils.forceDelete(file);
+                } catch (IOException e) {
+                    throw new IllegalStateException("[StreamPark] Failed to delete: " + path, e);
+                }
+            }
+        }
+    }
+
+    public static void move(String srcPath, String dstPath) {
+        if (Utils.isAnyBank(srcPath, dstPath)) {
+            return;
+        }
+        File srcFile = new File(srcPath);
+        File dstFile = new File(dstPath);
+        if (!srcFile.exists()) {
+            return;
+        }
+        try {
+            if (srcFile.getCanonicalPath().equals(dstFile.getCanonicalPath())) {
+                return;
+            }
+            FileUtils.moveToDirectory(srcFile, dstFile, true);
+        } catch (IOException e) {
+            throw new IllegalStateException(
+                "[StreamPark] Failed to move " + srcPath + " to " + dstPath, e);
+        }
+    }
+
+    public static void upload(String srcPath, String dstPath) {
+        upload(srcPath, dstPath, false, true);
+    }
+
+    public static void upload(String srcPath, String dstPath, boolean delSrc) {
+        upload(srcPath, dstPath, delSrc, true);
+    }
+
+    public static void upload(String srcPath, String dstPath, boolean delSrc, boolean overwrite) {
+        if (new File(srcPath).isDirectory()) {
+            copyDir(srcPath, dstPath, delSrc, overwrite);
+        } else {
+            copy(srcPath, dstPath, delSrc, overwrite);
+        }
+    }
+
+    /**
+     * When the suffixes of srcPath and dstPath are the same, or the file names are the same, copy to
+     * the file, otherwise copy to the directory.
+     */
+    public static void copy(String srcPath, String dstPath) {
+        copy(srcPath, dstPath, false, true);
+    }
+
+    public static void copy(String srcPath, String dstPath, boolean delSrc) {
+        copy(srcPath, dstPath, delSrc, true);
+    }
+
+    public static void copy(String srcPath, String dstPath, boolean delSrc, boolean overwrite) {
+        if (Utils.isAnyBank(srcPath, dstPath)) {
+            return;
+        }
+        File srcFile = new File(srcPath);
+        if (!srcFile.exists()) {
+            return;
+        }
+        if (!srcFile.isFile()) {
+            throw new IllegalArgumentException("[StreamPark] " + srcPath + " must be a file.");
+        }
+        File dstFile = resolveCopyDestination(srcFile, dstPath);
+        try {
+            if (srcFile.getCanonicalPath().equals(dstFile.getCanonicalPath())) {
+                throw new IllegalArgumentException(
+                    "[StreamPark] src and dst must not be the same path: " + srcPath);
+            }
+        } catch (IOException e) {
+            throw new IllegalStateException("[StreamPark] Failed to resolve copy paths", e);
+        }
+        boolean shouldCopy =
+            overwrite || !dstFile.exists() || !dstFile.getName().equals(srcFile.getName());
+        if (shouldCopy) {
+            try {
+                FileUtils.copyFile(srcFile, dstFile);
+                if (delSrc) {
+                    FileUtils.forceDelete(srcFile);
+                }
+            } catch (IOException e) {
+                throw new IllegalStateException(
+                    "[StreamPark] Failed to copy " + srcPath + " to " + dstPath, e);
+            }
+        }
+    }
+
+    public static void copyDir(String srcPath, String dstPath) {
+        copyDir(srcPath, dstPath, false, true);
+    }
+
+    public static void copyDir(String srcPath, String dstPath, boolean delSrc) {
+        copyDir(srcPath, dstPath, delSrc, true);
+    }
+
+    public static void copyDir(String srcPath, String dstPath, boolean delSrc, boolean overwrite) {
+        if (Utils.isAnyBank(srcPath, dstPath)) {
+            return;
+        }
+        File srcFile = new File(srcPath);
+        if (!srcFile.exists()) {
+            return;
+        }
+        if (!srcFile.isDirectory()) {
+            throw new IllegalArgumentException("[StreamPark] " + srcPath + " must be a directory.");
+        }
+        File dstFile = new File(dstPath);
+        boolean shouldCopy;
+        try {
+            shouldCopy = overwrite || !dstFile.exists()
+                || !srcFile.getCanonicalPath().equals(dstFile.getCanonicalPath());
+        } catch (IOException e) {
+            throw new IllegalStateException("[StreamPark] Failed to resolve copyDir paths", e);
+        }
+        if (shouldCopy) {
+            try {
+                FileUtils.copyDirectory(srcFile, dstFile);
+                if (delSrc) {
+                    FileUtils.deleteDirectory(srcFile);
+                }
+            } catch (IOException e) {
+                throw new IllegalStateException(
+                    "[StreamPark] Failed to copyDir " + srcPath + " to " + dstPath, e);
+            }
+        }
+    }
+
+    public static String fileMd5(String path) {
+        if (path == null || path.isEmpty()) {
+            throw new IllegalArgumentException("[StreamPark] LFsOperator.fileMd5: file must not be null.");
+        }
+        File file = new File(path);
+        if (!file.exists()) {
+            throw new IllegalArgumentException("[StreamPark] LFsOperator.fileMd5: file must exists.");
+        }
+        try (FileInputStream inputStream = new FileInputStream(path)) {
+            return DigestUtils.md5Hex(IOUtils.toByteArray(inputStream));
+        } catch (IOException e) {
+            throw new IllegalStateException("[StreamPark] Failed to compute md5 for: " + path, e);
+        }
+    }
+
+    /** Force delete directory and recreate it. */
+    public static void mkCleanDirs(String path) {
+        delete(path);
+        mkdirs(path);
+    }
+
+    /** list file under directory, one level of traversal only */
+    public static File[] listDir(String path) {
+        if (path == null || path.trim().isEmpty()) {
+            return new File[0];
+        }
+        File file = new File(path);
+        if (!file.exists()) {
+            return new File[0];
+        }
+        if (file.isFile()) {
+            return new File[]{file};
+        }
+        File[] files = file.listFiles();
+        return files != null ? files : new File[0];
+    }
+
+    private static File resolveCopyDestination(File srcFile, String dstPath) {
+        File dstFile = new File(dstPath);
+        if (dstFile.exists()) {
+            if (dstFile.isDirectory()) {
+                return new File(dstFile, srcFile.getName());
+            }
+            return dstFile;
+        }
+        if (!dstFile.getParentFile().exists()) {
+            throw new IllegalArgumentException(
+                "[StreamPark] dstPath is invalid and does not exist. Please check");
+        }
+        return dstFile;
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/AssertUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/AssertUtils.java
new file mode 100644
index 000000000..5e6c74e0d
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/AssertUtils.java
@@ -0,0 +1,126 @@
+/*
+ * 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.streampark.common.util;
+
+import javax.annotation.Nullable;
+
+import java.util.Collection;
+
+/** Assertion utilities. @since 2.2.0 */
+public final class AssertUtils {
+
+    private AssertUtils() {
+    }
+
+    public static void required(boolean condition) {
+        if (!condition)
+            throw new IllegalArgumentException();
+    }
+
+    public static void required(boolean condition, @Nullable String message) {
+        if (!condition)
+            throw new IllegalArgumentException(message);
+    }
+
+    public static void state(boolean condition) {
+        if (!condition)
+            throw new IllegalStateException();
+    }
+
+    public static void state(boolean condition, @Nullable String message) {
+        if (!condition)
+            throw new IllegalStateException(message);
+    }
+
+    public static <T> T notNull(@Nullable T reference) {
+        if (reference == null)
+            throw new NullPointerException();
+        return reference;
+    }
+
+    public static <T> T notNull(@Nullable T reference, @Nullable String message) {
+        if (reference == null)
+            throw new NullPointerException(message);
+        return reference;
+    }
+
+    public static void notEmpty(@Nullable Object reference) {
+        if (Utils.isEmpty(reference))
+            throw new IllegalArgumentException();
+    }
+
+    public static void notEmpty(@Nullable Object reference, String message) {
+        if (Utils.isEmpty(reference))
+            throw new IllegalArgumentException(message);
+    }
+
+    public static void noNullElements(@Nullable Object[] array, String message) {
+        if (array != null) {
+            for (Object element : array) {
+                if (element == null)
+                    throw new IllegalArgumentException(message);
+            }
+        }
+    }
+
+    public static void noNullElements(@Nullable Collection<?> collection, String message) {
+        if (collection != null) {
+            for (Object element : collection) {
+                if (element == null)
+                    throw new IllegalArgumentException(message);
+            }
+        }
+    }
+
+    public static void hasLength(@Nullable String text) {
+        if (!getHasLength(text))
+            throw new IllegalArgumentException();
+    }
+
+    public static void hasLength(@Nullable String text, String message) {
+        if (!getHasLength(text))
+            throw new IllegalArgumentException(message);
+    }
+
+    public static void hasText(@Nullable String text) {
+        if (!getHasText(text))
+            throw new IllegalArgumentException();
+    }
+
+    public static void hasText(@Nullable String text, String message) {
+        if (!getHasText(text))
+            throw new IllegalArgumentException(message);
+    }
+
+    private static boolean getHasLength(@Nullable String str) {
+        return str != null && !str.isEmpty();
+    }
+
+    private static boolean getHasText(@Nullable String str) {
+        return str != null && !str.isEmpty() && containsText(str);
+    }
+
+    private static boolean containsText(CharSequence str) {
+        int strLen = str.length();
+        for (int i = 0; i < strLen; i++) {
+            if (!Character.isWhitespace(str.charAt(i)))
+                return true;
+        }
+        return false;
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/AutoCloseUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/AutoCloseUtils.java
new file mode 100644
index 000000000..0f24d5a0a
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/AutoCloseUtils.java
@@ -0,0 +1,50 @@
+/*
+ * 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.streampark.common.util;
+
+import java.util.function.Function;
+
+/** AutoCloseable resource management utilities. */
+public final class AutoCloseUtils {
+
+    private AutoCloseUtils() {
+    }
+
+    public static <T extends AutoCloseable, R> R using(T autoCloseable, Function<T, R> func) {
+        return using(autoCloseable, func, null);
+    }
+
+    public static <T extends AutoCloseable, R> R using(
+                                                       T autoCloseable, Function<T, R> func,
+                                                       Function<Throwable, R> excFunc) {
+        try (T resource = autoCloseable) {
+            return func.apply(resource);
+        } catch (Throwable e) {
+            if (excFunc != null) {
+                return excFunc.apply(e);
+            }
+            if (e instanceof RuntimeException) {
+                throw (RuntimeException) e;
+            }
+            if (e instanceof Error) {
+                throw (Error) e;
+            }
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/CURLBuilder.java b/streampark-common/src/main/java/org/apache/streampark/common/util/CURLBuilder.java
new file mode 100644
index 000000000..b9d781d5b
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/CURLBuilder.java
@@ -0,0 +1,53 @@
+/*
+ * 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.streampark.common.util;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+public class CURLBuilder {
+
+    private final String url;
+    private final Map<String, String> headers = new HashMap<>();
+    private final Map<String, String> formData = new HashMap<>();
+    public CURLBuilder(String url) {
+        this.url = url;
+    }
+    public CURLBuilder addHeader(String k, String v) {
+        headers.put(k, v);
+        return this;
+    }
+    public CURLBuilder addFormData(String k, Serializable v) {
+        formData.put(k, v.toString());
+        return this;
+    }
+    public String build() {
+        if (url == null)
+            throw new IllegalArgumentException("[StreamPark] CURL build failed, url must not be null");
+        StringBuilder cURL = new StringBuilder("curl -X POST ");
+        cURL.append(String.format("'%s' \\\n", url));
+        for (String h : headers.keySet()) {
+            cURL.append(String.format("-H '%s: %s' \\\n", h, headers.get(h)));
+        }
+        for (Map.Entry<String, String> e : formData.entrySet()) {
+            cURL.append(String.format("--data-urlencode '%s=%s' \\\n", e.getKey(), e.getValue()));
+        }
+        return cURL.toString().trim().substring(0, cURL.toString().trim().length() - 1);
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/ChildFirstClassLoader.java b/streampark-common/src/main/java/org/apache/streampark/common/util/ChildFirstClassLoader.java
new file mode 100644
index 000000000..74b808af8
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/ChildFirstClassLoader.java
@@ -0,0 +1,105 @@
+/*
+ * 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.streampark.common.util;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.function.Predicate;
+
+public class ChildFirstClassLoader extends URLClassLoader {
+
+    static {
+        ClassLoader.registerAsParallelCapable();
+    }
+    private final List<String> parentFirstClasses;
+    private final Predicate<String> loadJarFilter;
+
+    public ChildFirstClassLoader(URL[] urls, ClassLoader parent, List<String> parentFirstClasses,
+                                 Predicate<String> loadJarFilter) {
+        super(urls, parent);
+        this.parentFirstClasses = parentFirstClasses;
+        this.loadJarFilter = loadJarFilter;
+    }
+
+    @Override
+    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+        synchronized (getClassLoadingLock(name)) {
+            Class<?> c = findLoadedClass(name);
+            if (c == null) {
+                boolean parentFirst = false;
+                for (String p : parentFirstClasses) {
+                    if (name.startsWith(p)) {
+                        parentFirst = true;
+                        break;
+                    }
+                }
+                if (parentFirst)
+                    c = super.loadClass(name, resolve);
+                else {
+                    try {
+                        c = findClass(name);
+                    } catch (ClassNotFoundException e) {
+                        c = super.loadClass(name, resolve);
+                    }
+                }
+            }
+            if (resolve)
+                resolveClass(c);
+            return c;
+        }
+    }
+
+    @Override
+    public URL getResource(String name) {
+        URL urlClassLoaderResource = findResource(name);
+        return urlClassLoaderResource != null ? urlClassLoaderResource : super.getResource(name);
+    }
+
+    private URL filterResource(URL urlClassLoaderResource) {
+        if (urlClassLoaderResource != null && "jar".equals(urlClassLoaderResource.getProtocol())) {
+            String spec = urlClassLoaderResource.getFile();
+            String jarName = new java.io.File(spec.substring(0, spec.indexOf("!/"))).getName();
+            if (loadJarFilter.test(jarName))
+                return null;
+        }
+        return urlClassLoaderResource;
+    }
+
+    private void addResources(List<URL> result, Enumeration<URL> resources) {
+        while (resources.hasMoreElements()) {
+            URL url = filterResource(resources.nextElement());
+            if (url != null)
+                result.add(url);
+        }
+    }
+
+    @Override
+    public Enumeration<URL> getResources(String name) throws IOException {
+        List<URL> result = new ArrayList<>();
+        addResources(result, findResources(name));
+        ClassLoader parent = getParent();
+        if (parent != null)
+            addResources(result, parent.getResources(name));
+        return Collections.enumeration(result);
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/ClassLoaderObjectInputStream.java b/streampark-common/src/main/java/org/apache/streampark/common/util/ClassLoaderObjectInputStream.java
new file mode 100644
index 000000000..8f97c670f
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/ClassLoaderObjectInputStream.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.streampark.common.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamClass;
+import java.lang.reflect.Proxy;
+
+public class ClassLoaderObjectInputStream extends ObjectInputStream {
+
+    private final ClassLoader classLoader;
+    public ClassLoaderObjectInputStream(ClassLoader classLoader, InputStream inputStream) throws IOException {
+        super(inputStream);
+        this.classLoader = classLoader;
+    }
+    @Override
+    protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
+        try {
+            return Class.forName(desc.getName(), false, classLoader);
+        } catch (ClassNotFoundException e) {
+            return super.resolveClass(desc);
+        }
+    }
+    @Override
+    protected Class<?> resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException {
+        Class<?>[] interfaceClasses = new Class<?>[interfaces.length];
+        for (int i = 0; i < interfaces.length; i++)
+            interfaceClasses[i] = Class.forName(interfaces[i], false, classLoader);
+        try {
+            return Proxy.getProxyClass(classLoader, interfaceClasses);
+        } catch (IllegalArgumentException e) {
+            return super.resolveProxyClass(interfaces);
+        }
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/ClassLoaderUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/ClassLoaderUtils.java
new file mode 100644
index 000000000..6e126bf94
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/ClassLoaderUtils.java
@@ -0,0 +1,163 @@
+/*
+ * 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.streampark.common.util;
+
+import org.apache.streampark.common.constants.Constants;
+
+import org.apache.streampark.shaded.org.slf4j.Logger;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Supplier;
+
+public final class ClassLoaderUtils {
+
+    private static final Logger LOG =
+        StreamParkLoggerFactory.loggerFactory().getLogger(ClassLoaderUtils.class.getName());
+    private static final ClassLoader ORIGINAL_CLASS_LOADER = Thread.currentThread().getContextClassLoader();
+    private ClassLoaderUtils() {
+    }
+
+    public static <R> R runAsClassLoader(ClassLoader targetClassLoader, Supplier<R> supplier) {
+        try {
+            Thread.currentThread().setContextClassLoader(targetClassLoader);
+            return supplier.get();
+        } finally {
+            Thread.currentThread().setContextClassLoader(ORIGINAL_CLASS_LOADER);
+        }
+    }
+
+    public static ClassLoader cloneClassLoader() throws IOException {
+        java.util.Enumeration<URL> urls = ORIGINAL_CLASS_LOADER.getResources(".");
+        List<URL> buffer = new ArrayList<>();
+        while (urls.hasMoreElements())
+            buffer.add(urls.nextElement());
+        return new URLClassLoader(buffer.toArray(new URL[0]), ORIGINAL_CLASS_LOADER);
+    }
+
+    public static void loadJar(String jarFilePath) {
+        File f = new File(jarFilePath);
+        if (!f.exists()) {
+            throw new IllegalArgumentException(
+                "[StreamPark] ClassLoaderUtils.loadJar: jarFilePath " + jarFilePath + " is not exists");
+        }
+        if (!f.isFile()) {
+            throw new IllegalArgumentException(
+                "[StreamPark] ClassLoaderUtils.loadJar: jarFilePath " + jarFilePath + " is not file");
+        }
+        try {
+            loadPath(f.getAbsolutePath());
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void loadJars(String path) {
+        File jarDir = new File(path);
+        if (!jarDir.exists()) {
+            throw new IllegalArgumentException(
+                "[StreamPark] ClassLoaderUtils.loadJars: jarPath " + path + " is not exists");
+        }
+        if (!jarDir.isDirectory()) {
+            throw new IllegalArgumentException(
+                "[StreamPark] ClassLoaderUtils.loadJars: jarPath " + path + " is not directory");
+        }
+        File[] files = jarDir.listFiles();
+        if (files == null || files.length == 0) {
+            throw new IllegalArgumentException(
+                "[StreamPark] ClassLoaderUtils.loadJars: have not jar in path:" + path);
+        }
+        for (File x : files) {
+            try {
+                loadPath(x.getAbsolutePath());
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    public static void loadResource(String filepath) throws Exception {
+        addURL(new File(filepath));
+    }
+    public static void loadResourceDir(String filepath) throws Exception {
+        loopDirs(new File(filepath));
+    }
+
+    private static void loadPath(String filepath) throws Exception {
+        loopFiles(new File(filepath), Arrays.asList(Constants.JAR_SUFFIX, Constants.ZIP_SUFFIX));
+    }
+
+    private static void loopDirs(File file) throws Exception {
+        if (file.isDirectory()) {
+            addURL(file);
+            File[] children = file.listFiles();
+            if (children != null)
+                for (File c : children)
+                    loopDirs(c);
+        }
+    }
+
+    private static void loopFiles(File file, List<String> ext) throws Exception {
+        if (file.isDirectory()) {
+            File[] children = file.listFiles();
+            if (children != null)
+                for (File c : children)
+                    loopFiles(c, ext);
+        } else if (ext.isEmpty())
+            addURL(file);
+        else
+            for (String e : ext)
+                if (file.getName().endsWith(e)) {
+                    Utils.requireCheckJarFile(file.toURI().toURL());
+                    addURL(file);
+                    break;
+                }
+    }
+
+    private static void addURL(File file) throws Exception {
+        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
+        if (classLoader instanceof URLClassLoader) {
+            java.lang.reflect.Method addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
+            addURL.setAccessible(true);
+            addURL.invoke(classLoader, file.toURI().toURL());
+        } else {
+            Class<?> clazz = classLoader.getClass();
+            java.lang.reflect.Field ucpField = null;
+            while (clazz != null && ucpField == null) {
+                try {
+                    ucpField = clazz.getDeclaredField("ucp");
+                } catch (NoSuchFieldException e) {
+                    clazz = clazz.getSuperclass();
+                }
+            }
+            if (ucpField == null)
+                throw new IllegalArgumentException(
+                    "[StreamPark] ClassLoaderUtils.addURL: cannot locate ucp field on classloader chain");
+            ucpField.setAccessible(true);
+            Object ucp = ucpField.get(classLoader);
+            java.lang.reflect.Method addURL = ucp.getClass().getDeclaredMethod("addURL", URL.class);
+            addURL.setAccessible(true);
+            addURL.invoke(ucp, file.toURI().toURL());
+        }
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/CommandUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/CommandUtils.java
new file mode 100644
index 000000000..12684915e
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/CommandUtils.java
@@ -0,0 +1,113 @@
+/*
+ * 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.streampark.common.util;
+
+import org.apache.streampark.shaded.org.slf4j.Logger;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Scanner;
+import java.util.function.Consumer;
+
+public final class CommandUtils {
+
+    private static final Logger LOG = StreamParkLoggerFactory.loggerFactory().getLogger(CommandUtils.class.getName());
+    private CommandUtils() {
+    }
+
+    public static class CommandResult {
+
+        public final int code;
+        public final String output;
+        public CommandResult(int code, String output) {
+            this.code = code;
+            this.output = output;
+        }
+    }
+
+    public static CommandResult execute(String command) {
+        try {
+            StringBuffer buffer = new StringBuffer();
+            Process process = Runtime.getRuntime().exec(command);
+            InputStreamReader reader = new InputStreamReader(process.getInputStream());
+            Scanner scanner = new Scanner(reader);
+            while (scanner.hasNextLine())
+                buffer.append(scanner.nextLine()).append("\n");
+            int code = waitFor(process);
+            reader.close();
+            scanner.close();
+            return new CommandResult(code, buffer.toString());
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new RuntimeException(e);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static int execute(String directory, Iterable<String> commands, Consumer<String> consumer) {
+        try {
+            if (commands == null || !commands.iterator().hasNext()) {
+                throw new IllegalArgumentException("[StreamPark] CommandUtils.execute: commands must not be null.");
+            }
+            StringBuilder debug = new StringBuilder();
+            for (String c : commands)
+                debug.append(c).append("\n");
+            LOG.debug("[StreamPark] Command execute:\n{}", debug);
+            List<String> interpreters = Utils.isWindows() ? Arrays.asList("cmd", "/k") : Arrays.asList("/bin/bash");
+            ProcessBuilder builder = new ProcessBuilder(interpreters).redirectErrorStream(true);
+            if (directory != null)
+                builder.directory(new File(directory));
+            Process process = builder.start();
+            PrintWriter out =
+                new PrintWriter(new BufferedWriter(new OutputStreamWriter(process.getOutputStream())), true);
+            String last = null;
+            for (String cmd : commands) {
+                out.println(cmd);
+                last = cmd;
+            }
+            if (last == null || !last.equalsIgnoreCase("exit"))
+                out.println("exit");
+            out.close();
+            Scanner scanner = new Scanner(process.getInputStream());
+            while (scanner.hasNextLine())
+                consumer.accept(scanner.nextLine());
+            scanner.close();
+            return waitFor(process);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new RuntimeException(e);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static int waitFor(Process process) throws InterruptedException, java.io.IOException {
+        int code = process.waitFor();
+        process.getErrorStream().close();
+        process.getInputStream().close();
+        process.getOutputStream().close();
+        process.destroy();
+        return code;
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/CompletableFutureUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/CompletableFutureUtils.java
new file mode 100644
index 000000000..cbb53119f
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/CompletableFutureUtils.java
@@ -0,0 +1,114 @@
+/*
+ * 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.streampark.common.util;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public final class CompletableFutureUtils {
+
+    private CompletableFutureUtils() {
+    }
+
+    private static class DaemonThreadFactory implements ThreadFactory {
+
+        @Override
+        public Thread newThread(Runnable r) {
+            Thread t = new Thread(r);
+            t.setDaemon(true);
+            t.setName("CompletableFutureDelayScheduler");
+            return t;
+        }
+    }
+
+    private static final ScheduledThreadPoolExecutor COMPLETABLE_DELAYER;
+
+    static {
+        COMPLETABLE_DELAYER = new ScheduledThreadPoolExecutor(1, new DaemonThreadFactory());
+        COMPLETABLE_DELAYER.setRemoveOnCancelPolicy(true);
+    }
+
+    private static <T> CompletableFuture<T> setTimeout(long timeout, TimeUnit unit) {
+        CompletableFuture<T> result = new CompletableFuture<>();
+        COMPLETABLE_DELAYER.schedule(
+            (Callable<Boolean>) () -> {
+                result.completeExceptionally(new TimeoutException());
+                return true;
+            },
+            timeout,
+            unit);
+        return result;
+    }
+
+    public static <T> CompletableFuture<T> supplyTimeout(
+                                                         CompletableFuture<T> future,
+                                                         long timeout,
+                                                         TimeUnit unit,
+                                                         Function<T, T> handle,
+                                                         Function<Throwable, T> exceptionally) {
+        return future.applyToEither(setTimeout(timeout, unit), handle)
+            .exceptionally(exceptionally)
+            .whenComplete(
+                (BiConsumer<T, Throwable>) (t, u) -> {
+                    if (!future.isDone()) {
+                        future.cancel(true);
+                    }
+                });
+    }
+
+    public static <T> CompletableFuture<Void> runTimeout(
+                                                         CompletableFuture<T> future, long timeout, TimeUnit unit) {
+        return runTimeout(future, timeout, unit, null, null);
+    }
+
+    public static <T> CompletableFuture<Void> runTimeout(
+                                                         CompletableFuture<T> future,
+                                                         long timeout,
+                                                         TimeUnit unit,
+                                                         Consumer<T> handle,
+                                                         Consumer<Throwable> exceptionally) {
+        return future.applyToEither(
+            setTimeout(timeout, unit),
+            (Function<T, Void>) t -> {
+                if (handle != null) {
+                    handle.accept(t);
+                }
+                return null;
+            })
+            .exceptionally(
+                (Function<Throwable, Void>) t -> {
+                    if (exceptionally != null) {
+                        exceptionally.accept(t);
+                    }
+                    return null;
+                })
+            .whenComplete(
+                (BiConsumer<Void, Throwable>) (t, u) -> {
+                    if (!future.isDone()) {
+                        future.cancel(true);
+                    }
+                });
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/ConfigUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/ConfigUtils.java
new file mode 100644
index 000000000..3835d172f
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/ConfigUtils.java
@@ -0,0 +1,181 @@
+/*
+ * 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.streampark.common.util;
+
+import org.apache.streampark.common.conf.ConfigKeys;
+import org.apache.streampark.common.constants.Constants;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+public final class ConfigUtils {
+
+    private ConfigUtils() {
+    }
+
+    public static Properties getConf(Map<String, String> parameter) {
+        return getConf(parameter, "", "", "");
+    }
+
+    public static Properties getConf(Map<String, String> parameter, String prefix) {
+        return getConf(parameter, prefix, "", "");
+    }
+
+    public static Properties getConf(
+                                     Map<String, String> parameter, String prefix, String addfix, String alias) {
+        Map<String, String> map = filterParam(parameter, prefix + alias);
+        Properties prop = new Properties();
+        for (Map.Entry<String, String> entry : map.entrySet()) {
+            if (entry.getValue() != null && !entry.getValue().isEmpty()) {
+                prop.put(addfix + entry.getKey(), entry.getValue());
+            }
+        }
+        return prop;
+    }
+
+    public static Properties getHBaseConfig(Map<String, String> parameter) {
+        return getConf(parameter, ConfigKeys.HBASE_PREFIX(), ConfigKeys.HBASE_PREFIX(), "");
+    }
+
+    public static Properties getInfluxConfig(Map<String, String> parameter) {
+        return getConf(parameter, ConfigKeys.INFLUX_PREFIX(), "", "");
+    }
+
+    public static Properties getKafkaSinkConf(Map<String, String> parameter, String topic) {
+        return getKafkaSinkConf(parameter, topic, "");
+    }
+
+    public static Properties getKafkaSinkConf(
+                                              Map<String, String> parameter, String topic, String alias) {
+        String prefix = ConfigKeys.KAFKA_SINK_PREFIX() + alias;
+        if (!prefix.endsWith(".")) {
+            prefix = prefix + ".";
+        }
+        Map<String, String> param = filterParam(parameter, prefix);
+        if (param.isEmpty()) {
+            throw new IllegalArgumentException(topic + " init error...");
+        }
+        Properties kafkaProperty = new Properties();
+        for (Map.Entry<String, String> entry : param.entrySet()) {
+            kafkaProperty.put(entry.getKey(), entry.getValue().trim());
+        }
+        String resolvedTopic = topic;
+        if (Constants.EMPTY_STRING.equals(topic)) {
+            Object top = kafkaProperty.get(ConfigKeys.KEY_KAFKA_TOPIC());
+            if (top == null || top.toString().split(",|\\s+").length > 1) {
+                throw new IllegalArgumentException(
+                    "Can't find a unique topic!!!,you must be input a topic");
+            }
+            resolvedTopic = top.toString();
+        }
+        boolean hasTopic = true;
+        for (Map.Entry<Object, Object> entry : kafkaProperty.entrySet()) {
+            if (ConfigKeys.KEY_KAFKA_TOPIC().equals(entry.getKey())) {
+                for (String t : entry.getValue().toString().split(",|\\s+")) {
+                    if (t.equals(resolvedTopic)) {
+                        hasTopic = false;
+                        break;
+                    }
+                }
+            }
+        }
+        if (hasTopic) {
+            throw new IllegalArgumentException("Can't find a topic of:" + resolvedTopic + "!!!");
+        }
+        kafkaProperty.put(ConfigKeys.KEY_KAFKA_TOPIC(), resolvedTopic);
+        return kafkaProperty;
+    }
+
+    public static Properties getJdbcConf(Map<String, String> parameter) {
+        return getJdbcConf(parameter, "");
+    }
+
+    public static Properties getJdbcConf(Map<String, String> parameter, String alias) {
+        String prefix;
+        if (alias == null || alias.isEmpty()) {
+            prefix = ConfigKeys.KEY_JDBC_PREFIX();
+        } else {
+            prefix = ConfigKeys.KEY_JDBC_PREFIX() + alias;
+            if (!prefix.endsWith(".")) {
+                prefix = prefix + ".";
+            }
+        }
+        String driver = parameter.getOrDefault(prefix + ConfigKeys.KEY_JDBC_DRIVER(), null);
+        String url = parameter.getOrDefault(prefix + ConfigKeys.KEY_JDBC_URL(), null);
+        String user = parameter.getOrDefault(prefix + ConfigKeys.KEY_JDBC_USER(), null);
+        String password = parameter.getOrDefault(prefix + ConfigKeys.KEY_JDBC_PASSWORD(), null);
+        if (driver == null || url == null) {
+            throw new IllegalArgumentException(
+                "Jdbc instance:" + prefix + " error,[driver|url] must not be null");
+        }
+        if ((user != null && password == null) || (user == null && password != null)) {
+            throw new IllegalArgumentException(
+                "Jdbc instance:"
+                    + prefix
+                    + " error, [user|password] must be all null,or all not null ");
+        }
+        Map<String, String> param = filterParam(parameter, prefix);
+        Properties properties = new Properties();
+        String aliasName = alias == null || alias.trim().isEmpty() ? "default" : alias;
+        properties.put(ConfigKeys.KEY_ALIAS(), aliasName);
+        properties.put(ConfigKeys.KEY_JDBC_DRIVER(), driver);
+        for (Map.Entry<String, String> entry : param.entrySet()) {
+            properties.put(entry.getKey(), entry.getValue());
+        }
+        return properties;
+    }
+
+    public static Properties getConf(Properties properties, String prefix, String addfix) {
+        return getConfFromProperties(properties, prefix, addfix, "");
+    }
+
+    public static Properties getConfFromProperties(
+                                                   Properties properties, String prefix, String addfix, String alias) {
+        Map<String, String> map = new HashMap<>();
+        if (properties != null) {
+            for (Map.Entry<Object, Object> entry : properties.entrySet()) {
+                if (entry.getKey() != null && entry.getValue() != null) {
+                    map.put(entry.getKey().toString(), entry.getValue().toString());
+                }
+            }
+        }
+        return getConf(map, prefix, addfix, alias);
+    }
+
+    public static Map<String, String> getConfMap(Properties properties, String prefix) {
+        Properties props = getConfFromProperties(properties, prefix, "", "");
+        Map<String, String> result = new HashMap<>();
+        for (Map.Entry<Object, Object> entry : props.entrySet()) {
+            result.put(entry.getKey().toString(), entry.getValue().toString());
+        }
+        return result;
+    }
+
+    private static Map<String, String> filterParam(Map<String, String> parameter, String fix) {
+        Map<String, String> result = new HashMap<>();
+        for (Map.Entry<String, String> entry : parameter.entrySet()) {
+            if (entry.getKey().startsWith(fix) && entry.getValue() != null) {
+                result.put(
+                    entry.getKey().substring(fix.length()).replaceFirst("^\\.", ""),
+                    entry.getValue());
+            }
+        }
+        return result;
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/DateUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/DateUtils.java
new file mode 100644
index 000000000..4b63fda2b
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/DateUtils.java
@@ -0,0 +1,250 @@
+/*
+ * 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.streampark.common.util;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
+
+public final class DateUtils {
+
+    public static final String FULL_FORMAT = "yyyy-MM-dd HH:mm:ss";
+    public static final String FORMAT_YYYYMMDD = "yyyyMMdd";
+
+    public static String fullFormat() {
+        return FULL_FORMAT;
+    }
+
+    private DateUtils() {
+    }
+
+    public static Date parse(String date) throws ParseException {
+        return parse(date, FULL_FORMAT, TimeZone.getDefault());
+    }
+    public static Date parse(String date, String format, TimeZone timeZone) throws ParseException {
+        SimpleDateFormat df = new SimpleDateFormat(format);
+        df.setTimeZone(timeZone);
+        return df.parse(date);
+    }
+    public static Date stringToDate(String date) throws ParseException {
+        return parse(date);
+    }
+    public static Date milliSecond2Date(long time) {
+        return new Date(time);
+    }
+    public static Date second2Date(long time) {
+        return milliSecond2Date(time * 1000);
+    }
+    public static String now() {
+        return now(FORMAT_YYYYMMDD, TimeZone.getDefault());
+    }
+    public static String now(String dateFormat, TimeZone timeZone) {
+        SimpleDateFormat df = new SimpleDateFormat(dateFormat);
+        df.setTimeZone(timeZone);
+        return df.format(new Date());
+    }
+    public static int minuteOfDay() {
+        return minuteOfDay(new Date(), TimeZone.getDefault());
+    }
+
+    public static int minuteOfDay(Date date) {
+        return minuteOfDay(date, TimeZone.getDefault());
+    }
+
+    public static int minuteOfDay(Date date, TimeZone timeZone) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTimeZone(timeZone);
+        calendar.setTime(date);
+        return calendar.get(Calendar.HOUR_OF_DAY) * 60 + calendar.get(Calendar.MINUTE);
+    }
+    public static int minuteOf() {
+        return minuteOf(new Date());
+    }
+    public static int minuteOf(Date date) {
+        return (int) (date.getTime() / 1000 / 60);
+    }
+    public static int secondOf() {
+        return secondOf(new Date());
+    }
+    public static int secondOf(Date date) {
+        return (int) (date.getTime() / 1000);
+    }
+    public static int secondOfDay() {
+        return secondOfDay(new Date(), TimeZone.getDefault());
+    }
+
+    public static int secondOfDay(Date date) {
+        return secondOfDay(date, TimeZone.getDefault());
+    }
+
+    public static int secondOfDay(Date date, TimeZone timeZone) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTimeZone(timeZone);
+        calendar.setTime(date);
+        return minuteOfDay(date, timeZone) * 60 + calendar.get(Calendar.SECOND);
+    }
+    public static String format() {
+        return format(new Date(), FULL_FORMAT, TimeZone.getDefault());
+    }
+    public static String format(Date date, String fmt, TimeZone timeZone) {
+        if (date == null)
+            return null;
+        SimpleDateFormat sdf = new SimpleDateFormat(fmt);
+        sdf.setTimeZone(timeZone);
+        return sdf.format(date);
+    }
+    public static long getTime(String time) {
+        return getTime(time, FULL_FORMAT, TimeZone.getDefault());
+    }
+    public static long getTime(String time, String fmt, TimeZone timeZone) {
+        SimpleDateFormat sdf = new SimpleDateFormat(fmt);
+        sdf.setTimeZone(timeZone);
+        try {
+            long t = sdf.parse(time).getTime();
+            return t > 0 ? t : System.currentTimeMillis();
+        } catch (ParseException e) {
+            return System.currentTimeMillis();
+        }
+    }
+    public static Date addAndSubtract(int i) {
+        return addAndSubtract(i, new Date(), TimeZone.getDefault());
+    }
+    public static Date addAndSubtract(int i, Date date, TimeZone timeZone) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTimeZone(timeZone);
+        cal.setTime(date);
+        cal.add(Calendar.DATE, i);
+        return cal.getTime();
+    }
+    public static Date localToUTC(String localTime) throws ParseException {
+        return localToUTC(localTime, FULL_FORMAT);
+    }
+    public static Date localToUTC(String localTime, String format) throws ParseException {
+        return localToUTC(new SimpleDateFormat(format).parse(localTime));
+    }
+    public static Date localToUTC(Date localTime) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTimeInMillis(localTime.getTime());
+        int zoneOffset = calendar.get(Calendar.ZONE_OFFSET);
+        int dstOffset = calendar.get(Calendar.DST_OFFSET);
+        calendar.add(Calendar.MILLISECOND, -(zoneOffset + dstOffset));
+        return new Date(calendar.getTimeInMillis());
+    }
+    public static Date utcToLocal(Date utcDate) throws ParseException {
+        SimpleDateFormat sdf = new SimpleDateFormat();
+        sdf.setTimeZone(TimeZone.getDefault());
+        String localTime = sdf.format(utcDate.getTime());
+        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
+        return sdf.parse(localTime);
+    }
+    public static Date utcToLocal(String utcTime) throws ParseException {
+        return utcToLocal(utcTime, FULL_FORMAT);
+    }
+    public static Date utcToLocal(String utcTime, String format) throws ParseException {
+        SimpleDateFormat sdf = new SimpleDateFormat(format);
+        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
+        return utcToLocal(sdf.parse(utcTime));
+    }
+    public static String toDuration(long milliseconds) {
+        long days = Duration.ofMillis(milliseconds).toDays();
+        long duration1 = milliseconds - TimeUnit.DAYS.toMillis(days);
+        long hours = TimeUnit.MILLISECONDS.toHours(duration1);
+        long duration2 = duration1 - TimeUnit.HOURS.toMillis(hours);
+        long minutes = TimeUnit.MILLISECONDS.toMinutes(duration2);
+        long duration3 = duration2 - TimeUnit.MINUTES.toMillis(minutes);
+        long seconds = TimeUnit.MILLISECONDS.toSeconds(duration3);
+        StringBuilder builder = new StringBuilder();
+        if (days > 0)
+            builder.append(days).append(" days ");
+        if (hours > 0 || minutes > 0 || seconds > 0)
+            builder.append(hours).append(" hours ");
+        if (minutes > 0 || seconds > 0)
+            builder.append(minutes).append(" minutes ");
+        if (seconds > 0)
+            builder.append(seconds).append(" seconds ");
+        return builder.toString();
+    }
+    public static long toSecondDuration(Date time1) {
+        return toSecondDuration(time1, new Date());
+    }
+    public static long toSecondDuration(Date time1, Date time2) {
+        return Math.abs(time1.getTime() - time2.getTime()) / 1000;
+    }
+    public static TimeUnitPair getTimeUnit(String time) {
+        return getTimeUnit(time, 5, TimeUnit.SECONDS);
+    }
+    public static TimeUnitPair getTimeUnit(String time, int defaultNum, TimeUnit defaultUnit) {
+        if (time == null || time.isEmpty())
+            return new TimeUnitPair(defaultNum, defaultUnit);
+        String trimmed = time.trim();
+        int digitEnd = 0;
+        while (digitEnd < trimmed.length() && Character.isDigit(trimmed.charAt(digitEnd))) {
+            digitEnd++;
+        }
+        if (digitEnd == 0) {
+            return new TimeUnitPair(defaultNum, defaultUnit);
+        }
+        int num = Integer.parseInt(trimmed.substring(0, digitEnd));
+        String unit = trimmed.substring(digitEnd).trim();
+        if (unit.isEmpty())
+            return new TimeUnitPair(num / 1000, TimeUnit.SECONDS);
+        switch (unit) {
+            case "s":
+                return new TimeUnitPair(num, TimeUnit.SECONDS);
+            case "m":
+            case "min":
+                return new TimeUnitPair(num, TimeUnit.MINUTES);
+            case "h":
+                return new TimeUnitPair(num, TimeUnit.HOURS);
+            case "d":
+            case "day":
+                return new TimeUnitPair(num, TimeUnit.DAYS);
+            default:
+                throw new IllegalArgumentException();
+        }
+    }
+    public static String formatFullTime(LocalDateTime localDateTime) {
+        return formatFullTime(localDateTime, FULL_FORMAT);
+    }
+    public static String formatFullTime(LocalDateTime localDateTime, String pattern) {
+        return localDateTime.format(DateTimeFormatter.ofPattern(pattern));
+    }
+    public static String formatCSTTime(String date, String format) throws ParseException {
+        SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US);
+        Date parsed = sdf.parse(date);
+        SimpleDateFormat output = new SimpleDateFormat(format);
+        output.setTimeZone(sdf.getTimeZone());
+        return output.format(parsed);
+    }
+    public static final class TimeUnitPair {
+
+        public final int num;
+        public final TimeUnit unit;
+        public TimeUnitPair(int num, TimeUnit unit) {
+            this.num = num;
+            this.unit = unit;
+        }
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/DeflaterUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/DeflaterUtils.java
new file mode 100644
index 000000000..3b2f33dd9
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/DeflaterUtils.java
@@ -0,0 +1,74 @@
+/*
+ * 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.streampark.common.util;
+
+import org.apache.streampark.shaded.org.slf4j.Logger;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Base64;
+import java.util.zip.DataFormatException;
+import java.util.zip.Deflater;
+import java.util.zip.Inflater;
+
+public final class DeflaterUtils {
+
+    private static final Logger LOG =
+        StreamParkLoggerFactory.loggerFactory().getLogger(DeflaterUtils.class.getName());
+
+    private DeflaterUtils() {
+    }
+
+    public static String zipString(String text) {
+        if (StringUtils.isBlank(text)) {
+            return "";
+        }
+        Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION);
+        deflater.setInput(text.getBytes());
+        deflater.finish();
+        byte[] bytes = new byte[256];
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream(256);
+        while (!deflater.finished()) {
+            int length = deflater.deflate(bytes);
+            outputStream.write(bytes, 0, length);
+        }
+        deflater.end();
+        return Base64.getEncoder().encodeToString(outputStream.toByteArray());
+    }
+
+    public static String unzipString(String zipString) {
+        byte[] decode = Base64.getDecoder().decode(zipString);
+        Inflater inflater = new Inflater();
+        inflater.setInput(decode);
+        byte[] bytes = new byte[256];
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream(256);
+        try {
+            while (!inflater.finished()) {
+                int length = inflater.inflate(bytes);
+                outputStream.write(bytes, 0, length);
+            }
+        } catch (DataFormatException e) {
+            LOG.warn("Failed to unzip string", e);
+            return null;
+        } finally {
+            inflater.end();
+        }
+        return outputStream.toString();
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/ExceptionUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/ExceptionUtils.java
new file mode 100644
index 000000000..59652e2fc
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/ExceptionUtils.java
@@ -0,0 +1,70 @@
+/*
+ * 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.streampark.common.util;
+
+import org.apache.streampark.shaded.org.slf4j.Logger;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+public final class ExceptionUtils {
+
+    private static final Logger LOG =
+        StreamParkLoggerFactory.loggerFactory().getLogger(ExceptionUtils.class.getName());
+
+    private ExceptionUtils() {
+    }
+
+    @Nonnull
+    public static String stringifyException(@Nullable Throwable throwable) {
+        if (throwable == null) {
+            return "(null)";
+        }
+        StringWriter stm = new StringWriter();
+        PrintWriter writer = new PrintWriter(stm);
+        try {
+            throwable.printStackTrace(writer);
+            return stm.toString();
+        } catch (RuntimeException e) {
+            LOG.warn("[StreamPark] error while printing stack trace", e);
+            return e.getClass().getName() + " (error while printing stack trace)";
+        } finally {
+            Utils.close(writer, stm);
+        }
+    }
+
+    @FunctionalInterface
+    public interface WrapperRuntimeExceptionHandler<I, O> {
+
+        O handle(I input) throws Exception;
+    }
+
+    public static <I, O> O wrapRuntimeException(I input, WrapperRuntimeExceptionHandler<I, O> handler) {
+        try {
+            return handler.handle(input);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new RuntimeException(e);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/FileUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/FileUtils.java
new file mode 100644
index 000000000..1193332ff
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/FileUtils.java
@@ -0,0 +1,334 @@
+/*
+ * 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.streampark.common.util;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.io.Serializable;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Scanner;
+import java.util.stream.Collectors;
+
+public final class FileUtils {
+
+    private FileUtils() {
+    }
+
+    public static File toCanonicalFile(String path) throws IOException {
+        Path normalized = Paths.get(path).normalize();
+        if (normalized.toString().contains("..")) {
+            throw new IOException("Invalid path: " + path);
+        }
+        return normalized.toFile().getCanonicalFile();
+    }
+
+    public static File resolveChildFile(File baseDir, String... childSegments) throws IOException {
+        Path base = baseDir.getCanonicalFile().toPath();
+        Path current = base;
+        for (String segment : childSegments) {
+            if (segment == null || segment.isEmpty() || segment.contains("..")) {
+                throw new IOException("Invalid path segment: " + segment);
+            }
+            current = current.resolve(segment);
+        }
+        Path resolved = current.normalize().toAbsolutePath();
+        if (!resolved.startsWith(base)) {
+            throw new IOException("Resolved path escapes base directory: " + resolved);
+        }
+        return resolved.toFile();
+    }
+
+    private static String bytesToHexString(byte[] src) {
+        if (src == null || src.length <= 0) {
+            return null;
+        }
+        StringBuilder stringBuilder = new StringBuilder();
+        for (byte b : src) {
+            int v = b & 0xFF;
+            String hv = Integer.toHexString(v).toUpperCase();
+            if (hv.length() < 2) {
+                stringBuilder.append(0);
+            }
+            stringBuilder.append(hv);
+        }
+        return stringBuilder.toString();
+    }
+
+    public static boolean isJarFileType(InputStream input) {
+        if (input == null) {
+            throw new RuntimeException("The inputStream can not be null");
+        }
+        return AutoCloseUtils.using(
+            input,
+            in -> {
+                byte[] b = new byte[4];
+                try {
+                    in.read(b, 0, b.length);
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+                return bytesToHexString(b);
+            })
+            .equals("504B0304");
+    }
+
+    public static boolean isJarFileType(File file) throws IOException {
+        if (!file.exists() || !file.isFile()) {
+            throw new RuntimeException("The file does not exist or the path is a directory");
+        }
+        return isJarFileType(new FileInputStream(file));
+    }
+
+    public static File createTempDir() {
+        final int TEMP_DIR_ATTEMPTS = 10000;
+        File baseDir = new File(System.getProperty("java.io.tmpdir"));
+        String baseName = System.currentTimeMillis() + "-";
+        for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
+            File tempDir = new File(baseDir, baseName + counter);
+            if (tempDir.mkdir()) {
+                return tempDir;
+            }
+        }
+        throw new IllegalStateException(
+            "[StreamPark] Failed to create directory within "
+                + TEMP_DIR_ATTEMPTS
+                + "  attempts (tried "
+                + baseName
+                + " 0 to "
+                + baseName
+                + (TEMP_DIR_ATTEMPTS - 1)
+                + ")");
+    }
+
+    public static void mkdir(File dir) throws IOException {
+        if (dir.exists() && !dir.isDirectory()) {
+            throw new IOException("File " + dir + " exists and is not a directory. Unable to create directory.");
+        } else if (!dir.mkdirs() && !dir.isDirectory()) {
+            throw new IOException("Unable to create directory " + dir);
+        }
+    }
+
+    public static String getPathFromEnv(String env) {
+        String path = System.getenv(env);
+        if (path == null) {
+            path = System.getProperty(env);
+        }
+        AssertUtils.notNull(path, "[StreamPark] FileUtils.getPathFromEnv: " + env + " is not set on system env");
+        File file = new File(path);
+        if (!file.exists()) {
+            throw new IllegalArgumentException("[StreamPark] FileUtils.getPathFromEnv: " + env + " is not exist!");
+        }
+        return file.getAbsolutePath();
+    }
+
+    public static String resolvePath(String parent, String child) {
+        File file = new File(parent, child);
+        if (!file.exists()) {
+            throw new IllegalArgumentException(
+                "[StreamPark] FileUtils.resolvePath: " + file.getAbsolutePath() + " is not exist!");
+        }
+        return file.getAbsolutePath();
+    }
+
+    public static String getSuffix(String filename) {
+        AssertUtils.notNull(filename);
+        return filename.substring(filename.lastIndexOf("."));
+    }
+
+    public static List<URL> listFileAsURL(String dirPath) {
+        File dir = new File(dirPath);
+        if (dir.exists() && dir.isDirectory()) {
+            File[] files = dir.listFiles();
+            if (files != null && files.length > 0) {
+                List<URL> urls = new ArrayList<>();
+                for (File f : files) {
+                    try {
+                        urls.add(f.toURI().toURL());
+                    } catch (Exception e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+                return urls;
+            }
+        }
+        return Collections.emptyList();
+    }
+
+    public static boolean exists(Serializable file) {
+        if (file == null) {
+            return false;
+        }
+        if (file instanceof File) {
+            return ((File) file).exists();
+        }
+        return new File(file.toString()).exists();
+    }
+
+    public static boolean directoryNotBlank(Serializable file) {
+        if (file == null) {
+            return false;
+        }
+        File f = file instanceof File ? (File) file : new File(file.toString());
+        String[] list = f.list();
+        return f.isDirectory() && list != null && list.length > 0;
+    }
+
+    public static boolean equals(File file1, File file2) {
+        try {
+            return equalsInternal(file1, file2);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static boolean equalsInternal(File file1, File file2) throws IOException {
+        if (file1 == null || file2 == null) {
+            return false;
+        }
+        if (!file1.exists() || !file2.exists()) {
+            return false;
+        }
+        if (file1.getAbsolutePath().equals(file2.getAbsolutePath())) {
+            return true;
+        }
+        BufferedInputStream first = new BufferedInputStream(new FileInputStream(file1));
+        BufferedInputStream second = new BufferedInputStream(new FileInputStream(file2));
+        if (first.available() != second.available()) {
+            Utils.close(first, second);
+            return false;
+        }
+        while (true) {
+            int firRead = first.read();
+            int secRead = second.read();
+            if (firRead != secRead) {
+                Utils.close(first, second);
+                return false;
+            }
+            if (firRead == -1) {
+                Utils.close(first, second);
+                return true;
+            }
+        }
+    }
+
+    public static void readInputStream(InputStream in, byte[] array) throws IOException {
+        try (InputStream input = in) {
+            int toRead = array.length;
+            int off = 0;
+            while (toRead > 0) {
+                int ret = input.read(array, off, toRead);
+                if (ret < 0) {
+                    throw new IOException("Bad inputStream, premature EOF");
+                }
+                toRead -= ret;
+                off += ret;
+            }
+        }
+    }
+
+    public static String readFile(String filename) throws IOException {
+        return SafePathUtils.readConfigFile(filename);
+    }
+
+    public static String readFile(File file) throws IOException {
+        return readFile(file.getPath());
+    }
+
+    public static void writeFile(String content, File file) throws IOException {
+        java.nio.channels.WritableByteChannel channel =
+            Channels.newChannel(Files.newOutputStream(file.toPath()));
+        ByteBuffer buffer = ByteBuffer.wrap(content.getBytes(StandardCharsets.UTF_8));
+        channel.write(buffer);
+        Utils.close(channel);
+    }
+
+    public static byte[] readEndOfFile(File file, long maxSize) throws IOException {
+        try (RandomAccessFile raFile = new RandomAccessFile(file, "r")) {
+            long readSize = maxSize;
+            if (raFile.length() > maxSize) {
+                raFile.seek(raFile.length() - maxSize);
+            } else if (raFile.length() < maxSize) {
+                readSize = raFile.length();
+            }
+            byte[] fileContent = new byte[(int) readSize];
+            raFile.read(fileContent);
+            return fileContent;
+        }
+    }
+
+    public static byte[] readFileFromOffset(File file, long startOffset, long maxSize) throws IOException {
+        if (file.length() < startOffset) {
+            throw new IllegalArgumentException(
+                "The startOffset "
+                    + startOffset
+                    + " is great than the file length "
+                    + file.length());
+        }
+        try (RandomAccessFile raFile = new RandomAccessFile(file, "r")) {
+            long readSize = Math.min(maxSize, file.length() - startOffset);
+            raFile.seek(startOffset);
+            byte[] fileContent = new byte[(int) readSize];
+            raFile.read(fileContent);
+            return fileContent;
+        }
+    }
+
+    public static String tailOf(String path, int offset, int limit) {
+        try {
+            File file = new File(path);
+            if (file.exists() && file.isFile()) {
+                return Files.lines(Paths.get(path)).skip(offset).limit(limit).collect(Collectors.joining("\r\n"));
+            }
+            return null;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static String readString(File file) throws IOException {
+        if (file == null || !file.isFile()) {
+            throw new IllegalArgumentException("file must be a normal file");
+        }
+        FileReader reader = new FileReader(file);
+        Scanner scanner = new Scanner(reader);
+        StringBuilder buffer = new StringBuilder();
+        if (scanner.hasNextLine()) {
+            buffer.append(scanner.nextLine());
+        }
+        while (scanner.hasNextLine()) {
+            buffer.append("\r\n");
+            buffer.append(scanner.nextLine());
+        }
+        Utils.close(scanner, reader);
+        return buffer.toString();
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/FlinkConfigurationUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/FlinkConfigurationUtils.java
new file mode 100644
index 000000000..751f6073c
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/FlinkConfigurationUtils.java
@@ -0,0 +1,236 @@
+/*
+ * 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.streampark.common.util;
+
+import org.apache.streampark.shaded.org.slf4j.Logger;
+
+import org.apache.commons.lang3.StringUtils;
+
+import javax.annotation.Nonnull;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/** Flink configuration parsing utilities. */
+public final class FlinkConfigurationUtils {
+
+    private static final Logger LOG =
+        StreamParkLoggerFactory.loggerFactory()
+            .getLogger(FlinkConfigurationUtils.class.getName());
+
+    private static final Pattern PROPERTY_PATTERN = Pattern.compile("(.*?)=(.*?)");
+
+    private static final Pattern MULTI_PROPERTY_PATTERN =
+        Pattern.compile("-D(.*?)\\s*=\\s*([\"'])(.*)\\2");
+
+    private FlinkConfigurationUtils() {
+    }
+
+    public static Map<String, String> loadFlinkConf(File file) {
+        AssertUtils.required(
+            file != null && file.exists() && file.isFile(),
+            "[StreamPark] loadFlinkConfYaml: file must not be null");
+        try {
+            return loadFlinkConf(
+                org.apache.commons.io.FileUtils.readFileToString(file, StandardCharsets.UTF_8));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static Map<String, String> loadFlinkConf(String yaml) {
+        AssertUtils.required(
+            yaml != null && !yaml.isEmpty(), "[StreamPark] loadFlinkConfYaml: yaml must not be null");
+        return new HashMap<>(PropertiesUtils.fromYamlText(yaml));
+    }
+
+    public static Map<String, String> loadLegacyFlinkConf(File file) {
+        AssertUtils.required(
+            file != null && file.exists() && file.isFile(),
+            "[StreamPark] loadFlinkConfYaml: file must not be null");
+        try {
+            return loadLegacyFlinkConf(
+                org.apache.commons.io.FileUtils.readFileToString(file, StandardCharsets.UTF_8));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static Map<String, String> loadLegacyFlinkConf(String yaml) {
+        AssertUtils.required(
+            yaml != null && !yaml.isEmpty(), "[StreamPark] loadFlinkConfYaml: yaml must not be null");
+        Map<String, String> flinkConf = new LinkedHashMap<>();
+        java.util.Scanner scanner = new java.util.Scanner(yaml);
+        AtomicInteger lineNo = new AtomicInteger(0);
+        while (scanner.hasNextLine()) {
+            String line = scanner.nextLine();
+            lineNo.incrementAndGet();
+            int commentStart = line.indexOf('#');
+            String conf = (commentStart >= 0 ? line.substring(0, commentStart) : line).trim();
+            if (!conf.isEmpty()) {
+                String[] kv = conf.split(": ", 2);
+                if (kv.length == 2) {
+                    String key = kv[0].trim();
+                    String value = kv[1].trim();
+                    if (!key.isEmpty() && !value.isEmpty()) {
+                        flinkConf.put(key, value);
+                    } else {
+                        LOG.warn(
+                            "[StreamPark] Error after splitting key and value in configuration {}: {}",
+                            lineNo.get(),
+                            line);
+                    }
+                } else {
+                    LOG.warn(
+                        "[StreamPark] Error while trying to split key and value in configuration. {} : {}",
+                        lineNo.get(),
+                        line);
+                }
+            }
+        }
+        scanner.close();
+        return flinkConf;
+    }
+
+    @Nonnull
+    public static Map<String, String> extractDynamicProperties(String properties) {
+        if (StringUtils.isEmpty(properties)) {
+            return Collections.emptyMap();
+        }
+        Map<String, String> map = new LinkedHashMap<>();
+        String simple = MULTI_PROPERTY_PATTERN.matcher(properties).replaceAll("");
+        String[] parts = simple.split("\\s?-D");
+        if (Utils.isNotEmpty(parts)) {
+            for (String x : parts) {
+                if (!x.isEmpty()) {
+                    Matcher p = PROPERTY_PATTERN.matcher(x.trim());
+                    if (p.matches()) {
+                        map.put(p.group(1).trim(), p.group(2).trim());
+                    }
+                }
+            }
+        }
+        Matcher matcher = MULTI_PROPERTY_PATTERN.matcher(properties);
+        while (matcher.find()) {
+            map.put(matcher.group(1).trim(), matcher.group(3));
+        }
+        return map;
+    }
+
+    @Nonnull
+    public static List<String> extractArguments(String args) {
+        if (StringUtils.isNotEmpty(args)) {
+            return extractArguments(args.split("\\s+"));
+        }
+        return new ArrayList<>();
+    }
+
+    public static List<String> extractArguments(String[] array) {
+        List<String> programArgs = new ArrayList<>();
+        Iterator<String> iter = Arrays.asList(array).iterator();
+        while (iter.hasNext()) {
+            String v = iter.next();
+            String p = v.substring(0, 1);
+            if ("'".equals(p) || "\"".equals(p)) {
+                String value = v;
+                if (!v.endsWith(p)) {
+                    while (!value.endsWith(p) && iter.hasNext()) {
+                        value += " " + iter.next();
+                    }
+                }
+                programArgs.add(value.substring(1, value.length() - 1));
+            } else {
+                int eqIndex = v.indexOf('=');
+                if (eqIndex > 0 && v.length() > 2) {
+                    char open = v.charAt(0);
+                    char close = v.charAt(v.length() - 1);
+                    if ((open == '\'' && close == '\'') || (open == '"' && close == '"')) {
+                        programArgs.add(v.substring(1, v.length() - 1));
+                    } else if (eqIndex > 1 && v.charAt(eqIndex - 1) == '\'' && v.endsWith("'")) {
+                        programArgs.add(v.substring(0, eqIndex - 1) + "=" + v.substring(eqIndex + 1, v.length() - 1));
+                    } else if (eqIndex > 1 && v.charAt(eqIndex - 1) == '"' && v.endsWith("\"")) {
+                        programArgs.add(v.substring(0, eqIndex - 1) + "=" + v.substring(eqIndex + 1, v.length() - 1));
+                    } else {
+                        programArgs.add(v);
+                    }
+                } else {
+                    programArgs.add(v);
+                }
+            }
+        }
+        return programArgs;
+    }
+
+    public static Map<String, Map<String, String>> extractMultipleArguments(String[] array) {
+        Iterator<String> iter = Arrays.asList(array).iterator();
+        Map<String, Map<String, String>> map = new LinkedHashMap<>();
+        while (iter.hasNext()) {
+            String v = iter.next();
+            if (v.length() >= 2 && v.startsWith("--")) {
+                String kv = iter.next();
+                int eqIndex = kv.indexOf('=');
+                if (eqIndex > 0) {
+                    String[] values = new String[]{kv.substring(0, eqIndex), kv.substring(eqIndex + 1)};
+                    String k1 = values[0].trim();
+                    String v1 = stripOuterQuotes(values[1].trim());
+                    String k = v.substring(2);
+                    map.computeIfAbsent(k, key -> new LinkedHashMap<>()).put(k1, v1);
+                }
+            }
+        }
+        return map;
+    }
+
+    @Nonnull
+    public static Map<String, String> extractDynamicPropertiesAsJava(String properties) {
+        return new HashMap<>(extractDynamicProperties(properties));
+    }
+
+    @Nonnull
+    public static Map<String, Map<String, String>> extractMultipleArgumentsAsJava(String[] args) {
+        Map<String, Map<String, String>> result = new HashMap<>();
+        for (Map.Entry<String, Map<String, String>> entry : extractMultipleArguments(args).entrySet()) {
+            result.put(entry.getKey(), new HashMap<>(entry.getValue()));
+        }
+        return result;
+    }
+
+    private static String stripOuterQuotes(String value) {
+        if (value == null || value.length() < 2) {
+            return value;
+        }
+        char first = value.charAt(0);
+        char last = value.charAt(value.length() - 1);
+        if ((first == '"' && last == '"') || (first == '\'' && last == '\'')) {
+            return value.substring(1, value.length() - 1);
+        }
+        return value;
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/HBaseClient.java b/streampark-common/src/main/java/org/apache/streampark/common/util/HBaseClient.java
new file mode 100644
index 000000000..08c59793b
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/HBaseClient.java
@@ -0,0 +1,83 @@
+/*
+ * 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.streampark.common.util;
+
+import org.apache.streampark.common.conf.ConfigKeys;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseConfiguration;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.client.Connection;
+import org.apache.hadoop.hbase.client.ConnectionFactory;
+import org.apache.hadoop.hbase.client.Table;
+import org.apache.hadoop.security.UserGroupInformation;
+
+import java.io.Serializable;
+import java.util.Properties;
+import java.util.function.Supplier;
+
+public class HBaseClient implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+    public static final Configuration CONF = HBaseConfiguration.create();
+    private final Supplier<Connection> func;
+    private transient Connection connection;
+
+    public HBaseClient(Supplier<Connection> func) {
+        this.func = func;
+    }
+
+    public Connection getConnection() {
+        if (connection == null)
+            connection = func.get();
+        return connection;
+    }
+
+    public Table table(String table) throws Exception {
+        return getConnection().getTable(TableName.valueOf(table));
+    }
+
+    public static HBaseClient apply(Properties prop) {
+        Object user = prop.remove(ConfigKeys.KEY_HBASE_AUTH_USER());
+        for (String key : prop.stringPropertyNames())
+            CONF.set(key, prop.getProperty(key));
+        return new HBaseClient(() -> {
+            try {
+                if (user != null) {
+                    UserGroupInformation.setConfiguration(CONF);
+                    UserGroupInformation remoteUser = UserGroupInformation.createRemoteUser(user.toString());
+                    UserGroupInformation.setLoginUser(remoteUser);
+                }
+                Connection connection = ConnectionFactory.createConnection(CONF);
+                Runtime.getRuntime()
+                    .addShutdownHook(
+                        new Thread(
+                            () -> {
+                                try {
+                                    connection.close();
+                                } catch (Exception ignored) {
+                                    // ignore
+                                }
+                            }));
+                return connection;
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        });
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/HadoopConfigUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/HadoopConfigUtils.java
new file mode 100644
index 000000000..62dfd119a
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/HadoopConfigUtils.java
@@ -0,0 +1,260 @@
+/*
+ * 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.streampark.common.util;
+
+import org.apache.streampark.common.conf.CommonConfig;
+import org.apache.streampark.common.conf.ConfigKeys;
+import org.apache.streampark.common.conf.InternalConfigHolder;
+import org.apache.streampark.common.fs.LfsOperator;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Properties;
+
+import scala.Option;
+
+/** Hadoop client configuration tools mainly for flink use. */
+public final class HadoopConfigUtils {
+
+    private static final String[] HADOOP_CLIENT_CONF_FILES =
+        {"core-site.xml", "hdfs-site.xml", "yarn-site.xml"};
+
+    private static final String[] HIVE_CLIENT_CONF_FILES =
+        {"core-site.xml", "hdfs-site.xml", "hive-site.xml"};
+
+    private static final Map<String, String> KERBEROS_CONF = loadKerberosConf();
+
+    public static final String HADOOP_USER_NAME =
+        InternalConfigHolder.get(CommonConfig.STREAMPARK_HADOOP_USER_NAME());
+
+    public static String hadoopUserName() {
+        return HADOOP_USER_NAME;
+    }
+
+    public static final String KERBEROS_DEBUG =
+        KERBEROS_CONF.getOrDefault(ConfigKeys.KEY_SECURITY_KERBEROS_DEBUG(), "false");
+
+    public static final boolean KERBEROS_ENABLE =
+        Boolean.parseBoolean(
+            KERBEROS_CONF.getOrDefault(ConfigKeys.KEY_SECURITY_KERBEROS_ENABLE(), "false"));
+
+    public static final String KERBEROS_PRINCIPAL =
+        KERBEROS_CONF.getOrDefault(ConfigKeys.KEY_SECURITY_KERBEROS_PRINCIPAL(), "").trim();
+
+    public static final String KERBEROS_KEYTAB =
+        KERBEROS_CONF.getOrDefault(ConfigKeys.KEY_SECURITY_KERBEROS_KEYTAB(), "").trim();
+
+    public static final String KERBEROS_KRB5 =
+        KERBEROS_CONF.getOrDefault(ConfigKeys.KEY_SECURITY_KERBEROS_KRB5_CONF(), "");
+
+    private HadoopConfigUtils() {
+    }
+
+    private static Map<String, String> loadKerberosConf() {
+        Properties props = System.getProperties();
+        Map<String, String> map = new HashMap<>();
+        for (String key : props.stringPropertyNames()) {
+            if (key.startsWith("security.kerberos")) {
+                map.put(key, props.getProperty(key));
+            }
+        }
+        return map;
+    }
+
+    public static Option<String> getSystemHadoopConfDir() {
+        return Option.apply(getSystemHadoopConfDirOptional().orElse(null));
+    }
+
+    private static Optional<String> getSystemHadoopConfDirOptional() {
+        try {
+            return Optional.of(FileUtils.getPathFromEnv("HADOOP_CONF_DIR"));
+        } catch (Exception e) {
+            try {
+                return Optional.of(
+                    FileUtils.resolvePath(FileUtils.getPathFromEnv("HADOOP_HOME"), "/etc/hadoop"));
+            } catch (Exception ignored) {
+                return Optional.empty();
+            }
+        }
+    }
+
+    public static Optional<String> getSystemHadoopConfDirAsJava() {
+        return getSystemHadoopConfDirOptional();
+    }
+
+    public static Option<String> getSystemHiveConfDir() {
+        return Option.apply(getSystemHiveConfDirOptional().orElse(null));
+    }
+
+    private static Optional<String> getSystemHiveConfDirOptional() {
+        try {
+            return Optional.of(FileUtils.getPathFromEnv("HIVE_CONF_DIR"));
+        } catch (Exception e) {
+            return Optional.empty();
+        }
+    }
+
+    public static Optional<String> getSystemHiveConfDirAsJava() {
+        return getSystemHiveConfDirOptional();
+    }
+
+    public static void replaceHostWithIP(File configFile) {
+        if (configFile.exists() && configFile.isFile() && configFile.getName().endsWith(".xml")) {
+            Map<String, String> hostsMap = HostsUtils.getSortSystemHosts();
+            if (!hostsMap.isEmpty()) {
+                rewriteHostIpMapper(configFile, hostsMap);
+            }
+        }
+    }
+
+    public static void batchReplaceHostWithIP(File configDir) {
+        batchReplaceHostWithIP(configDir, HADOOP_CLIENT_CONF_FILES);
+    }
+
+    public static void batchReplaceHostWithIP(File configDir, String[] filter) {
+        if (!configDir.isDirectory()) {
+            replaceHostWithIP(configDir);
+            return;
+        }
+        Map<String, String> hostsMap = HostsUtils.getSortSystemHosts();
+        if (hostsMap.isEmpty()) {
+            return;
+        }
+        File[] files = configDir.listFiles();
+        if (files == null) {
+            return;
+        }
+        for (File file : files) {
+            if (file.isFile()) {
+                for (String name : filter) {
+                    if (name.equals(file.getName())) {
+                        rewriteHostIpMapper(file, hostsMap);
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    private static void rewriteHostIpMapper(File configFile, Map<String, String> hostsMap) {
+        try {
+            List<String> rawLines =
+                org.apache.commons.io.FileUtils.readLines(configFile, StandardCharsets.UTF_8);
+            List<String> lines = new ArrayList<>();
+            for (String line : rawLines) {
+                if (!line.trim().startsWith("<value>")) {
+                    lines.add(line);
+                    continue;
+                }
+                String li = line;
+                Map.Entry<String, String> shot = findHostMatch(li, hostsMap);
+                while (shot != null) {
+                    li = li.replace(shot.getKey(), shot.getValue());
+                    shot = findHostMatch(li, hostsMap);
+                }
+                lines.add(li);
+            }
+            org.apache.commons.io.FileUtils.writeLines(configFile, lines);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static Map.Entry<String, String> findHostMatch(String line, Map<String, String> hostsMap) {
+        for (Map.Entry<String, String> entry : hostsMap.entrySet()) {
+            if (line.contains(entry.getKey())) {
+                return entry;
+            }
+        }
+        return null;
+    }
+
+    public static Map<String, String> readSystemHadoopConf() {
+        return getSystemHadoopConfDirOptional()
+            .map(
+                confDir -> {
+                    Map<String, String> map = new LinkedHashMap<>();
+                    File[] files = LfsOperator.listDir(confDir);
+                    if (files != null) {
+                        for (File f : files) {
+                            boolean matched = false;
+                            for (String name : HADOOP_CLIENT_CONF_FILES) {
+                                if (name.equals(f.getName())) {
+                                    matched = true;
+                                    break;
+                                }
+                            }
+                            if (!matched) {
+                                continue;
+                            }
+                            try {
+                                map.put(
+                                    f.getName(),
+                                    org.apache.commons.io.FileUtils.readFileToString(
+                                        f, StandardCharsets.UTF_8));
+                            } catch (IOException e) {
+                                throw new RuntimeException(e);
+                            }
+                        }
+                    }
+                    return map;
+                })
+            .orElse(Collections.emptyMap());
+    }
+
+    public static Map<String, String> readSystemHiveConf() {
+        return getSystemHiveConfDirOptional()
+            .map(
+                confDir -> {
+                    Map<String, String> map = new LinkedHashMap<>();
+                    File[] files = LfsOperator.listDir(confDir);
+                    if (files != null) {
+                        for (File f : files) {
+                            boolean matched = false;
+                            for (String name : HIVE_CLIENT_CONF_FILES) {
+                                if (name.equals(f.getName())) {
+                                    matched = true;
+                                    break;
+                                }
+                            }
+                            if (!matched) {
+                                continue;
+                            }
+                            try {
+                                map.put(
+                                    f.getName(),
+                                    org.apache.commons.io.FileUtils.readFileToString(
+                                        f, StandardCharsets.UTF_8));
+                            } catch (IOException e) {
+                                throw new RuntimeException(e);
+                            }
+                        }
+                    }
+                    return map;
+                })
+            .orElse(Collections.emptyMap());
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/HadoopUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/HadoopUtils.java
new file mode 100644
index 000000000..c19871f99
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/HadoopUtils.java
@@ -0,0 +1,420 @@
+/*
+ * 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.streampark.common.util;
+
+import org.apache.streampark.common.conf.CommonConfig;
+import org.apache.streampark.common.conf.ConfigKeys;
+import org.apache.streampark.common.conf.InternalConfigHolder;
+
+import org.apache.streampark.shaded.org.slf4j.Logger;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.LocalFileSystem;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.hdfs.DistributedFileSystem;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.service.Service;
+import org.apache.hadoop.yarn.api.records.ApplicationId;
+import org.apache.hadoop.yarn.api.records.FinalApplicationStatus;
+import org.apache.hadoop.yarn.api.records.YarnApplicationState;
+import org.apache.hadoop.yarn.client.api.YarnClient;
+import org.apache.hadoop.yarn.conf.YarnConfiguration;
+
+import javax.security.auth.kerberos.KerberosTicket;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.security.PrivilegedAction;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.ConcurrentHashMap;
+
+/** Hadoop filesystem and YARN client utilities. */
+public final class HadoopUtils {
+
+    private static final Logger LOG =
+        StreamParkLoggerFactory.loggerFactory().getLogger(HadoopUtils.class.getName());
+
+    private static final String HADOOP_HOME = "HADOOP_HOME";
+    private static final String HADOOP_CONF_DIR = "HADOOP_CONF_DIR";
+    private static final String CONF_SUFFIX = "/etc/hadoop";
+
+    private static YarnClient reusableYarnClient;
+    private static UserGroupInformation ugi;
+    private static Configuration reusableConf;
+    private static FileSystem reusableHdfs;
+    private static KerberosTicket tgt;
+
+    private static final Map<String, Configuration> CONFIGURATION_CACHE = new ConcurrentHashMap<>();
+
+    private static String hadoopConfDir;
+    private static Long tgtRefreshTime;
+
+    private HadoopUtils() {
+    }
+
+    public static UserGroupInformation getUgi() {
+        if (ugi == null) {
+            if (HadoopConfigUtils.KERBEROS_ENABLE) {
+                ugi = getKerberosUGI();
+            } else {
+                ugi = UserGroupInformation.createRemoteUser(HadoopConfigUtils.HADOOP_USER_NAME);
+            }
+        }
+        return ugi;
+    }
+
+    private static String getHadoopConfDir() {
+        if (hadoopConfDir == null) {
+            try {
+                hadoopConfDir = FileUtils.getPathFromEnv(HADOOP_CONF_DIR);
+            } catch (Exception e) {
+                try {
+                    hadoopConfDir =
+                        FileUtils.resolvePath(FileUtils.getPathFromEnv(HADOOP_HOME), CONF_SUFFIX);
+                } catch (Exception ignored) {
+                    hadoopConfDir = "";
+                }
+            }
+        }
+        return hadoopConfDir;
+    }
+
+    private static long getTgtRefreshTime() {
+        if (tgtRefreshTime == null) {
+            try {
+                UserGroupInformation user = UserGroupInformation.getLoginUser();
+                Method method = UserGroupInformation.class.getDeclaredMethod("getTGT");
+                method.setAccessible(true);
+                tgt = (KerberosTicket) method.invoke(user);
+                if (tgt != null) {
+                    long start = tgt.getStartTime().getTime();
+                    long end = tgt.getEndTime().getTime();
+                    tgtRefreshTime = (long) ((end - start) * 0.90f);
+                } else {
+                    LOG.warn("[StreamPark] get kerberos tgtRefreshTime failed, try get kerberos.ttl.");
+                    DateUtils.TimeUnitPair timeUnit =
+                        DateUtils.getTimeUnit(InternalConfigHolder.get(CommonConfig.KERBEROS_TTL()));
+                    switch (timeUnit.unit) {
+                        case SECONDS:
+                            tgtRefreshTime = (long) timeUnit.num * 1000;
+                            break;
+                        case MINUTES:
+                            tgtRefreshTime = (long) timeUnit.num * 60 * 1000;
+                            break;
+                        case HOURS:
+                            tgtRefreshTime = (long) timeUnit.num * 60 * 60 * 1000;
+                            break;
+                        case DAYS:
+                            tgtRefreshTime = (long) timeUnit.num * 60 * 60 * 24 * 1000;
+                            break;
+                        default:
+                            throw new IllegalArgumentException(
+                                "[StreamPark] parameter:"
+                                    + CommonConfig.KERBEROS_TTL().getKey()
+                                    + " invalided, unit options are [s|m|h|d]");
+                    }
+                }
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+        return tgtRefreshTime;
+    }
+
+    public static Configuration getConfigurationFromHadoopConfDir() {
+        return getConfigurationFromHadoopConfDir(getHadoopConfDir());
+    }
+
+    public static Configuration getConfigurationFromHadoopConfDir(String confDir) {
+        return CONFIGURATION_CACHE.computeIfAbsent(
+            confDir,
+            dir -> {
+                FileUtils.exists(dir);
+                File hadoopConfDirFile = new File(dir);
+                List<String> confName =
+                    Arrays.asList(
+                        "hdfs-default.xml",
+                        "core-site.xml",
+                        "hdfs-site.xml",
+                        "yarn-site.xml");
+                File[] allFiles = hadoopConfDirFile.listFiles();
+                HadoopConfiguration conf = new HadoopConfiguration();
+                if (allFiles != null && CollectionUtils.isNotEmpty(Arrays.asList(allFiles))) {
+                    for (File file : allFiles) {
+                        if (file.isFile() && confName.contains(file.getName())) {
+                            conf.addResource(new Path(file.getAbsolutePath()));
+                        }
+                    }
+                }
+                return conf;
+            });
+    }
+
+    public static Configuration hadoopConf() {
+        if (reusableConf == null) {
+            reusableConf = getConfigurationFromHadoopConfDir(getHadoopConfDir());
+            try {
+                ClassLoaderUtils.loadResource(getHadoopConfDir());
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+
+            if (StringUtils.isBlank(reusableConf.get("hadoop.tmp.dir"))) {
+                reusableConf.set("hadoop.tmp.dir", "/tmp");
+            }
+            if (StringUtils.isBlank(reusableConf.get("hbase.fs.tmp.dir"))) {
+                reusableConf.set("hbase.fs.tmp.dir", "/tmp");
+            }
+            reusableConf.set("yarn.timeline-service.enabled", "false");
+            reusableConf.set("fs.hdfs.impl", DistributedFileSystem.class.getName());
+            reusableConf.set("fs.file.impl", LocalFileSystem.class.getName());
+            reusableConf.set("fs.hdfs.impl.disable.cache", "true");
+        }
+        return reusableConf;
+    }
+
+    private static void closeHadoop() {
+        if (reusableHdfs != null) {
+            try {
+                reusableHdfs.close();
+            } catch (IOException e) {
+                LOG.warn("[StreamPark] close hdfs failed", e);
+            }
+            reusableHdfs = null;
+        }
+        if (reusableYarnClient != null) {
+            try {
+                reusableYarnClient.close();
+            } catch (IOException e) {
+                LOG.warn("[StreamPark] close yarn client failed", e);
+            }
+            reusableYarnClient = null;
+        }
+        if (tgt != null && !tgt.isDestroyed()) {
+            try {
+                tgt.destroy();
+            } catch (javax.security.auth.DestroyFailedException e) {
+                LOG.warn("[StreamPark] destroy kerberos ticket failed", e);
+            }
+            tgt = null;
+        }
+        reusableConf = null;
+        ugi = null;
+    }
+
+    private static UserGroupInformation getKerberosUGI() {
+        LOG.info("[StreamPark] kerberos login starting....");
+
+        if (HadoopConfigUtils.KERBEROS_PRINCIPAL.isEmpty()
+            || HadoopConfigUtils.KERBEROS_KEYTAB.isEmpty()) {
+            throw new IllegalArgumentException(
+                ConfigKeys.KEY_SECURITY_KERBEROS_PRINCIPAL()
+                    + " and "
+                    + ConfigKeys.KEY_SECURITY_KERBEROS_KEYTAB()
+                    + " must not be empty");
+        }
+
+        System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
+
+        if (!HadoopConfigUtils.KERBEROS_KRB5.isEmpty()) {
+            System.setProperty("java.security.krb5.conf", HadoopConfigUtils.KERBEROS_KRB5);
+            System.setProperty("java.security.krb5.conf.path", HadoopConfigUtils.KERBEROS_KRB5);
+        }
+
+        System.setProperty("sun.security.spnego.debug", HadoopConfigUtils.KERBEROS_DEBUG);
+        System.setProperty("sun.security.krb5.debug", HadoopConfigUtils.KERBEROS_DEBUG);
+        hadoopConf()
+            .set(
+                ConfigKeys.KEY_HADOOP_SECURITY_AUTHENTICATION(),
+                ConfigKeys.KEY_KERBEROS());
+
+        try {
+            UserGroupInformation.setConfiguration(hadoopConf());
+            UserGroupInformation kerberosUgi =
+                UserGroupInformation.loginUserFromKeytabAndReturnUGI(
+                    HadoopConfigUtils.KERBEROS_PRINCIPAL, HadoopConfigUtils.KERBEROS_KEYTAB);
+            UserGroupInformation.setLoginUser(kerberosUgi);
+            LOG.info("[StreamPark] kerberos authentication successful");
+            return kerberosUgi;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static FileSystem hdfs() {
+        if (reusableHdfs == null) {
+            try {
+                reusableHdfs =
+                    getUgi()
+                        .doAs(
+                            (PrivilegedAction<FileSystem>) () -> {
+                                try {
+                                    return FileSystem.get(hadoopConf());
+                                } catch (IOException e) {
+                                    throw new RuntimeException(e);
+                                }
+                            });
+                if (HadoopConfigUtils.KERBEROS_ENABLE) {
+                    Timer timer = new Timer();
+                    long refreshTime = getTgtRefreshTime();
+                    timer.schedule(
+                        new TimerTask() {
+
+                            @Override
+                            public void run() {
+                                closeHadoop();
+                                LOG.info(
+                                    "[StreamPark] Check Kerberos Tgt And reLogin From Keytab Finish:refresh time: {}",
+                                    DateUtils.format());
+                            }
+                        },
+                        refreshTime,
+                        refreshTime);
+                }
+            } catch (Exception e) {
+                throw new IllegalArgumentException("[StreamPark] access hdfs error: " + e, e);
+            }
+        }
+        return reusableHdfs;
+    }
+
+    public static YarnClient yarnClient() {
+        if (reusableYarnClient == null || !reusableYarnClient.isInState(Service.STATE.STARTED)) {
+            try {
+                reusableYarnClient =
+                    getUgi()
+                        .doAs(
+                            (PrivilegedAction<YarnClient>) () -> {
+                                YarnConfiguration yarnConf =
+                                    new YarnConfiguration(hadoopConf());
+                                YarnClient client = YarnClient.createYarnClient();
+                                client.init(yarnConf);
+                                client.start();
+                                return client;
+                            });
+            } catch (Exception e) {
+                throw new IllegalArgumentException(
+                    "[StreamPark] access yarnClient error: " + e, e);
+            }
+        }
+        return reusableYarnClient;
+    }
+
+    public static ApplicationId toApplicationId(String appId) {
+        if (appId == null) {
+            throw new IllegalArgumentException(
+                "[StreamPark] HadoopUtils.toApplicationId: applicationId muse not be null");
+        }
+        String[] timestampAndId = appId.split("_");
+        return ApplicationId.newInstance(
+            Long.parseLong(timestampAndId[1]), Integer.parseInt(timestampAndId[timestampAndId.length - 1]));
+    }
+
+    public static String downloadJar(String jarOnHdfs) throws IOException {
+        File tmpDir = FileUtils.createTempDir();
+        FileSystem fs = FileSystem.get(new Configuration());
+        Path sourcePath = fs.makeQualified(new Path(jarOnHdfs));
+        if (!fs.exists(sourcePath)) {
+            throw new IOException("jar file: " + jarOnHdfs + " doesn't exist.");
+        }
+        Path destPath = new Path(tmpDir.getAbsolutePath() + "/" + sourcePath.getName());
+        fs.copyToLocalFile(sourcePath, destPath);
+        return new File(destPath.toString()).getAbsolutePath();
+    }
+
+    public static YarnApplicationState toYarnState(String state) {
+        for (YarnApplicationState yarnState : YarnApplicationState.values()) {
+            if (yarnState.name().equals(state)) {
+                return yarnState;
+            }
+        }
+        return null;
+    }
+
+    public static FinalApplicationStatus toYarnFinalStatus(String state) {
+        for (FinalApplicationStatus status : FinalApplicationStatus.values()) {
+            if (status.name().equals(state)) {
+                return status;
+            }
+        }
+        return null;
+    }
+
+    private static final class HadoopConfiguration extends Configuration {
+
+        private static final List<String> REWRITE_NAMES =
+            Arrays.asList(
+                "dfs.blockreport.initialDelay",
+                "dfs.datanode.directoryscan.interval",
+                "dfs.heartbeat.interval",
+                "dfs.namenode.decommission.interval",
+                "dfs.namenode.replication.interval",
+                "dfs.namenode.checkpoint.period",
+                "dfs.namenode.checkpoint.check.period",
+                "dfs.client.datanode-restart.timeout",
+                "dfs.ha.log-roll.period",
+                "dfs.ha.tail-edits.period",
+                "dfs.datanode.bp-ready.timeout");
+
+        private static String getHexDigits(String value) {
+            boolean negative = false;
+            String str = value;
+            if (value.startsWith("-")) {
+                negative = true;
+                str = value.substring(1);
+            }
+            if (str.startsWith("0x") || str.startsWith("0X")) {
+                String hexString = str.substring(2);
+                if (negative) {
+                    hexString = "-" + hexString;
+                }
+                return hexString;
+            }
+            return null;
+        }
+
+        private String getSafeValue(String name) {
+            String value = getTrimmed(name);
+            if (REWRITE_NAMES.contains(name)) {
+                return value.replaceFirst("s$", "");
+            }
+            return value;
+        }
+
+        @Override
+        public long getLong(String name, long defaultValue) {
+            String valueString = getSafeValue(name);
+            if (valueString == null) {
+                return defaultValue;
+            }
+            String hexString = getHexDigits(valueString);
+            if (hexString != null) {
+                return Long.parseLong(hexString, 16);
+            }
+            return Long.parseLong(valueString);
+        }
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/HdfsUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/HdfsUtils.java
new file mode 100644
index 000000000..4a0919933
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/HdfsUtils.java
@@ -0,0 +1,206 @@
+/*
+ * 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.streampark.common.util;
+
+import org.apache.streampark.shaded.org.slf4j.Logger;
+
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.hadoop.fs.FSDataInputStream;
+import org.apache.hadoop.fs.FSDataOutputStream;
+import org.apache.hadoop.fs.FileStatus;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.FileUtil;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.hdfs.DistributedFileSystem;
+import org.apache.hadoop.io.IOUtils;
+import org.apache.hadoop.ipc.RPC;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.Arrays;
+import java.util.List;
+
+public final class HdfsUtils {
+
+    private static final Logger LOG =
+        StreamParkLoggerFactory.loggerFactory().getLogger(HdfsUtils.class.getName());
+
+    private HdfsUtils() {
+    }
+
+    public static String getDefaultFS() {
+        return HadoopUtils.hadoopConf().get(FileSystem.FS_DEFAULT_NAME_KEY);
+    }
+
+    public static List<FileStatus> list(String src) throws IOException {
+        return Arrays.asList(HadoopUtils.hdfs().listStatus(getPath(src)));
+    }
+
+    public static void move(String src, String dst) throws IOException {
+        HadoopUtils.hdfs().rename(getPath(src), getPath(dst));
+    }
+
+    public static void mkdirs(String path) throws IOException {
+        HadoopUtils.hdfs().mkdirs(getPath(path));
+    }
+
+    public static void copyHdfs(String src, String dst) throws IOException {
+        copyHdfs(src, dst, false, true);
+    }
+
+    public static void copyHdfs(String src, String dst, boolean delSrc, boolean overwrite) throws IOException {
+        Path srcPath = getPath(src);
+        Path dstPath = getPath(dst);
+        FileStatus dstStatus = HadoopUtils.hdfs().getFileStatus(dstPath);
+        Path dstFinalPath =
+            dstStatus.isFile() ? dstPath : getPath(dst + "/" + srcPath.getName());
+        FileUtil.copy(
+            HadoopUtils.hdfs(),
+            srcPath,
+            HadoopUtils.hdfs(),
+            dstFinalPath,
+            delSrc,
+            overwrite,
+            HadoopUtils.hadoopConf());
+    }
+
+    public static void copyHdfsDir(String src, String dst) throws IOException {
+        copyHdfsDir(src, dst, false, true);
+    }
+
+    public static void copyHdfsDir(String src, String dst, boolean delSrc, boolean overwrite) throws IOException {
+        for (FileStatus status : list(src)) {
+            FileUtil.copy(
+                HadoopUtils.hdfs(),
+                status,
+                HadoopUtils.hdfs(),
+                getPath(dst),
+                delSrc,
+                overwrite,
+                HadoopUtils.hadoopConf());
+        }
+    }
+
+    public static void upload(String src, String dst) throws IOException {
+        upload(src, dst, false, true);
+    }
+
+    public static void upload(String src, String dst, boolean delSrc, boolean overwrite) throws IOException {
+        HadoopUtils.hdfs().copyFromLocalFile(delSrc, overwrite, getPath(src), getPath(dst));
+    }
+
+    public static void uploadMulti(String[] src, String dst) throws IOException {
+        uploadMulti(src, dst, false, true);
+    }
+
+    public static void uploadMulti(String[] src, String dst, boolean delSrc, boolean overwrite) throws IOException {
+        Path[] paths = Arrays.stream(src).map(HdfsUtils::getPath).toArray(Path[]::new);
+        HadoopUtils.hdfs().copyFromLocalFile(delSrc, overwrite, paths, getPath(dst));
+    }
+
+    public static void download(String src, String dst) throws IOException {
+        download(src, dst, false, false);
+    }
+
+    public static void download(String src, String dst, boolean delSrc,
+                                boolean useRawLocalFileSystem) throws IOException {
+        HadoopUtils.hdfs()
+            .copyToLocalFile(delSrc, getPath(src), getPath(dst), useRawLocalFileSystem);
+    }
+
+    public static String getNameNode() throws IOException {
+        return getAddressOfActive(HadoopUtils.hdfs()).getHostString();
+    }
+
+    public static void create(String fileName, String content) throws IOException {
+        Path path = getPath(fileName);
+        if (!HadoopUtils.hdfs().exists(path)) {
+            throw new IllegalArgumentException("[StreamPark] HdfsUtils.create " + fileName + " is exists!! ");
+        }
+        FSDataOutputStream outputStream = HadoopUtils.hdfs().create(path);
+        outputStream.writeUTF(content);
+        outputStream.flush();
+        outputStream.close();
+    }
+
+    public static boolean exists(String path) throws IOException {
+        return HadoopUtils.hdfs().exists(getPath(path));
+    }
+
+    public static String read(String fileName) throws IOException {
+        Path path = getPath(fileName);
+        if (!HadoopUtils.hdfs().exists(path) || HadoopUtils.hdfs().isDirectory(path)) {
+            throw new IllegalArgumentException(
+                "[StreamPark] HdfsUtils.read: path(" + fileName + ") not exists or isDirectory ");
+        }
+        FSDataInputStream in = HadoopUtils.hdfs().open(path);
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        IOUtils.copyBytes(in, out, 4096, false);
+        out.flush();
+        IOUtils.closeStream(in);
+        IOUtils.closeStream(out);
+        return new String(out.toByteArray());
+    }
+
+    public static void delete(String src) throws IOException {
+        Path path = getPath(src);
+        if (HadoopUtils.hdfs().exists(path)) {
+            HadoopUtils.hdfs().delete(path, true);
+        } else {
+            LOG.warn("[StreamPark] HDFS delete {}, but file {} is not exists!", src, src);
+        }
+    }
+
+    public static String fileMd5(String fileName) throws IOException {
+        Path path = getPath(fileName);
+        FSDataInputStream in = HadoopUtils.hdfs().open(path);
+        try {
+            // MD5 is used for non-cryptographic file integrity checks only.
+            @SuppressWarnings("java:S4790")
+            String digest = DigestUtils.md5Hex(in);
+            return digest;
+        } finally {
+            in.close();
+        }
+    }
+
+    public static void downToLocal(String hdfsPath, String localPath) throws IOException {
+        Path path = getPath(hdfsPath);
+        FSDataInputStream input = HadoopUtils.hdfs().open(path);
+        String content = input.readUTF();
+        FileWriter fw = new FileWriter(localPath);
+        fw.write(content);
+        fw.close();
+        input.close();
+    }
+
+    private static Path getPath(String hdfsPath) {
+        return new Path(hdfsPath);
+    }
+
+    public static InetSocketAddress getAddressOfActive(FileSystem fs) throws IOException {
+        if (!(fs instanceof DistributedFileSystem)) {
+            throw new IllegalArgumentException("FileSystem " + fs + " is not a DFS.");
+        }
+        fs.exists(new Path("/"));
+        DistributedFileSystem dfs = (DistributedFileSystem) fs;
+        return RPC.getServerAddress(dfs.getClient().getNamenode());
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/HostsUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/HostsUtils.java
new file mode 100644
index 000000000..1d6bf1d58
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/HostsUtils.java
@@ -0,0 +1,73 @@
+/*
+ * 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.streampark.common.util;
+
+import io.netty.resolver.HostsFileParser;
+
+import java.net.InetAddress;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public final class HostsUtils {
+
+    private HostsUtils() {
+    }
+
+    public static Map<String, String> getSortSystemHosts() {
+        Map<String, String> ipMap = new HashMap<>();
+        try {
+            HostsFileParser.parseSilently()
+                .inet4Entries()
+                .forEach((hostname, addr) -> ipMap.put(hostname, addr.getHostAddress()));
+        } catch (Exception ignored) {
+            // ignore
+        }
+        return ipMap.entrySet().stream()
+            .sorted((a, b) -> Integer.compare(b.getKey().length(), a.getKey().length()))
+            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> a, LinkedHashMap::new));
+    }
+
+    public static Map<String, String> getSystemHosts() {
+        return getSystemHosts(false);
+    }
+    public static Map<String, String> getSystemHosts(boolean excludeLocalHost) {
+        Map<String, String> map = new HashMap<>();
+        try {
+            HostsFileParser.parseSilently()
+                .inet4Entries()
+                .forEach((hostname, addr) -> map.put(hostname, addr.getHostAddress()));
+        } catch (Exception ignored) {
+            // ignore
+        }
+        if (excludeLocalHost) {
+            try {
+                String localHostName = InetAddress.getLocalHost().getHostName();
+                map.entrySet().removeIf(e -> "localhost".equals(e.getKey()) || localHostName.equals(e.getKey())
+                    || "127.0.0.1".equals(e.getValue()));
+            } catch (Exception ignored) {
+            }
+        }
+        return map;
+    }
+
+    public static Map<String, String> getSystemHostsAsJava(boolean excludeLocalHost) {
+        return getSystemHosts(excludeLocalHost);
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/HttpClientUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/HttpClientUtils.java
new file mode 100644
index 000000000..a66f8ec04
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/HttpClientUtils.java
@@ -0,0 +1,201 @@
+/*
+ * 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.streampark.common.util;
+
+import org.apache.hc.client5.http.auth.AuthSchemeFactory;
+import org.apache.hc.client5.http.auth.AuthScope;
+import org.apache.hc.client5.http.auth.Credentials;
+import org.apache.hc.client5.http.auth.StandardAuthScheme;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
+import org.apache.hc.client5.http.config.RequestConfig;
+import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
+import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
+import org.apache.hc.client5.http.impl.auth.SPNegoSchemeFactory;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
+import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.http.config.RegistryBuilder;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.http.message.BasicNameValuePair;
+import org.apache.hc.core5.net.URIBuilder;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/** Apache HttpClient 5 utility methods. */
+public final class HttpClientUtils {
+
+    private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
+
+    private static final PoolingHttpClientConnectionManager CONNECTION_MANAGER;
+
+    static {
+        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
+        connectionManager.setMaxTotal(50);
+        connectionManager.setDefaultMaxPerRoute(5);
+        CONNECTION_MANAGER = connectionManager;
+    }
+
+    private HttpClientUtils() {
+    }
+
+    private static CloseableHttpClient getHttpClient() {
+        return HttpClients.custom().setConnectionManager(CONNECTION_MANAGER).build();
+    }
+
+    private static HttpGet getHttpGet(String url, Map<String, Object> params, RequestConfig config) {
+        HttpGet httpGet;
+        if (params == null) {
+            httpGet = new HttpGet(url);
+        } else {
+            URIBuilder uriBuilder = new URIBuilder();
+            uriBuilder.setPath(url);
+            uriBuilder.setParameters(paramsToNameValuePairs(params));
+            try {
+                httpGet = new HttpGet(uriBuilder.build());
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+        if (config != null) {
+            httpGet.setConfig(config);
+        }
+        return httpGet;
+    }
+
+    public static String httpGetRequest(String url, RequestConfig config) {
+        return getHttpResult(getHttpGet(url, null, config));
+    }
+
+    public static String httpGetRequest(
+                                        String url, RequestConfig config, Map<String, Object> params) {
+        return getHttpResult(getHttpGet(url, params, config));
+    }
+
+    public static String httpGetRequest(
+                                        String url,
+                                        RequestConfig config,
+                                        Map<String, Object> headers,
+                                        Map<String, Object> params) {
+        HttpGet httpGet = getHttpGet(url, params, config);
+        if (headers != null) {
+            for (Map.Entry<String, Object> entry : headers.entrySet()) {
+                httpGet.addHeader(entry.getKey(), String.valueOf(entry.getValue()));
+            }
+        }
+        return getHttpResult(httpGet);
+    }
+
+    public static String httpPostRequest(String url) {
+        HttpPost httpPost = new HttpPost(url);
+        return getHttpResult(httpPost);
+    }
+
+    public static String httpPostRequest(String url, Map<String, Object> params) {
+        HttpPost httpPost = new HttpPost(url);
+        httpPost.setEntity(new UrlEncodedFormEntity(paramsToNameValuePairs(params), DEFAULT_CHARSET));
+        return getHttpResult(httpPost);
+    }
+
+    public static String httpPostRequest(
+                                         String url, Map<String, Object> params, Map<String, Object> headers) {
+        return httpRequest(new HttpPost(url), headers, params);
+    }
+
+    private static String httpRequest(
+                                      HttpUriRequestBase httpUri, Map<String, Object> headers,
+                                      Map<String, Object> params) {
+        if (headers != null) {
+            for (Map.Entry<String, Object> entry : headers.entrySet()) {
+                httpUri.addHeader(entry.getKey(), String.valueOf(entry.getValue()));
+            }
+        }
+        httpUri.setEntity(new UrlEncodedFormEntity(paramsToNameValuePairs(params), DEFAULT_CHARSET));
+        return getHttpResult(httpUri);
+    }
+
+    private static List<NameValuePair> paramsToNameValuePairs(Map<String, Object> params) {
+        if (params == null) {
+            return Collections.emptyList();
+        }
+        List<NameValuePair> pairs = new ArrayList<>();
+        for (Map.Entry<String, Object> entry : params.entrySet()) {
+            pairs.add(new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue())));
+        }
+        return pairs;
+    }
+
+    public static String httpAuthGetRequest(String url, RequestConfig config) {
+        BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
+        Credentials credentials =
+            new Credentials() {
+
+                @Override
+                public char[] getPassword() {
+                    return null;
+                }
+
+                @Override
+                public Principal getUserPrincipal() {
+                    return null;
+                }
+            };
+        credentialsProvider.setCredentials(new AuthScope(null, -1), credentials);
+
+        RegistryBuilder<AuthSchemeFactory> authSchemeRegistry =
+            RegistryBuilder.<AuthSchemeFactory>create()
+                .register(StandardAuthScheme.SPNEGO, SPNegoSchemeFactory.DEFAULT);
+
+        CloseableHttpClient httpAuthClient =
+            HttpClientBuilder.create()
+                .setDefaultAuthSchemeRegistry(authSchemeRegistry.build())
+                .setDefaultCredentialsProvider(credentialsProvider)
+                .setConnectionManager(CONNECTION_MANAGER)
+                .build();
+
+        return getHttpResult(getHttpGet(url, null, config), httpAuthClient);
+    }
+
+    private static String getHttpResult(HttpUriRequestBase request) {
+        return getHttpResult(request, getHttpClient());
+    }
+
+    private static String getHttpResult(HttpUriRequestBase request, CloseableHttpClient httpClient) {
+        try {
+            return httpClient.execute(
+                request,
+                response -> {
+                    if (response.getEntity() != null) {
+                        return EntityUtils.toString(response.getEntity());
+                    }
+                    return null;
+                });
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/JdbcUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/JdbcUtils.java
new file mode 100644
index 000000000..ea02881af
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/JdbcUtils.java
@@ -0,0 +1,332 @@
+/*
+ * 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.streampark.common.util;
+
+import org.apache.streampark.common.conf.ConfigKeys;
+
+import org.apache.streampark.shaded.org.slf4j.Logger;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.Consumer;
+
+/**
+ * Based on the hikari connection pool implementation. Support multiple data sources, note that all
+ * modifications and additions are automatically committed transactions.
+ */
+public final class JdbcUtils {
+
+    private static final Logger LOG =
+        StreamParkLoggerFactory.loggerFactory().getLogger(JdbcUtils.class.getName());
+
+    private static final ConcurrentHashMap<String, ReentrantLock> LOCK_MAP = new ConcurrentHashMap<>();
+
+    private static final ConcurrentHashMap<String, HikariDataSource> DATA_SOURCE_HOLDER =
+        new ConcurrentHashMap<>();
+
+    private JdbcUtils() {
+    }
+
+    public static List<Map<String, Object>> select(String sql, Properties jdbcConfig) {
+        return select(sql, null, jdbcConfig);
+    }
+
+    public static List<Map<String, Object>> select(
+                                                   String sql, Consumer<ResultSet> func, Properties jdbcConfig) {
+        if (sql == null || sql.isEmpty()) {
+            return Collections.emptyList();
+        }
+        Connection conn = getConnection(jdbcConfig);
+        Statement stmt = null;
+        ResultSet result = null;
+        try {
+            stmt = createStatement(conn);
+            result = stmt.executeQuery(sql);
+            if (func != null) {
+                func.accept(result);
+            }
+            int count = result.getMetaData().getColumnCount();
+            List<Map<String, Object>> array = new ArrayList<>();
+            while (result.next()) {
+                Map<String, Object> map = new HashMap<>();
+                for (int x = 1; x <= count; x++) {
+                    String key = result.getMetaData().getColumnLabel(x);
+                    Object value = result.getObject(x);
+                    map.put(key, value);
+                }
+                array.add(map);
+            }
+            return array.isEmpty() ? Collections.emptyList() : array;
+        } catch (Exception ex) {
+            LOG.warn("Failed to execute JDBC select", ex);
+            return Collections.emptyList();
+        } finally {
+            close(result, stmt, conn);
+        }
+    }
+
+    public static long count(String sql, Properties jdbcConfig) {
+        Map<String, Object> row = unique(sql, jdbcConfig);
+        if (row.isEmpty()) {
+            return 0L;
+        }
+        return Long.parseLong(row.values().iterator().next().toString());
+    }
+
+    public static long count(Connection conn, String sql) {
+        Map<String, Object> row = unique(conn, sql);
+        if (row.isEmpty()) {
+            return 0L;
+        }
+        return Long.parseLong(row.values().iterator().next().toString());
+    }
+
+    public static int batch(Iterable<String> sql, Properties jdbcConfig) {
+        int size = 0;
+        for (String ignored : sql) {
+            size++;
+        }
+        if (size == 0) {
+            return 0;
+        }
+        if (size == 1) {
+            return update(sql.iterator().next(), jdbcConfig);
+        }
+        Connection conn = getConnection(jdbcConfig);
+        try {
+            Statement prepStat = conn.createStatement();
+            try {
+                int index = 0;
+                int batchSize = 1000;
+                int total = 0;
+                for (String x : sql) {
+                    prepStat.addBatch(x);
+                    index++;
+                    if (index > 0 && index % batchSize == 0) {
+                        int count = 0;
+                        for (int c : prepStat.executeBatch()) {
+                            count += c;
+                        }
+                        conn.commit();
+                        prepStat.clearBatch();
+                        total += count;
+                    }
+                }
+                for (int c : prepStat.executeBatch()) {
+                    total += c;
+                }
+                return total;
+            } catch (Exception ex) {
+                LOG.warn("Failed to execute JDBC select", ex);
+                return 0;
+            } finally {
+                conn.commit();
+                close(conn);
+            }
+        } catch (Exception ex) {
+            LOG.warn("Failed to execute JDBC select", ex);
+            return 0;
+        }
+    }
+
+    public static int update(String sql, Properties jdbcConfig) {
+        return update(getConnection(jdbcConfig), sql);
+    }
+
+    public static int update(Connection conn, String sql) {
+        Statement statement = null;
+        try {
+            statement = conn.createStatement();
+            return statement.executeUpdate(sql);
+        } catch (Exception ex) {
+            LOG.warn("Failed to execute JDBC select", ex);
+            return -1;
+        } finally {
+            close(statement, conn);
+        }
+    }
+
+    public static Map<String, Object> unique(String sql, Properties jdbcConfig) {
+        return unique(getConnection(jdbcConfig), sql);
+    }
+
+    public static Map<String, Object> unique(Connection conn, String sql) {
+        Statement stmt = null;
+        ResultSet result = null;
+        try {
+            stmt = createStatement(conn);
+            result = stmt.executeQuery(sql);
+            int count = result.getMetaData().getColumnCount();
+            if (!result.next()) {
+                return Collections.emptyMap();
+            }
+            Map<String, Object> map = new HashMap<>();
+            for (int x = 1; x <= count; x++) {
+                String key = result.getMetaData().getColumnLabel(x);
+                Object value = result.getObject(x);
+                map.put(key, value);
+            }
+            return map;
+        } catch (Exception ex) {
+            LOG.warn("Failed to execute JDBC select", ex);
+            return Collections.emptyMap();
+        } finally {
+            close(result, stmt, conn);
+        }
+    }
+
+    public static boolean execute(String sql, Properties jdbcConfig) {
+        return execute(getConnection(jdbcConfig), sql);
+    }
+
+    public static boolean execute(Connection conn, String sql) {
+        Statement stmt = null;
+        try {
+            stmt = conn.createStatement();
+            return stmt.execute(sql);
+        } catch (Exception ex) {
+            LOG.warn("Failed to execute JDBC select", ex);
+            return false;
+        } finally {
+            close(stmt, conn);
+        }
+    }
+
+    public static Connection getConnection(Properties prop) {
+        String alias = prop.getProperty(ConfigKeys.KEY_ALIAS());
+        ReentrantLock lock = LOCK_MAP.computeIfAbsent(alias, k -> new ReentrantLock());
+        lock.lock();
+        try {
+            HikariDataSource ds = DATA_SOURCE_HOLDER.get(alias);
+            if (ds == null) {
+                HikariConfig jdbcConfig = new HikariConfig();
+                for (String key : prop.stringPropertyNames()) {
+                    if (ConfigKeys.KEY_ALIAS().equals(key) || ConfigKeys.KEY_SEMANTIC().equals(key)) {
+                        continue;
+                    }
+                    String value = prop.getProperty(key);
+                    try {
+                        Field field = jdbcConfig.getClass().getDeclaredField(key);
+                        field.setAccessible(true);
+                        setFieldValue(field, jdbcConfig, value);
+                    } catch (NoSuchFieldException e) {
+                        String setMethod =
+                            "set" + key.substring(0, 1).toUpperCase() + key.substring(1);
+                        Method method = null;
+                        for (Method m : jdbcConfig.getClass().getMethods()) {
+                            if (m.getName().equals(setMethod) && m.getParameterCount() == 1) {
+                                method = m;
+                                break;
+                            }
+                        }
+                        if (method != null) {
+                            method.setAccessible(true);
+                            invokeSetter(method, jdbcConfig, value);
+                        } else {
+                            throw new IllegalArgumentException(
+                                "jdbcConfig error,property:"
+                                    + key
+                                    + " invalid,please see more properties jdbcConfig https://github.com/brettwooldridge/HikariCP");
+                        }
+                    }
+                }
+                ds = new HikariDataSource(jdbcConfig);
+                DATA_SOURCE_HOLDER.put(alias, ds);
+            }
+            return ds.getConnection();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    private static void setFieldValue(Field field, HikariConfig jdbcConfig,
+                                      String value) throws IllegalAccessException {
+        String type = field.getType().getSimpleName();
+        switch (type) {
+            case "String":
+                field.set(jdbcConfig, value);
+                break;
+            case "int":
+                field.setInt(jdbcConfig, Integer.parseInt(value));
+                break;
+            case "long":
+                field.setLong(jdbcConfig, Long.parseLong(value));
+                break;
+            case "boolean":
+                field.setBoolean(jdbcConfig, Boolean.parseBoolean(value));
+                break;
+            default:
+                break;
+        }
+    }
+
+    private static void invokeSetter(Method method, HikariConfig jdbcConfig, String value) throws Exception {
+        String type = method.getParameterTypes()[0].getSimpleName();
+        switch (type) {
+            case "String":
+                method.invoke(jdbcConfig, value);
+                break;
+            case "int":
+            case "Integer":
+                method.invoke(jdbcConfig, Integer.parseInt(value));
+                break;
+            case "long":
+            case "Long":
+                method.invoke(jdbcConfig, Long.parseLong(value));
+                break;
+            case "boolean":
+            case "Boolean":
+                method.invoke(jdbcConfig, Boolean.parseBoolean(value));
+                break;
+            default:
+                break;
+        }
+    }
+
+    private static Statement createStatement(Connection conn) throws Exception {
+        return conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
+    }
+
+    public static void close(AutoCloseable... closeable) {
+        for (AutoCloseable item : closeable) {
+            if (item != null) {
+                try {
+                    item.close();
+                } catch (Exception ignored) {
+                    // ignore
+                }
+            }
+        }
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/JsonUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/JsonUtils.java
new file mode 100644
index 000000000..312165c1e
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/JsonUtils.java
@@ -0,0 +1,55 @@
+/*
+ * 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.streampark.common.util;
+
+import org.apache.streampark.shaded.com.fasterxml.jackson.annotation.JsonInclude;
+import org.apache.streampark.shaded.com.fasterxml.jackson.databind.DeserializationFeature;
+import org.apache.streampark.shaded.com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.streampark.shaded.com.fasterxml.jackson.databind.SerializationFeature;
+
+import java.io.Serializable;
+import java.text.SimpleDateFormat;
+
+public final class JsonUtils implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private static final ObjectMapper MAPPER = new ObjectMapper();
+
+    static {
+        MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+        MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
+        MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
+        MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+        MAPPER.setDateFormat(new SimpleDateFormat(DateUtils.FULL_FORMAT));
+    }
+
+    private JsonUtils() {
+    }
+
+    public static <T> T read(Object obj, Class<T> clazz) throws Exception {
+        if (obj instanceof String) {
+            return MAPPER.readValue((String) obj, clazz);
+        }
+        return MAPPER.readValue(write(obj), clazz);
+    }
+
+    public static String write(Object obj) throws Exception {
+        return MAPPER.writeValueAsString(obj);
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/LoggerSupport.java b/streampark-common/src/main/java/org/apache/streampark/common/util/LoggerSupport.java
new file mode 100644
index 000000000..6421ae1ee
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/LoggerSupport.java
@@ -0,0 +1,109 @@
+/*
+ * 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.streampark.common.util;
+
+/**
+ * Java logging support using {@link StreamParkLoggerFactory}. Scala modules mix in {@code Logger}
+ * from streampark-common-scala-bridge instead.
+ */
+public abstract class LoggerSupport {
+
+    private static final String PREFIX = "[StreamPark]";
+
+    private org.apache.streampark.shaded.org.slf4j.Logger slf4jLogger;
+
+    protected String logName() {
+        String name = getClass().getName();
+        if (name.endsWith("$")) {
+            return name.substring(0, name.length() - 1);
+        }
+        return name;
+    }
+
+    protected org.apache.streampark.shaded.org.slf4j.Logger logger() {
+        if (slf4jLogger == null) {
+            slf4jLogger =
+                StreamParkLoggerFactory.loggerFactory().getLogger(logName());
+        }
+        return slf4jLogger;
+    }
+
+    protected void logInfo(String msg) {
+        if (logger().isInfoEnabled()) {
+            logger().info(PREFIX + " " + msg);
+        }
+    }
+
+    protected void logInfo(String msg, Throwable throwable) {
+        if (logger().isInfoEnabled()) {
+            logger().info(PREFIX + " " + msg, throwable);
+        }
+    }
+
+    protected void logDebug(String msg) {
+        if (logger().isDebugEnabled()) {
+            logger().debug(PREFIX + " " + msg);
+        }
+    }
+
+    protected void logDebug(String msg, Throwable throwable) {
+        if (logger().isDebugEnabled()) {
+            logger().debug(PREFIX + " " + msg, throwable);
+        }
+    }
+
+    public void logTrace(String msg) {
+        if (logger().isTraceEnabled()) {
+            logger().trace(PREFIX + " " + msg);
+        }
+    }
+
+    protected void logTrace(String msg, Throwable throwable) {
+        if (logger().isTraceEnabled()) {
+            logger().trace(PREFIX + " " + msg, throwable);
+        }
+    }
+
+    public void logWarn(String msg) {
+        if (logger().isWarnEnabled()) {
+            logger().warn(PREFIX + " " + msg);
+        }
+    }
+
+    protected void logWarn(String msg, Throwable throwable) {
+        if (logger().isWarnEnabled()) {
+            logger().warn(PREFIX + " " + msg, throwable);
+        }
+    }
+
+    protected void logError(String msg) {
+        if (logger().isErrorEnabled()) {
+            logger().error(PREFIX + " " + msg);
+        }
+    }
+
+    protected void logError(String msg, Throwable throwable) {
+        if (logger().isErrorEnabled()) {
+            logger().error(PREFIX + " " + msg, throwable);
+        }
+    }
+
+    protected boolean isTraceEnabled() {
+        return logger().isTraceEnabled();
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/MongoConfig.java b/streampark-common/src/main/java/org/apache/streampark/common/util/MongoConfig.java
new file mode 100644
index 000000000..374815cd9
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/MongoConfig.java
@@ -0,0 +1,189 @@
+/*
+ * 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.streampark.common.util;
+
+import org.apache.streampark.common.conf.ConfigKeys;
+
+import com.mongodb.MongoClient;
+import com.mongodb.MongoClientOptions;
+import com.mongodb.MongoClientURI;
+import com.mongodb.MongoCredential;
+import com.mongodb.ServerAddress;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.regex.Pattern;
+
+/** MongoDB client configuration helper. */
+public final class MongoConfig {
+
+    private static final Pattern LEADING_DOT_PATTERN = Pattern.compile("^\\.");
+
+    public static final String CLIENT_URI = "client-uri";
+    public static final String ADDRESS = "address";
+    public static final String REPLICA_SET = "replica-set";
+    public static final String DATABASE = "database";
+    public static final String USERNAME = "username";
+    public static final String PASSWORD = "password";
+    public static final String MIN_CONNECTIONS_PER_HOST = "min-connections-per-host";
+    public static final String MAX_CONNECTIONS_PER_HOST = "max-connections-per-host";
+    public static final String THREADS_ALLOWED_TO_BLOCK_FOR_CONNECTION_MULTIPLIER =
+        "threads-allowed-to-block-for-connection-multiplier";
+    public static final String SERVER_SELECTION_TIMEOUT = "server-selection-timeout";
+    public static final String MAX_WAIT_TIME = "max-wait-time";
+    public static final String MAX_CONNECTION_IDEL_TIME = "max-connection-idel-time";
+    public static final String MAX_CONNECTION_LIFE_TIME = "max-connection-life-time";
+    public static final String CONNECT_TIMEOUT = "connect-timeout";
+    public static final String SOCKET_TIMEOUT = "socket-timeout";
+    public static final String SOCKET_KEEP_ALIVE = "socket-keep-alive";
+    public static final String SSL_ENABLED = "ssl-enabled";
+    public static final String SSL_INVALID_HOST_NAME_ALLOWED = "ssl-invalid-host-name-allowed";
+    public static final String ALWAYS_USE_M_BEANS = "always-use-m-beans";
+    public static final String HEARTBEAT_SOCKET_TIMEOUT = "heartbeat-socket-timeout";
+    public static final String HEARTBEAT_CONNECT_TIMEOUT = "heartbeat-connect-timeout";
+    public static final String MIN_HEARTBEAT_FREQUENCY = "min-heartbeat-frequency";
+    public static final String HEARTBEAT_FREQUENCY = "heartbeat-frequency";
+    public static final String LOCAL_THRESHOLD = "local-threshold";
+    public static final String AUTHENTICATION_DATABASE = "authentication-database";
+
+    private MongoConfig() {
+    }
+
+    public static String getProperty(Properties properties, String k) {
+        return getProperty(properties, k, "");
+    }
+
+    public static String getProperty(Properties properties, String k, String alias) {
+        return getProperties(properties, alias).getProperty(k);
+    }
+
+    public static Properties getProperties(Properties properties) {
+        return getProperties(properties, "");
+    }
+
+    public static Properties getProperties(Properties properties, String alias) {
+        Properties prop = new Properties();
+        for (String key : properties.stringPropertyNames()) {
+            if (!key.startsWith(ConfigKeys.MONGO_PREFIX())) {
+                continue;
+            }
+            String value = properties.getProperty(key);
+            if (value == null || value.isEmpty()) {
+                continue;
+            }
+            String stripped = key.replace(ConfigKeys.MONGO_PREFIX() + alias, "");
+            String k = LEADING_DOT_PATTERN.matcher(stripped).replaceFirst("");
+            prop.put(k, value.trim());
+        }
+        return prop;
+    }
+
+    public static MongoClient getClient(Properties properties) {
+        return getClient(properties, "");
+    }
+
+    public static MongoClient getClient(Properties properties, String alias) {
+        Properties mongoParam = getProperties(properties, alias);
+        if (mongoParam.containsKey(CLIENT_URI)) {
+            MongoClientURI clientURI = new MongoClientURI(mongoParam.getProperty(CLIENT_URI));
+            return new MongoClient(clientURI);
+        }
+        MongoClientOptions.Builder builder = new MongoClientOptions.Builder();
+        if (mongoParam.containsKey(MAX_CONNECTIONS_PER_HOST)) {
+            builder.connectionsPerHost(Integer.parseInt(mongoParam.getProperty(MAX_CONNECTIONS_PER_HOST)));
+        }
+        if (mongoParam.containsKey(MIN_CONNECTIONS_PER_HOST)) {
+            builder.minConnectionsPerHost(Integer.parseInt(mongoParam.getProperty(MIN_CONNECTIONS_PER_HOST)));
+        }
+        if (mongoParam.containsKey(REPLICA_SET)) {
+            builder.requiredReplicaSetName(mongoParam.getProperty(REPLICA_SET));
+        }
+        if (mongoParam.containsKey(THREADS_ALLOWED_TO_BLOCK_FOR_CONNECTION_MULTIPLIER)) {
+            builder.threadsAllowedToBlockForConnectionMultiplier(
+                Integer.parseInt(mongoParam.getProperty(THREADS_ALLOWED_TO_BLOCK_FOR_CONNECTION_MULTIPLIER)));
+        }
+        if (mongoParam.containsKey(SERVER_SELECTION_TIMEOUT)) {
+            builder.serverSelectionTimeout(Integer.parseInt(mongoParam.getProperty(SERVER_SELECTION_TIMEOUT)));
+        }
+        if (mongoParam.containsKey(MAX_WAIT_TIME)) {
+            builder.maxWaitTime(Integer.parseInt(mongoParam.getProperty(MAX_WAIT_TIME)));
+        }
+        if (mongoParam.containsKey(MAX_CONNECTION_IDEL_TIME)) {
+            builder.maxConnectionIdleTime(Integer.parseInt(mongoParam.getProperty(MAX_CONNECTION_IDEL_TIME)));
+        }
+        if (mongoParam.containsKey(MAX_CONNECTION_LIFE_TIME)) {
+            builder.maxConnectionLifeTime(Integer.parseInt(mongoParam.getProperty(MAX_CONNECTION_LIFE_TIME)));
+        }
+        if (mongoParam.containsKey(CONNECT_TIMEOUT)) {
+            builder.connectTimeout(Integer.parseInt(mongoParam.getProperty(CONNECT_TIMEOUT)));
+        }
+        if (mongoParam.containsKey(SOCKET_TIMEOUT)) {
+            builder.socketTimeout(Integer.parseInt(mongoParam.getProperty(SOCKET_TIMEOUT)));
+        }
+        if (mongoParam.containsKey(SSL_ENABLED)) {
+            builder.sslEnabled(Boolean.parseBoolean(mongoParam.getProperty(SSL_ENABLED)));
+        }
+        if (mongoParam.containsKey(SSL_INVALID_HOST_NAME_ALLOWED)) {
+            builder.sslInvalidHostNameAllowed(
+                Boolean.parseBoolean(mongoParam.getProperty(SSL_INVALID_HOST_NAME_ALLOWED)));
+        }
+        if (mongoParam.containsKey(ALWAYS_USE_M_BEANS)) {
+            builder.alwaysUseMBeans(Boolean.parseBoolean(mongoParam.getProperty(ALWAYS_USE_M_BEANS)));
+        }
+        if (mongoParam.containsKey(HEARTBEAT_FREQUENCY)) {
+            builder.heartbeatFrequency(Integer.parseInt(mongoParam.getProperty(HEARTBEAT_FREQUENCY)));
+        }
+        if (mongoParam.containsKey(MIN_HEARTBEAT_FREQUENCY)) {
+            builder.minHeartbeatFrequency(Integer.parseInt(mongoParam.getProperty(MIN_HEARTBEAT_FREQUENCY)));
+        }
+        if (mongoParam.containsKey(HEARTBEAT_CONNECT_TIMEOUT)) {
+            builder.heartbeatConnectTimeout(Integer.parseInt(mongoParam.getProperty(HEARTBEAT_CONNECT_TIMEOUT)));
+        }
+        if (mongoParam.containsKey(HEARTBEAT_SOCKET_TIMEOUT)) {
+            builder.heartbeatSocketTimeout(Integer.parseInt(mongoParam.getProperty(HEARTBEAT_SOCKET_TIMEOUT)));
+        }
+        if (mongoParam.containsKey(LOCAL_THRESHOLD)) {
+            builder.localThreshold(Integer.parseInt(mongoParam.getProperty(LOCAL_THRESHOLD)));
+        }
+        MongoClientOptions mongoClientOptions = builder.build();
+        String[] addresses = mongoParam.getProperty(ADDRESS).split(",");
+        List<ServerAddress> serverAddresses = new ArrayList<>();
+        for (String x : addresses) {
+            String[] hostAndPort = x.split(":");
+            String host = hostAndPort[0];
+            int port = Integer.parseInt(hostAndPort[1]);
+            serverAddresses.add(new ServerAddress(host, port));
+        }
+        if (mongoParam.containsKey(USERNAME)) {
+            String db;
+            if (mongoParam.containsKey(AUTHENTICATION_DATABASE)) {
+                db = mongoParam.getProperty(AUTHENTICATION_DATABASE);
+            } else {
+                db = mongoParam.getProperty(DATABASE);
+            }
+            MongoCredential mongoCredential =
+                MongoCredential.createScramSha1Credential(
+                    mongoParam.getProperty(USERNAME),
+                    db,
+                    mongoParam.getProperty(PASSWORD).toCharArray());
+            return new MongoClient(serverAddresses, mongoCredential, mongoClientOptions);
+        }
+        return new MongoClient(serverAddresses, mongoClientOptions);
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/PropertiesUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/PropertiesUtils.java
new file mode 100644
index 000000000..01410bd3c
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/PropertiesUtils.java
@@ -0,0 +1,246 @@
+/*
+ * 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.streampark.common.util;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.typesafe.config.ConfigFactory;
+import org.yaml.snakeyaml.Yaml;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Scanner;
+import java.util.regex.Pattern;
+
+/** Properties, YAML and HOCON configuration parsing utilities. */
+public final class PropertiesUtils {
+
+    private static final Pattern HOCON_KEY_QUOTE_PATTERN = Pattern.compile("\"");
+
+    private PropertiesUtils() {
+    }
+
+    public static String readFile(String filename) throws IOException {
+        Path path = SafePathUtils.resolveConfigPath(filename);
+        if (!Files.exists(path)) {
+            throw new IllegalArgumentException("[StreamPark] readFile: file " + path + " does not exist");
+        }
+        if (!Files.isRegularFile(path)) {
+            throw new IllegalArgumentException("[StreamPark] readFile: file " + path + " is not a normal file");
+        }
+        StringBuilder buffer = new StringBuilder();
+        try (Scanner scanner = new Scanner(path)) {
+            while (scanner.hasNextLine()) {
+                buffer.append(scanner.nextLine()).append("\r\n");
+            }
+        }
+        return buffer.toString();
+    }
+
+    public static LinkedHashMap<String, String> fromYamlText(String text) {
+        try {
+            Map<String, Object> map = new Yaml().load(text);
+            return flatten(map);
+        } catch (RuntimeException e) {
+            throw new IllegalArgumentException("Failed when loading conf error:", e);
+        }
+    }
+
+    public static LinkedHashMap<String, String> fromHoconText(String conf) {
+        if (conf == null) {
+            throw new IllegalArgumentException("[StreamPark] fromHoconText: Hocon content must not be null");
+        }
+        return parseHoconByReader(new StringReader(conf));
+    }
+
+    public static LinkedHashMap<String, String> fromPropertiesText(String conf) {
+        try {
+            Properties properties = new Properties();
+            properties.load(new StringReader(conf));
+            LinkedHashMap<String, String> result = new LinkedHashMap<>();
+            for (String key : properties.stringPropertyNames()) {
+                result.put(key, properties.getProperty(key).trim());
+            }
+            return result;
+        } catch (IOException e) {
+            throw new IllegalArgumentException("Failed when loading properties ", e);
+        }
+    }
+
+    /** Load Yaml present in the given file. */
+    public static LinkedHashMap<String, String> fromYamlFile(String filename) {
+        try (InputStream inputStream = SafePathUtils.openConfigFile(filename)) {
+            return fromYamlFile(inputStream);
+        } catch (IOException e) {
+            throw new IllegalArgumentException("Failed when loading yaml from file " + filename, e);
+        }
+    }
+
+    public static LinkedHashMap<String, String> fromHoconFile(String filename) {
+        try (InputStream inputStream = SafePathUtils.openConfigFile(filename)) {
+            return fromHoconFile(inputStream);
+        } catch (IOException e) {
+            throw new IllegalArgumentException("Failed when loading Hocon from file " + filename, e);
+        }
+    }
+
+    /** Load properties present in the given file. */
+    public static LinkedHashMap<String, String> fromPropertiesFile(String filename) {
+        try (InputStream inputStream = SafePathUtils.openConfigFile(filename)) {
+            return fromPropertiesFile(inputStream);
+        } catch (IOException e) {
+            throw new IllegalArgumentException(
+                "[StreamPark] Failed when loading properties from file " + filename, e);
+        }
+    }
+
+    /** Load Yaml present in the given input stream. */
+    public static LinkedHashMap<String, String> fromYamlFile(InputStream inputStream) {
+        AssertUtils.required(
+            inputStream != null,
+            "[StreamPark] fromYamlFile: Properties inputStream  must not be null");
+        try {
+            Map<String, Object> map = new Yaml().load(inputStream);
+            return flatten(map);
+        } catch (RuntimeException e) {
+            throw new IllegalArgumentException("Failed when loading yaml from inputStream", e);
+        }
+    }
+
+    public static LinkedHashMap<String, String> fromHoconFile(InputStream inputStream) {
+        if (inputStream == null) {
+            throw new IllegalArgumentException("[StreamPark] fromHoconFile: Hocon inputStream  must not be null");
+        }
+        return parseHoconByReader(new InputStreamReader(inputStream));
+    }
+
+    private static LinkedHashMap<String, String> parseHoconByReader(Reader reader) {
+        try {
+            LinkedHashMap<String, String> result = new LinkedHashMap<>();
+            ConfigFactory.parseReader(reader)
+                .entrySet()
+                .forEach(
+                    entry -> {
+                        String key = HOCON_KEY_QUOTE_PATTERN.matcher(entry.getKey().trim()).replaceAll("");
+                        String value = entry.getValue().unwrapped().toString().trim();
+                        result.put(key, value);
+                    });
+            return result;
+        } catch (RuntimeException e) {
+            throw new IllegalArgumentException("Failed when loading Hocon ", e);
+        }
+    }
+
+    /** Load properties present in the given input stream. */
+    public static LinkedHashMap<String, String> fromPropertiesFile(InputStream inputStream) {
+        if (inputStream == null) {
+            throw new IllegalArgumentException(
+                "[StreamPark] fromPropertiesFile: Properties inputStream  must not be null");
+        }
+        try {
+            Properties properties = new Properties();
+            properties.load(inputStream);
+            LinkedHashMap<String, String> result = new LinkedHashMap<>();
+            for (String key : properties.stringPropertyNames()) {
+                result.put(key, properties.getProperty(key).trim());
+            }
+            return result;
+        } catch (IOException e) {
+            throw new IllegalArgumentException(
+                "[StreamPark] Failed when loading properties from inputStream", e);
+        }
+    }
+
+    public static Map<String, String> fromYamlTextAsJava(String text) {
+        return new HashMap<>(fromYamlText(text));
+    }
+
+    public static Map<String, String> fromHoconTextAsJava(String text) {
+        return new HashMap<>(fromHoconText(text));
+    }
+
+    public static Map<String, String> fromPropertiesTextAsJava(String text) {
+        return new HashMap<>(fromPropertiesText(text));
+    }
+
+    public static Map<String, String> fromYamlFileAsJava(String filename) {
+        return new HashMap<>(fromYamlFile(filename));
+    }
+
+    public static Map<String, String> fromHoconFileAsJava(String filename) {
+        return new HashMap<>(fromHoconFile(filename));
+    }
+
+    public static Map<String, String> fromPropertiesFileAsJava(String filename) {
+        return new HashMap<>(fromPropertiesFile(filename));
+    }
+
+    public static Map<String, String> fromYamlFileAsJava(InputStream inputStream) {
+        return new HashMap<>(fromYamlFile(inputStream));
+    }
+
+    public static Map<String, String> fromHoconFileAsJava(InputStream inputStream) {
+        return new HashMap<>(fromHoconFile(inputStream));
+    }
+
+    public static Map<String, String> fromPropertiesFileAsJava(InputStream inputStream) {
+        return new HashMap<>(fromPropertiesFile(inputStream));
+    }
+
+    private static LinkedHashMap<String, String> flatten(Map<String, Object> map) {
+        return flatten(map, "");
+    }
+
+    private static LinkedHashMap<String, String> flatten(Map<String, Object> map, String prefix) {
+        if (map == null || map.isEmpty()) {
+            return new LinkedHashMap<>();
+        }
+        LinkedHashMap<String, String> result = new LinkedHashMap<>();
+        for (Map.Entry<String, Object> entry : map.entrySet()) {
+            String key = entry.getKey();
+            Object value = entry.getValue();
+            if (value instanceof Map) {
+                @SuppressWarnings("unchecked")
+                Map<String, Object> nested = (Map<String, Object>) value;
+                result.putAll(flatten(nested, prefix + key + "."));
+            } else if (value instanceof String) {
+                if (StringUtils.isNotBlank((String) value)) {
+                    result.put(prefix + key, (String) value);
+                }
+            } else if (value instanceof Collection) {
+                Collection<?> collection = (Collection<?>) value;
+                if (!collection.isEmpty()) {
+                    result.put(prefix + key, collection.toString());
+                }
+            } else if (value != null) {
+                result.put(prefix + key, value.toString());
+            }
+        }
+        return result;
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/RedisClient.java b/streampark-common/src/main/java/org/apache/streampark/common/util/RedisClient.java
new file mode 100644
index 000000000..04fed6b43
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/RedisClient.java
@@ -0,0 +1,143 @@
+/*
+ * 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.streampark.common.util;
+
+import org.apache.streampark.common.constants.Constants;
+
+import org.apache.streampark.shaded.org.slf4j.Logger;
+
+import redis.clients.jedis.HostAndPort;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisCluster;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisPoolConfig;
+import redis.clients.jedis.exceptions.JedisConnectionException;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ThreadLocalRandom;
+
+/** Redis connection pool and cluster client factory. */
+public final class RedisClient {
+
+    private static final Logger LOG =
+        StreamParkLoggerFactory.loggerFactory().getLogger(RedisClient.class.getName());
+
+    private static final ConcurrentHashMap<RedisEndpoint, JedisPool> POOLS = new ConcurrentHashMap<>();
+
+    private static final ConcurrentHashMap<RedisEndpoint, JedisCluster> CLUSTERS = new ConcurrentHashMap<>();
+
+    private static final JedisPoolConfig POOL_CONFIG;
+
+    static {
+        JedisPoolConfig poolConfig = new JedisPoolConfig();
+        poolConfig.setMaxTotal(1000);
+        poolConfig.setMaxIdle(64);
+        poolConfig.setTestOnBorrow(true);
+        poolConfig.setTestOnReturn(false);
+        poolConfig.setTestWhileIdle(false);
+        poolConfig.setMinEvictableIdleTimeMillis(1800000);
+        poolConfig.setTimeBetweenEvictionRunsMillis(30000);
+        poolConfig.setNumTestsPerEvictionRun(-1);
+        POOL_CONFIG = poolConfig;
+    }
+
+    private RedisClient() {
+    }
+
+    public static Jedis connect(RedisEndpoint[] endpoints) {
+        if (endpoints.length == 0) {
+            throw new IllegalArgumentException("[StreamPark] The RedisEndpoint array is empty!!!");
+        }
+        int index = ThreadLocalRandom.current().nextInt(endpoints.length);
+        try {
+            return connect(endpoints[index]);
+        } catch (Exception e) {
+            LOG.error("[StreamPark] {}", e.getMessage());
+            RedisEndpoint[] remaining = new RedisEndpoint[endpoints.length - index - 1];
+            System.arraycopy(endpoints, index + 1, remaining, 0, remaining.length);
+            if (remaining.length == 0) {
+                throw e;
+            }
+            return connect(remaining);
+        }
+    }
+
+    public static Jedis connect(RedisEndpoint endpoint) {
+        JedisPool pool = POOLS.computeIfAbsent(endpoint, RedisClient::createJedisPool);
+        int sleepTime = 4;
+        Jedis conn = null;
+        while (conn == null) {
+            try {
+                conn = pool.getResource();
+            } catch (JedisConnectionException e) {
+                if (e.getCause() != null
+                    && e.getCause().toString().contains("ERR max number of clients reached")) {
+                    if (sleepTime < 500) {
+                        sleepTime *= 2;
+                    }
+                    try {
+                        Thread.sleep(sleepTime);
+                    } catch (InterruptedException ie) {
+                        Thread.currentThread().interrupt();
+                        throw new RuntimeException(ie);
+                    }
+                } else {
+                    throw e;
+                }
+            }
+        }
+        return conn;
+    }
+
+    public static JedisPool createJedisPool(RedisEndpoint endpoint) {
+        RedisEndpoint endpointEn = endpoint.withAuth(Constants.DEFAULT_DATAMASK_STRING);
+        LOG.info("[StreamPark] RedisClient: createJedisPool with {}", endpointEn);
+        return new JedisPool(
+            POOL_CONFIG,
+            endpoint.getHost(),
+            endpoint.getPort(),
+            endpoint.getTimeout(),
+            endpoint.getAuth(),
+            endpoint.getDb());
+    }
+
+    public static JedisCluster connectCluster(RedisEndpoint... endpoints) {
+        if (endpoints.length == 0) {
+            throw new IllegalArgumentException("[StreamPark] The RedisEndpoint array is empty!!!");
+        }
+        RedisEndpoint head = endpoints[0];
+        return CLUSTERS.computeIfAbsent(
+            head,
+            key -> {
+                Set<HostAndPort> hostPorts = new HashSet<>();
+                for (RedisEndpoint r : endpoints) {
+                    hostPorts.add(new HostAndPort(r.getHost(), r.getPort()));
+                }
+                return new JedisCluster(hostPorts, head.getTimeout(), 1000, 1, head.getAuth(), POOL_CONFIG);
+            });
+    }
+
+    public static void close() {
+        for (Map.Entry<RedisEndpoint, JedisPool> entry : POOLS.entrySet()) {
+            entry.getValue().close();
+        }
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/RedisEndpoint.java b/streampark-common/src/main/java/org/apache/streampark/common/util/RedisEndpoint.java
new file mode 100644
index 000000000..8bbcdddd6
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/RedisEndpoint.java
@@ -0,0 +1,377 @@
+/*
+ * 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.streampark.common.util;
+
+import org.apache.streampark.common.conf.ConfigKeys;
+
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.Protocol;
+import redis.clients.jedis.util.JedisClusterCRC16;
+import redis.clients.jedis.util.JedisURIHelper;
+import redis.clients.jedis.util.SafeEncoder;
+
+import java.io.Serializable;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * RedisEndpoint represents a redis connection endpoint info: host, port, auth password db number,
+ * and timeout.
+ */
+public class RedisEndpoint implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private final String host;
+    private final int port;
+    private final String auth;
+    private final int db;
+    private final int timeout;
+
+    public RedisEndpoint() {
+        this(Protocol.DEFAULT_HOST, Protocol.DEFAULT_PORT, null, Protocol.DEFAULT_DATABASE, Protocol.DEFAULT_TIMEOUT);
+    }
+
+    public RedisEndpoint(String host, int port, String auth, int db, int timeout) {
+        this.host = host;
+        this.port = port;
+        this.auth = auth;
+        this.db = db;
+        this.timeout = timeout;
+    }
+
+    public RedisEndpoint(Properties conf) {
+        this(
+            conf.getProperty(ConfigKeys.KEY_HOST(), Protocol.DEFAULT_HOST),
+            Integer.parseInt(
+                conf.getProperty(ConfigKeys.KEY_PORT(), String.valueOf(Protocol.DEFAULT_PORT))),
+            conf.getProperty(ConfigKeys.KEY_PASSWORD(), null),
+            Integer.parseInt(
+                conf.getProperty(ConfigKeys.KEY_DB(), String.valueOf(Protocol.DEFAULT_DATABASE))),
+            Integer.parseInt(
+                conf.getProperty(ConfigKeys.KEY_TIMEOUT(), String.valueOf(Protocol.DEFAULT_TIMEOUT))));
+    }
+
+    public RedisEndpoint(URI uri) {
+        this(
+            uri.getHost(),
+            uri.getPort(),
+            JedisURIHelper.getPassword(uri),
+            JedisURIHelper.getDBIndex(uri),
+            Protocol.DEFAULT_TIMEOUT);
+    }
+
+    public RedisEndpoint(String uri) {
+        this(URI.create(uri));
+    }
+
+    public String getHost() {
+        return host;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public String getAuth() {
+        return auth;
+    }
+
+    public int getDb() {
+        return db;
+    }
+
+    public int getTimeout() {
+        return timeout;
+    }
+
+    public RedisEndpoint withAuth(String newAuth) {
+        return new RedisEndpoint(host, port, newAuth, db, timeout);
+    }
+
+    public Jedis connect() {
+        return RedisClient.connect(this);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        RedisEndpoint that = (RedisEndpoint) o;
+        return port == that.port
+            && db == that.db
+            && timeout == that.timeout
+            && Objects.equals(host, that.host)
+            && Objects.equals(auth, that.auth);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(host, port, auth, db, timeout);
+    }
+
+    @Override
+    public String toString() {
+        return "RedisEndpoint{host='"
+            + host
+            + "', port="
+            + port
+            + ", db="
+            + db
+            + ", timeout="
+            + timeout
+            + '}';
+    }
+}
+
+class RedisNode implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private final RedisEndpoint endpoint;
+    private final int startSlot;
+    private final int endSlot;
+    private final int idx;
+    private final int total;
+
+    RedisNode(RedisEndpoint endpoint, int startSlot, int endSlot, int idx, int total) {
+        this.endpoint = endpoint;
+        this.startSlot = startSlot;
+        this.endSlot = endSlot;
+        this.idx = idx;
+        this.total = total;
+    }
+
+    public RedisEndpoint getEndpoint() {
+        return endpoint;
+    }
+
+    public int getStartSlot() {
+        return startSlot;
+    }
+
+    public int getEndSlot() {
+        return endSlot;
+    }
+
+    public int getIdx() {
+        return idx;
+    }
+
+    public int getTotal() {
+        return total;
+    }
+
+    public Jedis connect() {
+        return endpoint.connect();
+    }
+}
+
+/** RedisConfig holds the state of the cluster nodes, and uses consistent hashing to map keys to nodes. */
+class RedisConfig implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private final RedisEndpoint initialHost;
+    private final String initialAddr;
+    private final RedisNode[] hosts;
+    private final RedisNode[] nodes;
+
+    RedisConfig(RedisEndpoint initialHost) {
+        this.initialHost = initialHost;
+        this.initialAddr = initialHost.getHost();
+        this.nodes = getNodes(initialHost);
+        this.hosts =
+            Arrays.stream(nodes).filter(node -> node.getIdx() == 0).toArray(RedisNode[]::new);
+    }
+
+    public String getInitialAddr() {
+        return initialAddr;
+    }
+
+    public String getAuth() {
+        return initialHost.getAuth();
+    }
+
+    public int getDB() {
+        return initialHost.getDb();
+    }
+
+    public RedisNode getRandomNode() {
+        return hosts[ThreadLocalRandom.current().nextInt(hosts.length)];
+    }
+
+    public RedisNode[] getNodesBySlots(int sPos, int ePos) {
+        return Arrays.stream(nodes)
+            .filter(node -> intersect(sPos, ePos, node.getStartSlot(), node.getEndSlot()))
+            .filter(node -> node.getIdx() == 0)
+            .toArray(RedisNode[]::new);
+    }
+
+    public Jedis connectionForKey(String key) {
+        return getHost(key).connect();
+    }
+
+    public RedisNode getHost(String key) {
+        int slot = JedisClusterCRC16.getSlot(key);
+        return Arrays.stream(hosts)
+            .filter(host -> host.getStartSlot() <= slot && host.getEndSlot() >= slot)
+            .findFirst()
+            .orElseThrow(() -> new IllegalStateException("[StreamPark] No redis host for key: " + key));
+    }
+
+    public RedisNode[] getNodes(RedisEndpoint endpoint) {
+        if (clusterEnabled(endpoint)) {
+            return getClusterNodes(endpoint);
+        }
+        return getNonClusterNodes(endpoint);
+    }
+
+    private static boolean intersect(int sPos1, int ePos1, int sPos2, int ePos2) {
+        if (sPos1 <= sPos2) {
+            return ePos1 >= sPos2;
+        }
+        return ePos2 >= sPos1;
+    }
+
+    private boolean clusterEnabled(RedisEndpoint endpoint) {
+        Jedis conn = endpoint.connect();
+        try {
+            String[] info = conn.info().split("\n");
+            String version =
+                Arrays.stream(info).filter(line -> line.contains("redis_version:")).findFirst().orElse("");
+            String[] clusterEnable =
+                Arrays.stream(info).filter(line -> line.contains("cluster_enabled:")).toArray(String[]::new);
+            int mainVersion = Integer.parseInt(version.substring(14, version.indexOf('.')));
+            return mainVersion > 2 && clusterEnable.length > 0 && clusterEnable[0].contains("1");
+        } finally {
+            conn.close();
+        }
+    }
+
+    private RedisNode[] getNonClusterNodes(RedisEndpoint endpoint) {
+        String masterHost = endpoint.getHost();
+        int masterPort = endpoint.getPort();
+        Jedis conn = endpoint.connect();
+        String[] replinfo;
+        try {
+            replinfo = conn.info("Replication").split("\n");
+        } finally {
+            conn.close();
+        }
+
+        if (Arrays.stream(replinfo).anyMatch(line -> line.contains("role:slave"))) {
+            String host =
+                Arrays.stream(replinfo)
+                    .filter(line -> line.contains("master_host:"))
+                    .findFirst()
+                    .orElse("")
+                    .trim()
+                    .substring(12);
+            int port =
+                Integer.parseInt(
+                    Arrays.stream(replinfo)
+                        .filter(line -> line.contains("master_port:"))
+                        .findFirst()
+                        .orElse("")
+                        .trim()
+                        .substring(12));
+            return getNonClusterNodes(
+                new RedisEndpoint(host, port, endpoint.getAuth(), endpoint.getDb(), endpoint.getTimeout()));
+        }
+
+        List<String[]> slaves = new ArrayList<>();
+        for (String rl : replinfo) {
+            if (rl.contains("slave") && rl.contains("online")) {
+                String content = rl.substring(rl.indexOf(':') + 1);
+                String[] parts = content.split(",");
+                String ipPart = parts[0];
+                String portPart = parts[1];
+                slaves.add(
+                    new String[]{
+                            ipPart.substring(ipPart.indexOf('=') + 1),
+                            portPart.substring(portPart.indexOf('=') + 1)
+                    });
+            }
+        }
+
+        List<RedisNode> nodeList = new ArrayList<>();
+        nodeList.add(new RedisNode(endpoint, 0, 16383, 0, slaves.size() + 1));
+        for (int i = 0; i < slaves.size(); i++) {
+            String[] slave = slaves.get(i);
+            nodeList.add(
+                new RedisNode(
+                    new RedisEndpoint(
+                        slave[0],
+                        Integer.parseInt(slave[1]),
+                        endpoint.getAuth(),
+                        endpoint.getDb(),
+                        endpoint.getTimeout()),
+                    0,
+                    16383,
+                    i + 1,
+                    slaves.size() + 1));
+        }
+        return nodeList.toArray(new RedisNode[0]);
+    }
+
+    private RedisNode[] getClusterNodes(RedisEndpoint endpoint) {
+        Jedis conn = endpoint.connect();
+        try {
+            List<Object> slots = conn.clusterSlots();
+            List<RedisNode> result = new ArrayList<>();
+            for (Object slotInfoObj : slots) {
+                @SuppressWarnings("unchecked")
+                List<Object> slotInfo = (List<Object>) slotInfoObj;
+                int sPos = Integer.parseInt(slotInfo.get(0).toString());
+                int ePos = Integer.parseInt(slotInfo.get(1).toString());
+                int total = slotInfo.size() - 2;
+                for (int i = 0; i < total; i++) {
+                    @SuppressWarnings("unchecked")
+                    List<Object> node = (List<Object>) slotInfo.get(i + 2);
+                    String host = SafeEncoder.encode((byte[]) node.get(0));
+                    int port = Integer.parseInt(node.get(1).toString());
+                    result.add(
+                        new RedisNode(
+                            new RedisEndpoint(
+                                host,
+                                port,
+                                endpoint.getAuth(),
+                                endpoint.getDb(),
+                                endpoint.getTimeout()),
+                            sPos,
+                            ePos,
+                            i,
+                            total));
+                }
+            }
+            return result.toArray(new RedisNode[0]);
+        } finally {
+            conn.close();
+        }
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/RedisUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/RedisUtils.java
new file mode 100644
index 000000000..21fc775f8
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/RedisUtils.java
@@ -0,0 +1,479 @@
+/*
+ * 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.streampark.common.util;
+
+import org.apache.streampark.shaded.org.slf4j.Logger;
+
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisCluster;
+import redis.clients.jedis.Pipeline;
+import redis.clients.jedis.ScanParams;
+import redis.clients.jedis.Transaction;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+
+/** Redis command utilities. */
+public final class RedisUtils {
+
+    private static final Logger LOG =
+        StreamParkLoggerFactory.loggerFactory().getLogger(RedisUtils.class.getName());
+
+    private RedisUtils() {
+    }
+
+    public static boolean exists(String key, RedisEndpoint endpoint) {
+        return doRedis(jedis -> jedis.exists(key), endpoint);
+    }
+
+    public static boolean hexists(String key, String field, RedisEndpoint endpoint) {
+        return hexists(key, field, null, endpoint);
+    }
+
+    public static boolean hexists(String key, String field, Runnable func, RedisEndpoint endpoint) {
+        return doRedis(jedis -> jedis.hexists(key, field), func, endpoint);
+    }
+
+    public static String get(String key, RedisEndpoint endpoint) {
+        return doRedis(jedis -> jedis.get(key), endpoint);
+    }
+
+    public static String hget(String key, String field, RedisEndpoint endpoint) {
+        return doRedis(jedis -> jedis.hget(key, field), endpoint);
+    }
+
+    public static String setex(String key, String value, Integer ttl, RedisEndpoint endpoint) {
+        return setex(key, value, ttl, null, endpoint);
+    }
+
+    public static String setex(String key, String value, Integer ttl, Runnable func, RedisEndpoint endpoint) {
+        return doRedis(jedis -> jedis.setex(key, ttl, value), func, endpoint);
+    }
+
+    public static Long setnx(String key, String value, Integer ttl, RedisEndpoint endpoint) {
+        return setnx(key, value, ttl, null, endpoint);
+    }
+
+    public static Long setnx(String key, String value, Integer ttl, Runnable func, RedisEndpoint endpoint) {
+        return doRedis(
+            jedis -> {
+                Long x = jedis.setnx(key, value);
+                if (x == 1 && ttl != null) {
+                    jedis.expire(key, ttl);
+                }
+                return x;
+            },
+            func,
+            endpoint);
+    }
+
+    public static Long hsetnx(
+                              String key, String field, String value, Integer ttl, RedisEndpoint endpoint) {
+        return hsetnx(key, field, value, ttl, null, endpoint);
+    }
+
+    public static Long hsetnx(
+                              String key, String field, String value, Integer ttl, Runnable func,
+                              RedisEndpoint endpoint) {
+        return doRedis(
+            jedis -> {
+                Long x = jedis.hsetnx(key, field, value);
+                if (x == 1 && ttl != null) {
+                    jedis.expire(key, ttl);
+                }
+                return x;
+            },
+            func,
+            endpoint);
+    }
+
+    public static String[] mget(String[] keys, RedisEndpoint endpoint) {
+        return doRedis(jedis -> jedis.mget(keys).toArray(new String[0]), endpoint);
+    }
+
+    public static Long del(String key, RedisEndpoint endpoint) {
+        return del(key, null, endpoint);
+    }
+
+    public static Long del(String key, Runnable func, RedisEndpoint endpoint) {
+        return doRedis(jedis -> jedis.del(key), func, endpoint);
+    }
+
+    public static String set(String key, String value, Integer ttl, RedisEndpoint endpoint) {
+        return set(key, value, ttl, null, endpoint);
+    }
+
+    public static String set(String key, String value, Integer ttl, Runnable func, RedisEndpoint endpoint) {
+        return doRedis(
+            jedis -> {
+                String s = jedis.set(key, value);
+                if (ttl != null) {
+                    jedis.expire(key, ttl);
+                }
+                return s;
+            },
+            func,
+            endpoint);
+    }
+
+    public static Long hset(String key, String field, String value, Integer ttl, RedisEndpoint endpoint) {
+        return hset(key, field, value, ttl, null, endpoint);
+    }
+
+    public static Long hset(
+                            String key, String field, String value, Integer ttl, Runnable func,
+                            RedisEndpoint endpoint) {
+        return doRedis(
+            jedis -> {
+                Long s = jedis.hset(key, field, value);
+                if (ttl != null) {
+                    jedis.expire(key, ttl);
+                }
+                return s;
+            },
+            func,
+            endpoint);
+    }
+
+    public static String hmset(
+                               String key, Map<String, String> hash, Integer ttl, RedisEndpoint endpoint) {
+        return hmset(key, hash, ttl, null, endpoint);
+    }
+
+    public static String hmset(
+                               String key, Map<String, String> hash, Integer ttl, Runnable func,
+                               RedisEndpoint endpoint) {
+        return doRedis(
+            jedis -> {
+                String s = jedis.hmset(key, hash);
+                if (ttl != null) {
+                    jedis.expire(key, ttl);
+                }
+                return s;
+            },
+            func,
+            endpoint);
+    }
+
+    public static List<String> hmget(String key, String[] fields, RedisEndpoint endpoint) {
+        return doRedis(jedis -> jedis.hmget(key, fields), endpoint);
+    }
+
+    public static Map<String, String> hgetAll(String key, RedisEndpoint endpoint) {
+        return doRedis(jedis -> new HashMap<>(jedis.hgetAll(key)), endpoint);
+    }
+
+    public static Long hdel(String key, List<String> fields, RedisEndpoint endpoint) {
+        return hdel(key, fields, null, endpoint);
+    }
+
+    public static Long hdel(String key, List<String> fields, Runnable func, RedisEndpoint endpoint) {
+        if (key == null || fields == null || fields.isEmpty()) {
+            return 0L;
+        }
+        return doRedis(jedis -> jedis.hdel(key, fields.toArray(new String[0])), func, endpoint);
+    }
+
+    public static Long sadd(String key, List<String> members, Integer ttl, RedisEndpoint endpoint) {
+        return sadd(key, members, ttl, null, endpoint);
+    }
+
+    public static Long sadd(
+                            String key, List<String> members, Integer ttl, Runnable func, RedisEndpoint endpoint) {
+        return doRedis(
+            jedis -> {
+                Long res = jedis.sadd(key, members.toArray(new String[0]));
+                if (ttl != null) {
+                    jedis.expire(key, ttl);
+                }
+                return res;
+            },
+            func,
+            endpoint);
+    }
+
+    public static Set<String> smembers(String key, RedisEndpoint endpoint) {
+        return doRedis(jedis -> jedis.smembers(key), endpoint);
+    }
+
+    public static Long srem(String key, List<String> members, RedisEndpoint endpoint) {
+        return srem(key, members, null, endpoint);
+    }
+
+    public static Long srem(String key, List<String> members, Runnable func, RedisEndpoint endpoint) {
+        return doRedis(jedis -> jedis.srem(key, members.toArray(new String[0])), func, endpoint);
+    }
+
+    public static String getOrElseHset(
+                                       String key, String field, String value, Integer ttl, RedisEndpoint endpoint) {
+        return getOrElseHset(key, field, value, ttl, null, endpoint);
+    }
+
+    public static String getOrElseHset(
+                                       String key,
+                                       String field,
+                                       String value,
+                                       Integer ttl,
+                                       Runnable func,
+                                       RedisEndpoint endpoint) {
+        return doRedis(
+            jedis -> {
+                String v = jedis.hget(key, field);
+                if (v == null) {
+                    jedis.hset(key, field, value);
+                    if (ttl != null) {
+                        jedis.expire(key, ttl);
+                    }
+                }
+                return v;
+            },
+            func,
+            endpoint);
+    }
+
+    public static String getOrElseSet(String key, String value, Integer ttl, RedisEndpoint endpoint) {
+        return getOrElseSet(key, value, ttl, null, endpoint);
+    }
+
+    public static String getOrElseSet(
+                                      String key, String value, Integer ttl, Runnable func, RedisEndpoint endpoint) {
+        return doRedis(
+            jedis -> {
+                String v = jedis.get(key);
+                if (v == null) {
+                    jedis.set(key, value);
+                    if (ttl != null) {
+                        jedis.expire(key, ttl);
+                    }
+                }
+                return v;
+            },
+            func,
+            endpoint);
+    }
+
+    public static Long hincrBy(
+                               String key, String field, long value, Integer ttl, RedisEndpoint endpoint) {
+        return hincrBy(key, field, value, ttl, null, endpoint);
+    }
+
+    public static Long hincrBy(
+                               String key, String field, long value, Integer ttl, Runnable func,
+                               RedisEndpoint endpoint) {
+        return doRedis(
+            jedis -> {
+                Long reply = jedis.hincrBy(key, field, value);
+                if (ttl != null) {
+                    jedis.expire(key, ttl);
+                }
+                return reply;
+            },
+            func,
+            endpoint);
+    }
+
+    public static Double hincrByFloat(
+                                      String key, String field, double value, Integer ttl, RedisEndpoint endpoint) {
+        return hincrByFloat(key, field, value, ttl, null, endpoint);
+    }
+
+    public static Double hincrByFloat(
+                                      String key, String field, double value, Integer ttl, Runnable func,
+                                      RedisEndpoint endpoint) {
+        return doRedis(
+            jedis -> {
+                Double reply = jedis.hincrByFloat(key, field, value);
+                if (ttl != null) {
+                    jedis.expire(key, ttl);
+                }
+                return reply;
+            },
+            func,
+            endpoint);
+    }
+
+    public static Long incrBy(String key, long value, Integer ttl, RedisEndpoint endpoint) {
+        return incrBy(key, value, ttl, null, endpoint);
+    }
+
+    public static Long incrBy(
+                              String key, long value, Integer ttl, Runnable func, RedisEndpoint endpoint) {
+        return doRedis(
+            jedis -> {
+                Long reply = jedis.incrBy(key, value);
+                if (ttl != null) {
+                    jedis.expire(key, ttl);
+                }
+                return reply;
+            },
+            func,
+            endpoint);
+    }
+
+    public static Double incrByFloat(String key, double value, Integer ttl, RedisEndpoint endpoint) {
+        return incrByFloat(key, value, ttl, null, endpoint);
+    }
+
+    public static Double incrByFloat(
+                                     String key, double value, Integer ttl, Runnable func, RedisEndpoint endpoint) {
+        return doRedis(
+            jedis -> {
+                Double reply = jedis.incrByFloat(key, value);
+                if (ttl != null) {
+                    jedis.expire(key, ttl);
+                }
+                return reply;
+            },
+            func,
+            endpoint);
+    }
+
+    public static Long mSets(List<Map.Entry<String, String>> kvs, Integer ttl, RedisEndpoint endpoint) {
+        return mSets(kvs, ttl, null, endpoint);
+    }
+
+    public static Long mSets(
+                             List<Map.Entry<String, String>> kvs, Integer ttl, Runnable func, RedisEndpoint endpoint) {
+        return doRedis(
+            jedis -> {
+                long start = System.currentTimeMillis();
+                Pipeline pipe = jedis.pipelined();
+                for (Map.Entry<String, String> kv : kvs) {
+                    pipe.mset(kv.getKey(), kv.getValue());
+                    if (ttl != null) {
+                        pipe.expire(kv.getKey(), ttl);
+                    }
+                }
+                pipe.sync();
+                return System.currentTimeMillis() - start;
+            },
+            func,
+            endpoint);
+    }
+
+    public static Long mSetex(List<Map.Entry<String, String>> kvs, Integer ttl, RedisEndpoint endpoint) {
+        return mSetex(kvs, ttl, null, endpoint);
+    }
+
+    public static Long mSetex(
+                              List<Map.Entry<String, String>> kvs, Integer ttl, Runnable func, RedisEndpoint endpoint) {
+        return doRedis(
+            jedis -> {
+                long start = System.currentTimeMillis();
+                Pipeline pipe = jedis.pipelined();
+                for (Map.Entry<String, String> kv : kvs) {
+                    pipe.setnx(kv.getKey(), kv.getValue());
+                    if (ttl != null) {
+                        pipe.expire(kv.getKey(), ttl);
+                    }
+                }
+                pipe.sync();
+                return System.currentTimeMillis() - start;
+            },
+            func,
+            endpoint);
+    }
+
+    public static Long expire(String key, int seconds, RedisEndpoint endpoint) {
+        return doRedis(jedis -> jedis.expire(key, seconds), endpoint);
+    }
+
+    public static void delByPattern(String key, RedisEndpoint endpoint) {
+        delByPattern(key, null, endpoint);
+    }
+
+    public static void delByPattern(String key, Runnable func, RedisEndpoint endpoint) {
+        doRedis(
+            jedis -> {
+                ScanParams scanParams = new ScanParams();
+                scanParams.match(key);
+                scanParams.count(10000);
+                String cursor = ScanParams.SCAN_POINTER_START;
+                do {
+                    redis.clients.jedis.ScanResult<String> scanResult = jedis.scan(cursor, scanParams);
+                    cursor = scanResult.getCursor();
+                    List<String> keys = scanResult.getResult();
+                    if (!keys.isEmpty()) {
+                        jedis.del(keys.toArray(new String[0]));
+                    }
+                } while (!"0".equals(cursor));
+                return null;
+            },
+            func,
+            endpoint);
+    }
+
+    public static Long hlen(String key, RedisEndpoint endpoint) {
+        return doRedis(jedis -> jedis.hlen(key), endpoint);
+    }
+
+    public static <R> R doRedis(Function<Jedis, R> f, RedisEndpoint endpoint) {
+        return doRedis(f, null, endpoint);
+    }
+
+    public static <R> R doRedis(Function<Jedis, R> f, Runnable func, RedisEndpoint endpoint) {
+        Jedis redis = RedisClient.connect(endpoint);
+        R result;
+        if (func == null) {
+            result = f.apply(redis);
+        } else {
+            Transaction transaction = redis.multi();
+            result = f.apply(redis);
+            func.run();
+            transaction.exec();
+            transaction.close();
+        }
+        try {
+            redis.close();
+            LOG.debug("[StreamPark] jedis.close successful.");
+        } catch (Exception e) {
+            LOG.error("[StreamPark] jedis.close failed.");
+        }
+        return result;
+    }
+
+    public static <R> R doCluster(Function<JedisCluster, R> f, RedisEndpoint... endpoints) {
+        JedisCluster cluster = RedisClient.connectCluster(endpoints);
+        R result = f.apply(cluster);
+        try {
+            cluster.close();
+            LOG.debug("[StreamPark] cluster.close successful.");
+        } catch (Exception e) {
+            LOG.error("[StreamPark] cluster.close failed.");
+        }
+        return result;
+    }
+
+    public static <R> R doPipeline(Function<Pipeline, R> f, RedisEndpoint endpoint) {
+        Jedis redis = RedisClient.connect(endpoint);
+        Pipeline pipe = redis.pipelined();
+        R result = f.apply(pipe);
+        try {
+            pipe.sync();
+            pipe.close();
+            redis.close();
+            LOG.debug("[StreamPark] pipe.close successful.");
+        } catch (Exception e) {
+            LOG.error("[StreamPark] pipe.close failed.");
+        }
+        return result;
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/ReflectUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/ReflectUtils.java
new file mode 100644
index 000000000..dab566c7f
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/ReflectUtils.java
@@ -0,0 +1,101 @@
+/*
+ * 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.streampark.common.util;
+
+import org.apache.streampark.shaded.org.slf4j.Logger;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+public final class ReflectUtils {
+
+    private static final Logger LOG = StreamParkLoggerFactory.loggerFactory().getLogger(ReflectUtils.class.getName());
+    private ReflectUtils() {
+    }
+
+    public static Field getField(Class<?> beanClass, String name) {
+        for (Field f : beanClass.getDeclaredFields()) {
+            if (Objects.equals(name, f.getName()))
+                return f;
+        }
+        return null;
+    }
+
+    public static Object getFieldValue(Object obj, String fieldName) throws IllegalAccessException {
+        Field field = getField(obj.getClass(), fieldName);
+        return getFieldValue(obj, field);
+    }
+
+    public static Object getFieldValue(Object obj, Field field) throws IllegalAccessException {
+        if (obj == null || field == null)
+            return null;
+        field.setAccessible(true);
+        return field.get(obj);
+    }
+
+    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
+        Field field = getAccessibleField(obj, fieldName);
+        if (field == null)
+            throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");
+        try {
+            field.set(obj, value);
+        } catch (IllegalAccessException e) {
+            LOG.error("[StreamPark] Failed to assign to the element.", e);
+            throw new Exception(e.getMessage());
+        }
+    }
+
+    private static Field getAccessibleField(Object obj, String fieldName) {
+        AssertUtils.notNull(obj, "object can't be null");
+        AssertUtils.required(StringUtils.isNotBlank(fieldName), "fieldName can't be blank");
+        Class<?> superClass = obj.getClass();
+        while (superClass != Object.class) {
+            try {
+                Field field = superClass.getDeclaredField(fieldName);
+                makeAccessible(field);
+                return field;
+            } catch (NoSuchFieldException ignored) {
+                superClass = superClass.getSuperclass();
+            }
+        }
+        return null;
+    }
+
+    private static void makeAccessible(Field field) {
+        if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())
+            || Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) {
+            field.setAccessible(true);
+        }
+    }
+
+    public static List<Method> getMethodsByAnnotation(Class<?> beanClass, Class<? extends Annotation> annotClazz) {
+        List<Method> methods = new ArrayList<>();
+        for (Method m : beanClass.getDeclaredMethods()) {
+            if (m.getDeclaredAnnotation(annotClazz) != null)
+                methods.add(m);
+        }
+        return methods;
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/SafePathUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/SafePathUtils.java
new file mode 100644
index 000000000..9e7609586
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/SafePathUtils.java
@@ -0,0 +1,99 @@
+/*
+ * 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.streampark.common.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/** Path resolution helpers for user-supplied file locations. */
+public final class SafePathUtils {
+
+    private SafePathUtils() {
+    }
+
+    public static Path resolveConfigPath(String filename) {
+        validateConfigFilename(filename);
+        Path base = Paths.get("").toAbsolutePath().normalize();
+        Path resolved = base.resolve(filename).normalize();
+        if (!Paths.get(filename).isAbsolute() && !resolved.startsWith(base)) {
+            throw new IllegalArgumentException("invalid file path: " + filename);
+        }
+        return resolved;
+    }
+
+    public static InputStream openConfigFile(String filename) throws IOException {
+        validateConfigFilename(filename);
+        Path base = Paths.get("").toAbsolutePath().normalize();
+        Path resolved = base.resolve(filename).normalize();
+        if (!Paths.get(filename).isAbsolute() && !resolved.startsWith(base)) {
+            throw new IOException("invalid file path: " + filename);
+        }
+        return Files.newInputStream(resolved); // NOSONAR javasecurity:S2083 - path validated via Path.resolve above
+    }
+
+    public static String readConfigFile(String filename) throws IOException {
+        validateConfigFilename(filename);
+        Path base = Paths.get("").toAbsolutePath().normalize();
+        Path resolved = base.resolve(filename).normalize();
+        if (!Paths.get(filename).isAbsolute() && !resolved.startsWith(base)) {
+            throw new IOException("invalid file path: " + filename);
+        }
+        return Files.readString(resolved, StandardCharsets.UTF_8); // NOSONAR javasecurity:S2083 - path validated via
+                                                                   // Path.resolve above
+    }
+
+    public static Path resolveJarPath(java.net.URL jar) throws IOException {
+        try {
+            String location = jar.toString();
+            if (location.contains("..")) {
+                throw new IOException("JAR file path is invalid " + jar);
+            }
+            Path base = Paths.get("").toAbsolutePath().normalize();
+            return base.resolve(Paths.get(jar.toURI())).normalize();
+        } catch (Exception e) {
+            throw new IOException("JAR file path is invalid " + jar, e);
+        }
+    }
+
+    public static InputStream openJarFile(java.net.URL jar) throws IOException {
+        String location = jar.toString();
+        if (location.contains("..")) {
+            throw new IOException("JAR file path is invalid " + jar);
+        }
+        try {
+            Path base = Paths.get("").toAbsolutePath().normalize();
+            Path resolved = base.resolve(Paths.get(jar.toURI())).normalize();
+            return Files.newInputStream(resolved); // NOSONAR javasecurity:S2083 - path validated via Path.resolve above
+        } catch (Exception e) {
+            throw new IOException("JAR file path is invalid " + jar, e);
+        }
+    }
+
+    private static void validateConfigFilename(String filename) {
+        if (filename == null || filename.isEmpty()) {
+            throw new IllegalArgumentException("filename must not be blank");
+        }
+        if (filename.contains("..")) {
+            throw new IllegalArgumentException("invalid file path: " + filename);
+        }
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/SparkConfigurationUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/SparkConfigurationUtils.java
new file mode 100644
index 000000000..2a9eff7c6
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/SparkConfigurationUtils.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.streampark.common.util;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.google.common.collect.Lists;
+
+import javax.annotation.Nonnull;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+public final class SparkConfigurationUtils {
+
+    private static final Pattern SPARK_PROPERTY_COMPLEX_PATTERN =
+        Pattern.compile("^[\"']?([^=]+)=(.*)[\"']?$");
+
+    private SparkConfigurationUtils() {
+    }
+
+    @Nonnull
+    public static Map<String, String> extractPropertiesAsJava(String properties) {
+        return new HashMap<>(extractProperties(properties));
+    }
+
+    @Nonnull
+    public static Map<String, String> extractProperties(String properties) {
+        if (StringUtils.isEmpty(properties)) {
+            return new HashMap<>();
+        }
+        Map<String, String> map = new HashMap<>();
+        for (String x : splitSparkConfSegments(properties)) {
+            if (Utils.isNotEmpty(x) && !x.isEmpty()) {
+                java.util.regex.Matcher p = SPARK_PROPERTY_COMPLEX_PATTERN.matcher(x);
+                if (p.matches()) {
+                    map.put(p.group(1).trim(), p.group(2).trim());
+                }
+            }
+        }
+        return map;
+    }
+
+    @Nonnull
+    public static List<String> extractArgumentsAsJava(String arguments) {
+        if (StringUtils.isEmpty(arguments)) {
+            return Lists.newArrayList();
+        }
+        List<String> result = new ArrayList<>();
+        StringBuilder token = new StringBuilder();
+        boolean inQuote = false;
+        char quoteChar = 0;
+        for (int i = 0; i < arguments.length(); i++) {
+            char c = arguments.charAt(i);
+            if (inQuote) {
+                token.append(c);
+                if (c == quoteChar) {
+                    inQuote = false;
+                }
+            } else if (c == '"' || c == '\'') {
+                inQuote = true;
+                quoteChar = c;
+                token.append(c);
+            } else if (Character.isWhitespace(c)) {
+                if (token.length() > 0) {
+                    result.add(token.toString());
+                    token.setLength(0);
+                }
+            } else {
+                token.append(c);
+            }
+        }
+        if (token.length() > 0) {
+            result.add(token.toString());
+        }
+        return result;
+    }
+
+    private static List<String> splitSparkConfSegments(String properties) {
+        List<String> segments = new ArrayList<>();
+        StringBuilder current = new StringBuilder();
+        int i = 0;
+        while (i < properties.length()) {
+            if (startsWithAt(properties, i, "--conf")) {
+                flushSegment(segments, current);
+                i += 6;
+            } else if (startsWithAt(properties, i, "-c")
+                && (i + 2 >= properties.length() || Character.isWhitespace(properties.charAt(i + 2)))) {
+                flushSegment(segments, current);
+                i += 2;
+            } else {
+                current.append(properties.charAt(i++));
+            }
+        }
+        flushSegment(segments, current);
+        return segments;
+    }
+
+    private static boolean startsWithAt(String value, int index, String token) {
+        if (index + token.length() > value.length()) {
+            return false;
+        }
+        return value.regionMatches(index, token, 0, token.length());
+    }
+
+    private static void flushSegment(List<String> segments, StringBuilder current) {
+        if (current.length() > 0) {
+            segments.add(current.toString().trim());
+            current.setLength(0);
+        }
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/SqlConvertUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/SqlConvertUtils.java
new file mode 100644
index 000000000..e71176343
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/SqlConvertUtils.java
@@ -0,0 +1,264 @@
+/*
+ * 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.streampark.common.util;
+
+import org.apache.streampark.shaded.org.slf4j.Logger;
+
+import java.util.AbstractMap;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/** SQL dialect conversion utilities. */
+public final class SqlConvertUtils {
+
+    private static final Logger LOG =
+        StreamParkLoggerFactory.loggerFactory().getLogger(SqlConvertUtils.class.getName());
+
+    private static final Pattern FIELD_REGEXP =
+        Pattern.compile(
+            "\\s*(.*?)\\s+(([a-z]+)\\((.*?)\\)|[a-z]+)(\\s*|((.*?)(comment)\\s+(['|\"](.*?)['|\"])|(.*?))),$",
+            Pattern.CASE_INSENSITIVE);
+
+    private static final Pattern PRIMARY_REGEXP =
+        Pattern.compile("primary\\s+key\\s+\\((.*?)\\)", Pattern.CASE_INSENSITIVE);
+
+    private static final Pattern COMMENT_REGEXP =
+        Pattern.compile("(comment)\\s+(['\"])", Pattern.CASE_INSENSITIVE);
+
+    private static final Pattern LENGTH_REGEXP = Pattern.compile("(.*?)\\s*\\([^\\\\)|^\\n]+,$");
+
+    private SqlConvertUtils() {
+    }
+
+    private static String toFlinkDataType(String dataType, String length) {
+        switch (dataType.toUpperCase()) {
+            case "TEXT":
+            case "LONGTEXT":
+                return "VARCHAR";
+            case "DATETIME":
+                return "TIMESTAMP";
+            case "INT":
+                return "INTEGER";
+            default:
+                return dataType.toUpperCase();
+        }
+    }
+
+    private static String toClickhouseDataType(String dataType, String length) {
+        switch (dataType.toUpperCase()) {
+            case "TEXT":
+            case "LONGTEXT":
+            case "BLOB":
+            case "VARCHAR":
+            case "VARBINARY":
+                return "String";
+            case "DATETIME":
+            case "TIMESTAMP":
+                return "DateTime";
+            case "TINYINT":
+                return "Int8";
+            case "SMALLINT":
+                return "Int16";
+            case "FLOAT":
+                return "Float32";
+            case "DOUBLE":
+                return "Float64";
+            case "DATE":
+                return "Date";
+            case "BIGINT":
+            case "INT":
+                if (length == null) {
+                    return "Int32";
+                }
+                int len = Integer.parseInt(length.trim());
+                if (len < 3) {
+                    return "Int8";
+                } else if (len < 5) {
+                    return "Int16";
+                } else if (len < 9) {
+                    return "Int32";
+                }
+                return "Int64";
+            case "DECIMAL":
+                String[] parts = length.split(",");
+                if (parts.length == 2) {
+                    int p = Integer.parseInt(parts[0].trim());
+                    int s = Integer.parseInt(parts[1].trim());
+                    int sum = p + s;
+                    if (sum <= 32) {
+                        return "Decimal32(" + s + ")";
+                    } else if (sum <= 64) {
+                        return "Decimal64(" + s + ")";
+                    }
+                    return "Decimal128(" + s + ")";
+                }
+                throw new IllegalArgumentException("Invalid decimal length: " + length);
+            default:
+                return dataType.toUpperCase();
+        }
+    }
+
+    private static String formatSql(String sql) {
+        String body =
+            sql.substring(sql.indexOf('('), sql.lastIndexOf(')') + 1)
+                .replaceAll("\r|\n|\r\n", "")
+                .replaceFirst("\\(", "(\n")
+                .replaceFirst("\\)$", "\n)")
+                .replaceAll(",", ",\n");
+
+        Scanner scanner = new Scanner(body);
+        Map<Integer, String> map = new HashMap<>();
+        while (scanner.hasNextLine()) {
+            map.put(map.size(), scanner.nextLine().trim());
+        }
+        StringBuilder sqlBuffer = new StringBuilder(sql.substring(0, sql.indexOf('(')));
+        int skipNo = -1;
+        for (Map.Entry<Integer, String> entry : map.entrySet()) {
+            int idx = entry.getKey();
+            if (idx > skipNo) {
+                AbstractMap.SimpleEntry<Integer, String> length = lengthJoin(map, idx, entry.getValue());
+                if (length.getKey() > idx) {
+                    sqlBuffer.append(length.getValue()).append('\n');
+                    skipNo = length.getKey();
+                } else {
+                    AbstractMap.SimpleEntry<Integer, String> comment = commentJoin(map, idx, entry.getValue());
+                    if (comment.getKey() > idx) {
+                        sqlBuffer.append(comment.getValue()).append('\n');
+                        skipNo = comment.getKey();
+                    } else {
+                        sqlBuffer.append(entry.getValue()).append('\n');
+                    }
+                }
+            }
+        }
+        scanner.close();
+        return sqlBuffer.toString().trim().concat(sql.substring(sql.lastIndexOf(')') + 1));
+    }
+
+    private static AbstractMap.SimpleEntry<Integer, String> commentJoin(
+                                                                        Map<Integer, String> map, int index,
+                                                                        String segment) {
+        Matcher matcher = COMMENT_REGEXP.matcher(segment);
+        if (!matcher.find()) {
+            return new AbstractMap.SimpleEntry<>(index, segment);
+        }
+        String quote = matcher.group(2);
+        String regexp = "\\" + quote + "(,|)$";
+        String cleaned =
+            segment.replaceFirst("(?i)(comment)\\s+(['\"])", "").replace("\\" + quote, "");
+        if (cleaned.matches(".*" + regexp)) {
+            return new AbstractMap.SimpleEntry<>(index, segment);
+        }
+        String nextLine = map.get(index + 1);
+        return commentJoin(map, index + 1, segment + nextLine);
+    }
+
+    private static AbstractMap.SimpleEntry<Integer, String> lengthJoin(
+                                                                       Map<Integer, String> map, int index,
+                                                                       String segment) {
+        if (!LENGTH_REGEXP.matcher(segment).find()) {
+            return commentJoin(map, index, segment);
+        }
+        String nextLine = map.get(index + 1);
+        return lengthJoin(map, index + 1, segment.trim() + nextLine);
+    }
+
+    private interface TypeConverter {
+
+        String convert(String dataType, String length);
+    }
+
+    private interface KeyConverter {
+
+        String convert(String line);
+    }
+
+    private static String convertSql(
+                                     String sql, TypeConverter typeFunc, KeyConverter keyFunc, String postfix) {
+        String formattedSql = formatSql(sql);
+        Scanner scanner = new Scanner(formattedSql);
+        StringBuilder sqlBuffer = new StringBuilder();
+        while (scanner.hasNextLine()) {
+            String rawLine = scanner.nextLine().trim();
+            String line = null;
+            String upper = rawLine.toUpperCase().trim();
+            if (upper.startsWith("CREATE ")) {
+                line = rawLine;
+            } else if (upper.startsWith("PRIMARY KEY ")) {
+                if (keyFunc != null) {
+                    line = keyFunc.convert(rawLine);
+                }
+            } else if (!upper.startsWith("UNIQUE KEY ") && !upper.startsWith("KEY ")) {
+                Matcher matcher = FIELD_REGEXP.matcher(rawLine);
+                if (matcher.find()) {
+                    String fieldName = matcher.group(1);
+                    String dataType;
+                    if (matcher.group(3) != null) {
+                        dataType = matcher.group(3);
+                    } else {
+                        dataType = matcher.group(2);
+                    }
+                    String length = matcher.group(4);
+                    if (dataType != null) {
+                        String fieldType = typeFunc.convert(dataType, length);
+                        if (matcher.group(8) != null) {
+                            line = fieldName + " " + fieldType + " COMMENT '" + matcher.group(10) + "'";
+                        } else {
+                            line = fieldName + " " + fieldType;
+                        }
+                    }
+                }
+            }
+            if (line != null) {
+                sqlBuffer.append(line);
+                if (line.toUpperCase().trim().startsWith("CREATE ")) {
+                    sqlBuffer.append('\n');
+                } else {
+                    sqlBuffer.append(",\n");
+                }
+            }
+        }
+        scanner.close();
+        String result = sqlBuffer.toString().trim().replaceAll(",$", "\n)");
+        if (postfix != null) {
+            return result + " " + postfix;
+        }
+        return result;
+    }
+
+    public static String mysqlToFlinkSql(String sql, String postfix) {
+        return convertSql(
+            sql,
+            SqlConvertUtils::toFlinkDataType,
+            line -> {
+                Matcher matcher = PRIMARY_REGEXP.matcher(line);
+                if (matcher.find()) {
+                    return matcher.group() + " NOT ENFORCED";
+                }
+                return null;
+            },
+            postfix);
+    }
+
+    public static String mysqlToClickhouse(String sql, String postfix) {
+        return convertSql(sql, SqlConvertUtils::toClickhouseDataType, null, postfix);
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/StreamParkLoggerFactory.java b/streampark-common/src/main/java/org/apache/streampark/common/util/StreamParkLoggerFactory.java
new file mode 100644
index 000000000..f56c784e6
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/StreamParkLoggerFactory.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.streampark.common.util;
+
+import org.apache.streampark.shaded.ch.qos.logback.classic.LoggerContext;
+import org.apache.streampark.shaded.ch.qos.logback.classic.joran.JoranConfigurator;
+import org.apache.streampark.shaded.ch.qos.logback.classic.util.ContextInitializer;
+import org.apache.streampark.shaded.ch.qos.logback.classic.util.ContextSelectorStaticBinder;
+import org.apache.streampark.shaded.ch.qos.logback.core.CoreConstants;
+import org.apache.streampark.shaded.ch.qos.logback.core.LogbackException;
+import org.apache.streampark.shaded.ch.qos.logback.core.status.StatusUtil;
+import org.apache.streampark.shaded.ch.qos.logback.core.util.StatusPrinter;
+import org.apache.streampark.shaded.org.slf4j.ILoggerFactory;
+import org.apache.streampark.shaded.org.slf4j.spi.LoggerFactoryBinder;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+
+/** Shaded logback SLF4J logger factory binder. */
+public final class StreamParkLoggerFactory implements LoggerFactoryBinder {
+
+    private static final StreamParkLoggerFactory INSTANCE = new StreamParkLoggerFactory();
+
+    private static final ContextSelectorStaticBinder CONTEXT_SELECTOR_BINDER;
+
+    static {
+        LoggerContext defaultLoggerContext = new LoggerContext();
+        try {
+            new ShadedContextInitializer(defaultLoggerContext).autoConfig();
+        } catch (Exception e) {
+            System.err.println("Failed to auto configure default logger context: " + e);
+        }
+        if (!StatusUtil.contextHasStatusListener(defaultLoggerContext)) {
+            StatusPrinter.printInCaseOfErrorsOrWarnings(defaultLoggerContext);
+        }
+        ContextSelectorStaticBinder selectorBinder = new ContextSelectorStaticBinder();
+        try {
+            selectorBinder.init(defaultLoggerContext, new Object());
+        } catch (ReflectiveOperationException e) {
+            throw new ExceptionInInitializerError(e);
+        }
+        CONTEXT_SELECTOR_BINDER = selectorBinder;
+    }
+
+    private StreamParkLoggerFactory() {
+    }
+
+    /** Returns the SLF4J logger factory for StreamPark shaded logging. */
+    public static ILoggerFactory loggerFactory() {
+        return INSTANCE.getLoggerFactory();
+    }
+
+    @Override
+    public ILoggerFactory getLoggerFactory() {
+        if (CONTEXT_SELECTOR_BINDER.getContextSelector() == null) {
+            throw new IllegalStateException(
+                "'contextSelector' cannot be null. See also "
+                    + CoreConstants.CODES_URL
+                    + "#null_CS");
+        }
+        return CONTEXT_SELECTOR_BINDER.getContextSelector().getLoggerContext();
+    }
+
+    @Override
+    public String getLoggerFactoryClassStr() {
+        return CONTEXT_SELECTOR_BINDER.getClass().getName();
+    }
+
+    private static final class ShadedContextInitializer extends ContextInitializer {
+
+        private static final String SHADED_PACKAGE = "org.apache.streampark.shaded";
+        private final LoggerContext loggerContext;
+
+        private ShadedContextInitializer(LoggerContext loggerContext) {
+            super(loggerContext);
+            this.loggerContext = loggerContext;
+        }
+
+        @Override
+        public void configureByResource(URL url) {
+            AssertUtils.notNull(url, "URL argument cannot be null");
+            String path = url.getPath();
+            if (path.endsWith("xml")) {
+                JoranConfigurator configurator = new JoranConfigurator();
+                configurator.setContext(loggerContext);
+                try {
+                    String text =
+                        FileUtils.readFile(new File(path))
+                            .replaceAll("org.slf4j", SHADED_PACKAGE + ".org.slf4j")
+                            .replaceAll("ch.qos.logback", SHADED_PACKAGE + ".ch.qos.logback")
+                            .replaceAll("org.apache.log4j", SHADED_PACKAGE + ".org.apache.log4j");
+                    ByteArrayInputStream input =
+                        new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+                    configurator.doConfigure(input);
+                } catch (Exception e) {
+                    throw new LogbackException("Failed to configure logger context from " + url, e);
+                }
+            } else {
+                throw new LogbackException(
+                    "Unexpected filename extension of file ["
+                        + url
+                        + "]. Should be .xml");
+            }
+        }
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/StringCastUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/StringCastUtils.java
new file mode 100644
index 000000000..d88359eaa
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/StringCastUtils.java
@@ -0,0 +1,48 @@
+/*
+ * 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.streampark.common.util;
+
+/** String to primitive/boxed type conversion utilities. */
+public final class StringCastUtils {
+
+    private StringCastUtils() {
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T> T cast(String v, Class<?> classType) {
+        if (classType == String.class) {
+            return (T) v;
+        } else if (classType == byte.class || classType == Byte.class) {
+            return (T) (classType == byte.class ? (Object) Byte.parseByte(v) : Byte.valueOf(v));
+        } else if (classType == int.class || classType == Integer.class) {
+            return (T) (classType == int.class ? (Object) Integer.parseInt(v) : Integer.valueOf(v));
+        } else if (classType == long.class || classType == Long.class) {
+            return (T) (classType == long.class ? (Object) Long.parseLong(v) : Long.valueOf(v));
+        } else if (classType == float.class || classType == Float.class) {
+            return (T) (classType == float.class ? (Object) Float.parseFloat(v) : Float.valueOf(v));
+        } else if (classType == double.class || classType == Double.class) {
+            return (T) (classType == double.class ? (Object) Double.parseDouble(v) : Double.valueOf(v));
+        } else if (classType == short.class || classType == Short.class) {
+            return (T) (classType == short.class ? (Object) Short.parseShort(v) : Short.valueOf(v));
+        } else if (classType == boolean.class || classType == Boolean.class) {
+            return (T) (classType == boolean.class ? (Object) Boolean.parseBoolean(v) : Boolean.valueOf(v));
+        } else {
+            throw new IllegalArgumentException("Unsupported type: " + classType);
+        }
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/SystemPropertyUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/SystemPropertyUtils.java
new file mode 100644
index 000000000..c35d9954b
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/SystemPropertyUtils.java
@@ -0,0 +1,163 @@
+/*
+ * 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.streampark.common.util;
+
+import org.apache.streampark.shaded.org.slf4j.Logger;
+import org.apache.streampark.shaded.org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+public final class SystemPropertyUtils {
+
+    private static final Pattern CLASSES_SUFFIX_PATTERN = Pattern.compile("classes/$");
+
+    private static final Logger LOG =
+        LoggerFactory.getLogger(SystemPropertyUtils.class);
+
+    private SystemPropertyUtils() {
+    }
+
+    public static String getUserHome() {
+        return System.getProperty("user.home");
+    }
+
+    public static boolean contains(String key) {
+        return get(key) != null;
+    }
+
+    public static String get(String key) {
+        return get(key, null);
+    }
+
+    public static String get(String key, String defaultValue) {
+        if (key == null) {
+            throw new IllegalArgumentException("[StreamPark] key must not be null.");
+        }
+        if (key.isEmpty()) {
+            throw new IllegalArgumentException("Key must not be empty.");
+        }
+        try {
+            String value;
+            if (System.getSecurityManager() == null) {
+                value = System.getProperty(key);
+            } else {
+                value =
+                    AccessController.doPrivileged(
+                        (PrivilegedAction<String>) () -> System.getProperty(key));
+            }
+            return value != null ? value : defaultValue;
+        } catch (Exception e) {
+            LOG.warn(
+                "[StreamPark] Unable to retrieve a system property '{}'; default values will be used, {}.",
+                key,
+                e.getMessage());
+            return defaultValue;
+        }
+    }
+
+    public static boolean getBoolean(String key, boolean defaultValue) {
+        String value = get(key);
+        if (value == null) {
+            return defaultValue;
+        }
+        switch (value) {
+            case "true":
+            case "yes":
+            case "1":
+                return true;
+            case "false":
+            case "no":
+            case "0":
+                return false;
+            default:
+                if (value.isEmpty()) {
+                    return false;
+                }
+                LOG.warn(
+                    "[StreamPark] Unable to parse the boolean system property '{}':{} - using the default value: {}.",
+                    key,
+                    value,
+                    defaultValue);
+                return defaultValue;
+        }
+    }
+
+    public static int getInt(String key, int defaultValue) {
+        try {
+            return Integer.parseInt(get(key));
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+    public static long getLong(String key, long defaultValue) {
+        try {
+            return Long.parseLong(get(key));
+        } catch (Exception e) {
+            return defaultValue;
+        }
+    }
+
+    public static String set(String key, String value) {
+        return (String) System.getProperties().setProperty(key, value);
+    }
+
+    public static void setEnv(String name, String value) throws Exception {
+        Class<?> envClass = Class.forName("java.lang.ProcessEnvironment");
+        java.lang.reflect.Method getEnv = envClass.getDeclaredMethod("getenv");
+        getEnv.setAccessible(true);
+        Object unmodifiableEnvironment = getEnv.invoke(null);
+        Class<?> clazz = Class.forName("java.util.Collections$UnmodifiableMap");
+        java.lang.reflect.Field field = clazz.getDeclaredField("m");
+        field.setAccessible(true);
+        @SuppressWarnings("unchecked")
+        Map<String, String> map = (Map<String, String>) field.get(unmodifiableEnvironment);
+        map.put(name, value);
+    }
+
+    public static String getOrElseUpdate(String key, String defaultValue) {
+        String existing = get(key);
+        if (existing == null) {
+            set(key, defaultValue);
+            return defaultValue;
+        }
+        return existing;
+    }
+
+    public static void setAppHome(String key, Class<?> clazz) {
+        if (get(key) == null) {
+            String jarOrClassPath = clazz.getProtectionDomain().getCodeSource().getLocation().getPath();
+            File file = new File(jarOrClassPath);
+            String appHome;
+            if (jarOrClassPath.endsWith("jar")) {
+                appHome = file.getParentFile().getParentFile().getPath();
+            } else {
+                appHome = CLASSES_SUFFIX_PATTERN.matcher(file.getPath()).replaceAll("");
+            }
+            SystemPropertyUtils.set(key, appHome);
+        }
+    }
+
+    public static String getTmpdir() {
+        return get("java.io.tmpdir", "temp");
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/ThreadUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/ThreadUtils.java
new file mode 100644
index 000000000..015dd902b
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/ThreadUtils.java
@@ -0,0 +1,56 @@
+/*
+ * 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.streampark.common.util;
+
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+public final class ThreadUtils {
+
+    private ThreadUtils() {
+    }
+
+    public static ThreadFactory threadFactory(String threadName, boolean isDaemon) {
+        return new ThreadFactoryBuilder()
+            .setNameFormat(threadName + "-%d")
+            .setDaemon(isDaemon)
+            .build();
+    }
+
+    public static ThreadFactory threadFactory(String threadName) {
+        return threadFactory(threadName, true);
+    }
+
+    public static void shutdownExecutorService(ExecutorService executorService) throws InterruptedException {
+        shutdownExecutorService(executorService, 5);
+    }
+
+    public static void shutdownExecutorService(ExecutorService executorService,
+                                               int timeoutS) throws InterruptedException {
+        if (executorService != null && !executorService.isShutdown()) {
+            executorService.shutdown();
+            if (!executorService.awaitTermination(timeoutS, TimeUnit.SECONDS)) {
+                executorService.shutdownNow();
+                executorService.awaitTermination(timeoutS, TimeUnit.SECONDS);
+            }
+        }
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/Utils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/Utils.java
new file mode 100644
index 000000000..1dfa2c9f5
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/Utils.java
@@ -0,0 +1,246 @@
+/*
+ * 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.streampark.common.util;
+
+import org.apache.streampark.shaded.org.slf4j.Logger;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.Flushable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Properties;
+import java.util.UUID;
+import java.util.concurrent.locks.LockSupport;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+import java.util.regex.Pattern;
+
+/** General utility methods. */
+public final class Utils {
+
+    private static final Logger LOG =
+        StreamParkLoggerFactory.loggerFactory().getLogger(Utils.class.getName());
+
+    private static final Pattern UUID_DASH_PATTERN = Pattern.compile("-");
+
+    private static final String OS = System.getProperty("os.name").toLowerCase();
+
+    private Utils() {
+    }
+
+    public static boolean isNotEmpty(Object elem) {
+        if (elem == null) {
+            return false;
+        }
+        if (elem instanceof Object[]) {
+            return ((Object[]) elem).length > 0;
+        }
+        if (elem instanceof CharSequence) {
+            return elem.toString().trim().length() > 0;
+        }
+        if (elem instanceof Collection) {
+            return !((Collection<?>) elem).isEmpty();
+        }
+        if (elem instanceof Iterable) {
+            return ((Iterable<?>) elem).iterator().hasNext();
+        }
+        if (elem instanceof Map) {
+            return !((Map<?, ?>) elem).isEmpty();
+        }
+        return true;
+    }
+
+    public static boolean isEmpty(Object elem) {
+        return !isNotEmpty(elem);
+    }
+
+    public static String uuid() {
+        return UUID_DASH_PATTERN.matcher(UUID.randomUUID().toString()).replaceAll("");
+    }
+
+    public static void requireCheckJarFile(URL jar) throws IOException {
+        try (
+            InputStream in = SafePathUtils.openJarFile(jar);
+            JarInputStream ignored = new JarInputStream(new BufferedInputStream(in))) {
+            // verify jar is readable
+        } catch (IOException e) {
+            throw new IOException("Error while opening jar file '" + jar + "'", e);
+        }
+    }
+
+    public static Manifest getJarManifest(File jarFile) throws IOException {
+        try (
+            InputStream in = SafePathUtils.openJarFile(jarFile.toURI().toURL());
+            JarInputStream jarInputStream = new JarInputStream(new BufferedInputStream(in))) {
+            return jarInputStream.getManifest();
+        }
+    }
+
+    public static String getJarManClass(File jarFile) {
+        try {
+            Manifest manifest = getJarManifest(jarFile);
+            String mainClass = manifest.getMainAttributes().getValue("Main-Class");
+            if (mainClass == null) {
+                mainClass = manifest.getMainAttributes().getValue("program-class");
+            }
+            return mainClass;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void copyProperties(Properties original, Properties target) {
+        for (String key : original.stringPropertyNames()) {
+            target.put(key, original.getProperty(key));
+        }
+    }
+
+    public static Properties toProperties(Map<String, String> map) {
+        Properties properties = new Properties();
+        if (map != null) {
+            map.forEach(properties::setProperty);
+        }
+        return properties;
+    }
+
+    public static boolean isLinux() {
+        return OS.indexOf("linux") >= 0;
+    }
+
+    public static boolean isWindows() {
+        return OS.indexOf("windows") >= 0;
+    }
+
+    public static boolean isAnyBank(String... items) {
+        if (items == null) {
+            return true;
+        }
+        for (String item : items) {
+            if (StringUtils.isBlank(item)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static double calPercent(long num1, long num2) {
+        if (num1 == 0 || num2 == 0) {
+            return 0.0;
+        }
+        return Double.parseDouble(String.format("%.1f", num1 * 100.0 / num2));
+    }
+
+    public static int hashCode(Object... elements) {
+        if (elements == null) {
+            return 0;
+        }
+        int result = 1;
+        for (Object elem : elements) {
+            int hash = elem == null ? 0 : elem.hashCode();
+            result = 31 * result + hash;
+        }
+        return result;
+    }
+
+    @SafeVarargs
+    public static void close(java.util.function.Consumer<Throwable> func, AutoCloseable... closeable) {
+        for (AutoCloseable c : closeable) {
+            try {
+                if (c != null) {
+                    if (c instanceof Flushable) {
+                        ((Flushable) c).flush();
+                    }
+                    c.close();
+                }
+            } catch (Throwable e) {
+                if (func != null) {
+                    func.accept(e);
+                }
+            }
+        }
+    }
+
+    public static void close(AutoCloseable... closeable) {
+        close(null, closeable);
+    }
+
+    public static <R> java.util.Optional<R> retry(int retryCount, Duration interval, RetrySupplier<R> supplier) {
+        if (retryCount < 0) {
+            throw new IllegalArgumentException("retryCount must be >= 0");
+        }
+        try {
+            return java.util.Optional.of(supplier.get());
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            return java.util.Optional.empty();
+        } catch (Exception e) {
+            if (retryCount > 0) {
+                LOG.warn("[StreamPark] Retry failed, execution caused by: ", e);
+                LOG.warn(
+                    "[StreamPark] {} times retry remaining, the next attempt will be in {} ms",
+                    retryCount,
+                    interval.toMillis());
+                LockSupport.parkNanos(interval.toNanos());
+                return retry(retryCount - 1, interval, supplier);
+            }
+            return java.util.Optional.empty();
+        }
+    }
+
+    public static boolean checkHttpURL(String urlString) {
+        try {
+            URL url = new URL(urlString);
+            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+            connection.setRequestMethod("HEAD");
+            return connection.getResponseCode() == HttpURLConnection.HTTP_OK;
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    public static void printLogo(String info) {
+        System.out.println("\n");
+        System.out.println("        _____ __                                             __       ");
+        System.out.println("       / ___// /_________  ____ _____ ___  ____  ____ ______/ /__     ");
+        System.out.println("       \\__ \\/ __/ ___/ _ \\/ __ `/ __ `__ \\/ __ \\  __ `/ ___/ //_/");
+        System.out.println("      ___/ / /_/ /  /  __/ /_/ / / / / / / /_/ / /_/ / /  / ,<        ");
+        System.out.println("     /____/\\__/_/   \\___/\\__,_/_/ /_/ /_/ ____/\\__,_/_/  /_/|_|   ");
+        System.out.println("                                       /_/                        \n\n");
+        System.out.println("    Version:  3.0.0-SNAPSHOT                                          ");
+        System.out.println("    WebSite:  https://streampark.apache.org                           ");
+        System.out.println("    GitHub :  https://github.com/apache/streampark                    ");
+        System.out.println("    Info   :  " + info + "                                 ");
+        System.out.println("    Time   :  " + LocalDateTime.now(ZoneId.systemDefault()) + "              \n\n");
+    }
+
+    @FunctionalInterface
+    public interface RetrySupplier<R> {
+
+        R get() throws Exception;
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/YarnUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/YarnUtils.java
new file mode 100644
index 000000000..62fed8ef9
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/YarnUtils.java
@@ -0,0 +1,301 @@
+/*
+ * 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.streampark.common.util;
+
+import org.apache.streampark.common.conf.CommonConfig;
+import org.apache.streampark.common.conf.InternalConfigHolder;
+import org.apache.streampark.common.constants.Constants;
+
+import org.apache.streampark.shaded.org.slf4j.Logger;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.CommonConfigurationKeys;
+import org.apache.hadoop.net.NetUtils;
+import org.apache.hadoop.yarn.api.records.ApplicationId;
+import org.apache.hadoop.yarn.api.records.ApplicationReport;
+import org.apache.hadoop.yarn.api.records.YarnApplicationState;
+import org.apache.hadoop.yarn.conf.HAUtil;
+import org.apache.hadoop.yarn.conf.YarnConfiguration;
+import org.apache.hadoop.yarn.util.RMHAUtils;
+import org.apache.hc.client5.http.config.RequestConfig;
+import org.apache.hc.core5.util.Timeout;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.security.PrivilegedExceptionAction;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+
+/** YARN cluster utilities. */
+public final class YarnUtils {
+
+    private static final Logger LOG =
+        StreamParkLoggerFactory.loggerFactory().getLogger(YarnUtils.class.getName());
+
+    private static String rmHttpURL;
+
+    public static final String PROXY_YARN_URL =
+        InternalConfigHolder.get(CommonConfig.STREAMPARK_PROXY_YARN_URL());
+
+    public static final boolean HAS_YARN_HTTP_KERBEROS_AUTH =
+        "kerberos"
+            .equalsIgnoreCase(InternalConfigHolder.get(CommonConfig.STREAMPARK_YARN_AUTH()));
+
+    public static final boolean HAS_YARN_HTTP_SIMPLE_AUTH =
+        "simple".equalsIgnoreCase(InternalConfigHolder.get(CommonConfig.STREAMPARK_YARN_AUTH()));
+
+    private YarnUtils() {
+    }
+
+    public static boolean hasYarnHttpKerberosAuth() {
+        return HAS_YARN_HTTP_KERBEROS_AUTH;
+    }
+
+    public static boolean hasYarnHttpSimpleAuth() {
+        return HAS_YARN_HTTP_SIMPLE_AUTH;
+    }
+
+    public static List<ApplicationId> getAppId(String appName) {
+        EnumSet<YarnApplicationState> appStates =
+            EnumSet.of(
+                YarnApplicationState.RUNNING,
+                YarnApplicationState.ACCEPTED,
+                YarnApplicationState.SUBMITTED);
+        try {
+            List<ApplicationId> appIds = new ArrayList<>();
+            for (ApplicationReport report : HadoopUtils.yarnClient().getApplications(appStates)) {
+                if (appName.equals(report.getName())) {
+                    appIds.add(report.getApplicationId());
+                }
+            }
+            return appIds;
+        } catch (Exception e) {
+            LOG.warn("Failed to list YARN applications for appName={}", appName, e);
+            return new ArrayList<>();
+        }
+    }
+
+    public static YarnApplicationState getState(String appId) {
+        ApplicationId applicationId = ApplicationId.fromString(appId);
+        try {
+            ApplicationReport applicationReport =
+                HadoopUtils.yarnClient().getApplicationReport(applicationId);
+            return applicationReport.getYarnApplicationState();
+        } catch (Exception e) {
+            LOG.warn("Failed to get YARN application state for appId={}", appId, e);
+            return null;
+        }
+    }
+
+    public static boolean isContains(String appName) {
+        try {
+            List<ApplicationReport> runningApps =
+                HadoopUtils.yarnClient().getApplications(EnumSet.of(YarnApplicationState.RUNNING));
+            if (runningApps != null) {
+                for (ApplicationReport app : runningApps) {
+                    if (appName.equals(app.getName())) {
+                        return true;
+                    }
+                }
+            }
+        } catch (Exception e) {
+            LOG.warn("Failed to check whether YARN contains appName={}", appName, e);
+        }
+        return false;
+    }
+
+    public static String getRMWebAppProxyURL() {
+        if (StringUtils.isNotBlank(PROXY_YARN_URL)) {
+            return PROXY_YARN_URL;
+        }
+        return getRMWebAppURL();
+    }
+
+    public static String getRMWebAppURL() {
+        return getRMWebAppURL(false);
+    }
+
+    public static String getRMWebAppURL(boolean getLatest) {
+        if (rmHttpURL == null || getLatest) {
+            synchronized (YarnUtils.class) {
+                Configuration conf = HadoopUtils.hadoopConf();
+                boolean useHttps = YarnConfiguration.useHttps(conf);
+                String addressPrefix;
+                String defaultPort;
+                String protocol;
+                if (useHttps) {
+                    addressPrefix = YarnConfiguration.RM_WEBAPP_HTTPS_ADDRESS;
+                    defaultPort = "8090";
+                    protocol = Constants.HTTPS_SCHEMA;
+                } else {
+                    addressPrefix = YarnConfiguration.RM_WEBAPP_ADDRESS;
+                    defaultPort = "8088";
+                    protocol = Constants.HTTP_SCHEMA;
+                }
+
+                String proxy = conf.get("yarn.web-proxy.address", null);
+                if (proxy != null) {
+                    rmHttpURL = protocol + proxy;
+                } else {
+                    String name;
+                    if (!HAUtil.isHAEnabled(conf)) {
+                        name = addressPrefix;
+                    } else {
+                        YarnConfiguration yarnConf = new YarnConfiguration(conf);
+                        String activeRMId = RMHAUtils.findActiveRMHAId(yarnConf);
+                        if (activeRMId == null) {
+                            LOG.warn(
+                                "[StreamPark] 'findActiveRMHAId' is null,config yarn.acl.enable:{},now http try it.",
+                                yarnConf.get("yarn.acl.enable"));
+                            Map<String, String> idUrlMap = new HashMap<>();
+                            for (String id : HAUtil.getRMHAIds(conf)) {
+                                String address = conf.get(HAUtil.addSuffix(addressPrefix, id));
+                                if (address == null) {
+                                    String hostname =
+                                        conf.get(HAUtil.addSuffix("yarn.resourcemanager.hostname", id));
+                                    address = hostname + ":" + defaultPort;
+                                }
+                                idUrlMap.put(protocol + address, id);
+                            }
+                            activeRMId = null;
+                            int rpcTimeoutForChecks =
+                                yarnConf.getInt(
+                                    CommonConfigurationKeys.HA_FC_CLI_CHECK_TIMEOUT_KEY,
+                                    CommonConfigurationKeys.HA_FC_CLI_CHECK_TIMEOUT_DEFAULT);
+                            for (Map.Entry<String, String> entry : idUrlMap.entrySet()) {
+                                String activeUrl = httpTestYarnRMUrl(entry.getKey(), rpcTimeoutForChecks);
+                                if (activeUrl != null) {
+                                    activeRMId = idUrlMap.get(activeUrl);
+                                    break;
+                                }
+                            }
+                        } else {
+                            LOG.info("[StreamPark] 'findActiveRMHAId' successful");
+                        }
+                        if (activeRMId == null) {
+                            throw new IllegalArgumentException(
+                                "[StreamPark] YarnUtils.getRMWebAppURL: can not found yarn active node");
+                        }
+                        LOG.info("[StreamPark] Current activeRMHAId: {}", activeRMId);
+                        String appActiveRMKey = HAUtil.addSuffix(addressPrefix, activeRMId);
+                        String hostnameActiveRMKey =
+                            HAUtil.addSuffix(YarnConfiguration.RM_HOSTNAME, activeRMId);
+                        if (HAUtil.getConfValueForRMInstance(appActiveRMKey, yarnConf) == null
+                            && HAUtil.getConfValueForRMInstance(hostnameActiveRMKey, yarnConf) != null) {
+                            LOG.info("[StreamPark] Find rm web address by : {}", hostnameActiveRMKey);
+                            name = hostnameActiveRMKey;
+                        } else {
+                            LOG.info("[StreamPark] Find rm web address by : {}", appActiveRMKey);
+                            name = appActiveRMKey;
+                        }
+                    }
+
+                    java.net.InetSocketAddress inetSocketAddress =
+                        conf.getSocketAddr(name, "0.0.0.0:" + defaultPort, Integer.parseInt(defaultPort));
+
+                    java.net.InetSocketAddress address = NetUtils.getConnectAddress(inetSocketAddress);
+
+                    StringBuilder buffer = new StringBuilder(protocol);
+                    java.net.InetAddress resolved = address.getAddress();
+                    if (resolved != null
+                        && !resolved.isAnyLocalAddress()
+                        && !resolved.isLoopbackAddress()) {
+                        buffer.append(address.getHostName());
+                    } else {
+                        try {
+                            buffer.append(InetAddress.getLocalHost().getCanonicalHostName());
+                        } catch (Exception e) {
+                            buffer.append(address.getHostName());
+                        }
+                    }
+                    buffer.append(':').append(address.getPort());
+                    rmHttpURL = buffer.toString();
+                }
+                LOG.info("[StreamPark] Yarn resourceManager webapp url:{}", rmHttpURL);
+            }
+        }
+        return rmHttpURL;
+    }
+
+    private static String httpTestYarnRMUrl(String url, int timeout) {
+        RequestConfig config =
+            RequestConfig.custom().setConnectTimeout(timeout, TimeUnit.MILLISECONDS).build();
+        return HttpClientUtils.httpGetRequest(url, config);
+    }
+
+    public static String getYarnAppTrackingUrl(ApplicationId applicationId) {
+        try {
+            return HadoopUtils.yarnClient().getApplicationReport(applicationId).getTrackingUrl();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static String restRequest(String url, Timeout timeout) throws IOException {
+        if (url == null) {
+            return null;
+        }
+        if (url.matches("^http(|s)://.*")) {
+            try {
+                return request(url, timeout);
+            } catch (Exception e) {
+                if (HAS_YARN_HTTP_KERBEROS_AUTH) {
+                    throw new IOException("yarnUtils authRestRequest error, url: " + url + ", detail: " + e, e);
+                }
+                throw new IOException("yarnUtils restRequest error, url: " + url + ", detail: " + e, e);
+            }
+        }
+        try {
+            return request(getRMWebAppURL() + "/" + url, timeout);
+        } catch (Exception first) {
+            Optional<String> retried =
+                Utils.retry(
+                    5,
+                    Duration.ofSeconds(5),
+                    () -> request(getRMWebAppURL(true) + "/" + url, timeout));
+            if (retried.isPresent()) {
+                return retried.get();
+            }
+            throw new IOException(
+                "yarnUtils restRequest retry 5 times all failed. detail: " + first, first);
+        }
+    }
+
+    private static String request(String reqUrl, Timeout timeout) throws Exception {
+        RequestConfig config = RequestConfig.custom().setConnectTimeout(timeout).build();
+        if (HAS_YARN_HTTP_KERBEROS_AUTH) {
+            return HadoopUtils.getUgi()
+                .doAs(
+                    (PrivilegedExceptionAction<String>) () -> HttpClientUtils.httpAuthGetRequest(reqUrl, config));
+        }
+        String url;
+        if (!HAS_YARN_HTTP_SIMPLE_AUTH) {
+            url = reqUrl;
+        } else {
+            url = reqUrl + "?user.name=" + HadoopConfigUtils.HADOOP_USER_NAME;
+        }
+        return HttpClientUtils.httpGetRequest(url, config);
+    }
+}
diff --git a/streampark-common/src/main/java/org/apache/streampark/common/util/ZooKeeperUtils.java b/streampark-common/src/main/java/org/apache/streampark/common/util/ZooKeeperUtils.java
new file mode 100644
index 000000000..a53d3f4af
--- /dev/null
+++ b/streampark-common/src/main/java/org/apache/streampark/common/util/ZooKeeperUtils.java
@@ -0,0 +1,165 @@
+/*
+ * 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.streampark.common.util;
+
+import org.apache.streampark.shaded.org.slf4j.Logger;
+
+import org.apache.curator.RetryPolicy;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.RetryNTimes;
+import org.apache.zookeeper.CreateMode;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public final class ZooKeeperUtils {
+
+    private static final Logger LOG =
+        StreamParkLoggerFactory.loggerFactory().getLogger(ZooKeeperUtils.class.getName());
+
+    private static final String CONNECT = "localhost:2181";
+    private static final Map<String, CuratorFramework> CLIENT_MAP = new ConcurrentHashMap<>();
+
+    private ZooKeeperUtils() {
+    }
+
+    public static CuratorFramework getClient() {
+        return getClient(CONNECT);
+    }
+
+    public static CuratorFramework getClient(String url) {
+        CuratorFramework existing = CLIENT_MAP.get(url);
+        if (existing != null) {
+            return existing;
+        }
+        try {
+            RetryPolicy retryPolicy = new RetryNTimes(5, 2000);
+            CuratorFramework client =
+                CuratorFrameworkFactory.builder()
+                    .connectString(url)
+                    .retryPolicy(retryPolicy)
+                    .connectionTimeoutMs(2000)
+                    .build();
+            client.start();
+            CLIENT_MAP.put(url, client);
+            return client;
+        } catch (Exception e) {
+            throw new IllegalStateException(e.getMessage(), e);
+        }
+    }
+
+    public static void close(String url) {
+        CuratorFramework client = getClient(url);
+        if (client != null) {
+            client.close();
+            CLIENT_MAP.remove(url);
+        }
+    }
+
+    public static List<String> listChildren(String path) throws Exception {
+        return listChildren(path, CONNECT);
+    }
+
+    public static List<String> listChildren(String path, String url) throws Exception {
+        CuratorFramework client = getClient(url);
+        if (client.checkExists().forPath(path) == null) {
+            return new ArrayList<>();
+        }
+        return client.getChildren().forPath(path);
+    }
+
+    public static boolean create(String path) throws Exception {
+        return create(path, null, CONNECT, false);
+    }
+
+    public static boolean create(String path, String value, String url, boolean persistent) throws Exception {
+        try {
+            CuratorFramework client = getClient(url);
+            if (client.checkExists().forPath(path) == null) {
+                byte[] data =
+                    value == null || value.isEmpty()
+                        ? new byte[0]
+                        : value.getBytes(StandardCharsets.UTF_8);
+                CreateMode mode = persistent ? CreateMode.PERSISTENT : CreateMode.EPHEMERAL;
+                String opResult =
+                    client.create().creatingParentsIfNeeded().withMode(mode).forPath(path, data);
+                return path.equals(opResult);
+            }
+            return false;
+        } catch (Exception e) {
+            LOG.warn("Failed to create ZooKeeper path {}", path, e);
+            return false;
+        }
+    }
+
+    public static boolean update(String path, String value, String url, boolean persistent) throws Exception {
+        try {
+            CuratorFramework client = getClient(url);
+            if (client.checkExists().forPath(path) == null) {
+                CreateMode mode = persistent ? CreateMode.PERSISTENT : CreateMode.EPHEMERAL;
+                String opResult =
+                    client.create()
+                        .creatingParentsIfNeeded()
+                        .withMode(mode)
+                        .forPath(path, value.getBytes(StandardCharsets.UTF_8));
+                return path.equals(opResult);
+            }
+            client.setData().forPath(path, value.getBytes(StandardCharsets.UTF_8));
+            return true;
+        } catch (Exception e) {
+            LOG.warn("Failed to update ZooKeeper path {}", path, e);
+            return false;
+        }
+    }
+
+    public static void delete(String path) throws Exception {
+        delete(path, CONNECT);
+    }
+
+    public static void delete(String path, String url) throws Exception {
+        try {
+            CuratorFramework client = getClient(url);
+            if (client.checkExists().forPath(path) != null) {
+                client.delete().deletingChildrenIfNeeded().forPath(path);
+            }
+        } catch (Exception e) {
+            LOG.warn("Failed to delete ZooKeeper path {}", path, e);
+        }
+    }
+
+    public static String get(String path) throws Exception {
+        return get(path, CONNECT);
+    }
+
+    public static String get(String path, String url) throws Exception {
+        try {
+            CuratorFramework client = getClient(url);
+            if (client.checkExists().forPath(path) == null) {
+                return null;
+            }
+            return new String(client.getData().forPath(path));
+        } catch (Exception e) {
+            LOG.warn("Failed to read ZooKeeper path {}", path, e);
+            return null;
+        }
+    }
+}
diff --git a/streampark-common/src/main/scala/org/apache/streampark/common/conf/CommonConfig.scala b/streampark-common/src/main/scala/org/apache/streampark/common/conf/CommonConfig.scala
deleted file mode 100644
index c2d2540a7..000000000
--- a/streampark-common/src/main/scala/org/apache/streampark/common/conf/CommonConfig.scala
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.streampark.common.conf
-
-import java.lang.{Integer => JavaInt, Long => JavaLong}
-
-object CommonConfig {
-
-  val STREAMPARK_WORKSPACE_LOCAL: InternalOption = InternalOption(
-    key = "streampark.workspace.local",
-    defaultValue = "/streampark",
-    classType = classOf[String])
-
-  val STREAMPARK_WORKSPACE_REMOTE: InternalOption = InternalOption(
-    key = "streampark.workspace.remote",
-    defaultValue = "/streampark",
-    classType = classOf[String])
-
-  val STREAMPARK_HADOOP_USER_NAME: InternalOption = InternalOption(
-    key = "streampark.hadoop-user-name",
-    defaultValue = "hdfs",
-    classType = classOf[String])
-
... 10174 lines suppressed ...