summaryrefslogtreecommitdiffstats
path: root/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginLoader.java
diff options
context:
space:
mode:
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.java83
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)