PageRenderTime 4686ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/java/com/google/gerrit/pgm/init/BaseInit.java

https://gitlab.com/chenfengxu/gerrit
Java | 525 lines | 442 code | 55 blank | 28 comment | 39 complexity | d0250417a8dc3ca9cd93e032cb3837e4 MD5 | raw file
  1. // Copyright (C) 2013 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.init;
  15. import static com.google.gerrit.reviewdb.server.ReviewDbUtil.unwrapDb;
  16. import static com.google.gerrit.server.schema.DataSourceProvider.Context.SINGLE_USER;
  17. import static com.google.inject.Scopes.SINGLETON;
  18. import static com.google.inject.Stage.PRODUCTION;
  19. import com.google.common.base.MoreObjects;
  20. import com.google.common.base.Strings;
  21. import com.google.gerrit.common.Die;
  22. import com.google.gerrit.common.IoUtil;
  23. import com.google.gerrit.metrics.DisabledMetricMaker;
  24. import com.google.gerrit.metrics.MetricMaker;
  25. import com.google.gerrit.pgm.init.api.ConsoleUI;
  26. import com.google.gerrit.pgm.init.api.InitFlags;
  27. import com.google.gerrit.pgm.init.api.InstallAllPlugins;
  28. import com.google.gerrit.pgm.init.api.InstallPlugins;
  29. import com.google.gerrit.pgm.init.api.LibraryDownload;
  30. import com.google.gerrit.pgm.init.index.IndexManagerOnInit;
  31. import com.google.gerrit.pgm.init.index.elasticsearch.ElasticIndexModuleOnInit;
  32. import com.google.gerrit.pgm.init.index.lucene.LuceneIndexModuleOnInit;
  33. import com.google.gerrit.pgm.util.SiteProgram;
  34. import com.google.gerrit.reviewdb.server.ReviewDb;
  35. import com.google.gerrit.server.config.GerritServerConfigModule;
  36. import com.google.gerrit.server.config.SitePath;
  37. import com.google.gerrit.server.config.SitePaths;
  38. import com.google.gerrit.server.git.GitRepositoryManager;
  39. import com.google.gerrit.server.index.IndexModule;
  40. import com.google.gerrit.server.plugins.JarScanner;
  41. import com.google.gerrit.server.schema.ReviewDbFactory;
  42. import com.google.gerrit.server.schema.SchemaUpdater;
  43. import com.google.gerrit.server.schema.UpdateUI;
  44. import com.google.gerrit.server.securestore.SecureStore;
  45. import com.google.gerrit.server.securestore.SecureStoreClassName;
  46. import com.google.gerrit.server.securestore.SecureStoreProvider;
  47. import com.google.gwtorm.jdbc.JdbcExecutor;
  48. import com.google.gwtorm.jdbc.JdbcSchema;
  49. import com.google.gwtorm.server.OrmException;
  50. import com.google.gwtorm.server.SchemaFactory;
  51. import com.google.gwtorm.server.StatementExecutor;
  52. import com.google.inject.AbstractModule;
  53. import com.google.inject.CreationException;
  54. import com.google.inject.Guice;
  55. import com.google.inject.Inject;
  56. import com.google.inject.Injector;
  57. import com.google.inject.Module;
  58. import com.google.inject.Provider;
  59. import com.google.inject.TypeLiteral;
  60. import com.google.inject.spi.Message;
  61. import com.google.inject.util.Providers;
  62. import java.io.FileNotFoundException;
  63. import java.io.IOException;
  64. import java.nio.file.FileVisitResult;
  65. import java.nio.file.Files;
  66. import java.nio.file.Path;
  67. import java.nio.file.Paths;
  68. import java.nio.file.SimpleFileVisitor;
  69. import java.nio.file.attribute.BasicFileAttributes;
  70. import java.util.ArrayList;
  71. import java.util.Collections;
  72. import java.util.List;
  73. import java.util.Set;
  74. import javax.sql.DataSource;
  75. import org.slf4j.Logger;
  76. import org.slf4j.LoggerFactory;
  77. /** Initialize a new Gerrit installation. */
  78. public class BaseInit extends SiteProgram {
  79. private static final Logger log = LoggerFactory.getLogger(BaseInit.class);
  80. private final boolean standalone;
  81. private final boolean initDb;
  82. protected final PluginsDistribution pluginsDistribution;
  83. private final List<String> pluginsToInstall;
  84. private Injector sysInjector;
  85. protected BaseInit(PluginsDistribution pluginsDistribution, List<String> pluginsToInstall) {
  86. this.standalone = true;
  87. this.initDb = true;
  88. this.pluginsDistribution = pluginsDistribution;
  89. this.pluginsToInstall = pluginsToInstall;
  90. }
  91. public BaseInit(
  92. Path sitePath,
  93. boolean standalone,
  94. boolean initDb,
  95. PluginsDistribution pluginsDistribution,
  96. List<String> pluginsToInstall) {
  97. this(sitePath, null, standalone, initDb, pluginsDistribution, pluginsToInstall);
  98. }
  99. public BaseInit(
  100. Path sitePath,
  101. final Provider<DataSource> dsProvider,
  102. boolean standalone,
  103. boolean initDb,
  104. PluginsDistribution pluginsDistribution,
  105. List<String> pluginsToInstall) {
  106. super(sitePath, dsProvider);
  107. this.standalone = standalone;
  108. this.initDb = initDb;
  109. this.pluginsDistribution = pluginsDistribution;
  110. this.pluginsToInstall = pluginsToInstall;
  111. }
  112. @Override
  113. public int run() throws Exception {
  114. final SiteInit init = createSiteInit();
  115. if (beforeInit(init)) {
  116. return 0;
  117. }
  118. init.flags.autoStart = getAutoStart() && init.site.isNew;
  119. init.flags.dev = isDev() && init.site.isNew;
  120. init.flags.skipPlugins = skipPlugins();
  121. init.flags.deleteCaches = getDeleteCaches();
  122. init.flags.isNew = init.site.isNew;
  123. final SiteRun run;
  124. try {
  125. init.initializer.run();
  126. init.flags.deleteOnFailure = false;
  127. Injector sysInjector = createSysInjector(init);
  128. IndexManagerOnInit indexManager = sysInjector.getInstance(IndexManagerOnInit.class);
  129. try {
  130. indexManager.start();
  131. run = createSiteRun(init);
  132. run.upgradeSchema();
  133. init.initializer.postRun(sysInjector);
  134. } finally {
  135. indexManager.stop();
  136. }
  137. } catch (Exception | Error failure) {
  138. if (init.flags.deleteOnFailure) {
  139. recursiveDelete(getSitePath());
  140. }
  141. throw failure;
  142. }
  143. System.err.println("Initialized " + getSitePath().toRealPath().normalize());
  144. afterInit(run);
  145. return 0;
  146. }
  147. protected boolean skipPlugins() {
  148. return false;
  149. }
  150. protected String getSecureStoreLib() {
  151. return null;
  152. }
  153. protected boolean skipAllDownloads() {
  154. return false;
  155. }
  156. protected List<String> getSkippedDownloads() {
  157. return Collections.emptyList();
  158. }
  159. /**
  160. * Invoked before site init is called.
  161. *
  162. * @param init initializer instance.
  163. * @throws Exception
  164. */
  165. protected boolean beforeInit(SiteInit init) throws Exception {
  166. return false;
  167. }
  168. /**
  169. * Invoked after site init is called.
  170. *
  171. * @param run completed run instance.
  172. * @throws Exception
  173. */
  174. protected void afterInit(SiteRun run) throws Exception {}
  175. protected List<String> getInstallPlugins() {
  176. try {
  177. if (pluginsToInstall != null && pluginsToInstall.isEmpty()) {
  178. return Collections.emptyList();
  179. }
  180. List<String> names = pluginsDistribution.listPluginNames();
  181. if (pluginsToInstall != null) {
  182. names.removeIf(n -> !pluginsToInstall.contains(n));
  183. }
  184. return names;
  185. } catch (FileNotFoundException e) {
  186. log.warn("Couldn't find distribution archive location. No plugin will be installed");
  187. return null;
  188. }
  189. }
  190. protected boolean installAllPlugins() {
  191. return false;
  192. }
  193. protected boolean getAutoStart() {
  194. return false;
  195. }
  196. public static class SiteInit {
  197. public final SitePaths site;
  198. final InitFlags flags;
  199. final ConsoleUI ui;
  200. final SitePathInitializer initializer;
  201. @Inject
  202. SiteInit(
  203. final SitePaths site,
  204. final InitFlags flags,
  205. final ConsoleUI ui,
  206. final SitePathInitializer initializer) {
  207. this.site = site;
  208. this.flags = flags;
  209. this.ui = ui;
  210. this.initializer = initializer;
  211. }
  212. }
  213. private SiteInit createSiteInit() {
  214. final ConsoleUI ui = getConsoleUI();
  215. final Path sitePath = getSitePath();
  216. final List<Module> m = new ArrayList<>();
  217. final SecureStoreInitData secureStoreInitData = discoverSecureStoreClass();
  218. final String currentSecureStoreClassName = getConfiguredSecureStoreClass();
  219. if (secureStoreInitData != null
  220. && currentSecureStoreClassName != null
  221. && !currentSecureStoreClassName.equals(secureStoreInitData.className)) {
  222. String err =
  223. String.format(
  224. "Different secure store was previously configured: %s. "
  225. + "Use SwitchSecureStore program to switch between implementations.",
  226. currentSecureStoreClassName);
  227. throw die(err);
  228. }
  229. m.add(new GerritServerConfigModule());
  230. m.add(new InitModule(standalone, initDb));
  231. m.add(
  232. new AbstractModule() {
  233. @Override
  234. protected void configure() {
  235. bind(ConsoleUI.class).toInstance(ui);
  236. bind(Path.class).annotatedWith(SitePath.class).toInstance(sitePath);
  237. List<String> plugins =
  238. MoreObjects.firstNonNull(getInstallPlugins(), new ArrayList<String>());
  239. bind(new TypeLiteral<List<String>>() {})
  240. .annotatedWith(InstallPlugins.class)
  241. .toInstance(plugins);
  242. bind(new TypeLiteral<Boolean>() {})
  243. .annotatedWith(InstallAllPlugins.class)
  244. .toInstance(installAllPlugins());
  245. bind(PluginsDistribution.class).toInstance(pluginsDistribution);
  246. String secureStoreClassName;
  247. if (secureStoreInitData != null) {
  248. secureStoreClassName = secureStoreInitData.className;
  249. } else {
  250. secureStoreClassName = currentSecureStoreClassName;
  251. }
  252. if (secureStoreClassName != null) {
  253. ui.message("Using secure store: %s\n", secureStoreClassName);
  254. }
  255. bind(SecureStoreInitData.class).toProvider(Providers.of(secureStoreInitData));
  256. bind(String.class)
  257. .annotatedWith(SecureStoreClassName.class)
  258. .toProvider(Providers.of(secureStoreClassName));
  259. bind(SecureStore.class).toProvider(SecureStoreProvider.class).in(SINGLETON);
  260. bind(new TypeLiteral<List<String>>() {})
  261. .annotatedWith(LibraryDownload.class)
  262. .toInstance(getSkippedDownloads());
  263. bind(Boolean.class).annotatedWith(LibraryDownload.class).toInstance(skipAllDownloads());
  264. bind(MetricMaker.class).to(DisabledMetricMaker.class);
  265. }
  266. });
  267. try {
  268. return Guice.createInjector(PRODUCTION, m).getInstance(SiteInit.class);
  269. } catch (CreationException ce) {
  270. final Message first = ce.getErrorMessages().iterator().next();
  271. Throwable why = first.getCause();
  272. if (why instanceof Die) {
  273. throw (Die) why;
  274. }
  275. final StringBuilder buf = new StringBuilder(ce.getMessage());
  276. while (why != null) {
  277. buf.append("\n");
  278. buf.append(why.getMessage());
  279. why = why.getCause();
  280. if (why != null) {
  281. buf.append("\n caused by ");
  282. }
  283. }
  284. throw die(buf.toString(), new RuntimeException("InitInjector failed", ce));
  285. }
  286. }
  287. protected ConsoleUI getConsoleUI() {
  288. return ConsoleUI.getInstance(false);
  289. }
  290. private SecureStoreInitData discoverSecureStoreClass() {
  291. String secureStore = getSecureStoreLib();
  292. if (Strings.isNullOrEmpty(secureStore)) {
  293. return null;
  294. }
  295. Path secureStoreLib = Paths.get(secureStore);
  296. if (!Files.exists(secureStoreLib)) {
  297. throw new InvalidSecureStoreException(String.format("File %s doesn't exist", secureStore));
  298. }
  299. try (JarScanner scanner = new JarScanner(secureStoreLib)) {
  300. List<String> secureStores = scanner.findSubClassesOf(SecureStore.class);
  301. if (secureStores.isEmpty()) {
  302. throw new InvalidSecureStoreException(
  303. String.format(
  304. "Cannot find class implementing %s interface in %s",
  305. SecureStore.class.getName(), secureStore));
  306. }
  307. if (secureStores.size() > 1) {
  308. throw new InvalidSecureStoreException(
  309. String.format(
  310. "%s has more that one implementation of %s interface",
  311. secureStore, SecureStore.class.getName()));
  312. }
  313. IoUtil.loadJARs(secureStoreLib);
  314. return new SecureStoreInitData(secureStoreLib, secureStores.get(0));
  315. } catch (IOException e) {
  316. throw new InvalidSecureStoreException(String.format("%s is not a valid jar", secureStore));
  317. }
  318. }
  319. public static class SiteRun {
  320. public final ConsoleUI ui;
  321. public final SitePaths site;
  322. public final InitFlags flags;
  323. final SchemaUpdater schemaUpdater;
  324. final SchemaFactory<ReviewDb> schema;
  325. final GitRepositoryManager repositoryManager;
  326. @Inject
  327. SiteRun(
  328. ConsoleUI ui,
  329. SitePaths site,
  330. InitFlags flags,
  331. SchemaUpdater schemaUpdater,
  332. @ReviewDbFactory SchemaFactory<ReviewDb> schema,
  333. GitRepositoryManager repositoryManager) {
  334. this.ui = ui;
  335. this.site = site;
  336. this.flags = flags;
  337. this.schemaUpdater = schemaUpdater;
  338. this.schema = schema;
  339. this.repositoryManager = repositoryManager;
  340. }
  341. void upgradeSchema() throws OrmException {
  342. final List<String> pruneList = new ArrayList<>();
  343. schemaUpdater.update(
  344. new UpdateUI() {
  345. @Override
  346. public void message(String message) {
  347. System.err.println(message);
  348. System.err.flush();
  349. }
  350. @Override
  351. public boolean yesno(boolean defaultValue, String message) {
  352. return ui.yesno(defaultValue, message);
  353. }
  354. @Override
  355. public void waitForUser() {
  356. ui.waitForUser();
  357. }
  358. @Override
  359. public String readString(
  360. String defaultValue, Set<String> allowedValues, String message) {
  361. return ui.readString(defaultValue, allowedValues, message);
  362. }
  363. @Override
  364. public boolean isBatch() {
  365. return ui.isBatch();
  366. }
  367. @Override
  368. public void pruneSchema(StatementExecutor e, List<String> prune) {
  369. for (String p : prune) {
  370. if (!pruneList.contains(p)) {
  371. pruneList.add(p);
  372. }
  373. }
  374. }
  375. });
  376. if (!pruneList.isEmpty()) {
  377. StringBuilder msg = new StringBuilder();
  378. msg.append("Execute the following SQL to drop unused objects:\n");
  379. msg.append("\n");
  380. for (String sql : pruneList) {
  381. msg.append(" ");
  382. msg.append(sql);
  383. msg.append(";\n");
  384. }
  385. if (ui.isBatch()) {
  386. System.err.print(msg);
  387. System.err.flush();
  388. } else if (ui.yesno(true, "%s\nExecute now", msg)) {
  389. try (JdbcSchema db = (JdbcSchema) unwrapDb(schema.open());
  390. JdbcExecutor e = new JdbcExecutor(db)) {
  391. for (String sql : pruneList) {
  392. e.execute(sql);
  393. }
  394. }
  395. }
  396. }
  397. }
  398. }
  399. private SiteRun createSiteRun(SiteInit init) {
  400. return createSysInjector(init).getInstance(SiteRun.class);
  401. }
  402. private Injector createSysInjector(SiteInit init) {
  403. if (sysInjector == null) {
  404. final List<Module> modules = new ArrayList<>();
  405. modules.add(
  406. new AbstractModule() {
  407. @Override
  408. protected void configure() {
  409. bind(ConsoleUI.class).toInstance(init.ui);
  410. bind(InitFlags.class).toInstance(init.flags);
  411. }
  412. });
  413. Injector dbInjector = createDbInjector(SINGLE_USER);
  414. switch (IndexModule.getIndexType(dbInjector)) {
  415. case LUCENE:
  416. modules.add(new LuceneIndexModuleOnInit());
  417. break;
  418. case ELASTICSEARCH:
  419. modules.add(new ElasticIndexModuleOnInit());
  420. break;
  421. default:
  422. throw new IllegalStateException("unsupported index.type");
  423. }
  424. sysInjector = dbInjector.createChildInjector(modules);
  425. }
  426. return sysInjector;
  427. }
  428. private static void recursiveDelete(Path path) {
  429. final String msg = "warn: Cannot remove ";
  430. try {
  431. Files.walkFileTree(
  432. path,
  433. new SimpleFileVisitor<Path>() {
  434. @Override
  435. public FileVisitResult visitFile(Path f, BasicFileAttributes attrs) throws IOException {
  436. try {
  437. Files.delete(f);
  438. } catch (IOException e) {
  439. System.err.println(msg + f);
  440. }
  441. return FileVisitResult.CONTINUE;
  442. }
  443. @Override
  444. public FileVisitResult postVisitDirectory(Path dir, IOException err) {
  445. try {
  446. // Previously warned if err was not null; if dir is not empty as a
  447. // result, will cause an error that will be logged below.
  448. Files.delete(dir);
  449. } catch (IOException e) {
  450. System.err.println(msg + dir);
  451. }
  452. return FileVisitResult.CONTINUE;
  453. }
  454. @Override
  455. public FileVisitResult visitFileFailed(Path f, IOException e) {
  456. System.err.println(msg + f);
  457. return FileVisitResult.CONTINUE;
  458. }
  459. });
  460. } catch (IOException e) {
  461. System.err.println(msg + path);
  462. }
  463. }
  464. protected boolean isDev() {
  465. return false;
  466. }
  467. protected boolean getDeleteCaches() {
  468. return false;
  469. }
  470. }