diff options
Diffstat (limited to 'java/com/google/gerrit/server/plugins/ServerPlugin.java')
-rw-r--r-- | java/com/google/gerrit/server/plugins/ServerPlugin.java | 308 |
1 files changed, 308 insertions, 0 deletions
diff --git a/java/com/google/gerrit/server/plugins/ServerPlugin.java b/java/com/google/gerrit/server/plugins/ServerPlugin.java new file mode 100644 index 0000000000..f2362020d0 --- /dev/null +++ b/java/com/google/gerrit/server/plugins/ServerPlugin.java @@ -0,0 +1,308 @@ +// Copyright (C) 2012 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.plugins; + +import com.google.common.base.Strings; +import com.google.common.collect.Lists; +import com.google.common.flogger.FluentLogger; +import com.google.gerrit.common.Nullable; +import com.google.gerrit.extensions.registration.RegistrationHandle; +import com.google.gerrit.extensions.registration.ReloadableRegistrationHandle; +import com.google.gerrit.lifecycle.LifecycleManager; +import com.google.gerrit.server.PluginUser; +import com.google.gerrit.server.config.GerritRuntime; +import com.google.gerrit.server.util.RequestContext; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Module; +import java.io.IOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.jar.Attributes; +import java.util.jar.Manifest; +import org.eclipse.jgit.internal.storage.file.FileSnapshot; + +public class ServerPlugin extends Plugin { + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + + private final Manifest manifest; + private final PluginContentScanner scanner; + private final Path dataDir; + private final String pluginCanonicalWebUrl; + private final ClassLoader classLoader; + private final String metricsPrefix; + private final GerritRuntime gerritRuntime; + protected Class<? extends Module> sysModule; + protected Class<? extends Module> batchModule; + protected Class<? extends Module> sshModule; + protected Class<? extends Module> httpModule; + + private Injector sysInjector; + private Injector sshInjector; + private Injector httpInjector; + private LifecycleManager serverManager; + private List<ReloadableRegistrationHandle<?>> reloadableHandles; + + public ServerPlugin( + String name, + String pluginCanonicalWebUrl, + PluginUser pluginUser, + Path srcJar, + FileSnapshot snapshot, + PluginContentScanner scanner, + Path dataDir, + ClassLoader classLoader, + String metricsPrefix, + GerritRuntime gerritRuntime) + throws InvalidPluginException { + super( + name, + srcJar, + pluginUser, + snapshot, + scanner == null ? ApiType.PLUGIN : Plugin.getApiType(getPluginManifest(scanner))); + this.pluginCanonicalWebUrl = pluginCanonicalWebUrl; + this.scanner = scanner; + this.dataDir = dataDir; + this.classLoader = classLoader; + this.manifest = scanner == null ? null : getPluginManifest(scanner); + this.metricsPrefix = metricsPrefix; + this.gerritRuntime = gerritRuntime; + if (manifest != null) { + loadGuiceModules(manifest, classLoader); + } + } + + private void loadGuiceModules(Manifest manifest, ClassLoader classLoader) + throws InvalidPluginException { + Attributes main = manifest.getMainAttributes(); + String sysName = main.getValue("Gerrit-Module"); + String sshName = main.getValue("Gerrit-SshModule"); + String httpName = main.getValue("Gerrit-HttpModule"); + String batchName = main.getValue("Gerrit-BatchModule"); + + if (!Strings.isNullOrEmpty(sshName) && getApiType() != Plugin.ApiType.PLUGIN) { + throw new InvalidPluginException( + String.format( + "Using Gerrit-SshModule requires Gerrit-ApiType: %s", Plugin.ApiType.PLUGIN)); + } + + try { + this.batchModule = load(batchName, classLoader); + this.sysModule = load(sysName, classLoader); + this.sshModule = load(sshName, classLoader); + this.httpModule = load(httpName, classLoader); + } catch (ClassNotFoundException e) { + throw new InvalidPluginException("Unable to load plugin Guice Modules", e); + } + } + + @SuppressWarnings("unchecked") + protected static Class<? extends Module> load(@Nullable String name, ClassLoader pluginLoader) + throws ClassNotFoundException { + if (Strings.isNullOrEmpty(name)) { + return null; + } + + Class<?> clazz = Class.forName(name, false, pluginLoader); + if (!Module.class.isAssignableFrom(clazz)) { + throw new ClassCastException( + String.format("Class %s does not implement %s", name, Module.class.getName())); + } + return (Class<? extends Module>) clazz; + } + + Path getDataDir() { + return dataDir; + } + + String getPluginCanonicalWebUrl() { + return pluginCanonicalWebUrl; + } + + String getMetricsPrefix() { + return metricsPrefix; + } + + private static Manifest getPluginManifest(PluginContentScanner scanner) + throws InvalidPluginException { + try { + return scanner.getManifest(); + } catch (IOException e) { + throw new InvalidPluginException("Cannot get plugin manifest", e); + } + } + + @Override + @Nullable + public String getVersion() { + Attributes main = manifest.getMainAttributes(); + return main.getValue(Attributes.Name.IMPLEMENTATION_VERSION); + } + + @Override + protected boolean canReload() { + Attributes main = manifest.getMainAttributes(); + String v = main.getValue("Gerrit-ReloadMode"); + if (Strings.isNullOrEmpty(v) || "reload".equalsIgnoreCase(v)) { + return true; + } else if ("restart".equalsIgnoreCase(v)) { + return false; + } else { + logger.atWarning().log( + "Plugin %s has invalid Gerrit-ReloadMode %s; assuming restart", getName(), v); + return false; + } + } + + @Override + protected void start(PluginGuiceEnvironment env) throws Exception { + RequestContext oldContext = env.enter(this); + try { + startPlugin(env); + } finally { + env.exit(oldContext); + } + } + + private void startPlugin(PluginGuiceEnvironment env) throws Exception { + Injector root = newRootInjector(env); + serverManager = new LifecycleManager(); + serverManager.add(root); + + if (gerritRuntime == GerritRuntime.BATCH) { + if (batchModule != null) { + sysInjector = root.createChildInjector(root.getInstance(batchModule)); + serverManager.add(sysInjector); + } else { + sysInjector = root; + } + + serverManager.start(); + return; + } + + AutoRegisterModules auto = null; + if (sysModule == null && sshModule == null && httpModule == null) { + auto = new AutoRegisterModules(getName(), env, scanner, classLoader); + auto.discover(); + } + + if (sysModule != null) { + sysInjector = root.createChildInjector(root.getInstance(sysModule)); + serverManager.add(sysInjector); + } else if (auto != null && auto.sysModule != null) { + sysInjector = root.createChildInjector(auto.sysModule); + serverManager.add(sysInjector); + } else { + sysInjector = root; + } + + if (env.hasSshModule()) { + List<Module> modules = new ArrayList<>(); + if (getApiType() == ApiType.PLUGIN) { + modules.add(env.getSshModule()); + } + if (sshModule != null) { + modules.add(sysInjector.getInstance(sshModule)); + sshInjector = sysInjector.createChildInjector(modules); + serverManager.add(sshInjector); + } else if (auto != null && auto.sshModule != null) { + modules.add(auto.sshModule); + sshInjector = sysInjector.createChildInjector(modules); + serverManager.add(sshInjector); + } + } + + if (env.hasHttpModule()) { + List<Module> modules = new ArrayList<>(); + if (getApiType() == ApiType.PLUGIN) { + modules.add(env.getHttpModule()); + } + if (httpModule != null) { + modules.add(sysInjector.getInstance(httpModule)); + httpInjector = sysInjector.createChildInjector(modules); + serverManager.add(httpInjector); + } else if (auto != null && auto.httpModule != null) { + modules.add(auto.httpModule); + httpInjector = sysInjector.createChildInjector(modules); + serverManager.add(httpInjector); + } + } + + serverManager.start(); + } + + private Injector newRootInjector(PluginGuiceEnvironment env) { + List<Module> modules = Lists.newArrayListWithCapacity(2); + if (getApiType() == ApiType.PLUGIN) { + modules.add(env.getSysModule()); + } + modules.add(new ServerPluginInfoModule(this, env.getServerMetrics())); + return Guice.createInjector(modules); + } + + @Override + protected void stop(PluginGuiceEnvironment env) { + if (serverManager != null) { + RequestContext oldContext = env.enter(this); + try { + serverManager.stop(); + } finally { + env.exit(oldContext); + } + serverManager = null; + sysInjector = null; + sshInjector = null; + httpInjector = null; + } + } + + @Override + public Injector getSysInjector() { + return sysInjector; + } + + @Override + @Nullable + public Injector getSshInjector() { + return sshInjector; + } + + @Override + @Nullable + public Injector getHttpInjector() { + return httpInjector; + } + + @Override + public void add(RegistrationHandle handle) { + if (serverManager != null) { + if (handle instanceof ReloadableRegistrationHandle) { + if (reloadableHandles == null) { + reloadableHandles = new ArrayList<>(); + } + reloadableHandles.add((ReloadableRegistrationHandle<?>) handle); + } + serverManager.add(handle); + } + } + + @Override + public PluginContentScanner getContentScanner() { + return scanner; + } +} |