PageRenderTime 87ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/java/com/google/gerrit/pgm/util/SiteProgram.java

https://gitlab.com/chenfengxu/gerrit
Java | 266 lines | 223 code | 26 blank | 17 comment | 29 complexity | 5ef765b29e55eee95888a2ae2fe4da24 MD5 | raw file
  1. // Copyright (C) 2009 The Android Open Source Project
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package com.google.gerrit.pgm.util;
  15. import static com.google.gerrit.server.config.GerritServerConfigModule.getSecureStoreClassName;
  16. import static com.google.inject.Scopes.SINGLETON;
  17. import static com.google.inject.Stage.PRODUCTION;
  18. import com.google.gerrit.common.Die;
  19. import com.google.gerrit.extensions.events.LifecycleListener;
  20. import com.google.gerrit.lifecycle.LifecycleModule;
  21. import com.google.gerrit.metrics.DisabledMetricMaker;
  22. import com.google.gerrit.metrics.MetricMaker;
  23. import com.google.gerrit.metrics.dropwizard.DropWizardMetricMaker;
  24. import com.google.gerrit.server.config.GerritServerConfig;
  25. import com.google.gerrit.server.config.GerritServerConfigModule;
  26. import com.google.gerrit.server.config.SitePath;
  27. import com.google.gerrit.server.git.GitRepositoryManagerModule;
  28. import com.google.gerrit.server.notedb.NotesMigration;
  29. import com.google.gerrit.server.schema.DataSourceModule;
  30. import com.google.gerrit.server.schema.DataSourceProvider;
  31. import com.google.gerrit.server.schema.DataSourceType;
  32. import com.google.gerrit.server.schema.DatabaseModule;
  33. import com.google.gerrit.server.schema.SchemaModule;
  34. import com.google.gerrit.server.securestore.SecureStoreClassName;
  35. import com.google.gwtorm.server.OrmException;
  36. import com.google.inject.AbstractModule;
  37. import com.google.inject.Binding;
  38. import com.google.inject.CreationException;
  39. import com.google.inject.Guice;
  40. import com.google.inject.Injector;
  41. import com.google.inject.Key;
  42. import com.google.inject.Module;
  43. import com.google.inject.Provider;
  44. import com.google.inject.ProvisionException;
  45. import com.google.inject.TypeLiteral;
  46. import com.google.inject.name.Named;
  47. import com.google.inject.name.Names;
  48. import com.google.inject.spi.Message;
  49. import com.google.inject.util.Providers;
  50. import java.lang.annotation.Annotation;
  51. import java.nio.file.Files;
  52. import java.nio.file.Path;
  53. import java.nio.file.Paths;
  54. import java.sql.Connection;
  55. import java.sql.SQLException;
  56. import java.util.ArrayList;
  57. import java.util.List;
  58. import javax.sql.DataSource;
  59. import org.eclipse.jgit.lib.Config;
  60. import org.kohsuke.args4j.Option;
  61. public abstract class SiteProgram extends AbstractProgram {
  62. @Option(
  63. name = "--site-path",
  64. aliases = {"-d"},
  65. usage = "Local directory containing site data"
  66. )
  67. private void setSitePath(String path) {
  68. sitePath = Paths.get(path);
  69. }
  70. protected Provider<DataSource> dsProvider;
  71. private Path sitePath = Paths.get(".");
  72. protected SiteProgram() {}
  73. protected SiteProgram(Path sitePath) {
  74. this.sitePath = sitePath;
  75. }
  76. protected SiteProgram(Path sitePath, Provider<DataSource> dsProvider) {
  77. this.sitePath = sitePath;
  78. this.dsProvider = dsProvider;
  79. }
  80. /** @return the site path specified on the command line. */
  81. protected Path getSitePath() {
  82. return sitePath;
  83. }
  84. /** Ensures we are running inside of a valid site, otherwise throws a Die. */
  85. protected void mustHaveValidSite() throws Die {
  86. if (!Files.exists(sitePath.resolve("etc").resolve("gerrit.config"))) {
  87. throw die("not a Gerrit site: '" + getSitePath() + "'\nPerhaps you need to run init first?");
  88. }
  89. }
  90. /** @return provides database connectivity and site path. */
  91. protected Injector createDbInjector(DataSourceProvider.Context context) {
  92. return createDbInjector(false, context);
  93. }
  94. /** @return provides database connectivity and site path. */
  95. protected Injector createDbInjector(boolean enableMetrics, DataSourceProvider.Context context) {
  96. Path sitePath = getSitePath();
  97. List<Module> modules = new ArrayList<>();
  98. Module sitePathModule =
  99. new AbstractModule() {
  100. @Override
  101. protected void configure() {
  102. bind(Path.class).annotatedWith(SitePath.class).toInstance(sitePath);
  103. bind(String.class)
  104. .annotatedWith(SecureStoreClassName.class)
  105. .toProvider(Providers.of(getConfiguredSecureStoreClass()));
  106. }
  107. };
  108. modules.add(sitePathModule);
  109. if (enableMetrics) {
  110. modules.add(new DropWizardMetricMaker.ApiModule());
  111. } else {
  112. modules.add(
  113. new AbstractModule() {
  114. @Override
  115. protected void configure() {
  116. bind(MetricMaker.class).to(DisabledMetricMaker.class);
  117. }
  118. });
  119. }
  120. modules.add(
  121. new LifecycleModule() {
  122. @Override
  123. protected void configure() {
  124. bind(DataSourceProvider.Context.class).toInstance(context);
  125. if (dsProvider != null) {
  126. bind(Key.get(DataSource.class, Names.named("ReviewDb")))
  127. .toProvider(dsProvider)
  128. .in(SINGLETON);
  129. if (LifecycleListener.class.isAssignableFrom(dsProvider.getClass())) {
  130. listener().toInstance((LifecycleListener) dsProvider);
  131. }
  132. } else {
  133. bind(Key.get(DataSource.class, Names.named("ReviewDb")))
  134. .toProvider(SiteLibraryBasedDataSourceProvider.class)
  135. .in(SINGLETON);
  136. listener().to(SiteLibraryBasedDataSourceProvider.class);
  137. }
  138. }
  139. });
  140. Module configModule = new GerritServerConfigModule();
  141. modules.add(configModule);
  142. Injector cfgInjector = Guice.createInjector(sitePathModule, configModule);
  143. Config cfg = cfgInjector.getInstance(Key.get(Config.class, GerritServerConfig.class));
  144. String dbType;
  145. if (dsProvider != null) {
  146. dbType = getDbType(dsProvider);
  147. } else {
  148. dbType = cfg.getString("database", null, "type");
  149. }
  150. if (dbType == null) {
  151. throw new ProvisionException("database.type must be defined");
  152. }
  153. DataSourceType dst =
  154. Guice.createInjector(new DataSourceModule(), configModule, sitePathModule)
  155. .getInstance(Key.get(DataSourceType.class, Names.named(dbType.toLowerCase())));
  156. modules.add(
  157. new AbstractModule() {
  158. @Override
  159. protected void configure() {
  160. bind(DataSourceType.class).toInstance(dst);
  161. }
  162. });
  163. modules.add(new DatabaseModule());
  164. modules.add(new SchemaModule());
  165. modules.add(cfgInjector.getInstance(GitRepositoryManagerModule.class));
  166. modules.add(new NotesMigration.Module());
  167. try {
  168. return Guice.createInjector(PRODUCTION, modules);
  169. } catch (CreationException ce) {
  170. Message first = ce.getErrorMessages().iterator().next();
  171. Throwable why = first.getCause();
  172. if (why instanceof SQLException) {
  173. throw die("Cannot connect to SQL database", why);
  174. }
  175. if (why instanceof OrmException
  176. && why.getCause() != null
  177. && "Unable to determine driver URL".equals(why.getMessage())) {
  178. why = why.getCause();
  179. if (isCannotCreatePoolException(why)) {
  180. throw die("Cannot connect to SQL database", why.getCause());
  181. }
  182. throw die("Cannot connect to SQL database", why);
  183. }
  184. StringBuilder buf = new StringBuilder();
  185. if (why != null) {
  186. buf.append(why.getMessage());
  187. why = why.getCause();
  188. } else {
  189. buf.append(first.getMessage());
  190. }
  191. while (why != null) {
  192. buf.append("\n caused by ");
  193. buf.append(why.toString());
  194. why = why.getCause();
  195. }
  196. throw die(buf.toString(), new RuntimeException("DbInjector failed", ce));
  197. }
  198. }
  199. protected final String getConfiguredSecureStoreClass() {
  200. return getSecureStoreClassName(sitePath);
  201. }
  202. private String getDbType(Provider<DataSource> dsProvider) {
  203. String dbProductName;
  204. try (Connection conn = dsProvider.get().getConnection()) {
  205. dbProductName = conn.getMetaData().getDatabaseProductName().toLowerCase();
  206. } catch (SQLException e) {
  207. throw new RuntimeException(e);
  208. }
  209. List<Module> modules = new ArrayList<>();
  210. modules.add(
  211. new AbstractModule() {
  212. @Override
  213. protected void configure() {
  214. bind(Path.class).annotatedWith(SitePath.class).toInstance(getSitePath());
  215. }
  216. });
  217. modules.add(new GerritServerConfigModule());
  218. modules.add(new DataSourceModule());
  219. Injector i = Guice.createInjector(modules);
  220. List<Binding<DataSourceType>> dsTypeBindings =
  221. i.findBindingsByType(new TypeLiteral<DataSourceType>() {});
  222. for (Binding<DataSourceType> binding : dsTypeBindings) {
  223. Annotation annotation = binding.getKey().getAnnotation();
  224. if (annotation instanceof Named) {
  225. if (((Named) annotation).value().toLowerCase().contains(dbProductName)) {
  226. return ((Named) annotation).value();
  227. }
  228. }
  229. }
  230. throw new IllegalStateException(
  231. String.format(
  232. "Cannot guess database type from the database product name '%s'", dbProductName));
  233. }
  234. @SuppressWarnings("deprecation")
  235. private static boolean isCannotCreatePoolException(Throwable why) {
  236. return why instanceof org.apache.commons.dbcp.SQLNestedException
  237. && why.getCause() != null
  238. && why.getMessage().startsWith("Cannot create PoolableConnectionFactory");
  239. }
  240. }