WebLogic 라이브러리 안정성 검증 로직 강화:
eapim-portal CI / build (push) Has been cancelled
eapim-portal Test / test (push) Has been cancelled

- jboss-logging/Guava 클래스 로드시충돌 점검 추가
- ReadinessController에 관련 체크 로직 반영
This commit is contained in:
Rinjae
2026-08-27 16:47:15 +09:00
parent 2d0ff2bf84
commit 9e32502ee3
@@ -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()) {