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