75 lines
5.4 KiB
Markdown
75 lines
5.4 KiB
Markdown
---
|
|
title: error / spotbugs-commons-lang3-bom-downgrade-noclassdef-2026-06-20
|
|
source_type: error-note
|
|
status: raw
|
|
related_branches: [feature-static-analysis-quality-contract]
|
|
related_projects: [ca-skeleton]
|
|
tags: [error, ca-skeleton, spotbugs, gradle, spring-dependency-management, bom, static-analysis, java21]
|
|
created: 2026-06-20
|
|
status_label: resolved
|
|
---
|
|
|
|
# error: spotbugs-commons-lang3-bom-downgrade-noclassdef-2026-06-20
|
|
|
|
> Layer: `raw/errors/` — SpotBugs 4.10.2 analysis worker crash caused by the Spring Boot BOM
|
|
> downgrading commons-lang3 on the `spotbugs` tool configuration. The classic "dependency
|
|
> management leaks onto a tool classpath" trap.
|
|
|
|
## Parent / 부모
|
|
|
|
- [[raw/branch-notes/feature-static-analysis-quality-contract]] — D3/D4 SpotBugs + FindSecBugs wiring. Hit while verifying Claim "SpotBugs 6.5.6(core 4.10.2) on Gradle 9.0.0".
|
|
|
|
## 증상 / Symptom
|
|
|
|
- 발생 컨텍스트: `./gradlew spotbugsMain` 을 처음 실행하자 8/10 모듈에서 `spotbugsMain FAILED` + `SpotBugs ended with exit code 4`. 버그가 발견된 게 아니라 **분석 자체가 죽음** — `build/reports/spotbugs/*.xml` 리포트가 아예 생성 안 됨(crash before report).
|
|
- `--console=plain` 로 보니 root cause:
|
|
```text
|
|
edu.umd.cs.findbugs.ba.AnalysisException: Exception was thrown during analysis
|
|
Caused by: java.lang.NoClassDefFoundError: org/apache/commons/lang3/Strings
|
|
Caused by: java.lang.ClassNotFoundException: org.apache.commons.lang3.Strings
|
|
at edu.umd.cs.findbugs.ba.vna.ValueNumberFrameModelingVisitor.visitLDC(...)
|
|
```
|
|
- 재현 가능 여부: `always` (Spring Boot dependency-management + SpotBugs 4.10.2 조합에서 항상)
|
|
|
|
## 재현 절차 / Reproduction
|
|
|
|
1. Spring Boot `io.spring.dependency-management` 가 적용된 Gradle 멀티모듈 빌드에서 `com.github.spotbugs` 6.5.6 plugin + `spotbugs { toolVersion = '4.10.2' }` 적용.
|
|
2. `./gradlew spotbugsMain` 실행.
|
|
3. `NoClassDefFoundError: org.apache.commons.lang3.Strings` 로 분석 worker crash.
|
|
|
|
## 조사 단계 / Investigation log
|
|
|
|
- `./gradlew :shared-contract:dependencies --configuration spotbugs | grep commons-lang3` →
|
|
`org.apache.commons:commons-lang3:3.20.0 -> 3.17.0`. SpotBugs 가 요구하는 3.20.0 이 BOM 에 의해 3.17.0 으로 강등됨.
|
|
- BOM 확인: `spring-boot-dependencies-3.5.15.pom` → `<commons-lang3.version>3.17.0</commons-lang3.version>`.
|
|
- SpotBugs POM 확인: `spotbugs-4.10.2.pom` → `commons-lang3` `3.20.0` (`org.apache.commons.lang3.Strings` 는 commons-lang3 3.18.0 에서 추가됨 → 3.17.0 엔 없음 → NoClassDefFound).
|
|
- `resolutionStrategy.force 'org.apache.commons:commons-lang3:3.20.0'` 를 `spotbugs` 설정에 적용 → **효과 없음**. 재확인 시 여전히 `3.20.0 -> 3.17.0`. `io.spring.dependency-management` 가 `force` 를 덮어쓴다.
|
|
- production main 코드에서 `org.apache.commons.lang3` import 0건 확인 → commons-lang3 는 사실상 SpotBugs 도구 classpath 에만 존재 → 버전 상향의 런타임 영향 없음.
|
|
|
|
## 근본 원인 / Root cause
|
|
|
|
- `io.spring.dependency-management` 는 BOM 의 managed version 을 **모든 configuration** 에 적용한다 — 런타임 classpath 뿐 아니라 `spotbugs`(SpotBugs 분석 worker) 같은 도구 전용 configuration 의 transitive 까지. 그래서 SpotBugs 가 가져오려던 commons-lang3 3.20.0 이 BOM 의 3.17.0 으로 강등되고, 3.17.0 엔 SpotBugs 4.10.2 가 LDC 모델링에서 참조하는 `org.apache.commons.lang3.Strings` 가 없어 worker 가 crash 한다.
|
|
- `resolutionStrategy.force` 가 안 먹힌 이유: dependency-management 플러그인이 자체 resolution 액션으로 managed version 을 강제하며, 이게 force 보다 우선한다.
|
|
|
|
## 해결 / Resolution
|
|
|
|
- BOM 이 관리하는 버전 property 자체를 override (Spring 공식 메커니즘):
|
|
```groovy
|
|
ext['commons-lang3.version'] = '3.20.0'
|
|
```
|
|
subprojects 블록에 두면 `dependencyManagement` 가 BOM placeholder 를 3.20.0 으로 해석 → `spotbugs` 설정의 commons-lang3 가 3.20.0 으로 resolve → crash 해소.
|
|
- 재확인: `dependencies --configuration spotbugs` 에서 `commons-lang3:3.20.0`(강등 화살표 사라짐), `./gradlew spotbugsMain` → 분석 정상 수행(이후 EI/보안 finding 은 reportLevel·exclude 로 별도 처리).
|
|
- production main 이 commons-lang3 를 안 쓰므로 글로벌 property override 의 실질 영향은 SpotBugs 도구 classpath 한정. 3.17→3.20 은 commons-lang3 3.x 의 backward-compatible minor 상향.
|
|
|
|
## 교훈 / Lessons
|
|
|
|
- **Spring dependency-management 는 도구 전용 configuration(spotbugs/checkstyle/errorprone 등)에도 BOM 을 적용한다.** 도구가 BOM 보다 최신 transitive 를 요구하면 조용히 강등되어 `NoClassDefFoundError`/`NoSuchMethodError` 로 런타임에 터진다.
|
|
- **`resolutionStrategy.force` 는 dependency-management 를 못 이긴다.** 도구 classpath 버전을 고치려면 `ext['<artifact>.version']` 로 **managed version property 자체를 override** 하는 게 정공법.
|
|
- **SpotBugs "exit code 4" + 리포트 부재 = 분석 crash(버그 발견 아님).** `--console=plain` 로 `AnalysisException`/`Caused by` 를 먼저 확인할 것. exit code 1 이 "버그 발견"이고 4 류는 analysis error 신호.
|
|
- 버전 충돌 디버깅은 `gradlew <module>:dependencies --configuration <toolConfig>` 로 강등 화살표(`X -> Y`)를 직접 본다.
|
|
|
|
## 관련 / Related
|
|
|
|
- [[raw/branch-notes/feature-static-analysis-quality-contract]]
|
|
- [[raw/blog-topics/gradle9-java21-static-analysis-baseline-2026-06-20]]
|