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:
@@ -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"),
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
44
src/main/java/com/google/gerrit/server/ReviewDbProvider.java
Normal file
44
src/main/java/com/google/gerrit/server/ReviewDbProvider.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user