提交 7f856b56 编写于 作者: J Jesse Glick

[FIXED JENKINS-12753] Added PluginStrategy.getShortName to avoid actually...

[FIXED JENKINS-12753] Added PluginStrategy.getShortName to avoid actually trying to unpack a plugin before throwing RestartRequiredException.
上级 9062a944
...@@ -55,6 +55,9 @@ Upcoming changes</a> ...@@ -55,6 +55,9 @@ Upcoming changes</a>
<!-- Record your changes in the trunk here. --> <!-- Record your changes in the trunk here. -->
<div id="trunk" style="display:none"><!--=TRUNK-BEGIN=--> <div id="trunk" style="display:none"><!--=TRUNK-BEGIN=-->
<ul class=image> <ul class=image>
<li class="bug">
Misleading error message trying to dynamically update a plugin (which is impossible) on an NFS filesystem.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-12753">issue 12753</a>)
<li class="bug"> <li class="bug">
Updated component used by the swap space monitor to better support Debian and AIX. Updated component used by the swap space monitor to better support Debian and AIX.
<li class="bug"> <li class="bug">
......
...@@ -66,6 +66,7 @@ import java.util.HashSet; ...@@ -66,6 +66,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Vector; import java.util.Vector;
import java.util.jar.Attributes; import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest; import java.util.jar.Manifest;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
...@@ -92,15 +93,26 @@ public class ClassicPluginStrategy implements PluginStrategy { ...@@ -92,15 +93,26 @@ public class ClassicPluginStrategy implements PluginStrategy {
this.pluginManager = pluginManager; this.pluginManager = pluginManager;
} }
public PluginWrapper createPluginWrapper(File archive) throws IOException { @Override public String getShortName(File archive) throws IOException {
final Manifest manifest; Manifest manifest;
URL baseResourceURL; if (isLinked(archive)) {
manifest = loadLinkedManifest(archive);
} else {
JarFile jf = new JarFile(archive, false);
try {
manifest = jf.getManifest();
} finally {
jf.close();
}
}
return PluginWrapper.computeShortName(manifest, archive);
}
File expandDir = null; private static boolean isLinked(File archive) {
// if .hpi, this is the directory where war is expanded return archive.getName().endsWith(".hpl") || archive.getName().endsWith(".jpl");
}
boolean isLinked = archive.getName().endsWith(".hpl") || archive.getName().endsWith(".jpl"); private static Manifest loadLinkedManifest(File archive) throws IOException {
if (isLinked) {
// resolve the .hpl file to the location of the manifest file // resolve the .hpl file to the location of the manifest file
final String firstLine = IOUtils.readFirstLine(new FileInputStream(archive), "UTF-8"); final String firstLine = IOUtils.readFirstLine(new FileInputStream(archive), "UTF-8");
if (firstLine.startsWith("Manifest-Version:")) { if (firstLine.startsWith("Manifest-Version:")) {
...@@ -112,12 +124,24 @@ public class ClassicPluginStrategy implements PluginStrategy { ...@@ -112,12 +124,24 @@ public class ClassicPluginStrategy implements PluginStrategy {
// then parse manifest // then parse manifest
FileInputStream in = new FileInputStream(archive); FileInputStream in = new FileInputStream(archive);
try { try {
manifest = new Manifest(in); return new Manifest(in);
} catch (IOException e) { } catch (IOException e) {
throw new IOException("Failed to load " + archive, e); throw new IOException("Failed to load " + archive, e);
} finally { } finally {
in.close(); in.close();
} }
}
@Override public PluginWrapper createPluginWrapper(File archive) throws IOException {
final Manifest manifest;
URL baseResourceURL;
File expandDir = null;
// if .hpi, this is the directory where war is expanded
boolean isLinked = isLinked(archive);
if (isLinked) {
manifest = loadLinkedManifest(archive);
} else { } else {
if (archive.isDirectory()) {// already expanded if (archive.isDirectory()) {// already expanded
expandDir = archive; expandDir = archive;
......
...@@ -412,11 +412,21 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas ...@@ -412,11 +412,21 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
*/ */
public void dynamicLoad(File arc) throws IOException, InterruptedException, RestartRequiredException { public void dynamicLoad(File arc) throws IOException, InterruptedException, RestartRequiredException {
LOGGER.info("Attempting to dynamic load "+arc); LOGGER.info("Attempting to dynamic load "+arc);
final PluginWrapper p = strategy.createPluginWrapper(arc); PluginWrapper p = null;
String sn = p.getShortName(); 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();
}
if (getPlugin(sn)!=null) if (getPlugin(sn)!=null)
throw new RestartRequiredException(Messages._PluginManager_PluginIsAlreadyInstalled_RestartRequired(sn)); throw new RestartRequiredException(Messages._PluginManager_PluginIsAlreadyInstalled_RestartRequired(sn));
if (p == null) {
p = strategy.createPluginWrapper(arc);
}
if (p.supportsDynamicLoad()== YesNoMaybe.NO) if (p.supportsDynamicLoad()== YesNoMaybe.NO)
throw new RestartRequiredException(Messages._PluginManager_PluginDoesntSupportDynamicLoad_RestartRequired(sn)); throw new RestartRequiredException(Messages._PluginManager_PluginDoesntSupportDynamicLoad_RestartRequired(sn));
...@@ -442,10 +452,11 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas ...@@ -442,10 +452,11 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas
// run initializers in the added plugin // run initializers in the added plugin
Reactor r = new Reactor(InitMilestone.ordering()); Reactor r = new Reactor(InitMilestone.ordering());
r.addAll(new InitializerFinder(p.classLoader) { final ClassLoader loader = p.classLoader;
r.addAll(new InitializerFinder(loader) {
@Override @Override
protected boolean filter(Method e) { protected boolean filter(Method e) {
return e.getDeclaringClass().getClassLoader()!=p.classLoader || super.filter(e); return e.getDeclaringClass().getClassLoader() != loader || super.filter(e);
} }
}.discoverTasks(r)); }.discoverTasks(r));
try { try {
......
...@@ -24,11 +24,10 @@ ...@@ -24,11 +24,10 @@
package hudson; package hudson;
import hudson.model.Hudson; import hudson.model.Hudson;
import jenkins.model.Jenkins;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import javax.annotation.Nonnull;
/** /**
* Pluggability point for how to create {@link PluginWrapper}. * Pluggability point for how to create {@link PluginWrapper}.
...@@ -50,6 +49,13 @@ public interface PluginStrategy extends ExtensionPoint { ...@@ -50,6 +49,13 @@ public interface PluginStrategy extends ExtensionPoint {
PluginWrapper createPluginWrapper(File archive) PluginWrapper createPluginWrapper(File archive)
throws IOException; throws IOException;
/**
* Finds the plugin name without actually unpacking anything {@link #createPluginWrapper} would.
* Needed by {@link PluginManager#dynamicLoad} to decide whether such a plugin is already installed.
* @return the {@link PluginWrapper#getShortName}
*/
@Nonnull String getShortName(File archive) throws IOException;
/** /**
* Loads the plugin and starts it. * Loads the plugin and starts it.
* *
......
...@@ -238,7 +238,7 @@ public class PluginWrapper implements Comparable<PluginWrapper>, ModelObject { ...@@ -238,7 +238,7 @@ public class PluginWrapper implements Comparable<PluginWrapper>, ModelObject {
return idx != null && idx.toString().contains(shortName) ? idx : null; return idx != null && idx.toString().contains(shortName) ? idx : null;
} }
private String computeShortName(Manifest manifest, File archive) { static String computeShortName(Manifest manifest, File archive) {
// use the name captured in the manifest, as often plugins // use the name captured in the manifest, as often plugins
// depend on the specific short name in its URLs. // depend on the specific short name in its URLs.
String n = manifest.getMainAttributes().getValue("Short-Name"); String n = manifest.getMainAttributes().getValue("Short-Name");
......
...@@ -40,6 +40,7 @@ import java.net.URLClassLoader; ...@@ -40,6 +40,7 @@ import java.net.URLClassLoader;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import jenkins.RestartRequiredException;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.tools.ant.filters.StringInputStream; import org.apache.tools.ant.filters.StringInputStream;
import static org.junit.Assert.*; import static org.junit.Assert.*;
...@@ -356,4 +357,22 @@ public class PluginManagerTest { ...@@ -356,4 +357,22 @@ public class PluginManagerTest {
assertTrue(r.jenkins.getExtensionList("org.jenkinsci.plugins.dependencytest.dependee.DependeeExtensionPoint").isEmpty()); assertTrue(r.jenkins.getExtensionList("org.jenkinsci.plugins.dependencytest.dependee.DependeeExtensionPoint").isEmpty());
} }
@Bug(12753)
@WithPlugin("tasks.jpi")
@Test public void dynamicLoadRestartRequiredException() throws Exception {
File jpi = new File(r.jenkins.getRootDir(), "plugins/tasks.jpi");
assertTrue(jpi.isFile());
FileUtils.touch(jpi);
File timestamp = new File(r.jenkins.getRootDir(), "plugins/tasks/.timestamp2");
assertTrue(timestamp.isFile());
long lastMod = timestamp.lastModified();
try {
r.jenkins.getPluginManager().dynamicLoad(jpi);
fail("should not have worked");
} catch (RestartRequiredException x) {
// good
}
assertEquals("should not have tried to delete & unpack", lastMod, timestamp.lastModified());
}
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册