diff options
Diffstat (limited to 'gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java')
-rw-r--r-- | gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java | 83 |
1 files changed, 56 insertions, 27 deletions
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java index d1c2aa40b3..035592cd48 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java @@ -18,10 +18,13 @@ import com.google.common.base.Joiner; import com.google.common.base.Strings; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import com.google.common.collect.Queues; import com.google.common.collect.Sets; import com.google.gerrit.extensions.annotations.PluginName; import com.google.gerrit.extensions.events.LifecycleListener; import com.google.gerrit.extensions.systemstatus.ServerInformation; +import com.google.gerrit.extensions.webui.JavaScriptPlugin; +import com.google.gerrit.server.PluginUser; import com.google.gerrit.server.config.ConfigUtil; import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.SitePaths; @@ -30,8 +33,8 @@ import com.google.inject.Module; import com.google.inject.Provider; import com.google.inject.Singleton; +import org.eclipse.jgit.internal.storage.file.FileSnapshot; import org.eclipse.jgit.lib.Config; -import org.eclipse.jgit.storage.file.FileSnapshot; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,7 +44,6 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.lang.ref.ReferenceQueue; import java.net.URL; import java.net.URLClassLoader; import java.text.SimpleDateFormat; @@ -49,8 +51,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; @@ -60,6 +64,7 @@ import java.util.jar.Manifest; @Singleton public class PluginLoader implements LifecycleListener { + static final String PLUGIN_TMP_PREFIX = "plugin_"; static final Logger log = LoggerFactory.getLogger(PluginLoader.class); private final File pluginsDir; @@ -67,11 +72,12 @@ public class PluginLoader implements LifecycleListener { private final File tmpDir; private final PluginGuiceEnvironment env; private final ServerInformationImpl srvInfoImpl; + private final PluginUser.Factory pluginUserFactory; private final ConcurrentMap<String, Plugin> running; private final ConcurrentMap<String, Plugin> disabled; private final Map<String, FileSnapshot> broken; - private final ReferenceQueue<ClassLoader> cleanupQueue; - private final ConcurrentMap<CleanupHandle, Boolean> cleanupHandles; + private final Map<Plugin, CleanupHandle> cleanupHandles; + private final Queue<Plugin> toCleanup; private final Provider<PluginCleanerTask> cleaner; private final PluginScannerThread scanner; @@ -79,6 +85,7 @@ public class PluginLoader implements LifecycleListener { public PluginLoader(SitePaths sitePaths, PluginGuiceEnvironment pe, ServerInformationImpl sii, + PluginUser.Factory puf, Provider<PluginCleanerTask> pct, @GerritServerConfig Config cfg) { pluginsDir = sitePaths.plugins_dir; @@ -86,10 +93,11 @@ public class PluginLoader implements LifecycleListener { tmpDir = sitePaths.tmp_dir; env = pe; srvInfoImpl = sii; + pluginUserFactory = puf; running = Maps.newConcurrentMap(); disabled = Maps.newConcurrentMap(); broken = Maps.newHashMap(); - cleanupQueue = new ReferenceQueue<ClassLoader>(); + toCleanup = Queues.newArrayDeque(); cleanupHandles = Maps.newConcurrentMap(); cleaner = pct; @@ -103,6 +111,14 @@ public class PluginLoader implements LifecycleListener { } } + public Plugin get(String name) { + Plugin p = running.get(name); + if (p != null) { + return p; + } + return disabled.get(name); + } + public Iterable<Plugin> getPlugins(boolean all) { if (!all) { return running.values(); @@ -178,6 +194,15 @@ public class PluginLoader implements LifecycleListener { } } + synchronized private void unloadPlugin(Plugin plugin) { + String name = plugin.getName(); + log.info(String.format("Unloading plugin %s", name)); + plugin.stop(env); + running.remove(name); + disabled.remove(name); + toCleanup.add(plugin); + } + public void disablePlugins(Set<String> names) { synchronized (this) { for (String name : names) { @@ -190,8 +215,7 @@ public class PluginLoader implements LifecycleListener { File off = new File(pluginsDir, active.getName() + ".jar.disabled"); active.getSrcJar().renameTo(off); - active.stop(); - running.remove(name); + unloadPlugin(active); try { FileSnapshot snapshot = FileSnapshot.save(off); Plugin offPlugin = loadPlugin(name, off, snapshot); @@ -244,12 +268,12 @@ public class PluginLoader implements LifecycleListener { srvInfoImpl.state = ServerInformation.State.SHUTDOWN; synchronized (this) { for (Plugin p : running.values()) { - p.stop(); + unloadPlugin(p); } running.clear(); disabled.clear(); broken.clear(); - if (cleanupHandles.size() > running.size()) { + if (!toCleanup.isEmpty()) { System.gc(); processPendingCleanups(); } @@ -296,6 +320,10 @@ public class PluginLoader implements LifecycleListener { dropRemovedDisabledPlugins(jars); for (File jar : jars) { + if (jar.getName().endsWith(".disabled")) { + continue; + } + String name = nameOf(jar); FileSnapshot brokenTime = broken.get(name); if (brokenTime != null && !brokenTime.isModified(jar)) { @@ -333,15 +361,14 @@ public class PluginLoader implements LifecycleListener { && oldPlugin.canReload() && newPlugin.canReload(); if (!reload && oldPlugin != null) { - oldPlugin.stop(); - running.remove(name); + unloadPlugin(oldPlugin); } if (!newPlugin.isDisabled()) { newPlugin.start(env); } if (reload) { env.onReloadPlugin(oldPlugin, newPlugin); - oldPlugin.stop(); + unloadPlugin(oldPlugin); } else if (!newPlugin.isDisabled()) { env.onStartPlugin(newPlugin); } @@ -366,8 +393,7 @@ public class PluginLoader implements LifecycleListener { } } for (String name : unload){ - log.info(String.format("Unloading plugin %s", name)); - running.remove(name).stop(); + unloadPlugin(running.get(name)); } } @@ -384,16 +410,19 @@ public class PluginLoader implements LifecycleListener { } synchronized int processPendingCleanups() { - CleanupHandle h; - while ((h = (CleanupHandle) cleanupQueue.poll()) != null) { - h.cleanup(); - cleanupHandles.remove(h); + Iterator<Plugin> iterator = toCleanup.iterator(); + while (iterator.hasNext()) { + Plugin plugin = iterator.next(); + iterator.remove(); + + CleanupHandle cleanupHandle = cleanupHandles.remove(plugin); + cleanupHandle.cleanup(); } - return Math.max(0, cleanupHandles.size() - running.size()); + return toCleanup.size(); } private void cleanInBackground() { - int cnt = Math.max(0, cleanupHandles.size() - running.size()); + int cnt = toCleanup.size(); if (0 < cnt) { cleaner.get().clean(cnt); } @@ -437,19 +466,17 @@ public class PluginLoader implements LifecycleListener { URL[] urls = {tmp.toURI().toURL()}; ClassLoader parentLoader = parentFor(type); 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); - keep = true; - return new Plugin(name, + Plugin plugin = new Plugin(name, pluginUserFactory.create(name), srcJar, snapshot, jarFile, manifest, new File(dataDir, name), type, pluginLoader, sysModule, sshModule, httpModule); + cleanupHandles.put(plugin, new CleanupHandle(tmp, jarFile)); + keep = true; + return plugin; } finally { if (!keep) { jarFile.close(); @@ -464,6 +491,8 @@ public class PluginLoader implements LifecycleListener { return PluginName.class.getClassLoader(); case PLUGIN: return PluginLoader.class.getClassLoader(); + case JS: + return JavaScriptPlugin.class.getClassLoader(); default: throw new InvalidPluginException("Unsupported ApiType " + type); } @@ -471,7 +500,7 @@ public class PluginLoader implements LifecycleListener { private static String tempNameFor(String name) { SimpleDateFormat fmt = new SimpleDateFormat("yyMMdd_HHmm"); - return "plugin_" + name + "_" + fmt.format(new Date()) + "_"; + return PLUGIN_TMP_PREFIX + name + "_" + fmt.format(new Date()) + "_"; } private Class<? extends Module> load(String name, ClassLoader pluginLoader) |