| // 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); | |
| } | |
| } | |
| @Nullable | |
| @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 | |
| @Nullable | |
| public String getApiVersion() { | |
| Attributes main = manifest.getMainAttributes(); | |
| return main.getValue("Gerrit-ApiVersion"); | |
| } | |
| @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; | |
| } | |
| } |