gnodet commented on PR #1923:
URL: https://github.com/apache/maven-resolver/pull/1923#issuecomment-4702778112

   ## In-Depth Review: PR #1923 — Fix ApacheTransport auth cache CCEx
   
   ### Design Assessment
   
   The `Keys` helper class is a clean, well-considered solution to the 
ClassLoader-scoping problem. Rather than chasing individual key sites with 
ad-hoc classloader-aware string concatenation (as the initial fix in `ec6bd858` 
did with `identityHashCode(classLoader)`), this PR correctly identifies that 
the **same mechanism** — using `Class` objects as key elements — naturally 
provides ClassLoader scoping, since `Class.equals()` is identity-based and 
Class instances are ClassLoader-scoped by definition.
   
   Placing `Keys` in `maven-resolver-api` is the right call: both `SessionData` 
and `RepositoryCache` live there, and the key contract is defined by those 
interfaces. The unified factory approach (`Keys.of(...)`) replaces a scattered 
mix of string concatenation, custom inner classes (`LoggedMirror`), and bare 
`new Object()` sentinels with a single, consistent pattern.
   
   The three key "flavors" (global/string-only, 
ClassLoader-aware/Class-element, private/Object-identity) emerge naturally from 
the element types without needing separate factory methods — this is elegant.
   
   ### Key Findings
   
   1. **Defensive copy (Keys.java:91)** — The varargs array is stored without 
cloning. Safe for all current callers but leaves a footgun in a public API 
class. See inline comment.
   
   2. **Javadoc nits (Keys.java:31–39)** — `<p/>` (should be `<p>`), "our" → 
"out", "od" → "of". See inline comment.
   
   3. **Subkey expansion asymmetry (Keys.java:75)** — Only first-position `Key` 
elements are expanded. The Javadoc could be more explicit about this. See 
inline comment.
   
   4. **Copy-paste bug fix confirmed 
(RemoteRepositoryFilterSourceSupport.java:121)** — `git blame` confirms 
`FileTrustedChecksumsSourceSupport.class` was copy-pasted from #1679 and never 
corrected. Good catch. See inline comment.
   
   5. **Public field type change (OptionalDependencySelector.java:41-42)** — 
`IGNORED_KEYS` and `UNSELECTED_KEYS` change from `String` to `Object`. Not in 
the public API surface (api/spi/util), but these are `public` fields. See 
inline comment.
   
   6. **`DefaultRemoteRepositoryManager`: `LoggedMirror` removal** — The inner 
class is replaced with `Keys.of(mirror.getId(), mirror.getUrl(), 
original.getId(), original.getUrl())`. Semantically equivalent (both use 
`Arrays.equals`/`hashCode` on the same 4 string elements). Clean simplification.
   
   7. **`DefaultUpdateCheckManager`: private key pattern preserved** — 
`Keys.of(new Object() { toString() })` correctly maintains the identity-based 
"private key" semantics of the original bare `new Object()`. The `toString()` 
override is preserved for debuggability. The parameter type tightening (`Object 
updateKey` → `String updateKey`) is a correct refinement.
   
   ### Correctness Analysis
   
   **The core fix is correct.** The root cause of the ClassCastException is:
   
   1. `ApacheTransporter` stored an `AuthCache` (Apache HttpClient class) in 
`RepositoryCache` under a string key containing `getClass().getSimpleName()` 
(just `"ApacheTransporter"`)
   2. When the same transport is loaded by two ClassRealms, both instances 
share the same string key
   3. Instance A stores an `AuthCache` from ClassLoader 1
   4. Instance B retrieves it and casts to `AuthCache` from ClassLoader 2 → 
`ClassCastException`
   
   The fix uses `getClass()` (a `Class` object, not its name) as a key element, 
so instances from different ClassLoaders produce different keys via 
identity-based `Class.equals()`. This is the correct approach.
   
   **All 15 migration sites use the right key scope:**
   
   | Component | Old pattern | New pattern | Scope correct? |
   |-----------|------------|-------------|----------------|
   | `ApacheTransporter` | `getSimpleName() + repo` | `Keys.of(getClass(), 
repo)` | Yes — ClassLoader-aware (transport module) |
   | `GlobalState` | `class.getName()` | `Keys.of(class)` | Yes — 
ClassLoader-aware (transport module) |
   | `DataPool` (4 keys) | `class.getName() + "$X"` | `Keys.of(class, "x")` | 
Yes — ClassLoader-aware (impl, safe for single-load) |
   | `PrioritizedComponents` | `class.getName() + disc + hash` | 
`Keys.of(class, disc, hash)` | Yes — ClassLoader-aware with discriminator |
   | `DefaultRepositoryKeyFunctionFactory` | `owner.getName() + ".X"` | 
