| // Copyright (C) 2009 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.httpd.init; | |
| import static com.google.inject.Stage.PRODUCTION; | |
| import com.google.common.base.Splitter; | |
| import com.google.common.flogger.FluentLogger; | |
| import com.google.gerrit.auth.AuthModule; | |
| import com.google.gerrit.extensions.client.AuthType; | |
| import com.google.gerrit.gpg.GpgModule; | |
| import com.google.gerrit.httpd.AllRequestFilter; | |
| import com.google.gerrit.httpd.GerritAuthModule; | |
| import com.google.gerrit.httpd.GetUserFilter; | |
| import com.google.gerrit.httpd.GitOverHttpModule; | |
| import com.google.gerrit.httpd.H2CacheBasedWebSession; | |
| import com.google.gerrit.httpd.HttpCanonicalWebUrlProvider; | |
| import com.google.gerrit.httpd.HttpdModule; | |
| import com.google.gerrit.httpd.RequestCleanupFilter; | |
| import com.google.gerrit.httpd.RequestContextFilter; | |
| import com.google.gerrit.httpd.RequestMetricsFilter; | |
| import com.google.gerrit.httpd.RequireSslFilter.RequireSslFilterModule; | |
| import com.google.gerrit.httpd.SetThreadNameFilter; | |
| import com.google.gerrit.httpd.WebModule; | |
| import com.google.gerrit.httpd.WebSshGlueModule; | |
| import com.google.gerrit.httpd.auth.oauth.OAuthModule; | |
| import com.google.gerrit.httpd.auth.openid.OpenIdModule; | |
| import com.google.gerrit.httpd.auth.restapi.OAuthRestModule; | |
| import com.google.gerrit.httpd.plugins.HttpPluginModule; | |
| import com.google.gerrit.httpd.raw.StaticModule; | |
| import com.google.gerrit.index.IndexType; | |
| import com.google.gerrit.lifecycle.LifecycleManager; | |
| 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.LogFileCompressorModule; | |
| import com.google.gerrit.server.DefaultRefLogIdentityProvider; | |
| import com.google.gerrit.server.LibModuleLoader; | |
| import com.google.gerrit.server.LibModuleType; | |
| import com.google.gerrit.server.ModuleOverloader; | |
| import com.google.gerrit.server.StartupChecks.StartupChecksModule; | |
| import com.google.gerrit.server.account.AccountDeactivator.AccountDeactivatorModule; | |
| import com.google.gerrit.server.account.InternalAccountDirectory.InternalAccountDirectoryModule; | |
| import com.google.gerrit.server.account.externalids.storage.notedb.ExternalIdCaseSensitivityMigrator; | |
| import com.google.gerrit.server.api.GerritApiModule; | |
| import com.google.gerrit.server.api.PluginApiModule; | |
| import com.google.gerrit.server.api.projects.ProjectQueryBuilderModule; | |
| import com.google.gerrit.server.audit.AuditModule; | |
| import com.google.gerrit.server.cache.h2.H2CacheModule; | |
| import com.google.gerrit.server.cache.mem.DefaultMemoryCacheModule; | |
| import com.google.gerrit.server.change.AttentionSetOwnerAdder.AttentionSetOwnerAdderModule; | |
| import com.google.gerrit.server.change.ChangeCleanupRunner.ChangeCleanupRunnerModule; | |
| import com.google.gerrit.server.config.AuthConfig; | |
| import com.google.gerrit.server.config.AuthConfigModule; | |
| import com.google.gerrit.server.config.CanonicalWebUrlModule; | |
| import com.google.gerrit.server.config.DefaultUrlFormatter.DefaultUrlFormatterModule; | |
| import com.google.gerrit.server.config.DownloadConfig; | |
| import com.google.gerrit.server.config.GerritGlobalModule; | |
| import com.google.gerrit.server.config.GerritInstanceNameModule; | |
| import com.google.gerrit.server.config.GerritOptions; | |
| import com.google.gerrit.server.config.GerritRuntime; | |
| import com.google.gerrit.server.config.GerritServerConfig; | |
| import com.google.gerrit.server.config.GerritServerConfigModule; | |
| import com.google.gerrit.server.config.SitePath; | |
| import com.google.gerrit.server.config.SysExecutorModule; | |
| import com.google.gerrit.server.events.EventBroker.EventBrokerModule; | |
| import com.google.gerrit.server.events.StreamEventsApiListener.StreamEventsApiListenerModule; | |
| import com.google.gerrit.server.git.GarbageCollectionModule; | |
| import com.google.gerrit.server.git.GitRepositoryManagerModule; | |
| import com.google.gerrit.server.git.SearchingChangeCacheImpl.SearchingChangeCacheImplModule; | |
| import com.google.gerrit.server.git.SystemReaderInstaller; | |
| import com.google.gerrit.server.git.WorkQueue.WorkQueueModule; | |
| import com.google.gerrit.server.index.IndexModule; | |
| import com.google.gerrit.server.index.OnlineUpgrader.OnlineUpgraderModule; | |
| import com.google.gerrit.server.index.VersionManager; | |
| import com.google.gerrit.server.index.options.AutoFlush; | |
| import com.google.gerrit.server.mail.EmailModule; | |
| import com.google.gerrit.server.mail.SignedTokenEmailTokenVerifier.SignedTokenEmailTokenVerifierModule; | |
| import com.google.gerrit.server.mail.receive.MailReceiver.MailReceiverModule; | |
| import com.google.gerrit.server.mail.send.SmtpEmailSender.SmtpEmailSenderModule; | |
| import com.google.gerrit.server.mime.MimeUtil2Module; | |
| import com.google.gerrit.server.patch.DiffExecutorModule; | |
| import com.google.gerrit.server.permissions.DefaultPermissionBackendModule; | |
| import com.google.gerrit.server.plugins.PluginGuiceEnvironment; | |
| import com.google.gerrit.server.plugins.PluginModule; | |
| import com.google.gerrit.server.project.DefaultProjectNameLockManager.DefaultProjectNameLockManagerModule; | |
| import com.google.gerrit.server.restapi.RestApiModule; | |
| import com.google.gerrit.server.schema.JdbcAccountPatchReviewStore.JdbcAccountPatchReviewStoreModule; | |
| import com.google.gerrit.server.schema.NoteDbSchemaVersionCheck; | |
| import com.google.gerrit.server.schema.SchemaModule; | |
| import com.google.gerrit.server.securestore.SecureStoreClassName; | |
| import com.google.gerrit.server.ssh.NoSshModule; | |
| import com.google.gerrit.server.ssh.SshAddressesModule; | |
| import com.google.gerrit.server.submit.LocalMergeSuperSetComputation.LocalMergeSuperSetComputationModule; | |
| import com.google.gerrit.server.submit.SubscriptionGraph.SubscriptionGraphModule; | |
| import com.google.gerrit.server.update.SuperprojectUpdateSubmissionListener.SuperprojectUpdateSubmissionListenerModule; | |
| import com.google.gerrit.sshd.SshHostKeyModule; | |
| import com.google.gerrit.sshd.SshKeyCacheImpl; | |
| import com.google.gerrit.sshd.SshModule; | |
| import com.google.gerrit.sshd.SshSessionFactoryInitializer; | |
| import com.google.gerrit.sshd.commands.DefaultCommandModule; | |
| import com.google.gerrit.sshd.commands.ExternalIdCommandsModule; | |
| import com.google.gerrit.sshd.commands.IndexCommandsModule; | |
| import com.google.gerrit.sshd.commands.SequenceCommandsModule; | |
| import com.google.gerrit.sshd.plugin.LfsPluginAuthCommand.LfsPluginAuthCommandModule; | |
| import com.google.inject.AbstractModule; | |
| import com.google.inject.CreationException; | |
| import com.google.inject.Guice; | |
| import com.google.inject.Injector; | |
| import com.google.inject.Key; | |
| import com.google.inject.Module; | |
| import com.google.inject.Provider; | |
| import com.google.inject.ProvisionException; | |
| import com.google.inject.servlet.GuiceFilter; | |
| import com.google.inject.servlet.GuiceServletContextListener; | |
| import com.google.inject.spi.Message; | |
| import com.google.inject.util.Providers; | |
| import java.io.IOException; | |
| import java.lang.reflect.InvocationTargetException; | |
| import java.lang.reflect.Method; | |
| import java.nio.file.Path; | |
| import java.nio.file.Paths; | |
| import java.util.ArrayList; | |
| import java.util.Collections; | |
| import java.util.List; | |
| import javax.servlet.Filter; | |
| import javax.servlet.FilterChain; | |
| import javax.servlet.FilterConfig; | |
| import javax.servlet.ServletContext; | |
| import javax.servlet.ServletContextEvent; | |
| import javax.servlet.ServletException; | |
| import javax.servlet.ServletRequest; | |
| import javax.servlet.ServletResponse; | |
| import javax.servlet.http.HttpServletRequest; | |
| import org.eclipse.jgit.lib.Config; | |
| /** Configures the web application environment for Gerrit Code Review. */ | |
| public class WebAppInitializer extends GuiceServletContextListener implements Filter { | |
| private static final FluentLogger logger = FluentLogger.forEnclosingClass(); | |
| private static final String GERRIT_SITE_PATH = "gerrit.site_path"; | |
| private Path sitePath; | |
| private Injector dbInjector; | |
| private Injector cfgInjector; | |
| private Config config; | |
| private Injector sysInjector; | |
| private Injector webInjector; | |
| private Injector sshInjector; | |
| private LifecycleManager manager; | |
| private GuiceFilter filter; | |
| private ServletContext servletContext; | |
| private IndexType indexType; | |
| @Override | |
| public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) | |
| throws IOException, ServletException { | |
| filter.doFilter(req, res, chain); | |
| } | |
| private synchronized void init() { | |
| if (manager == null) { | |
| String path = System.getProperty(GERRIT_SITE_PATH); | |
| if (path != null) { | |
| sitePath = Paths.get(path); | |
| } else { | |
| throw new ProvisionException(GERRIT_SITE_PATH + " must be defined"); | |
| } | |
| if (System.getProperty("gerrit.init") != null) { | |
| List<String> pluginsToInstall; | |
| String installPlugins = System.getProperty("gerrit.install_plugins"); | |
| if (installPlugins == null) { | |
| pluginsToInstall = null; | |
| } else { | |
| pluginsToInstall = | |
| Splitter.on(",").trimResults().omitEmptyStrings().splitToList(installPlugins); | |
| } | |
| new SiteInitializer( | |
| path, | |
| System.getProperty(GERRIT_SITE_PATH), | |
| new UnzippedDistribution(servletContext), | |
| pluginsToInstall) | |
| .init(); | |
| } | |
| try { | |
| cfgInjector = createCfgInjector(); | |
| } catch (CreationException ce) { | |
| final Message first = ce.getErrorMessages().iterator().next(); | |
| final StringBuilder buf = new StringBuilder(); | |
| buf.append(first.getMessage()); | |
| Throwable why = first.getCause(); | |
| while (why != null) { | |
| buf.append("\n caused by "); | |
| buf.append(why.toString()); | |
| why = why.getCause(); | |
| } | |
| if (first.getCause() != null) { | |
| buf.append("\n"); | |
| buf.append("\nResolve above errors before continuing."); | |
| buf.append("\nComplete stack trace follows:"); | |
| } | |
| logger.atSevere().withCause(first.getCause()).log("%s", buf); | |
| throw new CreationException(Collections.singleton(first)); | |
| } | |
| dbInjector = createDbInjector(); | |
| initIndexType(); | |
| config = cfgInjector.getInstance(Key.get(Config.class, GerritServerConfig.class)); | |
| sysInjector = createSysInjector(); | |
| if (!sshdOff()) { | |
| sshInjector = createSshInjector(); | |
| } | |
| webInjector = createWebInjector(); | |
| PluginGuiceEnvironment env = sysInjector.getInstance(PluginGuiceEnvironment.class); | |
| env.setDbCfgInjector(dbInjector, cfgInjector); | |
| if (sshInjector != null) { | |
| env.setSshInjector(sshInjector); | |
| } | |
| env.setHttpInjector(webInjector); | |
| // Push the Provider<HttpServletRequest> down into the canonical | |
| // URL provider. Its optional for that provider, but since we can | |
| // supply one we should do so, in case the administrator has not | |
| // setup the canonical URL in the configuration file. | |
| // | |
| // Note we have to do this manually as Guice failed to do the | |
| // injection here because the HTTP environment is not visible | |
| // to the core server modules. | |
| // | |
| sysInjector | |
| .getInstance(HttpCanonicalWebUrlProvider.class) | |
| .setHttpServletRequest(webInjector.getProvider(HttpServletRequest.class)); | |
| filter = webInjector.getInstance(GuiceFilter.class); | |
| manager = new LifecycleManager(); | |
| manager.add(dbInjector); | |
| manager.add(cfgInjector); | |
| manager.add(sysInjector); | |
| if (sshInjector != null) { | |
| manager.add(sshInjector); | |
| } | |
| manager.add(webInjector); | |
| } | |
| } | |
| private boolean sshdOff() { | |
| return new SshAddressesModule().provideListenAddresses(config).isEmpty(); | |
| } | |
| private Injector createCfgInjector() { | |
| final List<Module> modules = new ArrayList<>(); | |
| AbstractModule secureStore = createSecureStoreModule(); | |
| modules.add(secureStore); | |
| Module sitePathModule = | |
| new AbstractModule() { | |
| @Override | |
| protected void configure() { | |
| bind(Path.class).annotatedWith(SitePath.class).toInstance(sitePath); | |
| } | |
| }; | |
| modules.add(sitePathModule); | |
| Module configModule = new GerritServerConfigModule(); | |
| modules.add(configModule); | |
| modules.add( | |
| new LifecycleModule() { | |
| @Override | |
| protected void configure() { | |
| listener().to(SystemReaderInstaller.class); | |
| } | |
| }); | |
| modules.add(new DropWizardMetricMaker.ApiModule()); | |
| return Guice.createInjector(PRODUCTION, modules); | |
| } | |
| private Injector createDbInjector() { | |
| final List<Module> modules = new ArrayList<>(); | |
| modules.add(new SchemaModule()); | |
| modules.add(NoteDbSchemaVersionCheck.module()); | |
| modules.add(new AuthConfigModule()); | |
| return cfgInjector.createChildInjector( | |
| ModuleOverloader.override( | |
| modules, LibModuleLoader.loadModules(cfgInjector, LibModuleType.DB_MODULE_TYPE))); | |
| } | |
| private Injector createSysInjector() { | |
| final List<Module> modules = new ArrayList<>(); | |
| modules.add(new DropWizardMetricMaker.RestModule()); | |
| modules.add(new LogFileCompressorModule()); | |
| modules.add(new EventBrokerModule()); | |
| modules.add(new JdbcAccountPatchReviewStoreModule(config)); | |
| modules.add(cfgInjector.getInstance(GitRepositoryManagerModule.class)); | |
| modules.add(new StreamEventsApiListenerModule(config)); | |
| modules.add(new SysExecutorModule()); | |
| modules.add(new DiffExecutorModule()); | |
| modules.add(new MimeUtil2Module()); | |
| modules.add(cfgInjector.getInstance(GerritGlobalModule.class)); | |
| modules.add(new GerritApiModule()); | |
| modules.add(new ProjectQueryBuilderModule()); | |
| modules.add(new DefaultRefLogIdentityProvider.Module()); | |
| modules.add(new PluginApiModule()); | |
| modules.add(new SearchingChangeCacheImplModule()); | |
| modules.add(new InternalAccountDirectoryModule()); | |
| modules.add(new DefaultPermissionBackendModule()); | |
| modules.add(new DefaultMemoryCacheModule()); | |
| modules.add(new H2CacheModule()); | |
| modules.add(cfgInjector.getInstance(MailReceiverModule.class)); | |
| modules.add(new EmailModule()); | |
| modules.add(new SmtpEmailSenderModule()); | |
| modules.add(new SignedTokenEmailTokenVerifierModule()); | |
| modules.add(new LocalMergeSuperSetComputationModule()); | |
| modules.add(new AuditModule()); | |
| modules.add(new GpgModule(config)); | |
| modules.add(new StartupChecksModule()); | |
| // Index module shutdown must happen before work queue shutdown, otherwise | |
| // work queue can get stuck waiting on index futures that will never return. | |
| modules.add(createIndexModule()); | |
| modules.add(new PluginModule()); | |
| if (VersionManager.getOnlineUpgrade(config)) { | |
| modules.add(new OnlineUpgraderModule()); | |
| } | |
| modules.add(new OAuthRestModule()); | |
| modules.add(new RestApiModule()); | |
| modules.add(new SubscriptionGraphModule()); | |
| modules.add(new SuperprojectUpdateSubmissionListenerModule()); | |
| modules.add(new WorkQueueModule()); | |
| modules.add(new GerritInstanceNameModule()); | |
| modules.add( | |
| new CanonicalWebUrlModule() { | |
| @Override | |
| protected Class<? extends Provider<String>> provider() { | |
| return HttpCanonicalWebUrlProvider.class; | |
| } | |
| }); | |
| modules.add(new DefaultUrlFormatterModule()); | |
| SshSessionFactoryInitializer.init(); | |
| modules.add(SshKeyCacheImpl.module()); | |
| modules.add( | |
| new AbstractModule() { | |
| @Override | |
| protected void configure() { | |
| bind(GerritOptions.class).toInstance(new GerritOptions(false, false)); | |
| bind(GerritRuntime.class).toInstance(GerritRuntime.DAEMON); | |
| } | |
| }); | |
| modules.add(new GarbageCollectionModule()); | |
| modules.add(new AttentionSetOwnerAdderModule()); | |
| modules.add(new ChangeCleanupRunnerModule()); | |
| modules.add(new AccountDeactivatorModule()); | |
| modules.add(new DefaultProjectNameLockManagerModule()); | |
| modules.add(new ExternalIdCaseSensitivityMigrator.ExternalIdCaseSensitivityMigratorModule()); | |
| return dbInjector.createChildInjector( | |
| ModuleOverloader.override( | |
| modules, LibModuleLoader.loadModules(cfgInjector, LibModuleType.SYS_MODULE_TYPE))); | |
| } | |
| private Module createIndexModule() { | |
| if (indexType.isLucene()) { | |
| return LuceneIndexModule.latestVersion(false, AutoFlush.ENABLED); | |
| } else if (indexType.isFake()) { | |
| // Use Reflection so that we can omit the fake index binary in production code. Test code does | |
| // compile the component in. | |
| try { | |
| Class<?> clazz = Class.forName("com.google.gerrit.index.testing.FakeIndexModule"); | |
| Method m = clazz.getMethod("latestVersion", boolean.class); | |
| return (Module) m.invoke(null, false); | |
| } catch (NoSuchMethodException | |
| | ClassNotFoundException | |
| | IllegalAccessException | |
| | InvocationTargetException e) { | |
| throw new IllegalStateException("can't create index", e); | |
| } | |
| } else { | |
| throw new IllegalStateException("unsupported index.type = " + indexType); | |
| } | |
| } | |
| private void initIndexType() { | |
| indexType = IndexModule.getIndexType(cfgInjector); | |
| } | |
| private Injector createSshInjector() { | |
| final List<Module> modules = new ArrayList<>(); | |
| modules.add(sysInjector.getInstance(SshModule.class)); | |
| modules.add(new SshHostKeyModule()); | |
| modules.add( | |
| new DefaultCommandModule( | |
| false, | |
| sysInjector.getInstance(DownloadConfig.class), | |
| sysInjector.getInstance(LfsPluginAuthCommandModule.class))); | |
| modules.add(new IndexCommandsModule(sysInjector)); | |
| modules.add(new SequenceCommandsModule()); | |
| modules.add(new ExternalIdCommandsModule()); | |
| return sysInjector.createChildInjector(modules); | |
| } | |
| private Injector createWebInjector() { | |
| final List<Module> modules = new ArrayList<>(); | |
| modules.add(RequestContextFilter.module()); | |
| modules.add(RequestMetricsFilter.module()); | |
| modules.add(sysInjector.getInstance(GerritAuthModule.class)); | |
| modules.add(sysInjector.getInstance(GitOverHttpModule.class)); | |
| modules.add(sysInjector.getInstance(HttpdModule.class)); | |
| modules.add(RequestCleanupFilter.module()); | |
| modules.add(SetThreadNameFilter.module()); | |
| modules.add(AllRequestFilter.module()); | |
| modules.add(sysInjector.getInstance(WebModule.class)); | |
| modules.add(sysInjector.getInstance(RequireSslFilterModule.class)); | |
| if (sshInjector != null) { | |
| modules.add(sshInjector.getInstance(WebSshGlueModule.class)); | |
| } else { | |
| modules.add(new NoSshModule()); | |
| } | |
| modules.add(H2CacheBasedWebSession.module()); | |
| modules.add(new HttpPluginModule()); | |
| AuthConfig authConfig = cfgInjector.getInstance(AuthConfig.class); | |
| if (authConfig.getAuthType() == AuthType.OPENID) { | |
| modules.add(new OpenIdModule()); | |
| } else if (authConfig.getAuthType() == AuthType.OAUTH) { | |
| modules.add(new OAuthModule()); | |
| } | |
| modules.add(new AuthModule(authConfig)); | |
| modules.add(sysInjector.getInstance(GetUserFilter.GetUserFilterModule.class)); | |
| // StaticModule contains a "/*" wildcard, place it last. | |
| GerritOptions opts = sysInjector.getInstance(GerritOptions.class); | |
| if (opts.enableMasterFeatures()) { | |
| modules.add(sysInjector.getInstance(StaticModule.class)); | |
| } | |
| return sysInjector.createChildInjector(modules); | |
| } | |
| @Override | |
| protected Injector getInjector() { | |
| init(); | |
| return webInjector; | |
| } | |
| @Override | |
| public void init(FilterConfig cfg) throws ServletException { | |
| servletContext = cfg.getServletContext(); | |
| contextInitialized(new ServletContextEvent(servletContext)); | |
| init(); | |
| manager.start(); | |
| } | |
| @Override | |
| public void destroy() { | |
| if (manager != null) { | |
| manager.stop(); | |
| manager = null; | |
| } | |
| } | |
| private AbstractModule createSecureStoreModule() { | |
| return new AbstractModule() { | |
| @Override | |
| public void configure() { | |
| String secureStoreClassName = GerritServerConfigModule.getSecureStoreClassName(sitePath); | |
| bind(String.class) | |
| .annotatedWith(SecureStoreClassName.class) | |
| .toProvider(Providers.of(secureStoreClassName)); | |
| } | |
| }; | |
| } | |
| } |