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
This commit is contained in:
Luca Milanesio 2016-11-15 10:22:38 -08:00 committed by Shawn Pearce
parent 8e5418db0a
commit 62cc3503b9
4 changed files with 85 additions and 0 deletions

View File

@ -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.

View File

@ -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);

View File

@ -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<Module> 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<Module> loadModule(String className) {
try {
return (Class<Module>) Class.forName(className);
} catch (ClassNotFoundException | LinkageError e) {
String msg = "Cannot load LibModule " + className;
log.error(msg, e);
throw new ProvisionException(msg, e);
}
}
}

View File

@ -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);
}