Support serving static/ and Documentation/ from plugins

The static/ and Documentation/ resource directories of a plugin can be
served over HTTP for any loaded and running plugin, even if it has no
other HTTP handlers. This permits a plugin to supply icons or other
graphics for the web UI, or documentation content to help users learn
how to use the plugin.

Change-Id: I267176cc76e161617d780438f88531fc50c1c2b8
This commit is contained in:
Shawn O. Pearce
2012-05-09 14:55:05 -07:00
parent 5ad16ea1a1
commit 0ad46d4d12
4 changed files with 187 additions and 59 deletions

View File

@@ -15,20 +15,31 @@
package com.google.gerrit.server.plugins;
import java.io.File;
import java.io.IOException;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.jar.JarFile;
class CleanupHandle extends WeakReference<ClassLoader> {
private final File tmpFile;
private final JarFile jarFile;
CleanupHandle(File jarFile,
CleanupHandle(File tmpFile,
JarFile jarFile,
ClassLoader ref,
ReferenceQueue<ClassLoader> queue) {
super(ref, queue);
this.tmpFile = jarFile;
this.tmpFile = tmpFile;
this.jarFile = jarFile;
}
void cleanup() {
tmpFile.delete();
try {
jarFile.close();
} catch (IOException err) {
}
if (!tmpFile.delete() && tmpFile.exists()) {
PluginLoader.log.warn("Cannot delete " + tmpFile.getAbsolutePath());
}
}
}

View File

@@ -27,6 +27,7 @@ import org.eclipse.jgit.storage.file.FileSnapshot;
import java.io.File;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import javax.annotation.Nullable;
@@ -40,9 +41,10 @@ public class Plugin {
}
private final String name;
private final File jar;
private final Manifest manifest;
private final File srcJar;
private final FileSnapshot snapshot;
private final JarFile jarFile;
private final Manifest manifest;
private Class<? extends Module> sysModule;
private Class<? extends Module> sshModule;
private Class<? extends Module> httpModule;
@@ -53,23 +55,25 @@ public class Plugin {
private LifecycleManager manager;
public Plugin(String name,
File jar,
Manifest manifest,
File srcJar,
FileSnapshot snapshot,
JarFile jarFile,
Manifest manifest,
@Nullable Class<? extends Module> sysModule,
@Nullable Class<? extends Module> sshModule,
@Nullable Class<? extends Module> httpModule) {
this.name = name;
this.jar = jar;
this.manifest = manifest;
this.srcJar = srcJar;
this.snapshot = snapshot;
this.jarFile = jarFile;
this.manifest = manifest;
this.sysModule = sysModule;
this.sshModule = sshModule;
this.httpModule = httpModule;
}
File getJar() {
return jar;
File getSrcJar() {
return srcJar;
}
public String getName() {
@@ -151,6 +155,10 @@ public class Plugin {
}
}
public JarFile getJarFile() {
return jarFile;
}
@Nullable
public Injector getSshInjector() {
return sshInjector;

View File

@@ -169,7 +169,7 @@ public class PluginLoader implements LifecycleListener {
log.info(String.format("Disabling plugin %s", name));
File off = new File(pluginsDir, active.getName() + ".jar.disabled");
active.getJar().renameTo(off);
active.getSrcJar().renameTo(off);
active.stop();
running.remove(name);
@@ -304,34 +304,45 @@ public class PluginLoader implements LifecycleListener {
return 0 < ext ? name.substring(0, ext) : name;
}
private Plugin loadPlugin(String name, File jarFile, FileSnapshot snapshot)
private Plugin loadPlugin(String name, File srcJar, FileSnapshot snapshot)
throws IOException, ClassNotFoundException {
File tmp;
FileInputStream in = new FileInputStream(jarFile);
FileInputStream in = new FileInputStream(srcJar);
try {
tmp = asTemp(in, tempNameFor(name), ".jar", tmpDir);
} finally {
in.close();
}
Manifest manifest = new JarFile(tmp).getManifest();
Attributes main = manifest.getMainAttributes();
String sysName = main.getValue("Gerrit-Module");
String sshName = main.getValue("Gerrit-SshModule");
String httpName = main.getValue("Gerrit-HttpModule");
JarFile jarFile = new JarFile(tmp);
boolean keep = false;
try {
Manifest manifest = jarFile.getManifest();
Attributes main = manifest.getMainAttributes();
String sysName = main.getValue("Gerrit-Module");
String sshName = main.getValue("Gerrit-SshModule");
String httpName = main.getValue("Gerrit-HttpModule");
URL[] urls = {tmp.toURI().toURL()};
ClassLoader parentLoader = PluginLoader.class.getClassLoader();
ClassLoader pluginLoader = new URLClassLoader(urls, parentLoader);
cleanupHandles.put(
new CleanupHandle(tmp, pluginLoader, cleanupQueue),
Boolean.TRUE);
URL[] urls = {tmp.toURI().toURL()};
ClassLoader parentLoader = PluginLoader.class.getClassLoader();
ClassLoader pluginLoader = new URLClassLoader(urls, parentLoader);
cleanupHandles.put(
new CleanupHandle(tmp, jarFile, pluginLoader, cleanupQueue),
Boolean.TRUE);
Class<? extends Module> sysModule = load(sysName, pluginLoader);
Class<? extends Module> sshModule = load(sshName, pluginLoader);
Class<? extends Module> httpModule = load(httpName, pluginLoader);
return new Plugin(name, jarFile, manifest, snapshot,
sysModule, sshModule, httpModule);
Class<? extends Module> sysModule = load(sysName, pluginLoader);
Class<? extends Module> sshModule = load(sshName, pluginLoader);
Class<? extends Module> httpModule = load(httpName, pluginLoader);
keep = true;
return new Plugin(name,
srcJar, snapshot,
jarFile, manifest,
sysModule, sshModule, httpModule);
} finally {
if (!keep) {
jarFile.close();
}
}
}
private static String tempNameFor(String name) {