| // 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.pgm; |
| |
| import static com.google.gerrit.server.schema.DataSourceProvider.Context.MULTI_USER; |
| |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.common.base.MoreObjects; |
| import com.google.gerrit.common.ChangeHookRunner; |
| import com.google.gerrit.httpd.AllRequestFilter; |
| import com.google.gerrit.httpd.GerritOptions; |
| 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.RequestContextFilter; |
| 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.plugins.HttpPluginModule; |
| import com.google.gerrit.lifecycle.LifecycleManager; |
| import com.google.gerrit.lucene.LuceneIndexModule; |
| import com.google.gerrit.pgm.http.jetty.JettyEnv; |
| import com.google.gerrit.pgm.http.jetty.JettyModule; |
| import com.google.gerrit.pgm.http.jetty.ProjectQoSFilter; |
| import com.google.gerrit.pgm.util.ErrorLogFile; |
| import com.google.gerrit.pgm.util.GarbageCollectionLogFile; |
| import com.google.gerrit.pgm.util.LogFileCompressor; |
| import com.google.gerrit.pgm.util.RuntimeShutdown; |
| import com.google.gerrit.pgm.util.SiteProgram; |
| import com.google.gerrit.reviewdb.client.AuthType; |
| import com.google.gerrit.server.account.InternalAccountDirectory; |
| import com.google.gerrit.server.cache.h2.DefaultCacheFactory; |
| 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.CanonicalWebUrlProvider; |
| import com.google.gerrit.server.config.GerritGlobalModule; |
| import com.google.gerrit.server.config.GerritServerConfig; |
| import com.google.gerrit.server.config.MasterNodeStartup; |
| import com.google.gerrit.server.config.RestCacheAdminModule; |
| import com.google.gerrit.server.contact.ContactStoreModule; |
| import com.google.gerrit.server.contact.HttpContactStoreConnection; |
| import com.google.gerrit.server.git.GarbageCollectionRunner; |
| import com.google.gerrit.server.git.ReceiveCommitsExecutorModule; |
| import com.google.gerrit.server.git.WorkQueue; |
| import com.google.gerrit.server.index.DummyIndexModule; |
| import com.google.gerrit.server.index.IndexModule; |
| import com.google.gerrit.server.index.IndexModule.IndexType; |
| import com.google.gerrit.server.mail.SignedTokenEmailTokenVerifier; |
| import com.google.gerrit.server.mail.SmtpEmailSender; |
| import com.google.gerrit.server.mime.MimeUtil2Module; |
| import com.google.gerrit.server.patch.DiffExecutorModule; |
| import com.google.gerrit.server.plugins.PluginGuiceEnvironment; |
| import com.google.gerrit.server.plugins.PluginRestApiModule; |
| import com.google.gerrit.server.schema.DataSourceProvider; |
| import com.google.gerrit.server.schema.SchemaVersionCheck; |
| import com.google.gerrit.server.securestore.DefaultSecureStore; |
| import com.google.gerrit.server.securestore.SecureStore; |
| import com.google.gerrit.server.securestore.SecureStoreClassName; |
| import com.google.gerrit.server.securestore.SecureStoreProvider; |
| import com.google.gerrit.server.ssh.NoSshKeyCache; |
| import com.google.gerrit.server.ssh.NoSshModule; |
| import com.google.gerrit.server.ssh.SshAddressesModule; |
| import com.google.gerrit.solr.SolrIndexModule; |
| import com.google.gerrit.sshd.SshHostKeyModule; |
| import com.google.gerrit.sshd.SshKeyCacheImpl; |
| import com.google.gerrit.sshd.SshModule; |
| import com.google.gerrit.sshd.commands.DefaultCommandModule; |
| import com.google.inject.AbstractModule; |
| 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.Stage; |
| |
| import org.eclipse.jgit.lib.Config; |
| import org.kohsuke.args4j.Option; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.lang.Thread.UncaughtExceptionHandler; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import javax.servlet.http.HttpServletRequest; |
| |
| /** Run SSH daemon portions of Gerrit. */ |
| public class Daemon extends SiteProgram { |
| private static final Logger log = LoggerFactory.getLogger(Daemon.class); |
| |
| @Option(name = "--enable-httpd", usage = "Enable the internal HTTP daemon") |
| private Boolean httpd; |
| |
| @Option(name = "--disable-httpd", usage = "Disable the internal HTTP daemon") |
| void setDisableHttpd(@SuppressWarnings("unused") boolean arg) { |
| httpd = false; |
| } |
| |
| @Option(name = "--enable-sshd", usage = "Enable the internal SSH daemon") |
| private boolean sshd = true; |
| |
| @Option(name = "--disable-sshd", usage = "Disable the internal SSH daemon") |
| void setDisableSshd(@SuppressWarnings("unused") boolean arg) { |
| sshd = false; |
| } |
| |
| @Option(name = "--slave", usage = "Support fetch only") |
| private boolean slave; |
| |
| @Option(name = "--console-log", usage = "Log to console (not $site_path/logs)") |
| private boolean consoleLog; |
| |
| @Option(name = "-s", usage = "Start interactive shell") |
| private boolean inspector; |
| |
| @Option(name = "--run-id", usage = "Cookie to store in $site_path/logs/gerrit.run") |
| private String runId; |
| |
| @Option(name = "--headless", usage = "Don't start the UI frontend") |
| private boolean headless; |
| |
| @Option(name = "--init", aliases = {"-i"}, |
| usage = "Init site before starting the daemon") |
| private boolean doInit; |
| |
| private final LifecycleManager manager = new LifecycleManager(); |
| private Injector dbInjector; |
| private Injector cfgInjector; |
| private Injector sysInjector; |
| private Injector sshInjector; |
| private Injector webInjector; |
| private Injector httpdInjector; |
| private File runFile; |
| private boolean test; |
| private AbstractModule luceneModule; |
| |
| private Runnable serverStarted; |
| |
| public Daemon() { |
| } |
| |
| @VisibleForTesting |
| public Daemon(Runnable serverStarted) { |
| this.serverStarted = serverStarted; |
| } |
| |
| public void setEnableHttpd(boolean enable) { |
| httpd = enable; |
| } |
| |
| @Override |
| public int run() throws Exception { |
| if (doInit) { |
| try { |
| new Init(getSitePath()).run(); |
| } catch (Exception e) { |
| throw die("Init failed", e); |
| } |
| } |
| mustHaveValidSite(); |
| Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() { |
| @Override |
| public void uncaughtException(Thread t, Throwable e) { |
| log.error("Thread " + t.getName() + " threw exception", e); |
| } |
| }); |
| |
| if (runId != null) { |
| runFile = new File(new File(getSitePath(), "logs"), "gerrit.run"); |
| } |
| |
| if (httpd == null) { |
| httpd = !slave; |
| } |
| |
| if (!httpd && !sshd) { |
| throw die("No services enabled, nothing to do"); |
| } |
| |
| manager.add(GarbageCollectionLogFile.start(getSitePath())); |
| if (consoleLog) { |
| } else { |
| manager.add(ErrorLogFile.start(getSitePath())); |
| } |
| |
| try { |
| start(); |
| RuntimeShutdown.add(new Runnable() { |
| @Override |
| public void run() { |
| log.info("caught shutdown, cleaning up"); |
| if (runId != null) { |
| runFile.delete(); |
| } |
| manager.stop(); |
| } |
| }); |
| |
| log.info("Gerrit Code Review " + myVersion() + " ready"); |
| if (runId != null) { |
| try { |
| runFile.createNewFile(); |
| runFile.setReadable(true, false); |
| |
| FileOutputStream out = new FileOutputStream(runFile); |
| try { |
| out.write((runId + "\n").getBytes("UTF-8")); |
| } finally { |
| out.close(); |
| } |
| } catch (IOException err) { |
| log.warn("Cannot write --run-id to " + runFile, err); |
| } |
| } |
| |
| if (serverStarted != null) { |
| serverStarted.run(); |
| } |
| |
| if (inspector) { |
| JythonShell shell = new JythonShell(); |
| shell.set("m", manager); |
| shell.set("ds", dbInjector.getInstance(DataSourceProvider.class)); |
| shell.set("schk", dbInjector.getInstance(SchemaVersionCheck.class)); |
| shell.set("d", this); |
| shell.run(); |
| } else { |
| RuntimeShutdown.waitFor(); |
| } |
| return 0; |
| } catch (Throwable err) { |
| log.error("Unable to start daemon", err); |
| return 1; |
| } |
| } |
| |
| @VisibleForTesting |
| public LifecycleManager getLifecycleManager() { |
| return manager; |
| } |
| |
| @VisibleForTesting |
| public void setDatabaseForTesting(List<Module> modules) { |
| dbInjector = Guice.createInjector(Stage.PRODUCTION, modules); |
| test = true; |
| headless = true; |
| } |
| |
| @VisibleForTesting |
| public void setLuceneModule(LuceneIndexModule m) { |
| luceneModule = m; |
| test = true; |
| } |
| |
| @VisibleForTesting |
| public void start() { |
| if (dbInjector == null) { |
| dbInjector = createDbInjector(MULTI_USER); |
| } |
| cfgInjector = createCfgInjector(); |
| sysInjector = createSysInjector(); |
| sysInjector.getInstance(PluginGuiceEnvironment.class) |
| .setDbCfgInjector(dbInjector, cfgInjector); |
| manager.add(dbInjector, cfgInjector, sysInjector); |
| |
| sshd &= !sshdOff(); |
| if (sshd) { |
| initSshd(); |
| } |
| |
| if (MoreObjects.firstNonNull(httpd, true)) { |
| initHttpd(); |
| } |
| |
| manager.start(); |
| } |
| |
| @VisibleForTesting |
| public void stop() { |
| manager.stop(); |
| } |
| |
| private boolean sshdOff() { |
| Config cfg = cfgInjector.getInstance(Key.get(Config.class, GerritServerConfig.class)); |
| return new SshAddressesModule().getListenAddresses(cfg).isEmpty(); |
| } |
| |
| private String myVersion() { |
| return com.google.gerrit.common.Version.getVersion(); |
| } |
| |
| private Injector createCfgInjector() { |
| final List<Module> modules = new ArrayList<>(); |
| modules.add(new AuthConfigModule()); |
| return dbInjector.createChildInjector(modules); |
| } |
| |
| private Injector createSysInjector() { |
| final List<Module> modules = new ArrayList<>(); |
| modules.add(SchemaVersionCheck.module()); |
| modules.add(new LogFileCompressor.Module()); |
| modules.add(new WorkQueue.Module()); |
| modules.add(new ChangeHookRunner.Module()); |
| modules.add(new ReceiveCommitsExecutorModule()); |
| modules.add(new DiffExecutorModule()); |
| modules.add(new MimeUtil2Module()); |
| modules.add(cfgInjector.getInstance(GerritGlobalModule.class)); |
| modules.add(new InternalAccountDirectory.Module()); |
| modules.add(new DefaultCacheFactory.Module()); |
| modules.add(new SmtpEmailSender.Module()); |
| modules.add(new SignedTokenEmailTokenVerifier.Module()); |
| modules.add(new PluginRestApiModule()); |
| modules.add(new RestCacheAdminModule()); |
| modules.add(createIndexModule()); |
| if (MoreObjects.firstNonNull(httpd, true)) { |
| modules.add(new CanonicalWebUrlModule() { |
| @Override |
| protected Class<? extends Provider<String>> provider() { |
| return HttpCanonicalWebUrlProvider.class; |
| } |
| }); |
| } else { |
| modules.add(new CanonicalWebUrlModule() { |
| @Override |
| protected Class<? extends Provider<String>> provider() { |
| return CanonicalWebUrlProvider.class; |
| } |
| }); |
| } |
| if (sshd) { |
| modules.add(SshKeyCacheImpl.module()); |
| } else { |
| modules.add(NoSshKeyCache.module()); |
| } |
| if (!slave) { |
| modules.add(new MasterNodeStartup()); |
| } |
| modules.add(new AbstractModule() { |
| @Override |
| protected void configure() { |
| bind(GerritOptions.class).toInstance(new GerritOptions(headless, slave)); |
| if (test) { |
| bind(String.class).annotatedWith(SecureStoreClassName.class) |
| .toInstance(DefaultSecureStore.class.getName()); |
| bind(SecureStore.class).toProvider(SecureStoreProvider.class); |
| } |
| } |
| }); |
| modules.add(GarbageCollectionRunner.module()); |
| return cfgInjector.createChildInjector(modules); |
| } |
| |
| private AbstractModule createIndexModule() { |
| if (slave) { |
| return new DummyIndexModule(); |
| } |
| IndexType indexType = IndexModule.getIndexType(cfgInjector); |
| switch (indexType) { |
| case LUCENE: |
| return luceneModule != null ? luceneModule : new LuceneIndexModule(); |
| case SOLR: |
| return new SolrIndexModule(); |
| default: |
| throw new IllegalStateException("unsupported index.type = " + indexType); |
| } |
| } |
| |
| private void initSshd() { |
| sshInjector = createSshInjector(); |
| sysInjector.getInstance(PluginGuiceEnvironment.class) |
| .setSshInjector(sshInjector); |
| manager.add(sshInjector); |
| } |
| |
| private Injector createSshInjector() { |
| final List<Module> modules = new ArrayList<>(); |
| modules.add(sysInjector.getInstance(SshModule.class)); |
| if (!test) { |
| modules.add(new SshHostKeyModule()); |
| } |
| modules.add(new DefaultCommandModule(slave)); |
| |
| return sysInjector.createChildInjector(modules); |
| } |
| |
| private void initHttpd() { |
| webInjector = createWebInjector(); |
| |
| sysInjector.getInstance(PluginGuiceEnvironment.class) |
| .setHttpInjector(webInjector); |
| |
| sysInjector.getInstance(HttpCanonicalWebUrlProvider.class) |
| .setHttpServletRequest( |
| webInjector.getProvider(HttpServletRequest.class)); |
| |
| httpdInjector = createHttpdInjector(); |
| manager.add(webInjector, httpdInjector); |
| } |
| |
| private Injector createWebInjector() { |
| final List<Module> modules = new ArrayList<>(); |
| if (sshd) { |
| modules.add(new ProjectQoSFilter.Module()); |
| } |
| modules.add(RequestContextFilter.module()); |
| modules.add(AllRequestFilter.module()); |
| modules.add(H2CacheBasedWebSession.module()); |
| modules.add(HttpContactStoreConnection.module()); |
| modules.add(sysInjector.getInstance(GitOverHttpModule.class)); |
| modules.add(sysInjector.getInstance(WebModule.class)); |
| modules.add(new HttpPluginModule()); |
| modules.add(new ContactStoreModule()); |
| if (sshd) { |
| modules.add(sshInjector.getInstance(WebSshGlueModule.class)); |
| } else { |
| modules.add(new NoSshModule()); |
| } |
| |
| AuthConfig authConfig = cfgInjector.getInstance(AuthConfig.class); |
| if (authConfig.getAuthType() == AuthType.OPENID || |
| authConfig.getAuthType() == AuthType.OPENID_SSO) { |
| modules.add(new OpenIdModule()); |
| } else if (authConfig.getAuthType() == AuthType.OAUTH) { |
| modules.add(new OAuthModule()); |
| } |
| modules.add(sysInjector.getInstance(GetUserFilter.Module.class)); |
| |
| return sysInjector.createChildInjector(modules); |
| } |
| |
| private Injector createHttpdInjector() { |
| final List<Module> modules = new ArrayList<>(); |
| modules.add(new JettyModule(new JettyEnv(webInjector))); |
| return webInjector.createChildInjector(modules); |
| } |
| } |