Skip to content

Fix local cache path fallback for Spring Boot 3 executable JARs#136

Open
nobodyiam wants to merge 1 commit intoapolloconfig:mainfrom
nobodyiam:codex/issue-5592-spring-boot3-cache
Open

Fix local cache path fallback for Spring Boot 3 executable JARs#136
nobodyiam wants to merge 1 commit intoapolloconfig:mainfrom
nobodyiam:codex/issue-5592-spring-boot3-cache

Conversation

@nobodyiam
Copy link
Copy Markdown
Member

@nobodyiam nobodyiam commented Apr 7, 2026

What's the purpose of this PR

Fix Apollo Java local cache directory resolution when running from a Spring Boot 3 executable JAR.

ClassLoaderUtil previously assumed that jar-internal classpath locations would contain .jar!, but Spring Boot 3 executable JARs expose nested URLs such as jar:nested:/.../!BOOT-INF/classes/!/. When Apollo falls back to ClassLoaderUtil.getClassPath() for local cache persistence, that nested URL was treated as a filesystem path and config-cache directory creation failed.

Which issue(s) this PR fixes:

Fixes apolloconfig/apollo#5592

Brief changelog

  • Only treat file: resources as filesystem classpath locations and fall back to user.dir for nested-jar URLs.
  • Add regression tests for normal file URL resolution and Spring Boot 3 nested-jar fallback.
  • Validate the affected paths with:
    • mvn -pl apollo-core -Dtest=ClassLoaderUtilTest test -Dmaven.gitcommitid.skip=true
    • mvn -pl apollo-client -am -Dtest=ClassLoaderUtilTest,LocalFileConfigRepositoryTest,DefaultConfigTest -Dsurefire.failIfNoSpecifiedTests=false test -Dmaven.gitcommitid.skip=true

Follow this checklist to help us incorporate your contribution quickly and easily:

  • Read the Contributing Guide before making this pull request.
  • Write a pull request description that is detailed enough to understand what the pull request does, how, and why.
  • Write necessary unit tests to verify the code.
  • Run mvn clean test to make sure this pull request doesn't break anything.
  • Update the CHANGES log.

Summary by CodeRabbit

  • Bug Fixes

    • Improved classpath detection and fallback behavior to better support executable JAR runs.
  • Tests

    • Added unit tests for classpath resolution and strengthened integration test cleanup to preserve/restore broader environment properties.
  • Documentation

    • Updated release notes entry under Apollo Java 2.6.0 to reference the fix.

Copilot AI review requested due to automatic review settings April 7, 2026 14:10
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 7, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f2465ab5-118f-436c-a1f3-298feb961cbe

📥 Commits

Reviewing files that changed from the base of the PR and between 1828bd8 and ea5f128.

📒 Files selected for processing (4)
  • CHANGES.md
  • apollo-client-config-data/src/test/java/com/ctrip/framework/apollo/config/data/integration/ConfigDataIntegrationTest.java
  • apollo-core/src/main/java/com/ctrip/framework/apollo/core/utils/ClassLoaderUtil.java
  • apollo-core/src/test/java/com/ctrip/framework/apollo/core/utils/ClassLoaderUtilTest.java
✅ Files skipped from review due to trivial changes (1)
  • CHANGES.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • apollo-core/src/main/java/com/ctrip/framework/apollo/core/utils/ClassLoaderUtil.java

📝 Walkthrough

Walkthrough

Refactors classpath detection in ClassLoaderUtil by extracting resolution into resolveClassPath(...) and adding getDefaultClassPath(); updates unit tests for the resolver; updates CHANGES.md release note; and broadens test lifecycle handling to snapshot, clear, and restore all Apollo system properties in ConfigDataIntegrationTest.

Changes

Cohort / File(s) Summary
Release Notes
CHANGES.md
Replaced an empty bullet with a release-note entry linking the PR that fixes Apollo client local cache fallback for Spring Boot 3.
Classpath Detection (core)
apollo-core/src/main/java/com/ctrip/framework/apollo/core/utils/ClassLoaderUtil.java
Added package-private resolveClassPath(ClassLoader,String) and private getDefaultClassPath(); static initializer delegates to resolver and uses getDefaultClassPath() when resolver returns null/empty or on exception; preserved warning log behavior.
Classpath Detection Tests
apollo-core/src/test/java/com/ctrip/framework/apollo/core/utils/ClassLoaderUtilTest.java
Added multiple JUnit tests for resolveClassPath(...) (file URL decoding, nested jar: URL behavior, Windows percent-encoded file URL, and getClassPath fallback to user.dir); added helpers for custom ClassLoader/URL handling and dynamic class loading; added trailing newline.
Test setup/teardown
apollo-client-config-data/src/test/java/.../ConfigDataIntegrationTest.java
Test ExternalResource lifecycle now snapshots all keys in ApolloApplicationContextInitializer.APOLLO_SYSTEM_PROPERTIES, clears them before tests, resets Apollo static state, and restores each captured property (clearing those originally unset); @After cleanup clears same properties and re-applies test app.id and env.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • Anilople

Poem

