提交 516866e0 编写于 作者: K kohsuke

adding the notion that a plugin can be pinned to avoid overwritten by a bundled plugin.

git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@21977 71c3de6d-444a-0410-be80-ed276b4c234a
上级 16a1ae58
......@@ -324,7 +324,7 @@ THE SOFTWARE.
<dependency>
<groupId>org.kohsuke.stapler</groupId>
<artifactId>stapler-jelly</artifactId>
<version>1.115</version>
<version>1.116</version>
<exclusions>
<exclusion>
<groupId>dom4j</groupId>
......
......@@ -23,10 +23,23 @@
*/
package hudson;
import hudson.model.*;
import hudson.model.AbstractModelObject;
import hudson.model.Failure;
import hudson.model.Hudson;
import hudson.model.UpdateCenter;
import hudson.util.Service;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.LogFactory;
import org.kohsuke.stapler.HttpRedirect;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.WebApp;
import java.util.Enumeration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.io.File;
......@@ -34,27 +47,16 @@ import java.io.FilenameFilter;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.io.FileUtils;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.WebApp;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.HttpRedirect;
/**
* Manages {@link PluginWrapper}s.
*
......@@ -110,7 +112,7 @@ public final class PluginManager extends AbstractModelObject {
if(!rootDir.exists())
rootDir.mkdirs();
loadBundledPlugins();
Collection<String> bundledPlugins = loadBundledPlugins();
File[] archives = rootDir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
......@@ -142,6 +144,7 @@ public final class PluginManager extends AbstractModelObject {
for( File arc : archivesList ) {
try {
PluginWrapper p = strategy.createPluginWrapper(arc);
p.isBundled = bundledPlugins.contains(arc.getName());
plugins.add(p);
if(p.isActive())
activePlugins.add(p);
......@@ -183,15 +186,19 @@ public final class PluginManager extends AbstractModelObject {
/**
* If the war file has any "/WEB-INF/plugins/*.hpi", extract them into the plugin directory.
*
* @return
* File names of the bundled plugins. Like {"ssh-slaves.hpi","subvesrion.hpi"}
*/
private void loadBundledPlugins() {
private Collection<String> loadBundledPlugins() {
// this is used in tests, when we want to override the default bundled plugins with .hpl versions
if (System.getProperty("hudson.bundled.plugins") != null) {
return;
return Collections.emptySet();
}
Set paths = context.getResourcePaths("/WEB-INF/plugins");
if(paths==null) return; // crap
for( String path : (Set<String>) paths) {
Set<String> names = new HashSet<String>();
for( String path : Util.fixNull((Set<String>)context.getResourcePaths("/WEB-INF/plugins"))) {
String fileName = path.substring(path.lastIndexOf('/')+1);
if(fileName.length()==0) {
// see http://www.nabble.com/404-Not-Found-error-when-clicking-on-help-td24508544.html
......@@ -199,12 +206,17 @@ public final class PluginManager extends AbstractModelObject {
continue;
}
try {
names.add(fileName);
URL url = context.getResource(path);
long lastModified = url.openConnection().getLastModified();
File file = new File(rootDir, fileName);
// TODO: allow upgrade of bundled plugins; code below will overwrite the newly
// upgraded file with the bundled version since lastModified has changed.
if (!file.exists() || file.lastModified() != lastModified) {
File pinFile = new File(rootDir, fileName+".pinned");
// update file if:
// - no file exists today
// - bundled version and current version differs (by timestamp), and the file isn't pinned.
if (!file.exists() || (file.lastModified() != lastModified && !pinFile.exists())) {
FileUtils.copyURLToFile(url, file);
file.setLastModified(url.openConnection().getLastModified());
// lastModified is set for two reasons:
......@@ -216,6 +228,8 @@ public final class PluginManager extends AbstractModelObject {
LOGGER.log(Level.SEVERE, "Failed to extract the bundled plugin "+fileName,e);
}
}
return names;
}
/**
......
......@@ -38,7 +38,8 @@ import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
import org.apache.commons.logging.LogFactory;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.HttpResponses;
/**
* Represents a Hudson plug-in and associated control information
......@@ -94,6 +95,15 @@ public final class PluginWrapper {
*/
private final File disableFile;
/**
* Used to control the unpacking of the bundled plugin.
* If a pin file exists, Hudson assumes that the user wants to pin down a particular version
* of a plugin, and will not try to overwrite it. Otherwise, it'll be overwritten
* by a bundled copy, to ensure consistency across upgrade/downgrade.
* @since 1.325
*/
private final File pinFile;
/**
* Short name of the plugin. The artifact Id of the plugin.
* This is also used in the URL within Hudson, so it needs
......@@ -111,6 +121,11 @@ public final class PluginWrapper {
private final List<Dependency> dependencies;
private final List<Dependency> optionalDependencies;
/**
* Is this plugin bundled in hudson.war?
*/
/*package*/ boolean isBundled;
static final class Dependency {
public final String shortName;
public final String version;
......@@ -162,6 +177,7 @@ public final class PluginWrapper {
this.baseResourceURL = baseResourceURL;
this.classLoader = classLoader;
this.disableFile = disableFile;
this.pinFile = new File(archive.getPath() + ".pinned");
this.active = !disableFile.exists();
this.dependencies = dependencies;
this.optionalDependencies = optionalDependencies;
......@@ -317,6 +333,10 @@ public final class PluginWrapper {
return active;
}
public boolean isBundled() {
return isBundled;
}
/**
* If true, the plugin is going to be activated next time
* Hudson runs.
......@@ -377,16 +397,28 @@ public final class PluginWrapper {
// Action methods
//
//
public void doMakeEnabled(StaplerResponse rsp) throws IOException {
public HttpResponse doMakeEnabled() throws IOException {
Hudson.getInstance().checkPermission(Hudson.ADMINISTER);
enable();
rsp.setStatus(200);
return HttpResponses.ok();
}
public void doMakeDisabled(StaplerResponse rsp) throws IOException {
public HttpResponse doMakeDisabled() throws IOException {
Hudson.getInstance().checkPermission(Hudson.ADMINISTER);
disable();
rsp.setStatus(200);
return HttpResponses.ok();
}
public HttpResponse doPin() throws IOException {
Hudson.getInstance().checkPermission(Hudson.ADMINISTER);
new FileOutputStream(pinFile).close();
return HttpResponses.ok();
}
public HttpResponse doUnpin() throws IOException {
Hudson.getInstance().checkPermission(Hudson.ADMINISTER);
pinFile.delete();
return HttpResponses.ok();
}
......
......@@ -80,6 +80,8 @@ import java.util.ResourceBundle;
import java.util.SimpleTimeZone;
import java.util.StringTokenizer;
import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
......@@ -852,6 +854,14 @@ public class Util {
return fixEmpty(s.trim());
}
public static <T> List<T> fixNull(List<T> l) {
return l!=null ? l : Collections.<T>emptyList();
}
public static <T> Set<T> fixNull(Set<T> l) {
return l!=null ? l : Collections.<T>emptySet();
}
/**
* Cuts all the leading path portion and get just the file name.
*/
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册