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

diqiu50 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/main by this push:
     new a6a980c921 [#10705] improvement(build): add switch to enable web-ui in 
configuration and gradlew build parameter (#10706)
a6a980c921 is described below

commit a6a980c921543e6cc46f25ef5df954bfd4744d10
Author: Qi Yu <[email protected]>
AuthorDate: Thu Apr 16 09:56:16 2026 +0800

    [#10705] improvement(build): add switch to enable web-ui in configuration 
and gradlew build parameter (#10706)
    
    ### What changes were proposed in this pull request?
    - Add `-PskipWebWar=true` support to skip web/web-v2 war build and
    packaging.
    - Add `gravitino.server.webui.enable` (default `true`) and make
    `GravitinoServer` respect it, so deploy mode can start without war when
    web is skipped.
    - Update integration-test build scripts to make web build dependency
    optional under `skipWebWar`.
    - Update non-frontend GitHub workflows to pass `-PskipWebWar=true`.
    - Add server test to verify startup with web UI disabled.
    
    ### Why are the changes needed?
    In many non-frontend scenarios (especially integration tests), building
    web war is unnecessary and slows CI. This change avoids that cost while
    keeping server startup valid.
    
    Fix: #10705
    
    ### Does this PR introduce _any_ user-facing change?
    - New config: `gravitino.server.webui.enable` (default `true`).
    - New build property: `-PskipWebWar=true`.
    
    ### How was this patch tested?
    - `./gradlew --no-daemon :server:test -PskipITs`
    - `./gradlew --no-daemon compileDistribution -PskipWebWar=true -x test`
    - Local sanity check for CI arg compatibility: `./gradlew help
    -PskipWebWar=true -q`
    
    ---------
    
    Signed-off-by: Roshan1299 <[email protected]>
    Signed-off-by: George T. C. Lai <[email protected]>
    Co-authored-by: Copilot <[email protected]>
    Co-authored-by: tanya0793 <[email protected]>
    Co-authored-by: Alex Schönbeck <[email protected]>
    Co-authored-by: AmitaWhite <[email protected]>
    Co-authored-by: AmitaWhite <[email protected]>
    Co-authored-by: Tushar Singh 
<[email protected]>
    Co-authored-by: ChangJun Rho <[email protected]>
    Co-authored-by: Jalina2007 <[email protected]>
    Co-authored-by: Babu Mahesh <[email protected]>
    Co-authored-by: Aditi102005 <[email protected]>
    Co-authored-by: Roshan Banisetti 
<[email protected]>
    Co-authored-by: HONGGEUN JI <[email protected]>
    Co-authored-by: yunhwan <[email protected]>
    Co-authored-by: Yuhui <[email protected]>
    Co-authored-by: Claude Opus 4.6 <[email protected]>
    Co-authored-by: geyanggang <[email protected]>
    Co-authored-by: George T. C. Lai <[email protected]>
    Co-authored-by: Jerry Shao <[email protected]>
    Co-authored-by: Lukas Simek <[email protected]>
    Co-authored-by: Bharath Krishna <[email protected]>
    Co-authored-by: mchades <[email protected]>
    Co-authored-by: Sun Yuhan <[email protected]>
    Co-authored-by: Sun <[email protected]>
    Co-authored-by: jarred0214 <[email protected]>
    Co-authored-by: jarred0214 <[email protected]>
    Co-authored-by: Akshay Thorat <[email protected]>
    Co-authored-by: Sachin Ranjalkar 
<[email protected]>
---
 .../workflows/access-control-integration-test.yml  |   8 +-
 .../workflows/backend-integration-test-action.yml  |   4 +-
 .github/workflows/build.yml                        |  12 ++-
 .github/workflows/cron-integration-test.yml        |   4 +-
 .../workflows/flink-integration-test-action.yml    |   8 +-
 .github/workflows/gvfs-fuse-build-test.yml         |   5 +-
 .../maintenance-integration-test-action.yml        |   6 +-
 .github/workflows/python-integration-test.yml      |   4 +-
 .../workflows/spark-integration-test-action.yml    |  10 +-
 .github/workflows/trino-integration-test.yml       |   2 +-
 .github/workflows/trino-multi-version-test.yml     |   2 +-
 README.md                                          |   6 ++
 build.gradle.kts                                   |  61 +++++++----
 docs/how-to-build.md                               |   6 ++
 .../apache/gravitino/server/web/JettyServer.java   | 111 ++++++++++++++-------
 .../apache/gravitino/server/GravitinoServer.java   |   9 +-
 web-v2/integration-test/build.gradle.kts           |   5 +-
 web/integration-test/build.gradle.kts              |   5 +-
 18 files changed, 174 insertions(+), 94 deletions(-)

diff --git a/.github/workflows/access-control-integration-test.yml 
b/.github/workflows/access-control-integration-test.yml
index ca2f00a4b4..c4fbc2ee3e 100644
--- a/.github/workflows/access-control-integration-test.yml
+++ b/.github/workflows/access-control-integration-test.yml
@@ -78,7 +78,7 @@ jobs:
 
       - name: Package Gravitino
         run: |
-          ./gradlew compileDistribution -x test 
+          ./gradlew compileDistribution -PskipWeb=true -x test
 
       - name: Free up disk space
         run: |
@@ -87,9 +87,9 @@ jobs:
       - name: Authorization Integration Test (JDK${{ matrix.java-version }})
         id: integrationTest
         run: |
-          ./gradlew -PtestMode=embedded -PjdbcBackend=h2 
-PskipDockerTests=false :authorizations:test
-          ./gradlew -PtestMode=deploy -PjdbcBackend=mysql 
-PskipDockerTests=false :authorizations:test
-          ./gradlew -PtestMode=deploy -PjdbcBackend=postgresql 
-PskipDockerTests=false :authorizations:test
+          ./gradlew -PtestMode=embedded -PjdbcBackend=h2 
-PskipDockerTests=false -PskipWeb=true :authorizations:test
+          ./gradlew -PtestMode=deploy -PjdbcBackend=mysql 
-PskipDockerTests=false -PskipWeb=true :authorizations:test
+          ./gradlew -PtestMode=deploy -PjdbcBackend=postgresql 
-PskipDockerTests=false -PskipWeb=true :authorizations:test
 
       - name: Upload integrate tests reports
         uses: actions/upload-artifact@v7
diff --git a/.github/workflows/backend-integration-test-action.yml 
b/.github/workflows/backend-integration-test-action.yml
index e4ef8abf29..cae3c9057b 100644
--- a/.github/workflows/backend-integration-test-action.yml
+++ b/.github/workflows/backend-integration-test-action.yml
@@ -52,7 +52,7 @@ jobs:
       - name: Package Gravitino
         if: ${{ inputs.test-mode == 'deploy' }}
         run: |
-          ./gradlew compileDistribution -x test -x web-v2:web:build
+          ./gradlew compileDistribution -PskipWeb=true -x test
 
       - name: Free up disk space
         run: |
@@ -78,7 +78,7 @@ jobs:
             done
           fi
 
-          ./gradlew test -PskipTests -PtestMode=${{ inputs.test-mode }} 
-PjdbcBackend=${{ inputs.backend }} -PskipDockerTests=false \
+          ./gradlew test -PskipTests -PtestMode=${{ inputs.test-mode }} 
-PjdbcBackend=${{ inputs.backend }} -PskipDockerTests=false -PskipWeb=true \
             -x :web:web:test -x :web:integration-test:test -x :web-v2:web:test 
-x :web-v2:integration-test:test -x :clients:client-python:test -x 
:flink-connector:flink:test -x :spark-connector:spark-common:test \
             -x :spark-connector:spark-3.3:test -x 
:spark-connector:spark-3.4:test -x :spark-connector:spark-3.5:test \
             -x :spark-connector:spark-runtime-3.3:test -x 
:spark-connector:spark-runtime-3.4:test -x 
:spark-connector:spark-runtime-3.5:test \
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 3a7902e74d..a5a44a776d 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -114,7 +114,7 @@ jobs:
 
       - name: Build with Gradle
         run: |
-          ./gradlew assemble
+          ./gradlew assemble -PskipWeb=true
 
   # To check the spark-connector is compatible with scala2.13
   spark-connector-build:
@@ -137,9 +137,9 @@ jobs:
 
       - name: Build with Scala2.13
         run: |
-          ./gradlew :spark-connector:spark-3.3:build -PscalaVersion=2.13 
-PskipITs -PskipDockerTests=false
-          ./gradlew :spark-connector:spark-3.4:build -PscalaVersion=2.13 
-PskipITs -PskipDockerTests=false
-          ./gradlew :spark-connector:spark-3.5:build -PscalaVersion=2.13 
-PskipITs -PskipDockerTests=false
+          ./gradlew :spark-connector:spark-3.3:build -PscalaVersion=2.13 
-PskipITs -PskipDockerTests=false -PskipWeb=true
+          ./gradlew :spark-connector:spark-3.4:build -PscalaVersion=2.13 
-PskipITs -PskipDockerTests=false -PskipWeb=true
+          ./gradlew :spark-connector:spark-3.5:build -PscalaVersion=2.13 
-PskipITs -PskipDockerTests=false -PskipWeb=true
 
       - name: Upload unit tests report
         uses: actions/upload-artifact@v7
@@ -171,7 +171,7 @@ jobs:
           cache: 'gradle'
 
       - name: Test publish to local
-        run: ./gradlew publishToMavenLocal -x test
+        run: ./gradlew publishToMavenLocal -PskipWeb=true -x test
 
       - name: Free up disk space
         run: |
@@ -190,6 +190,7 @@ jobs:
               :maintenance:updaters:build \
               :maintenance:optimizer:build \
               :maintenance:jobs:build \
+              -PskipWeb=true \
               -PskipITs \
               -PskipDockerTests=false
             exit 0
@@ -197,6 +198,7 @@ jobs:
 
           gradle_args=(
             build
+            -PskipWeb=true
             -PskipITs
             -PskipDockerTests=false
             -x :clients:client-python:build
diff --git a/.github/workflows/cron-integration-test.yml 
b/.github/workflows/cron-integration-test.yml
index 18f8a47fff..71cb552fe9 100644
--- a/.github/workflows/cron-integration-test.yml
+++ b/.github/workflows/cron-integration-test.yml
@@ -75,7 +75,7 @@ jobs:
 
       - name: Package Gravitino
         run: |
-          ./gradlew compileDistribution -x test 
+          ./gradlew compileDistribution -PskipWeb=true -x test
 
       - name: Free up disk space
         run: |
@@ -84,7 +84,7 @@ jobs:
       - name: Integration Test
         id: integrationTest
         run: |
-          ./gradlew test -PskipTests -PtestMode=${{ matrix.test-mode }} 
-PskipDockerTests=false
+          ./gradlew test -PskipTests -PtestMode=${{ matrix.test-mode }} 
-PskipDockerTests=false -PskipWeb=true
 
       - name: Upload integrate tests reports
         uses: actions/upload-artifact@v7
diff --git a/.github/workflows/flink-integration-test-action.yml 
b/.github/workflows/flink-integration-test-action.yml
index b2c8293ebd..d3e82683d9 100644
--- a/.github/workflows/flink-integration-test-action.yml
+++ b/.github/workflows/flink-integration-test-action.yml
@@ -38,7 +38,7 @@ jobs:
 
       - name: Package Gravitino
         run: |
-          ./gradlew compileDistribution -x test 
+          ./gradlew compileDistribution -PskipWeb=true -x test
 
       - name: Free up disk space
         run: |
@@ -48,8 +48,8 @@ jobs:
         id: integrationTest
         # run embedded mode and deploy mode integration tests
         run: |
-          ./gradlew -PskipTests -PtestMode=embedded -PskipDockerTests=false 
:flink-connector:flink:test --tests 
"org.apache.gravitino.flink.connector.integration.test.**"
-          ./gradlew -PskipTests -PtestMode=deploy -PskipDockerTests=false 
:flink-connector:flink:test --tests 
"org.apache.gravitino.flink.connector.integration.test.**"
+          ./gradlew -PskipTests -PtestMode=embedded -PskipDockerTests=false 
-PskipWeb=true :flink-connector:flink:test --tests 
"org.apache.gravitino.flink.connector.integration.test.**"
+          ./gradlew -PskipTests -PtestMode=deploy -PskipDockerTests=false 
-PskipWeb=true :flink-connector:flink:test --tests 
"org.apache.gravitino.flink.connector.integration.test.**"
 
       - name: Upload integrate tests reports
         uses: actions/upload-artifact@v7
@@ -63,4 +63,4 @@ jobs:
             distribution/package/logs/gravitino-server.out
             distribution/package/logs/gravitino-server.log
             catalogs/**/*.log
-            catalogs/**/*.tar
\ No newline at end of file
+            catalogs/**/*.tar
diff --git a/.github/workflows/gvfs-fuse-build-test.yml 
b/.github/workflows/gvfs-fuse-build-test.yml
index f82c654fb5..dc80cbf7bc 100644
--- a/.github/workflows/gvfs-fuse-build-test.yml
+++ b/.github/workflows/gvfs-fuse-build-test.yml
@@ -78,8 +78,8 @@ jobs:
 
       - name: Integration test
         run: |
-          ./gradlew build -x :clients:client-python:build -x test -x web 
-          ./gradlew compileDistribution -x :clients:client-python:build -x 
test -x web 
+          ./gradlew build -PskipWeb=true -x :clients:client-python:build -x 
test -x web
+          ./gradlew compileDistribution -PskipWeb=true -x 
:clients:client-python:build -x test -x web
           cd clients/filesystem-fuse
           make test-s3
           make test-fuse-it
@@ -97,4 +97,3 @@ jobs:
             clients/filesystem-fuse/target/debug/fuse.log
             distribution/package/logs/gravitino-server.out
             distribution/package/logs/gravitino-server.log
-
diff --git a/.github/workflows/maintenance-integration-test-action.yml 
b/.github/workflows/maintenance-integration-test-action.yml
index c9829cf54b..ba9880fe65 100644
--- a/.github/workflows/maintenance-integration-test-action.yml
+++ b/.github/workflows/maintenance-integration-test-action.yml
@@ -38,7 +38,7 @@ jobs:
 
       - name: Package Gravitino
         run: |
-          ./gradlew compileDistribution -x test
+          ./gradlew compileDistribution -PskipWeb=true -x test
 
       - name: Free up disk space
         run: |
@@ -48,8 +48,8 @@ jobs:
         id: integrationTest
         # run embedded mode and deploy mode integration tests
         run: |
-          ./gradlew -PskipTests -PtestMode=embedded -PskipDockerTests=false 
:maintenance:optimizer:test --tests 
"org.apache.gravitino.maintenance.optimizer.integration.test.**"
-          ./gradlew -PskipTests -PtestMode=deploy -PskipDockerTests=false 
:maintenance:optimizer:test --tests 
"org.apache.gravitino.maintenance.optimizer.integration.test.**"
+          ./gradlew -PskipTests -PtestMode=embedded -PskipDockerTests=false 
-PskipWeb=true :maintenance:optimizer:test --tests 
"org.apache.gravitino.maintenance.optimizer.integration.test.**"
+          ./gradlew -PskipTests -PtestMode=deploy -PskipDockerTests=false 
-PskipWeb=true :maintenance:optimizer:test --tests 
"org.apache.gravitino.maintenance.optimizer.integration.test.**"
 
       - name: Upload integrate tests reports
         uses: actions/upload-artifact@v7
diff --git a/.github/workflows/python-integration-test.yml 
b/.github/workflows/python-integration-test.yml
index f9d9424284..59549ad5c9 100644
--- a/.github/workflows/python-integration-test.yml
+++ b/.github/workflows/python-integration-test.yml
@@ -75,12 +75,12 @@ jobs:
       - name: Python Client Integration Test
         id: integrationTest
         run: |
-          ./gradlew compileDistribution -x test 
+          ./gradlew compileDistribution -PskipWeb=true -x test
 
           for pythonVersion in "3.10" "3.11" "3.12"
           do
             echo "Use Python version ${pythonVersion} to test the Python 
client."
-            ./gradlew -PpythonVersion=${pythonVersion} -PskipDockerTests=false 
:clients:client-python:test
+            ./gradlew -PpythonVersion=${pythonVersion} -PskipDockerTests=false 
-PskipWeb=true :clients:client-python:test
             # Clean Gravitino database to clean test data
             rm -rf ./distribution/package/data
           done
diff --git a/.github/workflows/spark-integration-test-action.yml 
b/.github/workflows/spark-integration-test-action.yml
index 62fd282241..c9312bacda 100644
--- a/.github/workflows/spark-integration-test-action.yml
+++ b/.github/workflows/spark-integration-test-action.yml
@@ -47,7 +47,7 @@ jobs:
       - name: Package Gravitino
         if: ${{ inputs.test-mode == 'deploy' }}
         run: |
-          ./gradlew compileDistribution -x test 
+          ./gradlew compileDistribution -PskipWeb=true -x test
 
       - name: Free up disk space
         run: |
@@ -57,10 +57,10 @@ jobs:
         id: integrationTest
         run: |
           if [ "${{ inputs.scala-version }}" == "2.12" ];then 
-            ./gradlew -PskipTests -PtestMode=${{ inputs.test-mode }} 
-PscalaVersion=${{ inputs.scala-version }} -PskipDockerTests=false 
:spark-connector:spark-3.3:test --tests 
"org.apache.gravitino.spark.connector.integration.test.**"
+            ./gradlew -PskipTests -PtestMode=${{ inputs.test-mode }} 
-PscalaVersion=${{ inputs.scala-version }} -PskipDockerTests=false 
-PskipWeb=true :spark-connector:spark-3.3:test --tests 
"org.apache.gravitino.spark.connector.integration.test.**"
           fi
-          ./gradlew -PskipTests -PtestMode=${{ inputs.test-mode }} 
-PscalaVersion=${{ inputs.scala-version }} -PskipDockerTests=false 
:spark-connector:spark-3.4:test --tests 
"org.apache.gravitino.spark.connector.integration.test.**"
-          ./gradlew -PskipTests -PtestMode=${{ inputs.test-mode }} 
-PscalaVersion=${{ inputs.scala-version }} -PskipDockerTests=false 
:spark-connector:spark-3.5:test --tests 
"org.apache.gravitino.spark.connector.integration.test.**"
+          ./gradlew -PskipTests -PtestMode=${{ inputs.test-mode }} 
-PscalaVersion=${{ inputs.scala-version }} -PskipDockerTests=false 
-PskipWeb=true :spark-connector:spark-3.4:test --tests 
"org.apache.gravitino.spark.connector.integration.test.**"
+          ./gradlew -PskipTests -PtestMode=${{ inputs.test-mode }} 
-PscalaVersion=${{ inputs.scala-version }} -PskipDockerTests=false 
-PskipWeb=true :spark-connector:spark-3.5:test --tests 
"org.apache.gravitino.spark.connector.integration.test.**"
 
       - name: Upload integrate tests reports
         uses: actions/upload-artifact@v7
@@ -73,4 +73,4 @@ jobs:
             spark-connector/v3.4/spark/build/spark-3.4-integration-test.log
             spark-connector/v3.5/spark/build/spark-3.5-integration-test.log
             distribution/package/logs/*.out
-            distribution/package/logs/*.log
\ No newline at end of file
+            distribution/package/logs/*.log
diff --git a/.github/workflows/trino-integration-test.yml 
b/.github/workflows/trino-integration-test.yml
index 77ae9561e8..705c231f64 100644
--- a/.github/workflows/trino-integration-test.yml
+++ b/.github/workflows/trino-integration-test.yml
@@ -76,7 +76,7 @@ jobs:
 
       - name: Build Gravitino
         run: |
-          ./gradlew build -x test \
+          ./gradlew build -PskipWeb=true -x test \
             -x :web:web:build -x :web:integration-test:build \
             -x :web-v2:web:build -x :web-v2:integration-test:build \
             -x :clients:client-python:build \
diff --git a/.github/workflows/trino-multi-version-test.yml 
b/.github/workflows/trino-multi-version-test.yml
index 9dc90a5747..e6e6074994 100644
--- a/.github/workflows/trino-multi-version-test.yml
+++ b/.github/workflows/trino-multi-version-test.yml
@@ -26,7 +26,7 @@ jobs:
 
       - name: Build Gravitino
         run: |
-          ./gradlew build -x test \
+          ./gradlew build -PskipWeb=true -x test \
             -x :web:web:build -x :web:integration-test:build \
             -x :web-v2:web:build -x :web-v2:integration-test:build \
             -x :clients:client-python:build \
diff --git a/README.md b/README.md
index 09600ddc5e..56288fafc8 100644
--- a/README.md
+++ b/README.md
@@ -125,6 +125,12 @@ Build a distribution:
 ./gradlew compileDistribution -x test
 ```
 
+Skip building and packaging both Web UIs:
+
+```bash
+./gradlew compileDistribution -PskipWebBuild=true -x test
+```
+
 Or compressed package:
 
 ```bash
diff --git a/build.gradle.kts b/build.gradle.kts
index cee839c828..d59e6daf13 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -66,6 +66,8 @@ if (scalaVersion !in listOf("2.12", "2.13")) {
   throw GradleException("Scala version $scalaVersion is not supported.")
 }
 
+val skipWeb: Boolean = (project.findProperty("skipWeb") as? 
String)?.toBoolean() ?: false
+
 project.extra["extraJvmArgs"] =
   listOf(
     "-XX:+IgnoreUnrecognizedVMOptions",
@@ -773,21 +775,24 @@ jacoco {
 tasks {
   val projectDir = layout.projectDirectory
   val outputDir = projectDir.dir("distribution")
-
   val compileDistribution by registering {
-    dependsOn(
-      "copyCatalogLibAndConfigs",
-      "copySubprojectDependencies",
-      "copySubprojectLib",
-      "copyCliLib",
-      "copyJobsLib",
-      ":authorizations:copyLibAndConfig",
-      ":iceberg:iceberg-rest-server:copyLibAndConfigs",
-      ":lance:lance-rest-server:copyLibAndConfigs",
-      ":maintenance:optimizer:copyLibAndConfigs",
-      ":web:web:build",
-      ":web-v2:web:build"
-    )
+    val dependencies =
+      mutableListOf(
+        "copyCatalogLibAndConfigs",
+        "copySubprojectDependencies",
+        "copySubprojectLib",
+        "copyCliLib",
+        "copyJobsLib",
+        ":authorizations:copyLibAndConfig",
+        ":iceberg:iceberg-rest-server:copyLibAndConfigs",
+        ":lance:lance-rest-server:copyLibAndConfigs",
+        ":maintenance:optimizer:copyLibAndConfigs"
+      )
+    if (!skipWeb) {
+      dependencies.add(":web:web:build")
+      dependencies.add(":web-v2:web:build")
+    }
+    dependsOn(dependencies)
 
     group = "gravitino distribution"
     outputs.dir(projectDir.dir("distribution/package"))
@@ -795,8 +800,14 @@ tasks {
       copy {
         from(projectDir.dir("conf")) { into("package/conf") }
         from(projectDir.dir("bin")) { into("package/bin") }
-        
from(projectDir.dir("web/web/build/libs/${rootProject.name}-web-$version.war")) 
{ into("package/web") }
-        
from(projectDir.dir("web-v2/web/build/libs/${rootProject.name}-web-$version.war"))
 { into("package/web-v2") }
+        if (!skipWeb) {
+          
from(projectDir.dir("web/web/build/libs/${rootProject.name}-web-$version.war")) 
{
+            into("package/web")
+          }
+          
from(projectDir.dir("web-v2/web/build/libs/${rootProject.name}-web-$version.war"))
 {
+            into("package/web-v2")
+          }
+        }
         from(projectDir.dir("scripts")) { into("package/scripts") }
         into(outputDir)
         rename { fileName ->
@@ -816,18 +827,24 @@ tasks {
         from(projectDir.file("LICENSE.bin")) { into("package") }
         from(projectDir.file("NOTICE.bin")) { into("package") }
         from(projectDir.file("README.md")) { into("package") }
-        from(projectDir.dir("web/web/licenses")) { 
into("package/web/licenses") }
-        from(projectDir.dir("web/web/LICENSE.bin")) { into("package/web") }
-        from(projectDir.dir("web/web/NOTICE.bin")) { into("package/web") }
-        from(projectDir.dir("web-v2/web/licenses")) { 
into("package/web-v2/licenses") }
-        from(projectDir.dir("web-v2/web/LICENSE.bin")) { 
into("package/web-v2") }
-        from(projectDir.dir("web-v2/web/NOTICE.bin")) { into("package/web-v2") 
}
+        if (!skipWeb) {
+          from(projectDir.dir("web/web/licenses")) { 
into("package/web/licenses") }
+          from(projectDir.dir("web/web/LICENSE.bin")) { into("package/web") }
+          from(projectDir.dir("web/web/NOTICE.bin")) { into("package/web") }
+          from(projectDir.dir("web-v2/web/licenses")) { 
into("package/web-v2/licenses") }
+          from(projectDir.dir("web-v2/web/LICENSE.bin")) { 
into("package/web-v2") }
+          from(projectDir.dir("web-v2/web/NOTICE.bin")) { 
into("package/web-v2") }
+        }
         into(outputDir)
         rename { fileName ->
           fileName.replace(".bin", "")
         }
       }
 
+      if (skipWeb) {
+        delete(projectDir.dir("distribution/package/web"), 
projectDir.dir("distribution/package/web-v2"))
+      }
+
       // Create the directory 'data' for storage.
       val directory = File("distribution/package/data")
       directory.mkdirs()
diff --git a/docs/how-to-build.md b/docs/how-to-build.md
index eecc87ff41..3314eb1bb7 100644
--- a/docs/how-to-build.md
+++ b/docs/how-to-build.md
@@ -93,6 +93,12 @@ license: "This software is licensed under the Apache License 
version 2."
    ./gradlew compileDistribution
    ```
 
+   To skip compiling and packaging the Web UI artifacts, use:
+
+   ```shell
+   ./gradlew compileDistribution -PskipWebBuild=true
+   ```
+
   The `compileDistribution` command creates a `distribution` directory in the 
Gravitino root directory. It contains two subdirectories: `package` and 
`package-all`. The difference between these two subdirectories is that 
`package` is the **Gravitino server distribution package**, while `package-all` 
contains extra catalogs in `catalogs-contrib` and all contents in `package`.
   So, if you want to use catalogs in `catalogs-contrib`, you should use the 
distribution package in `package-all`. 
 
diff --git 
a/server-common/src/main/java/org/apache/gravitino/server/web/JettyServer.java 
b/server-common/src/main/java/org/apache/gravitino/server/web/JettyServer.java
index 1af9ed8259..d340a4325b 100644
--- 
a/server-common/src/main/java/org/apache/gravitino/server/web/JettyServer.java
+++ 
b/server-common/src/main/java/org/apache/gravitino/server/web/JettyServer.java
@@ -79,8 +79,14 @@ public class JettyServer {
 
   private String serverName;
 
+  private boolean webUiEnabled;
+
   public JettyServer() {}
 
+  public synchronized void initialize(JettyServerConfig serverConfig, String 
serverName) {
+    initialize(serverConfig, serverName, true);
+  }
+
   public synchronized void initialize(
       JettyServerConfig serverConfig, String serverName, boolean 
shouldEnableUI) {
     this.serverConfig = serverConfig;
@@ -154,11 +160,12 @@ public class JettyServer {
       server.addConnector(httpConnector);
     }
 
-    // Initialize ServletContextHandler or WebAppContext
+    // Initialize ServletContextHandler or WebAppContext.
     if (shouldEnableUI) {
-      initializeWebAppServletContextHandler();
+      webUiEnabled = initializeServletContextHandler();
     } else {
       initializeBasicServletContextHandler();
+      webUiEnabled = false;
     }
 
     MetricsSystem metricsSystem = GravitinoEnv.getInstance().metricsSystem();
@@ -250,52 +257,88 @@ public class JettyServer {
         new FilterHolder(filter), pathSpec, 
EnumSet.allOf(DispatcherType.class));
   }
 
+  public boolean isWebUiEnabled() {
+    return webUiEnabled;
+  }
+
   private void initializeBasicServletContextHandler() {
     servletContextHandler = new ServletContextHandler();
     servletContextHandler.setContextPath("/");
     servletContextHandler.addServlet(DefaultServlet.class, "/");
   }
 
-  private void initializeWebAppServletContextHandler() {
-    servletContextHandler = new WebAppContext();
+  private boolean initializeServletContextHandler() {
+    Optional<String> warPath = resolveWebUiWarPath();
+    if (warPath.isPresent()) {
+      initializeWebAppServletContextHandler(warPath.get());
+      return true;
+    }
 
-    boolean isUnitTest = System.getenv("GRAVITINO_TEST") != null;
-
-    // If in development/test mode, you can set `war` file or `web/dist` 
directory in the
-    // `GRAVITINO_WAR` environment variable.
-    String warPath = System.getenv("GRAVITINO_WAR") != null ? 
System.getenv("GRAVITINO_WAR") : "";
-    if (warPath.isEmpty()) {
-      // Default deploy mode, read from 
`gravitino-${version}/web/gravitino-web.war`
-      String webPath = String.join(File.separator, 
System.getenv("GRAVITINO_HOME"), "web");
-
-      try (DirectoryStream<Path> paths =
-          Files.newDirectoryStream(Paths.get(webPath), "gravitino-web-*.war")) 
{
-        int warCount = 0;
-        for (Path path : paths) {
-          warPath = path.toString();
-          warCount++;
-        }
-        if (warCount != 1 && !isUnitTest) {
-          throw new RuntimeException("Found multiple or no war files in the 
web path : " + webPath);
-        }
-      } catch (IOException e) {
-        throw new RuntimeException("Failed to find war file in the web path : 
" + webPath, e);
+    LOG.info("Web UI WAR is not found, start server without Web UI.");
+    initializeBasicServletContextHandler();
+    return false;
+  }
+
+  private Optional<String> resolveWebUiWarPath() {
+    String warPathFromEnv = System.getenv("GRAVITINO_WAR");
+    if (StringUtils.isNotBlank(warPathFromEnv)) {
+      File warFile = new File(warPathFromEnv);
+      if (warFile.exists()) {
+        return Optional.of(warPathFromEnv);
       }
+
+      LOG.warn("GRAVITINO_WAR path {} does not exist, Web UI is disabled.", 
warPathFromEnv);
+      return Optional.empty();
+    }
+
+    String gravitinoHome = System.getenv("GRAVITINO_HOME");
+    if (StringUtils.isBlank(gravitinoHome)) {
+      LOG.warn("GRAVITINO_HOME is not set, Web UI is disabled.");
+      return Optional.empty();
     }
 
+    Optional<String> webWarPath =
+        findSingleWarInDirectory(String.join(File.separator, gravitinoHome, 
"web"));
+    if (webWarPath.isPresent()) {
+      return webWarPath;
+    }
+
+    return findSingleWarInDirectory(String.join(File.separator, gravitinoHome, 
"web-v2"));
+  }
+
+  private Optional<String> findSingleWarInDirectory(String webPath) {
+    int warCount = 0;
+    String warPath = null;
+    try (DirectoryStream<Path> paths =
+        Files.newDirectoryStream(Paths.get(webPath), "gravitino-web-*.war")) {
+      for (Path path : paths) {
+        warPath = path.toString();
+        warCount++;
+      }
+    } catch (IOException e) {
+      LOG.debug("Failed to read web path {}, ignore and disable Web UI.", 
webPath, e);
+      return Optional.empty();
+    }
+
+    if (warCount == 1) {
+      return Optional.of(warPath);
+    }
+
+    if (warCount > 1) {
+      LOG.warn("Found multiple war files in {}, Web UI is disabled.", webPath);
+    }
+    return Optional.empty();
+  }
+
+  private void initializeWebAppServletContextHandler(String warPath) {
+    servletContextHandler = new WebAppContext();
+
     File warFile = new File(warPath);
     if (!warFile.exists()) {
-      // Check war file if exists
-      if (isUnitTest) {
-        // In development/test mode, We don't have web files in the unit test, 
so only RESTful API
-        // are supported
-        servletContextHandler.setResourceBase("/");
-      } else {
-        // In deployment mode, war files must be available or an exception is 
thrown
-        throw new RuntimeException("Gravitino web path not found in " + 
warPath);
-      }
+      throw new RuntimeException("Gravitino web path not found in " + warPath);
     }
 
+    servletContextHandler = new WebAppContext();
     if (warFile.isDirectory()) {
       // Development mode, read from FS
       servletContextHandler.setResourceBase(warFile.getPath());
diff --git 
a/server/src/main/java/org/apache/gravitino/server/GravitinoServer.java 
b/server/src/main/java/org/apache/gravitino/server/GravitinoServer.java
index 1f96d7543b..af09fc37d4 100644
--- a/server/src/main/java/org/apache/gravitino/server/GravitinoServer.java
+++ b/server/src/main/java/org/apache/gravitino/server/GravitinoServer.java
@@ -103,7 +103,7 @@ public class GravitinoServer extends ResourceConfig {
 
     JettyServerConfig jettyServerConfig =
         JettyServerConfig.fromConfig(serverConfig, WEBSERVER_CONF_PREFIX);
-    server.initialize(jettyServerConfig, SERVER_NAME, true /* shouldEnableUI 
*/);
+    server.initialize(jettyServerConfig, SERVER_NAME);
 
     ServerAuthenticator.getInstance().initialize(serverConfig);
 
@@ -178,9 +178,10 @@ public class GravitinoServer extends ResourceConfig {
     server.addCustomFilters(API_ANY_PATH);
     server.addFilter(new VersioningFilter(), API_ANY_PATH);
     server.addSystemFilters(API_ANY_PATH);
-
-    server.addFilter(new WebUIFilter(), "/"); // Redirect to the /ui/index 
html page.
-    server.addFilter(new WebUIFilter(), "/ui/*"); // Redirect to the static 
html file.
+    if (server.isWebUiEnabled()) {
+      server.addFilter(new WebUIFilter(), "/"); // Redirect to the /ui/index 
html page.
+      server.addFilter(new WebUIFilter(), "/ui/*"); // Redirect to the static 
html file.
+    }
   }
 
   public void start() throws Exception {
diff --git a/web-v2/integration-test/build.gradle.kts 
b/web-v2/integration-test/build.gradle.kts
index 6618c54abd..502cf6f3b4 100644
--- a/web-v2/integration-test/build.gradle.kts
+++ b/web-v2/integration-test/build.gradle.kts
@@ -53,6 +53,7 @@ dependencies {
 }
 
 tasks.test {
+  val skipWeb = (rootProject.findProperty("skipWeb") as? String)?.toBoolean() 
?: false
   val skipITs = project.hasProperty("skipITs")
   if (skipITs) {
     exclude("*")
@@ -67,6 +68,8 @@ tasks.test {
     dependsOn(":catalogs:catalog-kafka:jar", 
":catalogs:catalog-kafka:runtimeJars")
 
     // Frontend tests depend on the web page, so we need to build the web 
module first.
-    dependsOn(":web-v2:web:build")
+    if (!skipWeb) {
+      dependsOn(":web-v2:web:build")
+    }
   }
 }
diff --git a/web/integration-test/build.gradle.kts 
b/web/integration-test/build.gradle.kts
index 11d941ecca..582fd4b579 100644
--- a/web/integration-test/build.gradle.kts
+++ b/web/integration-test/build.gradle.kts
@@ -53,6 +53,7 @@ dependencies {
 }
 
 tasks.test {
+  val skipWeb = (rootProject.findProperty("skipWeb") as? String)?.toBoolean() 
?: false
   val skipITs = project.hasProperty("skipITs")
   if (skipITs) {
     exclude("*")
@@ -67,6 +68,8 @@ tasks.test {
     dependsOn(":catalogs:catalog-kafka:jar", 
":catalogs:catalog-kafka:runtimeJars")
 
     // Frontend tests depend on the web page, so we need to build the web 
module first.
-    dependsOn(":web:web:build")
+    if (!skipWeb) {
+      dependsOn(":web:web:build")
+    }
   }
 }

Reply via email to