From 87e2a7e8d5ac995a05b82c1e7839285102c111d0 Mon Sep 17 00:00:00 2001 From: ikedam Date: Tue, 10 Dec 2013 23:53:38 +0900 Subject: [PATCH] [FIXED JENKINS-19976] Enable plugins to load classes from optionally depending plugins even without restarting after depended ones installed. --- .../java/hudson/ClassicPluginStrategy.java | 44 +++++++++++++++++++ core/src/main/java/hudson/PluginManager.java | 17 +++++++ core/src/main/java/hudson/PluginStrategy.java | 8 ++++ 3 files changed, 69 insertions(+) diff --git a/core/src/main/java/hudson/ClassicPluginStrategy.java b/core/src/main/java/hudson/ClassicPluginStrategy.java index f60166f34e..0cedd1ea25 100644 --- a/core/src/main/java/hudson/ClassicPluginStrategy.java +++ b/core/src/main/java/hudson/ClassicPluginStrategy.java @@ -377,6 +377,45 @@ public class ClassicPluginStrategy implements PluginStrategy { plugin.getPlugin().start(); } + /** + * Called when a plugin is deployed, and there is a plugin optionally depending on that plugin. + * The class loader of the existing depending plugin should be updated + * to load classes from the newly deployed plugin. + * + * @param depender the plugin to update its class loader + * @param dependee + * @see hudson.PluginStrategy#updateDependency(hudson.PluginWrapper, hudson.PluginWrapper) + */ + @Override + public void updateDependency(PluginWrapper depender, PluginWrapper dependee) { + DependencyClassLoader classLoader = findAncestorDependencyClassLoader(depender.classLoader); + if (classLoader != null) { + classLoader.updateTransientDependencies(); + LOGGER.log(Level.INFO, "Updated dependency of {0}", depender.getShortName()); + } + } + + private DependencyClassLoader findAncestorDependencyClassLoader(ClassLoader classLoader) + { + for (; classLoader != null; classLoader = classLoader.getParent()) { + if (classLoader instanceof DependencyClassLoader) { + return (DependencyClassLoader)classLoader; + } + + if (classLoader instanceof AntClassLoader) { + // AntClassLoaders hold parents not only as AntClassLoader#getParent() + // but also as AntClassLoader#getConfiguredParent() + DependencyClassLoader ret = findAncestorDependencyClassLoader( + ((AntClassLoader)classLoader).getConfiguredParent() + ); + if (ret != null) { + return ret; + } + } + } + return null; + } + private static File resolve(File base, String relative) { File rel = new File(relative); if(rel.isAbsolute()) @@ -523,6 +562,11 @@ public class ClassicPluginStrategy implements PluginStrategy { this.dependencies = dependencies; } + private void updateTransientDependencies() { + // This will be recalculated at the next time. + transientDependencies = null; + } + private List getTransitiveDependencies() { if (transientDependencies==null) { CyclicGraphDetector cgd = new CyclicGraphDetector() { diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java index dc657a9a0b..64371a5dd4 100644 --- a/core/src/main/java/hudson/PluginManager.java +++ b/core/src/main/java/hudson/PluginManager.java @@ -450,6 +450,23 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas } catch (ReactorException e) { throw new IOException("Failed to initialize "+ sn +" plugin",e); } + + // recalculate dependencies of plugins optionally depending the newly deployed one. + for (PluginWrapper depender: plugins) { + if (depender.equals(p)) { + // skip itself. + continue; + } + for (Dependency d: depender.getOptionalDependencies()) { + if (d.shortName.equals(p.getShortName())) { + // this plugin depends on the newly loaded one! + // recalculate dependencies! + getPluginStrategy().updateDependency(depender, p); + break; + } + } + } + LOGGER.info("Plugin " + sn + " dynamically installed"); } diff --git a/core/src/main/java/hudson/PluginStrategy.java b/core/src/main/java/hudson/PluginStrategy.java index 6024c3571f..d61e23732a 100644 --- a/core/src/main/java/hudson/PluginStrategy.java +++ b/core/src/main/java/hudson/PluginStrategy.java @@ -77,4 +77,12 @@ public interface PluginStrategy extends ExtensionPoint { * @since 1.400 */ List> findComponents(Class type, Hudson hudson); + + /** + * Called when a plugin that is depended by another plugin is newly deployed. + * + * @param depender plugin depending on dependee. + * @param dependee newly loaded plugin. + */ + void updateDependency(PluginWrapper depender, PluginWrapper dependee); } -- GitLab