diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt index 564d298e76..d5a35559ee 100644 --- a/Documentation/config-gerrit.txt +++ b/Documentation/config-gerrit.txt @@ -1997,6 +1997,23 @@ By default unset, as the HTTP daemon must be configured externally by the system administrator, and might not even be running on the same host as Gerrit. +[[gerrit.installModule]]gerrit.installModule:: ++ +Repeatable list of class name of additional Guice modules to load at +Gerrit startup and init phases. +Classes are resolved using the primary Gerrit class loader, hence the +class needs to be either declared in Gerrit or an additional JAR +located under the `/lib` directory. ++ +By default unset. ++ +Example: +---- +[gerrit] + installModule = com.googlesource.gerrit.libmodule.MyModule + installModule = com.example.abc.OurSpecialSauceModule +---- + [[gerrit.reportBugUrl]]gerrit.reportBugUrl:: + URL to direct users to when they need to report a bug. diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java index a2e0450fc9..f0cc5c5f56 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java @@ -24,6 +24,7 @@ import com.google.gerrit.lifecycle.LifecycleModule; import com.google.gerrit.metrics.DisabledMetricMaker; import com.google.gerrit.metrics.MetricMaker; import com.google.gerrit.metrics.dropwizard.DropWizardMetricMaker; +import com.google.gerrit.server.LibModuleLoader; import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.GerritServerConfigModule; import com.google.gerrit.server.config.SitePath; @@ -180,6 +181,7 @@ public abstract class SiteProgram extends AbstractProgram { modules.add(new SchemaModule()); modules.add(cfgInjector.getInstance(GitRepositoryManagerModule.class)); modules.add(new ConfigNotesMigration.Module()); + modules.addAll(LibModuleLoader.loadModules(cfgInjector)); try { return Guice.createInjector(PRODUCTION, modules); diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/LibModuleLoader.java b/gerrit-server/src/main/java/com/google/gerrit/server/LibModuleLoader.java new file mode 100644 index 0000000000..abea78f9d5 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/LibModuleLoader.java @@ -0,0 +1,64 @@ +// Copyright (C) 2016 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; + +import static java.util.stream.Collectors.toList; + +import com.google.gerrit.server.config.GerritServerConfig; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.Module; +import com.google.inject.ProvisionException; + +import org.eclipse.jgit.lib.Config; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; +import java.util.List; + +/** Loads configured Guice modules from {@code gerrit.installModule}. */ +public class LibModuleLoader { + private static final Logger log = + LoggerFactory.getLogger(LibModuleLoader.class); + + public static List loadModules(Injector parent) { + Config cfg = getConfig(parent); + return Arrays.stream(cfg.getStringList("gerrit", null, "installModule")) + .map(m -> createModule(parent, m)) + .collect(toList()); + } + + private static Config getConfig(Injector i) { + return i.getInstance(Key.get(Config.class, GerritServerConfig.class)); + } + + private static Module createModule(Injector injector, String className) { + Module m = injector.getInstance(loadModule(className)); + log.info("Installed module {}", className); + return m; + } + + @SuppressWarnings("unchecked") + private static Class loadModule(String className) { + try { + return (Class) Class.forName(className); + } catch (ClassNotFoundException | LinkageError e) { + String msg = "Cannot load LibModule " + className; + log.error(msg, e); + throw new ProvisionException(msg, e); + } + } +} diff --git a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java index 6c46acdb55..94168f447a 100644 --- a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java +++ b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java @@ -31,6 +31,7 @@ import com.google.gerrit.lifecycle.LifecycleModule; import com.google.gerrit.lucene.LuceneIndexModule; import com.google.gerrit.metrics.dropwizard.DropWizardMetricMaker; import com.google.gerrit.pgm.util.LogFileCompressor; +import com.google.gerrit.server.LibModuleLoader; import com.google.gerrit.server.account.InternalAccountDirectory; import com.google.gerrit.server.cache.h2.DefaultCacheFactory; import com.google.gerrit.server.change.ChangeCleanupRunner; @@ -339,6 +340,7 @@ public class WebAppInitializer extends GuiceServletContextListener }); modules.add(new GarbageCollectionModule()); modules.add(new ChangeCleanupRunner.Module()); + modules.addAll(LibModuleLoader.loadModules(cfgInjector)); return cfgInjector.createChildInjector(modules); }