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