mirror of https://github.com/jenkinsci/jenkins.git
[FIXED JENKINS-18677]
Integrated bytecode-compatibility-transformer that allows core to do signature changes on properties that plugins might depend on. The library performs necessary bytecode transformation to achieve this. The first use of this is to fix plugins that looks for List AbstractProject.triggers, thereby resolving JENKINS-18677. For the time being, I'm not loading such compatibility annotations from plugins, but I did code that in PluginManager. Let's see how this feature work out for a while in the core, and if it looks stable and solid, we'll open it up to plugins at that point.
This commit is contained in:
parent
e7b9143067
commit
47de54d070
|
@ -55,6 +55,12 @@ Upcoming changes</a>
|
|||
<!-- Record your changes in the trunk here. -->
|
||||
<div id="trunk" style="display:none"><!--=TRUNK-BEGIN=-->
|
||||
<ul class=image>
|
||||
<li class=bug>
|
||||
Fixed <tt>NoSuchFieldError: triggers</tt> with older Maven plugin
|
||||
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-18677">issue 18677</a>)
|
||||
<li class=rfe>
|
||||
Added bytecode transformation driven compatibility ensurance mechanism
|
||||
(<a href="https://groups.google.com/forum/#!topic/jenkinsci-dev/7qCClb36njo">discussion</a>)
|
||||
<li class=rfe>
|
||||
Improve search to locate items inside folders.
|
||||
(<a href="https://github.com/jenkinsci/jenkins/pull/848">pull request 848</a>)
|
||||
|
|
|
@ -214,6 +214,11 @@ THE SOFTWARE.
|
|||
<artifactId>annotation-indexer</artifactId>
|
||||
<version>1.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jenkins-ci</groupId>
|
||||
<artifactId>bytecode-compatibility-transformer</artifactId>
|
||||
<version>1.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jvnet.hudson</groupId>
|
||||
<artifactId>task-reactor</artifactId>
|
||||
|
|
|
@ -59,7 +59,6 @@ import java.io.IOException;
|
|||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
@ -223,19 +222,10 @@ public class ClassicPluginStrategy implements PluginStrategy {
|
|||
return classLoader;
|
||||
}
|
||||
}
|
||||
if(useAntClassLoader && !Closeable.class.isAssignableFrom(URLClassLoader.class)) {
|
||||
// using AntClassLoader with Closeable so that we can predictably release jar files opened by URLClassLoader
|
||||
AntClassLoader2 classLoader = new AntClassLoader2(parent);
|
||||
classLoader.addPathFiles(paths);
|
||||
return classLoader;
|
||||
} else {
|
||||
// Tom reported that AntClassLoader has a performance issue when Hudson keeps trying to load a class that doesn't exist,
|
||||
// so providing a legacy URLClassLoader support, too
|
||||
List<URL> urls = new ArrayList<URL>();
|
||||
for (File path : paths)
|
||||
urls.add(path.toURI().toURL());
|
||||
return new URLClassLoader(urls.toArray(new URL[urls.size()]),parent);
|
||||
}
|
||||
|
||||
AntClassLoader2 classLoader = new AntClassLoader2(parent);
|
||||
classLoader.addPathFiles(paths);
|
||||
return classLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -652,7 +642,7 @@ public class ClassicPluginStrategy implements PluginStrategy {
|
|||
* {@link AntClassLoader} with a few methods exposed and {@link Closeable} support.
|
||||
* Deprecated as of Java 7, retained only for Java 5/6.
|
||||
*/
|
||||
private static final class AntClassLoader2 extends AntClassLoader implements Closeable {
|
||||
private final class AntClassLoader2 extends AntClassLoader implements Closeable {
|
||||
private final Vector pathComponents;
|
||||
|
||||
private AntClassLoader2(ClassLoader parent) {
|
||||
|
@ -669,6 +659,7 @@ public class ClassicPluginStrategy implements PluginStrategy {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public void addPathFiles(Collection<File> paths) throws IOException {
|
||||
for (File f : paths)
|
||||
addPathFile(f);
|
||||
|
@ -699,6 +690,11 @@ public class ClassicPluginStrategy implements PluginStrategy {
|
|||
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class defineClassFromData(File container, byte[] classData, String classname) throws IOException {
|
||||
return super.defineClassFromData(container, pluginManager.getCompatibilityTransformer().transform(classname,classData), classname);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean useAntClassLoader = Boolean.getBoolean(ClassicPluginStrategy.class.getName()+".useAntClassLoader");
|
||||
|
|
|
@ -59,6 +59,7 @@ import org.apache.commons.fileupload.servlet.ServletFileUpload;
|
|||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jenkinsci.bytecode.Transformer;
|
||||
import org.jvnet.hudson.reactor.Executable;
|
||||
import org.jvnet.hudson.reactor.Reactor;
|
||||
import org.jvnet.hudson.reactor.ReactorException;
|
||||
|
@ -151,6 +152,8 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
|
|||
// and load plugin-contributed classes.
|
||||
public final ClassLoader uberClassLoader = new UberClassLoader();
|
||||
|
||||
private final Transformer compatibilityTransformer = new Transformer();
|
||||
|
||||
/**
|
||||
* Once plugin is uploaded, this flag becomes true.
|
||||
* This is used to report a message that Jenkins needs to be restarted
|
||||
|
@ -179,6 +182,17 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
|
|||
rootDir.mkdirs();
|
||||
|
||||
strategy = createPluginStrategy();
|
||||
|
||||
// load up rules for the core first
|
||||
try {
|
||||
compatibilityTransformer.loadRules(getClass().getClassLoader());
|
||||
} catch (IOException e) {
|
||||
LOGGER.log(Level.WARNING, "Failed to load compatibility rewrite rules",e);
|
||||
}
|
||||
}
|
||||
|
||||
public Transformer getCompatibilityTransformer() {
|
||||
return compatibilityTransformer;
|
||||
}
|
||||
|
||||
public Api getApi() {
|
||||
|
@ -301,6 +315,13 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
|
|||
}
|
||||
});
|
||||
|
||||
// Let's see for a while until we open this functionality up to plugins
|
||||
// g.followedBy().attains(PLUGINS_LISTED).add("Load compatibility rules", new Executable() {
|
||||
// public void run(Reactor reactor) throws Exception {
|
||||
// compatibilityTransformer.loadRules(uberClassLoader);
|
||||
// }
|
||||
// });
|
||||
|
||||
session.addAll(g.discoverTasks(session));
|
||||
|
||||
pluginListed = true; // technically speaking this is still too early, as at this point tasks are merely scheduled, not necessarily executed.
|
||||
|
|
|
@ -95,6 +95,7 @@ import net.sf.json.JSONObject;
|
|||
import org.acegisecurity.Authentication;
|
||||
import org.acegisecurity.context.SecurityContext;
|
||||
import org.acegisecurity.context.SecurityContextHolder;
|
||||
import org.jenkinsci.bytecode.AdaptField;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
import org.kohsuke.args4j.Argument;
|
||||
|
@ -242,6 +243,7 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
|
|||
/**
|
||||
* List of all {@link Trigger}s for this project.
|
||||
*/
|
||||
@AdaptField(was=List.class)
|
||||
protected volatile DescribableList<Trigger<?>,TriggerDescriptor> triggers = new DescribableList<Trigger<?>,TriggerDescriptor>(this);
|
||||
private static final AtomicReferenceFieldUpdater<AbstractProject,DescribableList> triggersUpdater
|
||||
= AtomicReferenceFieldUpdater.newUpdater(AbstractProject.class,DescribableList.class,"triggers");
|
||||
|
|
Loading…
Reference in New Issue