WebLogic 라이브러리 안정성 검증 로직 강화:
- jboss-logging/Guava 클래스 로드시충돌 점검 추가 - ReadinessController에 관련 체크 로직 반영
This commit is contained in:
@@ -5,6 +5,7 @@ import java.sql.SQLException;
|
|||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
@@ -12,19 +13,28 @@ import org.springframework.web.bind.annotation.GetMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.thymeleaf.spring5.SpringTemplateEngine;
|
import org.thymeleaf.spring5.SpringTemplateEngine;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.Futures;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Readiness probe.
|
* Readiness probe.
|
||||||
*
|
*
|
||||||
* <p>Distinct from {@link HealthCheckController} (liveness — servlet alive?).
|
* <p>Distinct from {@link HealthCheckController} (liveness — servlet alive?).
|
||||||
* This endpoint validates both EMS and Gateway datasources via JDBC
|
* This endpoint validates both EMS and Gateway datasources via JDBC
|
||||||
* {@code Connection.isValid(timeout)}, and the Thymeleaf {@link SpringTemplateEngine}
|
* {@code Connection.isValid(timeout)}, plus three libraries that WebLogic's
|
||||||
* configuration, to confirm the app is ready to serve real page requests.
|
* oracle_common bundles at a different version from this app (see weblogic.xml
|
||||||
|
* {@code prefer-application-packages}): Thymeleaf's {@link SpringTemplateEngine}
|
||||||
|
* (jackson), jboss-logging, and Guava.
|
||||||
*
|
*
|
||||||
* <p>The template-engine check exists because this controller bypasses view
|
* <p>These exist because this controller bypasses view resolution entirely
|
||||||
* resolution entirely (plain {@code @RestController} JSON) — a broken
|
* (plain {@code @RestController} JSON) — a classpath split between the app's
|
||||||
* {@code TemplateEngine.getConfiguration()} (e.g. classpath split causing
|
* copy and WebLogic's bundled copy of a preferred package can throw
|
||||||
* {@link java.util.ServiceConfigurationError} during dialect/module discovery)
|
* {@link LinkageError}/{@link java.util.ServiceConfigurationError} only on the
|
||||||
* previously left every real page returning 500 while this probe still reported 200.
|
* real code paths that touch it, while this probe kept reporting 200. That
|
||||||
|
* already happened once for jackson (Thymeleaf's dialect/module discovery
|
||||||
|
* failed on every real page while /health/ready stayed green) — jboss-logging
|
||||||
|
* and Guava carry the same risk (confirmed version mismatch against
|
||||||
|
* oracle_common, not yet observed failing in production) so they get the same
|
||||||
|
* kind of forced-touch check here.
|
||||||
*
|
*
|
||||||
* <p>HTTP 200 + JSON when all checks pass.
|
* <p>HTTP 200 + JSON when all checks pass.
|
||||||
* <p>HTTP 503 + JSON when any check fails — body still includes the per-component
|
* <p>HTTP 503 + JSON when any check fails — body still includes the per-component
|
||||||
@@ -50,16 +60,21 @@ public class ReadinessController {
|
|||||||
|
|
||||||
@GetMapping(value = "/health/ready", produces = MediaType.APPLICATION_JSON_VALUE)
|
@GetMapping(value = "/health/ready", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
public ResponseEntity<String> ready() {
|
public ResponseEntity<String> ready() {
|
||||||
String ems = check(portalDataSource);
|
String ems = check(portalDataSource);
|
||||||
String gw = check(gatewayDataSource);
|
String gw = check(gatewayDataSource);
|
||||||
String view = checkTemplateEngine();
|
String view = checkTemplateEngine();
|
||||||
boolean ok = "UP".equals(ems) && "UP".equals(gw) && "UP".equals(view);
|
String logging = checkJbossLogging();
|
||||||
|
String guava = checkGuava();
|
||||||
|
boolean ok = "UP".equals(ems) && "UP".equals(gw) && "UP".equals(view)
|
||||||
|
&& "UP".equals(logging) && "UP".equals(guava);
|
||||||
|
|
||||||
String body = "{"
|
String body = "{"
|
||||||
+ "\"status\":\"" + (ok ? "UP" : "DOWN") + "\","
|
+ "\"status\":\"" + (ok ? "UP" : "DOWN") + "\","
|
||||||
+ "\"ems\":\"" + ems + "\","
|
+ "\"ems\":\"" + ems + "\","
|
||||||
+ "\"gateway\":\"" + gw + "\","
|
+ "\"gateway\":\"" + gw + "\","
|
||||||
+ "\"view\":\"" + view + "\""
|
+ "\"view\":\"" + view + "\","
|
||||||
|
+ "\"logging\":\"" + logging + "\","
|
||||||
|
+ "\"guava\":\"" + guava + "\""
|
||||||
+ "}";
|
+ "}";
|
||||||
|
|
||||||
return ResponseEntity.status(ok ? 200 : 503)
|
return ResponseEntity.status(ok ? 200 : 503)
|
||||||
@@ -76,11 +91,40 @@ public class ReadinessController {
|
|||||||
templateEngine.getConfiguration();
|
templateEngine.getConfiguration();
|
||||||
return "UP";
|
return "UP";
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
String msg = t.getMessage() == null ? t.getClass().getSimpleName() : t.getMessage();
|
return down(t);
|
||||||
return "DOWN:" + msg.replace('"', '\'').replace('\n', ' ').replace('\r', ' ');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Forces jboss-logging's {@code Logger} + its bound provider (LogManager/JBoss/JUL
|
||||||
|
* discovery) to load and actually run a log-level check, not just resolve the class. */
|
||||||
|
private static String checkJbossLogging() {
|
||||||
|
try {
|
||||||
|
Logger log = Logger.getLogger(ReadinessController.class);
|
||||||
|
log.isDebugEnabled();
|
||||||
|
return "UP";
|
||||||
|
} catch (Throwable t) {
|
||||||
|
return down(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@code Futures.immediateFuture} touches Guava's {@code util.concurrent} machinery
|
||||||
|
* (AbstractFuture/InternalFutureFailureAccess) — the part backed by the separate
|
||||||
|
* {@code failureaccess} jar, which is exactly where an app/WebLogic version mismatch
|
||||||
|
* would split across classloaders. */
|
||||||
|
private static String checkGuava() {
|
||||||
|
try {
|
||||||
|
Futures.immediateFuture(Boolean.TRUE).isDone();
|
||||||
|
return "UP";
|
||||||
|
} catch (Throwable t) {
|
||||||
|
return down(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String down(Throwable t) {
|
||||||
|
String msg = t.getMessage() == null ? t.getClass().getSimpleName() : t.getMessage();
|
||||||
|
return "DOWN:" + msg.replace('"', '\'').replace('\n', ' ').replace('\r', ' ');
|
||||||
|
}
|
||||||
|
|
||||||
private static String check(DataSource ds) {
|
private static String check(DataSource ds) {
|
||||||
if (ds == null) return "DOWN:NO_DATASOURCE";
|
if (ds == null) return "DOWN:NO_DATASOURCE";
|
||||||
try (Connection c = ds.getConnection()) {
|
try (Connection c = ds.getConnection()) {
|
||||||
|
|||||||
Reference in New Issue
Block a user