Skip to content

fix: Prevent stale JAR cache in ReflectionsClassFinder under Gradle daemon (CP: 24.9)#23969

Merged
mcollovati merged 2 commits into24.9from
feature/gradle_jarCache_issue_249
Mar 24, 2026
Merged

fix: Prevent stale JAR cache in ReflectionsClassFinder under Gradle daemon (CP: 24.9)#23969
mcollovati merged 2 commits into24.9from
feature/gradle_jarCache_issue_249

Conversation

@mcollovati
Copy link
Copy Markdown
Collaborator

Close URLClassLoader on cleanup to release JAR file handles, and disable JVM-level JAR caching in getResource() by wrapping jar: URLs with a URLStreamHandler that sets useCaches(false).

The Gradle daemon reuses JVMs across builds. When a sibling module's JAR is rewritten, two independent caching layers can hold stale file handles:

  1. URLClassLoader internal cache (URLClassPathJarLoader)
  2. JarFileFactory static HashMap (populated via JarURLConnection)

The URLClassLoader.close() call addresses layer 1, but layer 2 is JVM-global and independent of the class loader. Setting useCaches(false) on jar: URL connections prevents JarFileFactory from caching JarFile instances, matching the approach used by Spring's PathMatchingResourcePatternResolver (SPR-4639).

Fixes #15458

… daemon

Close `URLClassLoader` on cleanup to release JAR file handles, and
disable JVM-level JAR caching in `getResource()` by wrapping `jar:` URLs
with a `URLStreamHandler` that sets `useCaches(false)`.

The Gradle daemon reuses JVMs across builds. When a sibling module's JAR
is rewritten, two independent caching layers can hold stale file handles:
1. `URLClassLoader` internal cache (`URLClassPath` → `JarLoader`)
2. `JarFileFactory` static HashMap (populated via `JarURLConnection`)

The `URLClassLoader.close()` call addresses layer 1, but layer 2 is
JVM-global and independent of the class loader. Setting
`useCaches(false)` on `jar:` URL connections prevents `JarFileFactory`
from caching `JarFile` instances, matching the approach used by Spring's
`PathMatchingResourcePatternResolver` (SPR-4639).

Fixes #15458
caalador
caalador previously approved these changes Mar 24, 2026
Comment on lines 86 to 87
val tokenFile = BuildFrontendUtil.getTokenFile(adapter.get())
check(tokenFile.exists()) { "token file $tokenFile doesn't exist!" }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

These lines have not bee formatted to correct indentation after the added try{}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

fixed

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 24, 2026

Test Results

1 287 files  ±0  1 287 suites  ±0   1h 16m 10s ⏱️ + 1m 57s
8 823 tests  - 5  8 760 ✅  - 5  63 💤 ±0  0 ❌ ±0 
9 135 runs  +3  9 066 ✅ +2  69 💤 +1  0 ❌ ±0 

Results for commit 87d1aa1. ± Comparison against base commit 65590b2.

♻️ This comment has been updated with latest results.

@mcollovati mcollovati changed the title fix: Prevent stale JAR cache in ReflectionsClassFinder under Gradle daemon (24.9) fix: Prevent stale JAR cache in ReflectionsClassFinder under Gradle daemon (CP: 24.9) Mar 24, 2026
@sonarqubecloud
Copy link
Copy Markdown

@mcollovati mcollovati merged commit 6ba497b into 24.9 Mar 24, 2026
29 checks passed
@mcollovati mcollovati deleted the feature/gradle_jarCache_issue_249 branch March 24, 2026 10:02
vaadin-bot pushed a commit that referenced this pull request Mar 24, 2026
… daemon (CP: 24.9) (#23969)

Close `URLClassLoader` on cleanup to release JAR file handles, and
disable JVM-level JAR caching in `getResource()` by wrapping `jar:` URLs
with a `URLStreamHandler` that sets `useCaches(false)`.

The Gradle daemon reuses JVMs across builds. When a sibling module's JAR
is rewritten, two independent caching layers can hold stale file handles:
1. `URLClassLoader` internal cache (`URLClassPath` → `JarLoader`)
2. `JarFileFactory` static HashMap (populated via `JarURLConnection`)

The `URLClassLoader.close()` call addresses layer 1, but layer 2 is
JVM-global and independent of the class loader. Setting
`useCaches(false)` on `jar:` URL connections prevents `JarFileFactory`
from caching `JarFile` instances, matching the approach used by Spring's
`PathMatchingResourcePatternResolver` (SPR-4639).

Fixes #15458
vaadin-bot added a commit that referenced this pull request Mar 24, 2026
… daemon (CP: 24.9) (#23969) (#23976)

Close `URLClassLoader` on cleanup to release JAR file handles, and
disable JVM-level JAR caching in `getResource()` by wrapping `jar:` URLs
with a `URLStreamHandler` that sets `useCaches(false)`.

The Gradle daemon reuses JVMs across builds. When a sibling module's JAR
is rewritten, two independent caching layers can hold stale file handles:
1. `URLClassLoader` internal cache (`URLClassPath` → `JarLoader`)
2. `JarFileFactory` static HashMap (populated via `JarURLConnection`)

The `URLClassLoader.close()` call addresses layer 1, but layer 2 is
JVM-global and independent of the class loader. Setting
`useCaches(false)` on `jar:` URL connections prevents `JarFileFactory`
from caching `JarFile` instances, matching the approach used by Spring's
`PathMatchingResourcePatternResolver` (SPR-4639).

Fixes #15458

Co-authored-by: Marco Collovati <marco@vaadin.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants