This is an automated email from the ASF dual-hosted git repository.
jerryshao pushed a commit to branch 1.2.0-hotfix
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/1.2.0-hotfix by this push:
new fd738b9946 [#11101] feat(iceberg-rest-server): Add health check
endpoints to the Iceberg REST Catalog server (#11102)
fd738b9946 is described below
commit fd738b99468c2a92e011f8dfa7ce275a58795d50
Author: Jerry Shao <[email protected]>
AuthorDate: Fri May 15 16:55:53 2026 +0800
[#11101] feat(iceberg-rest-server): Add health check endpoints to the
Iceberg REST Catalog server (#11102)
Adds MicroProfile-style health check endpoints to the Iceberg REST
Catalog (IRC) server, mirroring the existing endpoints on the main
Gravitino server:
- `GET /iceberg/health/live` — liveness probe; always 200 while the HTTP
thread is responsive
- `GET /iceberg/health/ready` — readiness probe; 200 when
`IcebergCatalogWrapperManager` is initialized, 503 otherwise
- `GET /iceberg/health` — aggregate; 200 only when both pass
Root-level GTM aliases (`/health/*`, `/health.html`) are registered
unconditionally (standalone and auxiliary mode) and forward to
`/iceberg/health*`.
All health paths bypass `IcebergAuthenticationFilter` so infrastructure
probes work without credentials.
`HealthAliasServlet` is moved from `:server` to `:server-common` so it
can be shared by both modules.
`AuthenticationFilter.isHealthCheckRequest` is widened from `private` to
`protected` to allow the override in `IcebergAuthenticationFilter`.
The IRC server runs on its own Jetty instance and has no health
endpoints, making it impossible for Kubernetes probes, load balancers,
and enterprise GTMs to monitor its availability.
Fix: #11101
Yes — three new REST endpoints are available on the IRC server: `GET
/iceberg/health`, `GET /iceberg/health/live`, `GET
/iceberg/health/ready`, plus root-level GTM aliases `/health`,
`/health/live`, `/health/ready`, `/health.html`.
- `TestIcebergHealthOperations` — 5 unit tests covering all UP/DOWN
combinations for liveness, readiness, and aggregate endpoints
- `TestIcebergAuthenticationFilter` — 2 new parametrized tests verifying
`/iceberg/health*` paths bypass auth and non-health paths do not
- `TestHealthAliasServlet` — extended with custom-prefix cases covering
`/iceberg` forwarding
---------
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
---
.../org/apache/gravitino/iceberg/RESTService.java | 6 +
.../service/rest/IcebergHealthOperations.java | 138 +++++++++++++++++++++
.../service/rest/TestIcebergHealthOperations.java | 94 ++++++++++++++
.../authentication/AuthenticationFilter.java | 15 ++-
.../gravitino/server/web/HealthAliasServlet.java | 31 +++--
.../org/apache/gravitino/server/web/Utils.java | 7 ++
.../authentication/TestAuthenticationFilter.java | 12 +-
.../server/web/TestHealthAliasServlet.java | 20 +++
.../server/web/rest/HealthOperations.java | 24 +---
.../server/web/rest/VersionOperations.java | 3 +-
10 files changed, 318 insertions(+), 32 deletions(-)
diff --git
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/RESTService.java
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/RESTService.java
index 23f1fcf042..2ce3e6fde8 100644
---
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/RESTService.java
+++
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/RESTService.java
@@ -49,6 +49,7 @@ import
org.apache.gravitino.iceberg.service.provider.IcebergConfigProviderFactor
import org.apache.gravitino.listener.EventBus;
import org.apache.gravitino.metrics.MetricsSystem;
import org.apache.gravitino.metrics.source.MetricsSource;
+import org.apache.gravitino.server.web.HealthAliasServlet;
import org.apache.gravitino.server.web.HttpServerMetricsSource;
import org.apache.gravitino.server.web.JettyServer;
import org.apache.gravitino.server.web.JettyServerConfig;
@@ -154,6 +155,11 @@ public class RESTService implements
GravitinoAuxiliaryService {
server.addServlet(servlet, ICEBERG_SPEC);
server.addCustomFilters(ICEBERG_SPEC);
server.addSystemFilters(ICEBERG_SPEC);
+
+ // Root-level aliases for health checks to improve compatibility with
various monitoring
+ // systems that expect a /health endpoint.
+ server.addServlet(new HealthAliasServlet("/iceberg"), "/health/*");
+ server.addServlet(new HealthAliasServlet("/iceberg"), "/health.html");
}
@Override
diff --git
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergHealthOperations.java
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergHealthOperations.java
new file mode 100644
index 0000000000..e91fe5e0a8
--- /dev/null
+++
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergHealthOperations.java
@@ -0,0 +1,138 @@
+/*
+ * 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.gravitino.iceberg.service.rest;
+
+import com.codahale.metrics.annotation.ResponseMetered;
+import com.codahale.metrics.annotation.Timed;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.gravitino.dto.HealthCheckDTO;
+import org.apache.gravitino.dto.responses.HealthResponse;
+import org.apache.gravitino.iceberg.service.IcebergCatalogWrapperManager;
+import org.apache.gravitino.metrics.MetricNames;
+import org.apache.gravitino.server.web.Utils;
+
+/**
+ * Health check endpoints for the Iceberg REST server. Follows the same
MicroProfile Health
+ * semantics as the main Gravitino server.
+ *
+ * <ul>
+ * <li>{@code GET /iceberg/health/live} — liveness, 200 as long as the HTTP
thread can respond
+ * <li>{@code GET /iceberg/health/ready} — readiness, 200 when the catalog
wrapper manager is
+ * initialized
+ * <li>{@code GET /iceberg/health} — aggregate, 200 when both pass
+ * </ul>
+ *
+ * All endpoints return 503 with a JSON body describing the failed check(s)
when unhealthy.
+ */
+@Path("/health")
+@Produces(MediaType.APPLICATION_JSON)
+public class IcebergHealthOperations {
+
+ private static final String CHECK_HTTP_SERVER = "httpServer";
+ private static final String CHECK_CATALOG_WRAPPER_MANAGER =
"catalogWrapperManager";
+
+ @Inject private IcebergCatalogWrapperManager catalogWrapperManager;
+
+ /** Default constructor for Jersey auto-discovery. */
+ public IcebergHealthOperations() {}
+
+ /**
+ * Liveness probe. Returns 200 as long as the HTTP thread can respond.
+ *
+ * @return 200 OK with an UP {@link HealthResponse}
+ */
+ @GET
+ @Path("/live")
+ @Timed(name = "iceberg.health.live." + MetricNames.HTTP_PROCESS_DURATION,
absolute = true)
+ @ResponseMetered(name = "iceberg.health.live", absolute = true)
+ public Response live() {
+ HealthCheckDTO check = up(CHECK_HTTP_SERVER, Collections.emptyMap());
+ return Utils.ok(new HealthResponse(HealthCheckDTO.Status.UP,
Collections.singletonList(check)));
+ }
+
+ /**
+ * Readiness probe. Returns 200 when the {@link
IcebergCatalogWrapperManager} is initialized, 503
+ * otherwise.
+ *
+ * @return 200 OK when ready, 503 Service Unavailable with a DOWN {@link
HealthResponse} otherwise
+ */
+ @GET
+ @Path("/ready")
+ @Timed(name = "iceberg.health.ready." + MetricNames.HTTP_PROCESS_DURATION,
absolute = true)
+ @ResponseMetered(name = "iceberg.health.ready", absolute = true)
+ public Response ready() {
+ HealthCheckDTO managerCheck = checkCatalogWrapperManager();
+ HealthCheckDTO.Status overall = managerCheck.getStatus();
+ HealthResponse body = new HealthResponse(overall,
Collections.singletonList(managerCheck));
+ return overall == HealthCheckDTO.Status.UP ? Utils.ok(body) :
Utils.serviceUnavailable(body);
+ }
+
+ /**
+ * Aggregate health check. Returns 200 when both liveness and readiness
pass, 503 otherwise.
+ *
+ * @return 200 OK when healthy, 503 Service Unavailable with failing checks
described in the body
+ */
+ @GET
+ @Timed(name = "iceberg.health." + MetricNames.HTTP_PROCESS_DURATION,
absolute = true)
+ @ResponseMetered(name = "iceberg.health", absolute = true)
+ public Response health() {
+ List<HealthCheckDTO> checks = new ArrayList<>(2);
+ checks.add(up(CHECK_HTTP_SERVER, Collections.emptyMap()));
+ checks.add(checkCatalogWrapperManager());
+
+ HealthCheckDTO.Status overall =
+ checks.stream().anyMatch(c -> c.getStatus() ==
HealthCheckDTO.Status.DOWN)
+ ? HealthCheckDTO.Status.DOWN
+ : HealthCheckDTO.Status.UP;
+
+ HealthResponse body = new HealthResponse(overall, checks);
+ return overall == HealthCheckDTO.Status.UP ? Utils.ok(body) :
Utils.serviceUnavailable(body);
+ }
+
+ private HealthCheckDTO checkCatalogWrapperManager() {
+ if (getCatalogWrapperManager() == null) {
+ return down(
+ CHECK_CATALOG_WRAPPER_MANAGER, "reason", "catalog wrapper manager
not initialized");
+ }
+ return up(CHECK_CATALOG_WRAPPER_MANAGER, Collections.emptyMap());
+ }
+
+ /** Visible for testing — subclasses override to inject a different manager
instance. */
+ IcebergCatalogWrapperManager getCatalogWrapperManager() {
+ return catalogWrapperManager;
+ }
+
+ private static HealthCheckDTO up(String name, Map<String, String> details) {
+ return new HealthCheckDTO(name, HealthCheckDTO.Status.UP, details);
+ }
+
+ private static HealthCheckDTO down(String name, String detailKey, String
detailValue) {
+ return new HealthCheckDTO(
+ name, HealthCheckDTO.Status.DOWN, Collections.singletonMap(detailKey,
detailValue));
+ }
+}
diff --git
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/TestIcebergHealthOperations.java
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/TestIcebergHealthOperations.java
new file mode 100644
index 0000000000..376479140c
--- /dev/null
+++
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/rest/TestIcebergHealthOperations.java
@@ -0,0 +1,94 @@
+/*
+ * 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.gravitino.iceberg.service.rest;
+
+import static org.mockito.Mockito.mock;
+
+import javax.ws.rs.core.Response;
+import org.apache.gravitino.dto.HealthCheckDTO;
+import org.apache.gravitino.dto.responses.HealthResponse;
+import org.apache.gravitino.iceberg.service.IcebergCatalogWrapperManager;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TestIcebergHealthOperations {
+
+ private static IcebergHealthOperations operationsWithManager(
+ IcebergCatalogWrapperManager manager) {
+ return new IcebergHealthOperations() {
+ @Override
+ IcebergCatalogWrapperManager getCatalogWrapperManager() {
+ return manager;
+ }
+ };
+ }
+
+ @Test
+ public void testLiveReturns200() {
+ IcebergHealthOperations ops = operationsWithManager(null);
+ Response resp = ops.live();
+ Assertions.assertEquals(Response.Status.OK.getStatusCode(),
resp.getStatus());
+ HealthResponse body = (HealthResponse) resp.getEntity();
+ Assertions.assertEquals(HealthCheckDTO.Status.UP, body.getStatus());
+ }
+
+ @Test
+ public void testReadyReturns200WhenManagerInitialized() {
+ IcebergCatalogWrapperManager manager =
mock(IcebergCatalogWrapperManager.class);
+ IcebergHealthOperations ops = operationsWithManager(manager);
+ Response resp = ops.ready();
+ Assertions.assertEquals(Response.Status.OK.getStatusCode(),
resp.getStatus());
+ HealthResponse body = (HealthResponse) resp.getEntity();
+ Assertions.assertEquals(HealthCheckDTO.Status.UP, body.getStatus());
+ }
+
+ @Test
+ public void testReadyReturns503WhenManagerNotInitialized() {
+ IcebergHealthOperations ops = operationsWithManager(null);
+ Response resp = ops.ready();
+
Assertions.assertEquals(Response.Status.SERVICE_UNAVAILABLE.getStatusCode(),
resp.getStatus());
+ HealthResponse body = (HealthResponse) resp.getEntity();
+ Assertions.assertEquals(HealthCheckDTO.Status.DOWN, body.getStatus());
+ Assertions.assertFalse(body.getChecks().isEmpty());
+ Assertions.assertEquals("catalogWrapperManager",
body.getChecks().get(0).getName());
+ }
+
+ @Test
+ public void testHealthReturns200WhenManagerInitialized() {
+ IcebergCatalogWrapperManager manager =
mock(IcebergCatalogWrapperManager.class);
+ IcebergHealthOperations ops = operationsWithManager(manager);
+ Response resp = ops.health();
+ Assertions.assertEquals(Response.Status.OK.getStatusCode(),
resp.getStatus());
+ HealthResponse body = (HealthResponse) resp.getEntity();
+ Assertions.assertEquals(HealthCheckDTO.Status.UP, body.getStatus());
+ Assertions.assertEquals(2, body.getChecks().size());
+ }
+
+ @Test
+ public void testHealthReturns503WhenManagerNotInitialized() {
+ IcebergHealthOperations ops = operationsWithManager(null);
+ Response resp = ops.health();
+
Assertions.assertEquals(Response.Status.SERVICE_UNAVAILABLE.getStatusCode(),
resp.getStatus());
+ HealthResponse body = (HealthResponse) resp.getEntity();
+ Assertions.assertEquals(HealthCheckDTO.Status.DOWN, body.getStatus());
+ boolean hasCatalogCheck =
+ body.getChecks().stream().anyMatch(c ->
"catalogWrapperManager".equals(c.getName()));
+ Assertions.assertTrue(hasCatalogCheck);
+ }
+}
diff --git
a/server-common/src/main/java/org/apache/gravitino/server/authentication/AuthenticationFilter.java
b/server-common/src/main/java/org/apache/gravitino/server/authentication/AuthenticationFilter.java
index 37bb5af9b4..2a91858081 100644
---
a/server-common/src/main/java/org/apache/gravitino/server/authentication/AuthenticationFilter.java
+++
b/server-common/src/main/java/org/apache/gravitino/server/authentication/AuthenticationFilter.java
@@ -132,7 +132,14 @@ public class AuthenticationFilter implements Filter {
response.sendError(status, exception.getMessage());
}
- private static boolean isHealthCheckRequest(ServletRequest request) {
+ /**
+ * Returns {@code true} if the request targets a health check endpoint that
should bypass
+ * authentication. Subclasses may override this to add additional bypass
paths.
+ *
+ * @param request the incoming servlet request
+ * @return {@code true} if the request should skip authentication
+ */
+ protected boolean isHealthCheckRequest(ServletRequest request) {
if (!(request instanceof HttpServletRequest)) {
return false;
}
@@ -142,11 +149,15 @@ public class AuthenticationFilter implements Filter {
}
// Also match /health, /health/*, and /health.html — root-level aliases
that forward to
// /api/health/*. During a forward, getRequestURI() returns the original
URI, not the target.
+ // Also match /iceberg/health and /iceberg/health/* for the Iceberg REST
server health
+ // endpoints.
return path.equals("/health")
|| path.startsWith("/health/")
|| path.equals("/health.html")
|| path.equals("/api/health")
- || path.startsWith("/api/health/");
+ || path.startsWith("/api/health/")
+ || path.equals("/iceberg/health")
+ || path.startsWith("/iceberg/health/");
}
@Override
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/HealthAliasServlet.java
b/server-common/src/main/java/org/apache/gravitino/server/web/HealthAliasServlet.java
similarity index 62%
rename from
server/src/main/java/org/apache/gravitino/server/web/HealthAliasServlet.java
rename to
server-common/src/main/java/org/apache/gravitino/server/web/HealthAliasServlet.java
index 532bc44e4a..c9d2c75722 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/HealthAliasServlet.java
+++
b/server-common/src/main/java/org/apache/gravitino/server/web/HealthAliasServlet.java
@@ -26,22 +26,39 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
- * Serves root-level health paths ({@code /health}, {@code /health/live},
{@code /health/ready},
- * {@code /health.html}) by forwarding to the canonical {@code /api/health/*}
endpoints.
+ * Forwards root-level health probe paths to canonical health endpoints.
+ *
+ * <p>The no-arg constructor targets {@code /api/health} (for the main
Gravitino server). Pass a
+ * custom {@code targetPrefix} (e.g. {@code "/iceberg"}) to forward to a
different base path.
*
* <p>This alias exists for compatibility with enterprise global traffic
managers that require
- * probes at well-known root paths. The canonical implementation remains at
{@code /api/health}.
+ * probes at well-known root paths such as {@code /health}, {@code
/health/live}, {@code
+ * /health/ready}, and {@code /health.html}.
*/
public class HealthAliasServlet extends HttpServlet {
+ private final String targetPrefix;
+
+ /** Forwards to {@code /api/health*} (main Gravitino server default). */
+ public HealthAliasServlet() {
+ this.targetPrefix = "/api";
+ }
+
+ /**
+ * Forwards to {@code <targetPrefix>/health*}.
+ *
+ * @param targetPrefix the path prefix of the canonical health endpoint,
e.g. {@code "/iceberg"}
+ */
+ public HealthAliasServlet(String targetPrefix) {
+ this.targetPrefix = targetPrefix;
+ }
+
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
- // Map root-level health paths to their canonical /api/health counterparts.
- // /health and /health.html both target the aggregate /api/health; legacy
GTM
- // standards sometimes hardcode the .html extension.
+ // /health.html maps to the aggregate endpoint; other paths keep their
sub-path.
String uri = req.getRequestURI();
- String targetPath = "/health.html".equals(uri) ? "/api/health" : "/api" +
uri;
+ String targetPath = "/health.html".equals(uri) ? targetPrefix + "/health"
: targetPrefix + uri;
RequestDispatcher dispatcher = req.getRequestDispatcher(targetPath);
if (dispatcher == null) {
resp.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "health
dispatcher unavailable");
diff --git
a/server-common/src/main/java/org/apache/gravitino/server/web/Utils.java
b/server-common/src/main/java/org/apache/gravitino/server/web/Utils.java
index 6cf48f6e95..4ef45b6a50 100644
--- a/server-common/src/main/java/org/apache/gravitino/server/web/Utils.java
+++ b/server-common/src/main/java/org/apache/gravitino/server/web/Utils.java
@@ -191,6 +191,13 @@ public class Utils {
.build();
}
+ public static <T> Response serviceUnavailable(T t) {
+ return Response.status(Response.Status.SERVICE_UNAVAILABLE)
+ .entity(t)
+ .type(MediaType.APPLICATION_JSON)
+ .build();
+ }
+
public static Response doAs(
HttpServletRequest httpRequest, PrivilegedExceptionAction<Response>
action) throws Exception {
UserPrincipal principal =
diff --git
a/server-common/src/test/java/org/apache/gravitino/server/authentication/TestAuthenticationFilter.java
b/server-common/src/test/java/org/apache/gravitino/server/authentication/TestAuthenticationFilter.java
index 8c227727b4..9878841533 100644
---
a/server-common/src/test/java/org/apache/gravitino/server/authentication/TestAuthenticationFilter.java
+++
b/server-common/src/test/java/org/apache/gravitino/server/authentication/TestAuthenticationFilter.java
@@ -136,7 +136,10 @@ public class TestAuthenticationFilter {
"/api/health",
"/api/health/",
"/api/health/live",
- "/api/health/ready"
+ "/api/health/ready",
+ "/iceberg/health",
+ "/iceberg/health/live",
+ "/iceberg/health/ready"
};
for (String path : healthPaths) {
Authenticator authenticator = mock(Authenticator.class);
@@ -161,7 +164,12 @@ public class TestAuthenticationFilter {
// Regression guard against an overly broad exemption. Paths that merely
contain
// "health" or share a prefix with "/api/health" must still be
authenticated.
String[] nonHealthPaths = {
- "/api/metalakes/health_metalake", "/api/healthcheck", "/api/version",
"/api/metalakes"
+ "/api/metalakes/health_metalake",
+ "/api/healthcheck",
+ "/api/version",
+ "/api/metalakes",
+ "/iceberg/healthcheck",
+ "/iceberg/v1/namespaces"
};
for (String path : nonHealthPaths) {
Authenticator authenticator = mock(Authenticator.class);
diff --git
a/server/src/test/java/org/apache/gravitino/server/web/TestHealthAliasServlet.java
b/server-common/src/test/java/org/apache/gravitino/server/web/TestHealthAliasServlet.java
similarity index 76%
rename from
server/src/test/java/org/apache/gravitino/server/web/TestHealthAliasServlet.java
rename to
server-common/src/test/java/org/apache/gravitino/server/web/TestHealthAliasServlet.java
index 1d0e6e3e8a..b753de0ebe 100644
---
a/server/src/test/java/org/apache/gravitino/server/web/TestHealthAliasServlet.java
+++
b/server-common/src/test/java/org/apache/gravitino/server/web/TestHealthAliasServlet.java
@@ -51,6 +51,26 @@ public class TestHealthAliasServlet {
verify(dispatcher).forward(req, resp);
}
+ @ParameterizedTest
+ @CsvSource({
+ "/health, /iceberg/health",
+ "/health.html, /iceberg/health",
+ "/health/live, /iceberg/health/live",
+ "/health/ready, /iceberg/health/ready"
+ })
+ public void testDoGetForwardsWithCustomPrefix(String incoming, String
expected) throws Exception {
+ HealthAliasServlet servlet = new HealthAliasServlet("/iceberg");
+ HttpServletRequest req = mock(HttpServletRequest.class);
+ HttpServletResponse resp = mock(HttpServletResponse.class);
+ RequestDispatcher dispatcher = mock(RequestDispatcher.class);
+ when(req.getRequestURI()).thenReturn(incoming.strip());
+ when(req.getRequestDispatcher(expected.strip())).thenReturn(dispatcher);
+
+ servlet.doGet(req, resp);
+
+ verify(dispatcher).forward(req, resp);
+ }
+
@Test
public void testDoGetReturns503WhenDispatcherIsNull() throws Exception {
HealthAliasServlet servlet = new HealthAliasServlet();
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/rest/HealthOperations.java
b/server/src/main/java/org/apache/gravitino/server/web/rest/HealthOperations.java
index 60c44aeb58..ea2aa6ffc0 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/rest/HealthOperations.java
+++
b/server/src/main/java/org/apache/gravitino/server/web/rest/HealthOperations.java
@@ -34,7 +34,6 @@ import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
-import javax.servlet.http.HttpServlet;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
@@ -48,6 +47,7 @@ import org.apache.gravitino.dto.HealthCheckDTO;
import org.apache.gravitino.dto.responses.HealthResponse;
import org.apache.gravitino.metrics.MetricNames;
import org.apache.gravitino.server.ServerConfig;
+import org.apache.gravitino.server.web.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -66,7 +66,7 @@ import org.slf4j.LoggerFactory;
*/
@Path("/health")
@Produces(MediaType.APPLICATION_JSON)
-public class HealthOperations extends HttpServlet {
+public class HealthOperations {
private static final Logger LOG =
LoggerFactory.getLogger(HealthOperations.class);
@@ -110,7 +110,7 @@ public class HealthOperations extends HttpServlet {
@ResponseMetered(name = "health.live", absolute = true)
public Response live() {
HealthCheckDTO check = up(CHECK_HTTP_SERVER, Collections.emptyMap());
- return ok(new HealthResponse(HealthCheckDTO.Status.UP,
Collections.singletonList(check)));
+ return Utils.ok(new HealthResponse(HealthCheckDTO.Status.UP,
Collections.singletonList(check)));
}
@GET
@@ -122,7 +122,7 @@ public class HealthOperations extends HttpServlet {
HealthCheckDTO entityStoreCheck = checkEntityStore();
HealthCheckDTO.Status overall = entityStoreCheck.getStatus();
HealthResponse body = new HealthResponse(overall,
Collections.singletonList(entityStoreCheck));
- return overall == HealthCheckDTO.Status.UP ? ok(body) :
serviceUnavailable(body);
+ return overall == HealthCheckDTO.Status.UP ? Utils.ok(body) :
Utils.serviceUnavailable(body);
}
@GET
@@ -140,7 +140,7 @@ public class HealthOperations extends HttpServlet {
: HealthCheckDTO.Status.UP;
HealthResponse body = new HealthResponse(overall, checks);
- return overall == HealthCheckDTO.Status.UP ? ok(body) :
serviceUnavailable(body);
+ return overall == HealthCheckDTO.Status.UP ? Utils.ok(body) :
Utils.serviceUnavailable(body);
}
private HealthCheckDTO checkEntityStore() {
@@ -227,18 +227,4 @@ public class HealthOperations extends HttpServlet {
return new HealthCheckDTO(
name, HealthCheckDTO.Status.DOWN, Collections.singletonMap(detailKey,
detailValue));
}
-
- private static Response ok(HealthResponse body) {
- return Response.status(Response.Status.OK)
- .entity(body)
- .type(MediaType.APPLICATION_JSON)
- .build();
- }
-
- private static Response serviceUnavailable(HealthResponse body) {
- return Response.status(Response.Status.SERVICE_UNAVAILABLE)
- .entity(body)
- .type(MediaType.APPLICATION_JSON)
- .build();
- }
}
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/rest/VersionOperations.java
b/server/src/main/java/org/apache/gravitino/server/web/rest/VersionOperations.java
index 0491e87873..8da2767f4e 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/rest/VersionOperations.java
+++
b/server/src/main/java/org/apache/gravitino/server/web/rest/VersionOperations.java
@@ -20,7 +20,6 @@ package org.apache.gravitino.server.web.rest;
import com.codahale.metrics.annotation.ResponseMetered;
import com.codahale.metrics.annotation.Timed;
-import javax.servlet.http.HttpServlet;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
@@ -35,7 +34,7 @@ import org.apache.gravitino.server.web.Utils;
@Path("/version")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
-public class VersionOperations extends HttpServlet {
+public class VersionOperations {
@GET
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "version." + MetricNames.HTTP_PROCESS_DURATION, absolute =
true)