`Keys.of(owner, "X")` | Yes — `owner` is already a Class param |
   | `DefaultRemoteRepositoryFilterManager` | `class.getName() + ".instance." + 
sessionHash` | `Keys.of(class, "instance", sessionHash)` | Yes |
   | `GroupIdRemoteRepositoryFilterSource` (4 keys) | `getClass().getName() + 
".X"` | `Keys.of(class, "X")` | Yes |
   | `PrefixesRemoteRepositoryFilterSource` (2 keys) | `getClass().getName() + 
".X"` | `Keys.of(class, "X")` | Yes |
   | `TrustedChecksumsArtifactResolverPostProcessor` | `class.getName() + ".X"` 
| `Keys.of(class, "X")` | Yes |
   | `OptionalDependencySelector` (2 keys) | `class.getName() + ".X"` | 
`Keys.of(class, "X")` | Yes |
   | `NamedLockFactoryAdapterFactoryImpl` | `class.getName() + ".adapter"` | 
`Keys.of(class, "adapter")` | Yes |
   | `SmartExecutorUtils` | `class.getSimpleName() + "-" + prefix` | 
`Keys.of(class, prefix)` | Yes |
   | `DefaultRemoteRepositoryManager` | `LoggedMirror` inner class | `Keys.of(4 
strings)` | Yes — global (no CL scoping needed) |
   | `DefaultUpdateCheckManager` | `new Object()` sentinel | `Keys.of(new 
Object())` | Yes — private key |
   
   ### API & Compatibility
   
   The PR description's claim of "no API breakage in public surface of api, 
spi, util modules" is **verified as correct**:
   
   - `Keys` is a **new** class in `maven-resolver-api` — purely additive
   - No method signatures in api/spi/util change
   - No public classes removed
   
   In `maven-resolver-impl` (internal, not public API):
   - `OptionalDependencySelector.IGNORED_KEYS` / `UNSELECTED_KEYS` change from 
`String` to `Object` — source-incompatible for any code referencing these as 
`String`, but these fields were added very recently (#1899) and are in 
`internal.impl`
   - `DefaultUpdateCheckManager.isAlreadyUpdated` / `setUpdated` parameter type 
tightens from `Object` to `String` — package-private methods, no external impact
   - `LoggedMirror` inner class removed — was `private`, no external impact
   
   ### Test Coverage
   
   `KeysTest.java` covers the three key flavors well:
   - `globalKeys()` — string-only keys, equality and inequality
   - `classLoaderAwareKeys()` — two ClassLoaders loading the same class, 
verifies distinct keys
   - `privateKeys()` — Object-identity keys
   - `subKey()` — key expansion/concatenation
   - Error cases: `noElement()`, `nullVararg()`
   
   **Gaps:**
   - No test for `null` as a key element (e.g., `Keys.of("a", null, "b")`) — 
currently this silently creates a key with a null element. Worth either 
rejecting or documenting.
   - No test for mixed-type key elements (e.g., `Keys.of("string", 42, 
SomeClass.class)`) — would increase confidence in the 
`Arrays.hashCode`/`Arrays.equals` delegation
   - No test for `toString()` output format (useful for debugging scenarios the 
Javadoc mentions)
   - The `classLoaderAwareKeys` test uses `Paths.get("target/classes")` — this 
works with Maven Surefire's working directory convention but could be fragile 
in IDE-based test runners
   - No integration test that would catch a ClassCastException regression in 
the actual transport layer. Understandable given the complexity of setting up 
multi-ClassLoader Maven-like scenarios, but the gap is worth noting.
   
   ### Minor Notes
   
   - The `DefaultUpdateCheckManager.SESSION_CHECKS` comment `// instance bound 
private key` is helpful — consider adding similar comments at other sites where 
the key scope choice is non-obvious
   - `GlobalState.java` still has its own `CompoundKey` inner class (used for 
`userTokens`/`expectContinues`) — this is correct, as those keys serve a 
different purpose than the session cache key
   
   ### Verdict
   
   The fix is correct and well-designed. The `Keys` abstraction is a clean 
improvement over the scattered key patterns, and the ClassLoader-scoping 
mechanism is sound. The migration is comprehensive and each site uses the 
appropriate key scope.
   
   The findings above are all minor — the most actionable ones are the Javadoc 
typos and the optional defensive copy. The copy-paste bug fix in 
`RemoteRepositoryFilterSourceSupport` is a nice bonus find.
   
   Overall: this looks good to merge, pending the minor fixes above.
   
   ---
   _In-depth review by Claude Code on behalf of @gnodet_


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to