Java tutorial
// Copyright (C) 2015 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.googlesource.gerrit.plugins.verifystatus.init; import static com.google.inject.Scopes.SINGLETON; import static com.google.inject.Stage.PRODUCTION; import com.google.common.base.Strings; import com.google.common.collect.Sets; import com.google.gerrit.extensions.annotations.PluginName; import com.google.gerrit.lifecycle.LifecycleModule; import com.google.gerrit.pgm.init.api.ConsoleUI; import com.google.gerrit.pgm.init.api.InitStep; import com.google.gerrit.pgm.init.api.Section; import com.google.gerrit.reviewdb.client.CurrentSchemaVersion; import com.google.gerrit.server.config.SitePaths; import com.google.gwtorm.jdbc.JdbcExecutor; import com.google.gwtorm.jdbc.JdbcSchema; import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.SchemaFactory; import com.google.gwtorm.server.StatementExecutor; import com.google.inject.AbstractModule; import com.google.inject.Binding; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.Module; import com.google.inject.Provider; import com.google.inject.Singleton; import com.google.inject.TypeLiteral; import com.google.inject.name.Named; import com.google.inject.name.Names; import com.googlesource.gerrit.plugins.verifystatus.server.CiDb; import com.googlesource.gerrit.plugins.verifystatus.server.schema.CiDataSourceModule; import com.googlesource.gerrit.plugins.verifystatus.server.schema.CiDataSourceProvider; import com.googlesource.gerrit.plugins.verifystatus.server.schema.CiDataSourceType; import com.googlesource.gerrit.plugins.verifystatus.server.schema.CiDataSourceTypeGuesser; import com.googlesource.gerrit.plugins.verifystatus.server.schema.CiDatabaseModule; import com.googlesource.gerrit.plugins.verifystatus.server.schema.SchemaVersion; import com.googlesource.gerrit.plugins.verifystatus.server.schema.UpdateUI; import java.lang.annotation.Annotation; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import javax.sql.DataSource; @Singleton public class InitPlugin implements InitStep { private final ConsoleUI ui; private final SitePaths site; private final Section configSection; private final Injector parent; private SchemaFactory<CiDb> dbFactory; private Provider<SchemaVersion> updater; @Inject InitPlugin(Section.Factory sections, @PluginName String pluginName, ConsoleUI ui, SitePaths site, Injector parent) { this.ui = ui; this.site = site; this.configSection = sections.get("plugin", pluginName); this.parent = parent; } @Override public void run() throws Exception { ui.header("SQL Database for CI plugin"); Set<String> allowedValues = Sets.newTreeSet(); Injector i = Guice.createInjector(PRODUCTION, new DatabaseConfigModule(site)); List<Binding<DatabaseConfigInitializer>> dbConfigBindings = i .findBindingsByType(new TypeLiteral<DatabaseConfigInitializer>() { }); for (Binding<DatabaseConfigInitializer> binding : dbConfigBindings) { Annotation annotation = binding.getKey().getAnnotation(); if (annotation instanceof Named) { allowedValues.add(((Named) annotation).value()); } } if (!Strings.isNullOrEmpty(configSection.get("dbUrl")) && Strings.isNullOrEmpty(configSection.get("dbType"))) { configSection.set("dbType", "h2"); } String dbType = configSection.select("Database server type", "dbType", "h2", allowedValues); DatabaseConfigInitializer dci = i .getInstance(Key.get(DatabaseConfigInitializer.class, Names.named(dbType.toLowerCase()))); /** * TODO(davido): We probably don't need that, as CI database would be from the same type as * ReviewDb. So we expect that the needed libraries were already installed. * * <p>if (dci instanceof MySqlInitializer) { libraries.mysqlDriver.downloadRequired(); } else if * (dci instanceof OracleInitializer) { libraries.oracleDriver.downloadRequired(); } else if * (dci instanceof DB2Initializer) { libraries.db2Driver.downloadRequired(); } */ dci.initConfig(configSection); } @Override public void postRun() throws Exception { Injector i = buildInjector(parent); updater = i.getProvider(SchemaVersion.class); this.dbFactory = i.getInstance(Key.get(new TypeLiteral<SchemaFactory<CiDb>>() { })); upgradeSchema(); } private Injector buildInjector(final Injector parent) { List<Module> modules = new ArrayList<>(); modules.add(new LifecycleModule() { @Override protected void configure() { // For bootstrap we need to retrieve the ds type first CiDataSourceTypeGuesser guesser = parent.createChildInjector(new CiDataSourceModule()) .getInstance(Key.get(CiDataSourceTypeGuesser.class)); // For the ds type we retrieve the underlying implementation CiDataSourceType dst = parent.createChildInjector(new CiDataSourceModule()) .getInstance(Key.get(CiDataSourceType.class, Names.named(guesser.guessDataSourceType()))); // Bind the type to the retrieved instance bind(CiDataSourceType.class).toInstance(dst); bind(CiDataSourceProvider.Context.class).toInstance(CiDataSourceProvider.Context.MULTI_USER); bind(Key.get(DataSource.class, Names.named("CiDb"))).toProvider(CiDataSourceProvider.class) .in(SINGLETON); listener().to(CiDataSourceProvider.class); } }); modules.add(new CiDatabaseModule()); modules.add(new AbstractModule() { @Override protected void configure() { bind(SchemaVersion.class).to(SchemaVersion.C); } }); return parent.createChildInjector(modules); } private void upgradeSchema() throws OrmException { final List<String> pruneList = new ArrayList<>(); update(new UpdateUI() { @Override public void message(String msg) { System.err.println(msg); System.err.flush(); } @Override public boolean yesno(boolean def, String msg) { return ui.yesno(def, msg); } @Override public boolean isBatch() { return ui.isBatch(); } @Override public void pruneSchema(StatementExecutor e, List<String> prune) { for (String p : prune) { if (!pruneList.contains(p)) { pruneList.add(p); } } } }); if (!pruneList.isEmpty()) { StringBuilder msg = new StringBuilder(); msg.append("Execute the following SQL to drop unused objects:\n"); msg.append("\n"); for (String sql : pruneList) { msg.append(" "); msg.append(sql); msg.append(";\n"); } if (ui.isBatch()) { System.err.print(msg); System.err.flush(); } else if (ui.yesno(true, "%s\nExecute now", msg)) { try (JdbcSchema db = (JdbcSchema) dbFactory.open(); JdbcExecutor e = new JdbcExecutor(db)) { for (String sql : pruneList) { e.execute(sql); } } } } } public void update(UpdateUI ui) throws OrmException { try (CiDb db = dbFactory.open()) { SchemaVersion u = updater.get(); CurrentSchemaVersion version = getSchemaVersion(db); if (version == null) { try (JdbcExecutor e = new JdbcExecutor((JdbcSchema) db)) { ((JdbcSchema) db).updateSchema(e); } final CurrentSchemaVersion sVer = CurrentSchemaVersion.create(); sVer.versionNbr = SchemaVersion.getBinaryVersion(); db.schemaVersion().insert(Collections.singleton(sVer)); } else { try { u.check(ui, version, db); } catch (SQLException e) { throw new OrmException("Cannot upgrade schema", e); } } } } private CurrentSchemaVersion getSchemaVersion(CiDb db) { try { return db.schemaVersion().get(new CurrentSchemaVersion.Key()); } catch (OrmException e) { return null; } } }