From 62cc3503b9224070b6ca9883a6298f64042e7bdf Mon Sep 17 00:00:00 2001 From: Luca Milanesio Date: Tue, 15 Nov 2016 10:22:38 -0800 Subject: [PATCH] Support for loading Guice modules from /lib Gerrit already has "pluggable" features that need to be installed at the early stages of the bootstrap phase and cannot be "unplugged" as plugins do. Attendees at the recent hackathon in Mountain View decided to call this new entities "LibModules", and configure them in the gerrit.config as follows: [gerrit] installModule = com.googlesource.gerrit.libmodule.MyModule installModule = com.example.abc.OurSpecialSauceModule Change-Id: Id8bf1a2e88b14c8e9125f20cb10a041d47799100 --- Documentation/config-gerrit.txt | 17 +++++ .../google/gerrit/pgm/util/SiteProgram.java | 2 + .../google/gerrit/server/LibModuleLoader.java | 64 +++++++++++++++++++ .../gerrit/httpd/WebAppInitializer.java | 2 + 4 files changed, 85 insertions(+) create mode 100644 gerrit-server/src/main/java/com/google/gerrit/server/LibModuleLoader.java 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); }