diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt index a59c08dabe..1b28424999 100644 --- a/Documentation/dev-plugins.txt +++ b/Documentation/dev-plugins.txt @@ -109,6 +109,24 @@ will be performed by scanning all classes in the plugin JAR for Gerrit-HttpModule: tld.example.project.HttpModuleClassName ---- +=== Batch runtime + +Gerrit can be run as a server, serving HTTP or SSH requests, or as an +offline program. Plugins can contribute Guice modules to this batch +runtime by binding `Gerrit-BatchModule` to one of their classes. +The Guice injector is bound to less classes, and some Gerrit features +will be absent - on purpose. + +This feature was originally introduced to support plugins during an +offline reindexing task. + +---- + Gerrit-BatchModule: tld.example.project.CoreModuleClassName +---- + +In this runtime, only the module designated by `Gerrit-BatchModule` is +enabled, not `Gerrit-SysModule`. + [[plugin_name]] === Plugin Name @@ -2840,6 +2858,9 @@ public class MyPluginModule extends AbstractModule { } ---- +Plugin authors should also consider binding their SubmitRule using a `Gerrit-BatchModule`. +See link:dev-plugins.html[Batch runtime] for more informations. + == SEE ALSO * link:js-api.html[JavaScript API] diff --git a/java/com/google/gerrit/pgm/MigrateToNoteDb.java b/java/com/google/gerrit/pgm/MigrateToNoteDb.java index 0b44ccfd73..07da3f7f7d 100644 --- a/java/com/google/gerrit/pgm/MigrateToNoteDb.java +++ b/java/com/google/gerrit/pgm/MigrateToNoteDb.java @@ -34,6 +34,7 @@ import com.google.gerrit.server.index.DummyIndexModule; import com.google.gerrit.server.index.change.ChangeSchemaDefinitions; import com.google.gerrit.server.notedb.rebuild.GcAllUsers; import com.google.gerrit.server.notedb.rebuild.NoteDbMigrator; +import com.google.gerrit.server.plugins.PluginGuiceEnvironment; import com.google.gerrit.server.schema.DataSourceType; import com.google.inject.Inject; import com.google.inject.Injector; @@ -118,6 +119,9 @@ public class MigrateToNoteDb extends SiteProgram { sysInjector.injectMembers(this); sysManager = new LifecycleManager(); sysManager.add(sysInjector); + sysInjector + .getInstance(PluginGuiceEnvironment.class) + .setDbCfgInjector(dbInjector, dbInjector); sysManager.start(); try (NoteDbMigrator migrator = diff --git a/java/com/google/gerrit/pgm/Reindex.java b/java/com/google/gerrit/pgm/Reindex.java index 2a34efab52..d26e4f7185 100644 --- a/java/com/google/gerrit/pgm/Reindex.java +++ b/java/com/google/gerrit/pgm/Reindex.java @@ -35,6 +35,7 @@ import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.index.IndexModule; import com.google.gerrit.server.index.IndexModule.IndexType; import com.google.gerrit.server.index.change.ChangeSchemaDefinitions; +import com.google.gerrit.server.plugins.PluginGuiceEnvironment; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Key; @@ -72,6 +73,7 @@ public class Reindex extends SiteProgram { private Injector dbInjector; private Injector sysInjector; + private Injector cfgInjector; private Config globalConfig; @Inject private Collection> indexDefs; @@ -80,6 +82,7 @@ public class Reindex extends SiteProgram { public int run() throws Exception { mustHaveValidSite(); dbInjector = createDbInjector(MULTI_USER); + cfgInjector = dbInjector.createChildInjector(); globalConfig = dbInjector.getInstance(Key.get(Config.class, GerritServerConfig.class)); threads = ThreadLimiter.limitThreads(dbInjector, threads); overrideConfig(); @@ -88,9 +91,11 @@ public class Reindex extends SiteProgram { dbManager.start(); sysInjector = createSysInjector(); + sysInjector.getInstance(PluginGuiceEnvironment.class).setDbCfgInjector(dbInjector, cfgInjector); LifecycleManager sysManager = new LifecycleManager(); sysManager.add(sysInjector); sysManager.start(); + sysInjector.injectMembers(this); checkIndicesOption(); diff --git a/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/java/com/google/gerrit/pgm/util/BatchProgramModule.java index ecaadbaf5c..8d77ed8b84 100644 --- a/java/com/google/gerrit/pgm/util/BatchProgramModule.java +++ b/java/com/google/gerrit/pgm/util/BatchProgramModule.java @@ -65,6 +65,7 @@ import com.google.gerrit.server.patch.DiffExecutorModule; import com.google.gerrit.server.patch.PatchListCacheImpl; import com.google.gerrit.server.permissions.DefaultPermissionBackendModule; import com.google.gerrit.server.permissions.SectionSortCache; +import com.google.gerrit.server.plugins.PluginModule; import com.google.gerrit.server.project.CommentLinkProvider; import com.google.gerrit.server.project.CommitResource; import com.google.gerrit.server.project.ProjectCacheImpl; @@ -111,7 +112,16 @@ public class BatchProgramModule extends FactoryModule { install(BatchUpdate.module()); install(PatchListCacheImpl.module()); - // Plugins are not loaded and we're just running through each change + // There is the concept of LifecycleModule, in Gerrit's own extension to Guice, which has these: + // listener().to(SomeClassImplementingLifecycleListener.class); + // and the start() methods of each such listener are executed in the order they are declared. + // Makes sure that PluginLoader.start() is executed before the LuceneIndexModule.start() so that + // plugins get loaded and the respective Guice modules installed so that the on-line reindexing + // will happen with the proper classes (e.g. group backends, custom Prolog predicates) and the + // associated rules ready to be evaluated. + install(new PluginModule()); + + // We're just running through each change // once, so don't worry about cache removal. bind(new TypeLiteral>() {}) .toInstance(DynamicSet.emptySet()); diff --git a/java/com/google/gerrit/server/plugins/ServerPlugin.java b/java/com/google/gerrit/server/plugins/ServerPlugin.java index 6ae00e18b8..f2362020d0 100644 --- a/java/com/google/gerrit/server/plugins/ServerPlugin.java +++ b/java/com/google/gerrit/server/plugins/ServerPlugin.java @@ -46,6 +46,7 @@ public class ServerPlugin extends Plugin { private final String metricsPrefix; private final GerritRuntime gerritRuntime; protected Class sysModule; + protected Class batchModule; protected Class sshModule; protected Class httpModule; @@ -91,6 +92,7 @@ public class ServerPlugin extends Plugin { 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( @@ -99,6 +101,7 @@ public class ServerPlugin extends Plugin { } try { + this.batchModule = load(batchName, classLoader); this.sysModule = load(sysName, classLoader); this.sshModule = load(sshName, classLoader); this.httpModule = load(httpName, classLoader); @@ -108,7 +111,7 @@ public class ServerPlugin extends Plugin { } @SuppressWarnings("unchecked") - protected static Class load(String name, ClassLoader pluginLoader) + protected static Class load(@Nullable String name, ClassLoader pluginLoader) throws ClassNotFoundException { if (Strings.isNullOrEmpty(name)) { return null; @@ -180,6 +183,18 @@ public class ServerPlugin extends Plugin { 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);