Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[FIXED JENKINS-42189] In Groovy 2.4.8+, ClassInfo.klazz is replaced b…
…y .classRef.

cleanUpGlobalClassValue needs to walk through those (not just Sentinel.class over and over);
and cleanUpGlobalClassSet is no longer relevant since classRef is weak.
  • Loading branch information
jglick committed Feb 23, 2017
1 parent 3aaceea commit 1a73b74
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 9 deletions.
1 change: 1 addition & 0 deletions Jenkinsfile
@@ -0,0 +1 @@
buildPlugin(jenkinsVersions: [null, '2.32.2', /* JENKINS-42189 */ '2.47'])
2 changes: 1 addition & 1 deletion pom.xml
Expand Up @@ -28,7 +28,7 @@
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>2.22</version>
<version>2.23</version>
<relativePath />
</parent>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
Expand Down
Expand Up @@ -107,6 +107,7 @@
import hudson.security.AccessControlled;
import java.beans.Introspector;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
Expand Down Expand Up @@ -996,19 +997,31 @@ private static void cleanUpGlobalClassValue(@Nonnull ClassLoader loader) throws
Object map = mapF.get(globalClassValue);
Class<?> groovyClassValuePreJava7Map = Class.forName("org.codehaus.groovy.reflection.GroovyClassValuePreJava7$GroovyClassValuePreJava7Map");
Collection entries = (Collection) groovyClassValuePreJava7Map.getMethod("values").invoke(map);
Field klazzF = classInfoC.getDeclaredField("klazz");
klazzF.setAccessible(true);
Method removeM = groovyClassValuePreJava7Map.getMethod("remove", Object.class);
Class<?> entryC = Class.forName("org.codehaus.groovy.util.AbstractConcurrentMapBase$Entry");
Method getValueM = entryC.getMethod("getValue");
List<Class<?>> toRemove = new ArrayList<>(); // not sure if it is safe against ConcurrentModificationException or not
for (Object entry : entries) {
Object value = getValueM.invoke(entry);
Class<?> klazz = (Class) klazzF.get(value);
try {
Field classRefF = classInfoC.getDeclaredField("classRef"); // 2.4.8+
classRefF.setAccessible(true);
for (Object entry : entries) {
Object value = getValueM.invoke(entry);
toRemove.add(((WeakReference<Class<?>>) classRefF.get(value)).get());
}
} catch (NoSuchFieldException x) {
Field klazzF = classInfoC.getDeclaredField("klazz"); // 2.4.7-
klazzF.setAccessible(true);
for (Object entry : entries) {
Object value = getValueM.invoke(entry);
toRemove.add((Class) klazzF.get(value));
}
}
Iterator<Class<?>> it = toRemove.iterator();
while (it.hasNext()) {
Class<?> klazz = it.next();
ClassLoader encounteredLoader = klazz.getClassLoader();
if (encounteredLoader == loader) {
toRemove.add(klazz);
} else {
if (encounteredLoader != loader) {
it.remove();
LOGGER.log(Level.FINEST, "ignoring {0} with loader {1}", new Object[] {klazz, /* do not hold from LogRecord */String.valueOf(encounteredLoader)});
}
}
Expand All @@ -1027,6 +1040,10 @@ private static void cleanUpGlobalClassSet(@Nonnull Class<?> clazz) throws Except
globalClassSet.getClass().getMethod("remove", Object.class).invoke(globalClassSet, clazz); // like Map but not
LOGGER.log(Level.FINER, "cleaning up {0} from GlobalClassSet", clazz.getName());
} catch (NoSuchMethodException x) { // Groovy 2
try {
Field classRefF = classInfoC.getDeclaredField("classRef");
return; // 2.4.8+, nothing to do here (classRef is weak anyway)
} catch (NoSuchFieldException x2) {} // 2.4.7-
// Cannot just call .values() since that returns a copy.
Field itemsF = globalClassSet.getClass().getDeclaredField("items");
itemsF.setAccessible(true);
Expand Down

0 comments on commit 1a73b74

Please sign in to comment.