summaryrefslogtreecommitdiffstats
path: root/java/com/google/gerrit/server/plugins/ServerPlugin.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/google/gerrit/server/plugins/ServerPlugin.java')
-rw-r--r--java/com/google/gerrit/server/plugins/ServerPlugin.java308
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;
+ }
+}