From 330b01c7a91116e49f5d31dc3f1476e37440a537 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 10 Aug 2026 21:59:50 +0900 Subject: [PATCH 1/4] test(logging): expose duplicate Commons Logging providers --- .../CommonsLoggingRuntimeBindingTest.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/test/java/com/clearfolio/viewer/config/CommonsLoggingRuntimeBindingTest.java diff --git a/src/test/java/com/clearfolio/viewer/config/CommonsLoggingRuntimeBindingTest.java b/src/test/java/com/clearfolio/viewer/config/CommonsLoggingRuntimeBindingTest.java new file mode 100644 index 00000000..1748203b --- /dev/null +++ b/src/test/java/com/clearfolio/viewer/config/CommonsLoggingRuntimeBindingTest.java @@ -0,0 +1,35 @@ +package com.clearfolio.viewer.config; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.net.URL; +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.Test; + +/** + * Verifies that the supported runtime contains exactly one Commons Logging API provider. + */ +final class CommonsLoggingRuntimeBindingTest { + + @Test + void commonsLoggingApiIsProvidedOnlyBySpringJcl() throws Exception { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + List providers = Collections.list( + loader.getResources("org/apache/commons/logging/LogFactory.class")) + .stream() + .map(URL::toExternalForm) + .sorted() + .toList(); + + assertEquals( + 1, + providers.size(), + () -> "Expected one Commons Logging API provider but found: " + providers); + assertTrue( + providers.getFirst().contains("spring-jcl"), + () -> "Expected Spring's spring-jcl bridge to own Commons Logging, but found: " + + providers.getFirst()); + } +} From c5d2c40809cf6ede5d411dcd1a9f38e993a48e0c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 10 Aug 2026 22:07:05 +0900 Subject: [PATCH 2/4] fix(logging): use Spring Commons Logging bridge for PDFBox --- pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pom.xml b/pom.xml index ebdd52f7..c07b55f4 100644 --- a/pom.xml +++ b/pom.xml @@ -140,6 +140,12 @@ org.apache.pdfbox pdfbox ${pdfbox.version} + + + commons-logging + commons-logging + + From 1eeeb5fbfc09b0feb3bda257d08ad10c9fd92e23 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 10 Aug 2026 22:37:47 +0900 Subject: [PATCH 3/4] test(logging): pin Commons Logging dependency boundary --- ...est_commons_logging_dependency_contract.py | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 scripts/test_commons_logging_dependency_contract.py diff --git a/scripts/test_commons_logging_dependency_contract.py b/scripts/test_commons_logging_dependency_contract.py new file mode 100644 index 00000000..668150b6 --- /dev/null +++ b/scripts/test_commons_logging_dependency_contract.py @@ -0,0 +1,41 @@ +from pathlib import Path +import xml.etree.ElementTree as ET + + +MAVEN = {"m": "http://maven.apache.org/POM/4.0.0"} +ROOT = Path(__file__).resolve().parents[1] +POM = ROOT / "pom.xml" + + +def _coordinates(dependency: ET.Element) -> tuple[str | None, str | None]: + return ( + dependency.findtext("m:groupId", namespaces=MAVEN), + dependency.findtext("m:artifactId", namespaces=MAVEN), + ) + + +def test_pdfbox_excludes_standalone_commons_logging_provider() -> None: + root = ET.parse(POM).getroot() + dependencies = root.findall("m:dependencies/m:dependency", MAVEN) + + pdfbox = [ + dependency + for dependency in dependencies + if _coordinates(dependency) == ("org.apache.pdfbox", "pdfbox") + ] + assert len(pdfbox) == 1 + + exclusions = { + _coordinates(exclusion) + for exclusion in pdfbox[0].findall("m:exclusions/m:exclusion", MAVEN) + } + assert ("commons-logging", "commons-logging") in exclusions + + +def test_project_does_not_reintroduce_commons_logging_directly() -> None: + root = ET.parse(POM).getroot() + dependencies = root.findall("m:dependencies/m:dependency", MAVEN) + coordinates = {_coordinates(dependency) for dependency in dependencies} + + assert ("commons-logging", "commons-logging") not in coordinates + assert ("org.springframework.boot", "spring-boot-starter-log4j2") in coordinates From 523dd8403ad1ccfcc88ed2fe7226c90d7f02c7b5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 10 Aug 2026 22:42:22 +0900 Subject: [PATCH 4/4] fix(test): avoid unsafe XML parser in dependency contract --- ...est_commons_logging_dependency_contract.py | 59 +++++++++++-------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/scripts/test_commons_logging_dependency_contract.py b/scripts/test_commons_logging_dependency_contract.py index 668150b6..e10716da 100644 --- a/scripts/test_commons_logging_dependency_contract.py +++ b/scripts/test_commons_logging_dependency_contract.py @@ -1,41 +1,48 @@ +import re from pathlib import Path -import xml.etree.ElementTree as ET -MAVEN = {"m": "http://maven.apache.org/POM/4.0.0"} ROOT = Path(__file__).resolve().parents[1] POM = ROOT / "pom.xml" -def _coordinates(dependency: ET.Element) -> tuple[str | None, str | None]: - return ( - dependency.findtext("m:groupId", namespaces=MAVEN), - dependency.findtext("m:artifactId", namespaces=MAVEN), - ) +def _pom_text() -> str: + return POM.read_text(encoding="utf-8") -def test_pdfbox_excludes_standalone_commons_logging_provider() -> None: - root = ET.parse(POM).getroot() - dependencies = root.findall("m:dependencies/m:dependency", MAVEN) +def _dependency_block(pom: str, group_id: str, artifact_id: str) -> str: + pattern = re.compile( + r"\s*" + rf"{re.escape(group_id)}\s*" + rf"{re.escape(artifact_id)}" + r".*?", + re.DOTALL, + ) + match = pattern.search(pom) + assert match is not None + return match.group(0) - pdfbox = [ - dependency - for dependency in dependencies - if _coordinates(dependency) == ("org.apache.pdfbox", "pdfbox") - ] - assert len(pdfbox) == 1 - exclusions = { - _coordinates(exclusion) - for exclusion in pdfbox[0].findall("m:exclusions/m:exclusion", MAVEN) - } - assert ("commons-logging", "commons-logging") in exclusions +def test_pdfbox_excludes_standalone_commons_logging_provider() -> None: + pom = _pom_text() + pdfbox = _dependency_block(pom, "org.apache.pdfbox", "pdfbox") + + exclusion = re.compile( + r"\s*" + r"commons-logging\s*" + r"commons-logging\s*" + r"" + ) + assert exclusion.search(pdfbox) is not None def test_project_does_not_reintroduce_commons_logging_directly() -> None: - root = ET.parse(POM).getroot() - dependencies = root.findall("m:dependencies/m:dependency", MAVEN) - coordinates = {_coordinates(dependency) for dependency in dependencies} + pom = _pom_text() - assert ("commons-logging", "commons-logging") not in coordinates - assert ("org.springframework.boot", "spring-boot-starter-log4j2") in coordinates + direct_commons_logging = re.compile( + r"\s*" + r"commons-logging\s*" + r"commons-logging" + ) + assert direct_commons_logging.search(pom) is None + _dependency_block(pom, "org.springframework.boot", "spring-boot-starter-log4j2")