Commit
…2732) * [FIXED JENKINS-41684] Ensure that PluginManager.dynamicLoad runs as SYSTEM. Test plugin source: package test; import hudson.Plugin; import jenkins.model.Jenkins; public class ThePlugin extends Plugin { @OverRide public void postInitialize() throws Exception { Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); } } * @daniel-beck wants this all reindented.
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -819,100 +819,102 @@ public void dynamicLoad(File arc) throws IOException, InterruptedException, Rest | |
*/ | ||
@Restricted(NoExternalUse.class) | ||
public void dynamicLoad(File arc, boolean removeExisting) throws IOException, InterruptedException, RestartRequiredException { | ||
LOGGER.info("Attempting to dynamic load "+arc); | ||
PluginWrapper p = null; | ||
String sn; | ||
try { | ||
sn = strategy.getShortName(arc); | ||
} catch (AbstractMethodError x) { | ||
LOGGER.log(WARNING, "JENKINS-12753 fix not active: {0}", x.getMessage()); | ||
p = strategy.createPluginWrapper(arc); | ||
sn = p.getShortName(); | ||
} | ||
PluginWrapper pw = getPlugin(sn); | ||
if (pw!=null) { | ||
if (removeExisting) { // try to load disabled plugins | ||
for (Iterator<PluginWrapper> i = plugins.iterator(); i.hasNext();) { | ||
pw = i.next(); | ||
if(sn.equals(pw.getShortName())) { | ||
i.remove(); | ||
pw = null; | ||
break; | ||
try (ACLContext context = ACL.as(ACL.SYSTEM)) { | ||
LOGGER.info("Attempting to dynamic load "+arc); | ||
PluginWrapper p = null; | ||
String sn; | ||
try { | ||
sn = strategy.getShortName(arc); | ||
} catch (AbstractMethodError x) { | ||
LOGGER.log(WARNING, "JENKINS-12753 fix not active: {0}", x.getMessage()); | ||
p = strategy.createPluginWrapper(arc); | ||
sn = p.getShortName(); | ||
} | ||
PluginWrapper pw = getPlugin(sn); | ||
if (pw!=null) { | ||
if (removeExisting) { // try to load disabled plugins | ||
for (Iterator<PluginWrapper> i = plugins.iterator(); i.hasNext();) { | ||
pw = i.next(); | ||
if(sn.equals(pw.getShortName())) { | ||
i.remove(); | ||
pw = null; | ||
break; | ||
} | ||
} | ||
} else { | ||
throw new RestartRequiredException(Messages._PluginManager_PluginIsAlreadyInstalled_RestartRequired(sn)); | ||
} | ||
} else { | ||
throw new RestartRequiredException(Messages._PluginManager_PluginIsAlreadyInstalled_RestartRequired(sn)); | ||
} | ||
} | ||
if (p == null) { | ||
p = strategy.createPluginWrapper(arc); | ||
} | ||
if (p.supportsDynamicLoad()== YesNoMaybe.NO) | ||
throw new RestartRequiredException(Messages._PluginManager_PluginDoesntSupportDynamicLoad_RestartRequired(sn)); | ||
if (p == null) { | ||
p = strategy.createPluginWrapper(arc); | ||
} | ||
if (p.supportsDynamicLoad()== YesNoMaybe.NO) | ||
throw new RestartRequiredException(Messages._PluginManager_PluginDoesntSupportDynamicLoad_RestartRequired(sn)); | ||
|
||
// there's no need to do cyclic dependency check, because we are deploying one at a time, | ||
// so existing plugins can't be depending on this newly deployed one. | ||
// there's no need to do cyclic dependency check, because we are deploying one at a time, | ||
// so existing plugins can't be depending on this newly deployed one. | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
daniel-beck
Member
|
||
|
||
plugins.add(p); | ||
if (p.isActive()) | ||
activePlugins.add(p); | ||
synchronized (((UberClassLoader) uberClassLoader).loaded) { | ||
((UberClassLoader) uberClassLoader).loaded.clear(); | ||
} | ||
|
||
try { | ||
p.resolvePluginDependencies(); | ||
strategy.load(p); | ||
plugins.add(p); | ||
if (p.isActive()) | ||
activePlugins.add(p); | ||
synchronized (((UberClassLoader) uberClassLoader).loaded) { | ||
((UberClassLoader) uberClassLoader).loaded.clear(); | ||
} | ||
|
||
Jenkins.getInstance().refreshExtensions(); | ||
try { | ||
p.resolvePluginDependencies(); | ||
strategy.load(p); | ||
|
||
p.getPlugin().postInitialize(); | ||
} catch (Exception e) { | ||
failedPlugins.add(new FailedPlugin(sn, e)); | ||
activePlugins.remove(p); | ||
plugins.remove(p); | ||
throw new IOException("Failed to install "+ sn +" plugin",e); | ||
} | ||
Jenkins.getInstance().refreshExtensions(); | ||
|
||
// run initializers in the added plugin | ||
Reactor r = new Reactor(InitMilestone.ordering()); | ||
final ClassLoader loader = p.classLoader; | ||
r.addAll(new InitializerFinder(loader) { | ||
@Override | ||
protected boolean filter(Method e) { | ||
return e.getDeclaringClass().getClassLoader() != loader || super.filter(e); | ||
p.getPlugin().postInitialize(); | ||
} catch (Exception e) { | ||
failedPlugins.add(new FailedPlugin(sn, e)); | ||
activePlugins.remove(p); | ||
plugins.remove(p); | ||
throw new IOException("Failed to install "+ sn +" plugin",e); | ||
} | ||
}.discoverTasks(r)); | ||
try { | ||
new InitReactorRunner().run(r); | ||
} 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; | ||
// run initializers in the added plugin | ||
Reactor r = new Reactor(InitMilestone.ordering()); | ||
final ClassLoader loader = p.classLoader; | ||
r.addAll(new InitializerFinder(loader) { | ||
@Override | ||
protected boolean filter(Method e) { | ||
return e.getDeclaringClass().getClassLoader() != loader || super.filter(e); | ||
} | ||
}.discoverTasks(r)); | ||
try { | ||
new InitReactorRunner().run(r); | ||
} catch (ReactorException e) { | ||
throw new IOException("Failed to initialize "+ sn +" plugin",e); | ||
} | ||
for (Dependency d: depender.getOptionalDependencies()) { | ||
if (d.shortName.equals(p.getShortName())) { | ||
// this plugin depends on the newly loaded one! | ||
// recalculate dependencies! | ||
try { | ||
getPluginStrategy().updateDependency(depender, p); | ||
} catch (AbstractMethodError x) { | ||
LOGGER.log(WARNING, "{0} does not yet implement updateDependency", getPluginStrategy().getClass()); | ||
|
||
// 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! | ||
try { | ||
getPluginStrategy().updateDependency(depender, p); | ||
} catch (AbstractMethodError x) { | ||
LOGGER.log(WARNING, "{0} does not yet implement updateDependency", getPluginStrategy().getClass()); | ||
} | ||
break; | ||
} | ||
break; | ||
} | ||
} | ||
} | ||
|
||
// Redo who depends on who. | ||
resolveDependantPlugins(); | ||
// Redo who depends on who. | ||
resolveDependantPlugins(); | ||
|
||
LOGGER.info("Plugin " + p.getShortName()+":"+p.getVersion() + " dynamically installed"); | ||
LOGGER.info("Plugin " + p.getShortName()+":"+p.getVersion() + " dynamically installed"); | ||
} | ||
} | ||
|
||
@Restricted(NoExternalUse.class) | ||
|
This is an invalid assumption. Existing plugins may have an optional dependency on the newly deployed one. We can argue the merits of such a situation, but we are where we are, optional dependencies exist. I suspect the pragmatic solution in this case would be to detect up front and disable the dynamic install for such a plugin.
/cc @oleg-nenashev I give up chasing the
git blame
any further and I don't have time to fight with JIRA right now ;-)