Skip to content

Commit 113dbbe

Browse files
gnodetclaude
andcommitted
Fix @PreDestroy ClassNotFoundException from premature ClassRealm disposal (PR #11825)
Cherry-pick from fix/realm-cache-dispose-ordering branch. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent ccbacea commit 113dbbe

4 files changed

Lines changed: 88 additions & 3 deletions

File tree

impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultExtensionRealmCache.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,6 @@ public void register(MavenProject project, Key key, CacheRecord record) {
149149

150150
@Override
151151
public void dispose() {
152-
flush();
152+
cache.clear();
153153
}
154154
}

impl/maven-core/src/main/java/org/apache/maven/plugin/DefaultPluginRealmCache.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,6 @@ public void register(MavenProject project, Key key, CacheRecord record) {
215215

216216
@Override
217217
public void dispose() {
218-
flush();
218+
cache.clear();
219219
}
220220
}

impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectRealmCache.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,6 @@ public void register(MavenProject project, Key key, CacheRecord record) {
125125

126126
@Override
127127
public void dispose() {
128-
flush();
128+
cache.clear();
129129
}
130130
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.plugin;
20+
21+
import java.util.List;
22+
23+
import org.apache.maven.artifact.Artifact;
24+
import org.codehaus.plexus.classworlds.ClassWorld;
25+
import org.codehaus.plexus.classworlds.realm.ClassRealm;
26+
import org.junit.jupiter.api.Test;
27+
28+
import static org.junit.jupiter.api.Assertions.assertNotNull;
29+
import static org.junit.jupiter.api.Assertions.assertNull;
30+
import static org.junit.jupiter.api.Assertions.assertTrue;
31+
32+
/**
33+
* Verifies that dispose() does not dispose ClassRealms prematurely.
34+
* <p>
35+
* Plexus Disposable.dispose() runs before Sisu's @PreDestroy callbacks.
36+
* If dispose() disposes ClassRealms, beans loaded from those realms will
37+
* get ClassNotFoundException when their @PreDestroy methods execute.
38+
* dispose() should only clear the cache map; flush() should dispose realms.
39+
*
40+
* @see <a href="https://github.com/apache/maven/issues/10571">MNG-8572</a>
41+
*/
42+
class DefaultRealmCacheDisposeTest {
43+
44+
@Test
45+
void disposeDoesNotDisposeClassRealms() throws Exception {
46+
ClassWorld world = new ClassWorld();
47+
ClassRealm realm = world.newRealm("test-plugin-realm");
48+
49+
DefaultPluginRealmCache cache = new DefaultPluginRealmCache();
50+
PluginRealmCache.CacheRecord record = new PluginRealmCache.CacheRecord(realm, List.<Artifact>of());
51+
cache.cache.put(new TestKey(), record);
52+
53+
cache.dispose();
54+
55+
assertTrue(cache.cache.isEmpty(), "dispose() should clear the cache");
56+
assertNotNull(world.getClassRealm("test-plugin-realm"), "dispose() should NOT dispose the ClassRealm");
57+
}
58+
59+
@Test
60+
void flushDisposesClassRealms() throws Exception {
61+
ClassWorld world = new ClassWorld();
62+
ClassRealm realm = world.newRealm("test-plugin-realm-flush");
63+
64+
DefaultPluginRealmCache cache = new DefaultPluginRealmCache();
65+
PluginRealmCache.CacheRecord record = new PluginRealmCache.CacheRecord(realm, List.<Artifact>of());
66+
cache.cache.put(new TestKey(), record);
67+
68+
cache.flush();
69+
70+
assertTrue(cache.cache.isEmpty(), "flush() should clear the cache");
71+
assertNull(world.getClassRealm("test-plugin-realm-flush"), "flush() SHOULD dispose the ClassRealm");
72+
}
73+
74+
private static class TestKey implements PluginRealmCache.Key {
75+
@Override
76+
public int hashCode() {
77+
return 1;
78+
}
79+
80+
@Override
81+
public boolean equals(Object obj) {
82+
return obj instanceof TestKey;
83+
}
84+
}
85+
}

0 commit comments

Comments
 (0)