diff options
Diffstat (limited to 'gerrit-server/src/main/java/com/google/gerrit/server/schema/DataSourceProvider.java')
-rw-r--r-- | gerrit-server/src/main/java/com/google/gerrit/server/schema/DataSourceProvider.java | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/DataSourceProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/DataSourceProvider.java new file mode 100644 index 0000000000..91d6346b59 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/DataSourceProvider.java @@ -0,0 +1,266 @@ +// Copyright (C) 2009 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.google.gerrit.server.schema; + +import com.google.gerrit.lifecycle.LifecycleListener; +import com.google.gerrit.server.config.ConfigUtil; +import com.google.gerrit.server.config.GerritServerConfig; +import com.google.gerrit.server.config.SitePaths; +import com.google.gwtorm.jdbc.SimpleDataSource; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.ProvisionException; +import com.google.inject.Singleton; + +import org.apache.commons.dbcp.BasicDataSource; +import org.eclipse.jgit.lib.Config; + +import java.io.File; +import java.io.IOException; +import java.sql.SQLException; +import java.util.Properties; + +import javax.sql.DataSource; + +/** Provides access to the DataSource. */ +@Singleton +public final class DataSourceProvider implements Provider<DataSource>, + LifecycleListener { + private final DataSource ds; + + @Inject + DataSourceProvider(final SitePaths site, + @GerritServerConfig final Config cfg, Context ctx) { + ds = open(site, cfg, ctx); + } + + @Override + public synchronized DataSource get() { + return ds; + } + + @Override + public void start() { + } + + @Override + public synchronized void stop() { + if (ds instanceof BasicDataSource) { + try { + ((BasicDataSource) ds).close(); + } catch (SQLException e) { + // Ignore the close failure. + } + } + } + + public static enum Context { + SINGLE_USER, MULTI_USER; + } + + public static enum Type { + DEFAULT, JDBC, POSTGRES, POSTGRESQL, H2, MYSQL; + } + + private DataSource open(final SitePaths site, final Config cfg, + final Context context) { + Type type = ConfigUtil.getEnum(cfg, "database", null, "type", Type.DEFAULT); + String driver = optional(cfg, "driver"); + String url = optional(cfg, "url"); + String username = optional(cfg, "username"); + String password = optional(cfg, "password"); + String hostname = optional(cfg, "hostname"); + String port = optional(cfg, "port"); + if (hostname == null) { + hostname = "localhost"; + } + + if (Type.DEFAULT == type && (driver == null || driver.isEmpty())) { + if (url != null && url.isEmpty()) { + + if (url.startsWith("jdbc:postgresql:")) { + type = Type.POSTGRES; + } else if (url.startsWith("postgresql:")) { + url = "jdbc:" + url; + type = Type.POSTGRES; + } else if (url.startsWith("postgres:")) { + url = "jdbc:postgresql:" + url.substring(url.indexOf(':') + 1); + type = Type.POSTGRES; + + } else if (url.startsWith("jdbc:h2:")) { + type = Type.H2; + } else if (url.startsWith("h2:")) { + url = "jdbc:" + url; + type = Type.H2; + + } else if (url.startsWith("jdbc:mysql:")) { + type = Type.MYSQL; + } else if (url.startsWith("mysql:")) { + url = "jdbc:" + url; + type = Type.MYSQL; + + } + + } else if (url == null || url.isEmpty()) { + type = Type.H2; + } + } + + switch (type) { + case POSTGRES: + case POSTGRESQL: { + final String pfx = "jdbc:postgresql://"; + driver = "org.postgresql.Driver"; + if (url == null) { + final StringBuilder b = new StringBuilder(); + b.append(pfx); + b.append(hostname); + if (port != null && !port.isEmpty()) { + b.append(":"); + b.append(port); + } + b.append("/"); + b.append(required(cfg, "database")); + url = b.toString(); + } + if (url == null || !url.startsWith(pfx)) { + throw new IllegalArgumentException("database.url must be " + pfx + + " and not " + url); + } + break; + } + + case H2: { + final String pfx = "jdbc:h2:"; + driver = "org.h2.Driver"; + if (url == null) { + String database = optional(cfg, "database"); + if (database == null || database.isEmpty()) { + database = "db/ReviewDB"; + } + + File db = site.resolve(database); + try { + db = db.getCanonicalFile(); + } catch (IOException e) { + db = db.getAbsoluteFile(); + } + url = pfx + db.toURI().toString(); + } + if (url == null || !url.startsWith(pfx)) { + throw new IllegalArgumentException("database.url must be " + pfx + + " and not " + url); + } + break; + } + + case MYSQL: { + final String pfx = "jdbc:mysql://"; + driver = "com.mysql.jdbc.Driver"; + if (url == null) { + final StringBuilder b = new StringBuilder(); + b.append(pfx); + b.append(hostname); + if (port != null && !port.isEmpty()) { + b.append(":"); + b.append(port); + } + b.append("/"); + b.append(required(cfg, "database")); + url = b.toString(); + } + if (url == null || !url.startsWith(pfx)) { + throw new IllegalArgumentException("database.url must be " + pfx + + " and not " + url); + } + break; + } + + case DEFAULT: + case JDBC: + default: + driver = required(cfg, "driver"); + url = required(cfg, "url"); + if (!url.startsWith("jdbc:")) { + throw new IllegalArgumentException("database.url must be jdbc: style"); + } + break; + } + + boolean usePool; + if (url.startsWith("jdbc:mysql:")) { + // MySQL has given us trouble with the connection pool, + // sometimes the backend disconnects and the pool winds + // up with a stale connection. Fortunately opening up + // a new MySQL connection is usually very fast. + // + usePool = false; + } else { + usePool = true; + } + usePool = cfg.getBoolean("database", "connectionpool", usePool); + if (context == Context.SINGLE_USER) { + usePool = false; + } + + if (usePool) { + final BasicDataSource ds = new BasicDataSource(); + ds.setDriverClassName(driver); + ds.setUrl(url); + if (username != null && !username.isEmpty()) { + ds.setUsername(username); + } + if (password != null && !password.isEmpty()) { + ds.setPassword(password); + } + ds.setMaxActive(cfg.getInt("database", "poollimit", 8)); + ds.setMinIdle(cfg.getInt("database", "poolminidle", 4)); + ds.setMaxIdle(cfg.getInt("database", "poolmaxidle", 4)); + ds.setMaxWait(cfg.getInt("database", "poolmaxwait", 30000)); + ds.setInitialSize(ds.getMinIdle()); + return ds; + + } else { + // Don't use the connection pool. + // + try { + final Properties p = new Properties(); + p.setProperty("driver", driver); + p.setProperty("url", url); + if (username != null) { + p.setProperty("user", username); + } + if (password != null) { + p.setProperty("password", password); + } + return new SimpleDataSource(p); + } catch (SQLException se) { + throw new ProvisionException("Database unavailable", se); + } + } + } + + private static String optional(final Config config, final String name) { + return config.getString("database", null, name); + } + + private static String required(final Config config, final String name) { + final String v = optional(config, name); + if (v == null || "".equals(v)) { + throw new IllegalArgumentException("No database." + name + " configured"); + } + return v; + } +} |