summaryrefslogtreecommitdiffstats
path: root/gerrit-server/src/main/java/com/google/gerrit/server/schema/DataSourceProvider.java
diff options
context:
space:
mode:
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.java266
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;
+ }
+}