🐇 I hopped through jars and file-paths bright,
Tuned the loader’s nose to find what's right,
Resolved the paths on Windows and nix,
Cleared configs, ran a handful of quick ticks,
Now caches wake at Spring Boot's light.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly summarizes the main fix: improving local cache path fallback for Spring Boot 3 executable JARs, which is the core problem addressed across all changed files.
Linked Issues check ✅ Passed All code changes directly address issue #5592: ClassLoaderUtil now properly detects file: protocol resources and falls back to user.dir for nested JAR URLs; tests validate both file URL decoding and Spring Boot 3 nested-jar fallback behavior.
Out of Scope Changes check ✅ Passed All changes are in scope: ClassLoaderUtil refactoring fixes Spring Boot 3 cache issues, test additions validate the fix, ConfigDataIntegrationTest updates improve test stability, and CHANGES.md documents the fix.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nobodyiam nobodyiam force-pushed the codex/issue-5592-spring-boot3-cache branch from 3bbe114 to 1055f6b Compare April 7, 2026 14:11
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes Apollo Java’s local cache directory resolution when running from Spring Boot 3 executable (nested) JARs by avoiding treating nested-jar classpath URLs as filesystem paths, and instead falling back to a safe default (user.dir).

Changes:

  • Refactors ClassLoaderUtil classpath resolution to only treat file: URLs as filesystem locations and otherwise fall back to a provided default.
  • Adds regression tests covering both file: URL resolution and Spring Boot 3 nested-jar URL fallback behavior.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
apollo-core/src/main/java/com/ctrip/framework/apollo/core/utils/ClassLoaderUtil.java Introduces resolveClassPath(...) using URL protocol checks and Paths.get(url.toURI()) to prevent nested-jar URLs from being used as filesystem paths.
apollo-core/src/test/java/com/ctrip/framework/apollo/core/utils/ClassLoaderUtilTest.java Adds tests validating correct handling for file: URLs and fallback behavior for nested-jar URLs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +57 to +66
static String resolveClassPath(ClassLoader classLoader, String defaultClassPath) throws Exception {
URL url = classLoader.getResource("");
if (url == null || !"file".equalsIgnoreCase(url.getProtocol())) {
return defaultClassPath;
}

String resolvedClassPath = Paths.get(url.toURI()).toString();
if (Strings.isNullOrEmpty(resolvedClassPath)) {
return defaultClassPath;
}
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolveClassPath now derives the classpath via Paths.get(url.toURI()).toString(), which changes the returned format on Windows (it will no longer include the leading / that URL#getPath() produced). There is existing code in the repo that compensates for the old behavior (e.g. apollo-client/src/test/java/com/ctrip/framework/apollo/BaseIntegrationTest.java strips the leading /), which will become incorrect with this change and can break Windows builds/tests. Consider either preserving the previous Windows formatting (e.g. keep using the decoded url.getPath() for file: URLs) or updating the affected call sites/tests to stop assuming a leading / from getClassPath().

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preserved the historical file: URL formatting in ClassLoaderUtil. resolveClassPath now decodes url.getPath() instead of using Paths.get(url.toURI()), so the existing Windows-leading-slash behavior is kept for callers that still rely on it. I also added a regression test for a file:/C:/Program%20Files/... URL while keeping the nested-jar fallback unchanged.

@nobodyiam
Copy link
Copy Markdown
Member Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1055f6b21a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

loader = ClassLoader.getSystemClassLoader();
}

String defaultClassPath = System.getProperty("user.dir");
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Move user.dir lookup back into fallback path

System.getProperty("user.dir") is now evaluated unconditionally at class initialization, so in JVMs that deny reading this property (for example, SecurityManager/policy-restricted Java 8 deployments) ClassLoaderUtil can fail to initialize with SecurityException even when classLoader.getResource("") resolves to a normal file: classpath. Before this change, user.dir was only read on fallback paths, so the common file-URL case did not require that permission.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adjusted this so user.dir is only read on the fallback path again. The static initializer now tries resolveClassPath(loader, null) first and only calls getDefaultClassPath() when the classpath cannot be resolved, which restores the previous permission behavior for normal file: classpaths.

@nobodyiam nobodyiam force-pushed the codex/issue-5592-spring-boot3-cache branch from 1055f6b to 1828bd8 Compare April 9, 2026 15:39
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 9, 2026

Codecov Report

❌ Patch coverage is 41.66667% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.98%. Comparing base (d4b76f8) to head (ea5f128).
⚠️ Report is 26 commits behind head on main.

Files with missing lines Patch % Lines
...p/framework/apollo/core/utils/ClassLoaderUtil.java 41.66% 4 Missing and 3 partials ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main     #136      +/-   ##
============================================
+ Coverage     68.68%   70.98%   +2.30%     
- Complexity     1503     1636     +133     
============================================
  Files           212      224      +12     
  Lines          6396     6732     +336     
  Branches        647      680      +33     
============================================
+ Hits           4393     4779     +386     
+ Misses         1673     1602      -71     
- Partials        330      351      +21     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@nobodyiam nobodyiam force-pushed the codex/issue-5592-spring-boot3-cache branch from 1828bd8 to ea5f128 Compare April 10, 2026 02:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

springboot 3 + apollo client无法创建本地缓存文件

2 participants