Atomic replace SSH and HTTP bindings during plugin reload
When a plugin is reloaded with a new version, start the new one before stopping the old copy of the same plugin. This allows the SSH and HTTP bindings to atomically swap in the new plugin before dropping the old one, so users never see the command or URL with a not found error. Allow Gerrit-ReloadMode: restart to prevent this atomic replace, which may be necessary for plugins that acquire an exclusive lock on a resource in a LifecycleListener's start method, and release it during stop. When the ReloadMode is restart Gerrit will first stop the old copy of the plugin before starting the new one. This means there is a race condition when the command disappears and is later registered. Change-Id: Iec165991dfcc89f669e50a9fb21525282307f074
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
package com.google.gerrit.sshd;
|
||||
|
||||
import com.google.gerrit.server.plugins.Plugin;
|
||||
import com.google.gerrit.server.plugins.ReloadPluginListener;
|
||||
import com.google.gerrit.server.plugins.StartPluginListener;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.Provider;
|
||||
@@ -27,7 +28,8 @@ import org.slf4j.LoggerFactory;
|
||||
import javax.inject.Inject;
|
||||
|
||||
@Singleton
|
||||
class SshPluginStarterCallback implements StartPluginListener {
|
||||
class SshPluginStarterCallback
|
||||
implements StartPluginListener, ReloadPluginListener {
|
||||
private static final Logger log = LoggerFactory
|
||||
.getLogger(SshPluginStarterCallback.class);
|
||||
|
||||
@@ -41,17 +43,31 @@ class SshPluginStarterCallback implements StartPluginListener {
|
||||
|
||||
@Override
|
||||
public void onStartPlugin(Plugin plugin) {
|
||||
if (plugin.getSshInjector() != null) {
|
||||
Key<Command> key = Commands.key(plugin.getName());
|
||||
Provider<Command> cmd;
|
||||
try {
|
||||
cmd = plugin.getSshInjector().getProvider(key);
|
||||
} catch (RuntimeException err) {
|
||||
log.warn(String.format("Plugin %s does not define command",
|
||||
plugin.getName()), err);
|
||||
return;
|
||||
}
|
||||
Provider<Command> cmd = load(plugin);
|
||||
if (cmd != null) {
|
||||
plugin.add(root.register(Commands.named(plugin.getName()), cmd));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReloadPlugin(Plugin oldPlugin, Plugin newPlugin) {
|
||||
Provider<Command> cmd = load(newPlugin);
|
||||
if (cmd != null) {
|
||||
newPlugin.add(root.replace(Commands.named(newPlugin.getName()), cmd));
|
||||
}
|
||||
}
|
||||
|
||||
private Provider<Command> load(Plugin plugin) {
|
||||
if (plugin.getSshInjector() != null) {
|
||||
Key<Command> key = Commands.key(plugin.getName());
|
||||
try {
|
||||
return plugin.getSshInjector().getProvider(key);
|
||||
} catch (RuntimeException err) {
|
||||
log.warn(String.format(
|
||||
"Plugin %s did not define its top-level command",
|
||||
plugin.getName()), err);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user