Get rid of the GerritServer static singleton

Instead we obtain it through Guice only, letting the Injector manage
providing it as required.

Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce
2009-07-28 11:54:19 -07:00
parent 20fe732921
commit 5fbee29514
7 changed files with 174 additions and 120 deletions

View File

@@ -39,7 +39,7 @@ import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.client.SchemaFactory;
import com.google.gwtorm.client.Transaction;
import com.google.gwtorm.jdbc.Database;
import com.google.gwtorm.jdbc.SimpleDataSource;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import net.sf.ehcache.Cache;
@@ -68,46 +68,22 @@ import org.spearce.jgit.lib.WindowCacheConfig;
import org.spearce.jgit.lib.RepositoryCache.FileKey;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.http.HttpServletRequest;
import javax.sql.DataSource;
/** Global server-side state for Gerrit. */
@Singleton
public class GerritServer {
private static final Logger log = LoggerFactory.getLogger(GerritServer.class);
private static DataSource datasource;
private static GerritServer impl;
private static CacheManager cacheMgr;
static void closeDataSource() {
if (datasource != null) {
try {
try {
Class.forName("com.mchange.v2.c3p0.DataSources").getMethod("destroy",
DataSource.class).invoke(null, datasource);
} catch (Throwable bad) {
// Oh well, its not a c3p0 pooled connection. Too bad its
// not standardized how "good applications cleanup".
}
} finally {
datasource = null;
}
}
static void closeCacheManager() {
if (cacheMgr != null) {
try {
cacheMgr.shutdown();
@@ -118,35 +94,6 @@ public class GerritServer {
}
}
/**
* Obtain the singleton server instance for this web application.
*
* @return the server instance. Never null.
* @throws OrmException the database could not be configured. There is
* something wrong with the schema configuration in {@link ReviewDb}
* that must be addressed by a developer.
* @throws XsrfException the XSRF support could not be correctly configured to
* protect the application against cross-site request forgery. The JVM
* is most likely lacking critical security algorithms.
*/
public static synchronized GerritServer getInstance() throws OrmException,
XsrfException {
if (impl == null) {
try {
impl = new GerritServer();
} catch (OrmException e) {
closeDataSource();
log.error("GerritServer ORM is unavailable", e);
throw e;
} catch (XsrfException e) {
closeDataSource();
log.error("GerritServer XSRF support failed to initailize", e);
throw e;
}
}
return impl;
}
public static String serverUrl(final HttpServletRequest req) {
// Assume this servlet is in the context with a simple name like "login"
// and we were accessed without any path info. Clipping the last part of
@@ -177,8 +124,10 @@ public class GerritServer {
private final SelfPopulatingCache diffCache;
private final SelfPopulatingCache sshKeysCache;
private GerritServer() throws OrmException, XsrfException {
db = createDatabase();
@Inject
GerritServer(final Database<ReviewDb> database)
throws OrmException, XsrfException {
db = database;
loadSystemConfig();
if (sConfig == null) {
throw new OrmException("No " + SystemConfig.class.getName() + " found");
@@ -353,54 +302,6 @@ public class GerritServer {
return r;
}
public static Database<ReviewDb> createDatabase() throws OrmException {
final String dsName = "java:comp/env/jdbc/ReviewDb";
try {
datasource = (DataSource) new InitialContext().lookup(dsName);
} catch (NamingException namingErr) {
final Properties p = readGerritDataSource();
if (p == null) {
throw new OrmException("Initialization error:\n" + " * No DataSource "
+ dsName + "\n" + " * No -DGerritServer=GerritServer.properties"
+ " on Java command line", namingErr);
}
try {
datasource = new SimpleDataSource(p);
} catch (SQLException se) {
throw new OrmException("Database unavailable", se);
}
}
return new Database<ReviewDb>(datasource, ReviewDb.class);
}
private static Properties readGerritDataSource() throws OrmException {
final Properties srvprop = new Properties();
String name = System.getProperty("GerritServer");
if (name == null) {
name = "GerritServer.properties";
}
try {
final InputStream in = new FileInputStream(name);
try {
srvprop.load(in);
} finally {
in.close();
}
} catch (IOException e) {
throw new OrmException("Cannot read " + name, e);
}
final Properties dbprop = new Properties();
for (final Map.Entry<Object, Object> e : srvprop.entrySet()) {
final String key = (String) e.getKey();
if (key.startsWith("database.")) {
dbprop.put(key.substring("database.".length()), e.getValue());
}
}
return dbprop;
}
private void initSystemConfig(final ReviewDb c) throws OrmException {
final AccountGroup admin =
new AccountGroup(new AccountGroup.NameKey("Administrators"),

View File

@@ -14,28 +14,34 @@
package com.google.gerrit.server;
import static com.google.inject.Scopes.SINGLETON;
import com.google.gerrit.client.data.GerritConfig;
import com.google.gerrit.client.reviewdb.ReviewDb;
import com.google.gerrit.git.ChangeMergeQueue;
import com.google.gerrit.git.MergeQueue;
import com.google.gerrit.git.PushReplication;
import com.google.gerrit.git.ReplicationQueue;
import com.google.gwtjsonrpc.server.XsrfException;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.jdbc.Database;
import com.google.inject.AbstractModule;
import static com.google.inject.Scopes.SINGLETON;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Names;
import javax.sql.DataSource;
/** Starts {@link GerritServer} with standard dependencies. */
public class GerritServerModule extends AbstractModule {
public static final Key<DataSource> DS =
Key.get(DataSource.class, Names.named("ReviewDb"));
@Override
protected void configure() {
try {
bind(GerritServer.class).toInstance(GerritServer.getInstance());
} catch (OrmException e) {
addError(e);
} catch (XsrfException e) {
addError(e);
}
bind(DS).toProvider(ReviewDbDataSourceProvider.class).in(SINGLETON);
bind(new TypeLiteral<Database<ReviewDb>>() {}).toProvider(
ReviewDbProvider.class).in(SINGLETON);
bind(GerritServer.class);
bind(ContactStore.class).toProvider(EncryptedContactStoreProvider.class);
bind(FileTypeRegistry.class).to(MimeUtilFileTypeRegistry.class);
bind(ReplicationQueue.class).to(PushReplication.class).in(SINGLETON);

View File

@@ -50,6 +50,7 @@ import java.util.HashSet;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletContextEvent;
import javax.sql.DataSource;
/** Configures the web application environment for Gerrit Code Review. */
public class GerritServletConfig extends GuiceServletContextListener {
@@ -227,15 +228,26 @@ public class GerritServletConfig extends GuiceServletContextListener {
// Assume it never started.
}
WorkQueue.terminate();
GerritServer.closeCacheManager();
try {
final GerritServer gs = injector.getInstance(GerritServer.class);
gs.closeDataSource();
final DataSource ds = injector.getInstance(GerritServerModule.DS);
try {
final Class<?> type = Class.forName("com.mchange.v2.c3p0.DataSources");
if (type.isInstance(ds)) {
type.getMethod("destroy", DataSource.class).invoke(null, ds);
}
} catch (Throwable bad) {
// Oh well, its not a c3p0 pooled connection. Too bad its
// not standardized how "good applications cleanup".
}
} catch (ConfigurationException ce) {
// Assume it never started.
} catch (ProviderException ce) {
// Assume it never started.
}
WorkQueue.terminate();
super.contextDestroyed(event);
}
}

View File

@@ -0,0 +1,82 @@
// 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;
import com.google.gwtorm.jdbc.SimpleDataSource;
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.Map;
import java.util.Properties;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
/** Provides access to the {@code ReviewDb} DataSource. */
final class ReviewDbDataSourceProvider implements Provider<DataSource> {
@Override
public DataSource get() {
final String dsName = "java:comp/env/jdbc/ReviewDb";
try {
return (DataSource) new InitialContext().lookup(dsName);
} catch (NamingException namingErr) {
final Properties p = readGerritDataSource();
if (p == null) {
throw new ProvisionException("Initialization error:\n"
+ " * No DataSource " + dsName + "\n"
+ " * No -DGerritServer=GerritServer.properties"
+ " on Java command line", namingErr);
}
try {
return new SimpleDataSource(p);
} catch (SQLException se) {
throw new ProvisionException("Database unavailable", se);
}
}
}
private static Properties readGerritDataSource() throws ProvisionException {
final Properties srvprop = new Properties();
String name = System.getProperty("GerritServer");
if (name == null) {
name = "GerritServer.properties";
}
try {
final InputStream in = new FileInputStream(name);
try {
srvprop.load(in);
} finally {
in.close();
}
} catch (IOException e) {
throw new ProvisionException("Cannot read " + name, e);
}
final Properties dbprop = new Properties();
for (final Map.Entry<Object, Object> e : srvprop.entrySet()) {
final String key = (String) e.getKey();
if (key.startsWith("database.")) {
dbprop.put(key.substring("database.".length()), e.getValue());
}
}
return dbprop;
}
}

View File

@@ -0,0 +1,44 @@
// 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;
import com.google.gerrit.client.reviewdb.ReviewDb;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.jdbc.Database;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
import com.google.inject.name.Named;
import javax.sql.DataSource;
/** Provides the {@link ReviewDb} database handle. */
final class ReviewDbProvider implements Provider<Database<ReviewDb>> {
private final DataSource datasource;
@Inject
ReviewDbProvider(@Named("ReviewDb") final DataSource ds) {
datasource = ds;
}
@Override
public Database<ReviewDb> get() {
try {
return new Database<ReviewDb>(datasource, ReviewDb.class);
} catch (OrmException e) {
throw new ProvisionException("Cannot create ReviewDb", e);
}
}
}