Merge branch 'master' into gwt-2.0

* master:
  Fix reading the $site_path/etc/ssh_host_key in serialized form
  Never compress a pid file under $site_path/logs
  Clean up the DWIMery for database.* configuration settings
  Completely remove GerritServer.properties
  Fix duplicate branches showing in the Branches tab
  Refactor GitRepositoryManager to be an interface

Conflicts:
	tools/gwtui_dbg.launch
	tools/gwtui_mac.launch

Change-Id: If50811015fa24804013338fa4261fc347c2a8812
This commit is contained in:
Shawn O. Pearce 2009-12-19 20:47:44 -08:00
commit 725fed4269
26 changed files with 675 additions and 422 deletions

1
.gitignore vendored
View File

@ -1,4 +1,3 @@
/.project
/.settings/org.maven.ide.eclipse.prefs
/GerritServer.properties
/test_site

View File

@ -531,7 +531,7 @@ records about user accounts and change reviews.
----
[database]
type = postgres
type = POSTGRESQL
hostname = localhost
database = reviewdb
username = gerrit2
@ -544,17 +544,21 @@ Type of database server to connect to. If set this value will be
used to automatically create correct database.driver and database.url
values to open the connection.
+
* `Postgres` or `PostgreSQL`
* `POSTGRESQL`
+
Connect to a PostgreSQL database server.
+
* `H2`
+
Connect to a local (or remote) H2 database.
Connect to a local embedded H2 database.
+
* `MySQL`
* `MYSQL`
+
Connect to a MySQL database server.
+
* `JDBC`
+
Connect using a JDBC driver class name and URL.
+
If not specified, database.driver and database.url are used as-is,
@ -571,10 +575,10 @@ of the server named by database.type.
[[database.database]]database.database::
+
For PostgreSQL or MySQL, the name of the database on the server.
For POSTGRESQL or MYSQL, the name of the database on the server.
+
For H2, this is the path to the database, and if not absolute is
relative to `$site_path`.
relative to `'$site_path'`.
[[database.username]]database.username::
+
@ -586,13 +590,15 @@ Password to authenticate to the database server with.
[[database.driver]]database.driver::
+
Name of the JDBC driver class to connect to the database.
Setting this usually isn't necessary, set database.type instead.
Name of the JDBC driver class to connect to the database with.
Setting this usually isn't necessary as it can be derived from
database.type or database.url for any supported database.
[[database.url]]database.url::
+
JDBC style URL for the database. Setting this usually isn't
necessary, set database.type instead.
'jdbc:' URL for the database. Setting this variable usually
isn't necessary as it can be constructed from the all of the
above properties.
[[gerrit]]Section gerrit

View File

@ -82,9 +82,9 @@ Duplicate the existing `pgm_daemon` launch configuration:
* Modify the name to be unique.
* Switch to Arguments tab.
* Edit the -d flag to match the path used during 'init'. The
template launch configuration resolves to ../test_site since
that is what the documentation recommends.
* Edit the `-d` program argument flag to match the path used during
'init'. The template launch configuration resolves to ../test_site
since that is what the documentation recommends.
* Switch to Common tab.
* Change Save as to be Local file.
@ -93,12 +93,22 @@ Duplicate the existing `pgm_daemon` launch configuration:
Running Hosted Mode
~~~~~~~~~~~~~~~~~~~
* Copy
`gerrit-war/src/main/webapp/WEB-INF/extra/GerritServer.properties_example`
to
`gerrit-parent/GerritServer.properties`.
* Edit to match your database parameters.
* Run the `gwtui_any` or `gwtui_mac` launch configuration.
Duplicate the existing `gwtui_any` (or `gwtui_mac` if on Mac OS X)
launch configuration:
* Run -> Debug Configurations ...
* Java Application -> `gwtui_any`
* Right click, Duplicate
* Modify the name to be unique.
* Switch to Arguments tab.
* Edit the `-Dgerrit.site_path=` VM argument to match the path
used during 'init'. The template launch configuration resolves
to ../test_site since that is what the documentation recommends.
* Switch to Common tab.
* Change Save as to be Local file.
GERRIT

View File

@ -39,6 +39,11 @@ limitations under the License.
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.junit</artifactId>
</dependency>
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt-servlet</artifactId>

View File

@ -34,7 +34,7 @@ import com.google.gerrit.httpd.GitWebConfig;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.LocalDiskRepositoryManager;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectControl;
import com.google.inject.Inject;
@ -77,12 +77,12 @@ class GitWebServlet extends HttpServlet {
private final Set<String> deniedActions;
private final int bufferSize = 8192;
private final File gitwebCgi;
private final GitRepositoryManager repoManager;
private final LocalDiskRepositoryManager repoManager;
private final ProjectControl.Factory projectControl;
private final EnvList _env;
@Inject
GitWebServlet(final GitRepositoryManager repoManager,
GitWebServlet(final LocalDiskRepositoryManager repoManager,
final ProjectControl.Factory projectControl,
final SitePaths site, final GerritConfig gerritConfig,
final GitWebConfig gitWebConfig) throws IOException {

View File

@ -64,6 +64,7 @@ class ListBranches extends Handler<List<Branch>> {
| ProjectControl.VISIBLE);
final List<Branch> branches = new ArrayList<Branch>();
Branch headBranch = null;
final Repository db = repoManager.openRepository(projectName.get());
try {
final Map<String, Ref> all = db.getAllRefs();
@ -84,14 +85,18 @@ class ListBranches extends Handler<List<Branch>> {
}
for (final Ref ref : all.values()) {
if (Constants.HEAD.equals(ref.getOrigName())) {
final Branch b = createBranch(Constants.HEAD);
if (Constants.HEAD.equals(ref.getOrigName())
&& !ref.getOrigName().equals(ref.getName())) {
// HEAD is a symbolic reference to another branch, instead of
// showing the resolved value, show the name it references.
//
headBranch = createBranch(Constants.HEAD);
String target = ref.getName();
if (target.startsWith(Constants.R_HEADS)) {
target = target.substring(Constants.R_HEADS.length());
}
b.setRevision(new RevId(target));
branches.add(b);
headBranch.setRevision(new RevId(target));
continue;
}
if (ref.getName().startsWith(Constants.R_HEADS)) {
@ -111,6 +116,9 @@ class ListBranches extends Handler<List<Branch>> {
return a.getName().compareTo(b.getName());
}
});
if (headBranch != null) {
branches.add(0, headBranch);
}
return branches;
}

View File

@ -0,0 +1,239 @@
// 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.httpd.rpc.project;
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.classextension.EasyMock.createStrictMock;
import static org.easymock.classextension.EasyMock.replay;
import static org.easymock.classextension.EasyMock.verify;
import static org.eclipse.jgit.lib.Constants.HEAD;
import static org.eclipse.jgit.lib.Constants.R_HEADS;
import com.google.gerrit.reviewdb.Branch;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gwtorm.client.KeyUtil;
import com.google.gwtorm.server.StandardKeyEncoder;
import org.easymock.IExpectationSetters;
import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class ListBranchesTest extends LocalDiskRepositoryTestCase {
static {
KeyUtil.setEncoderImpl(new StandardKeyEncoder());
}
private ObjectId idA;
private Project.NameKey name;
private Repository realDb;
private Repository mockDb;
private ProjectControl.Factory pcf;
private ProjectControl pc;
private GitRepositoryManager grm;
@Override
protected void setUp() throws Exception {
super.setUp();
idA = ObjectId.fromString("df84c2f4f7ce7e0b25cdeac84b8870bcff319885");
name = new Project.NameKey("test");
realDb = createBareRepository();
mockDb = createStrictMock(Repository.class);
pc = createStrictMock(ProjectControl.class);
pcf = createStrictMock(ProjectControl.Factory.class);
grm = createStrictMock(GitRepositoryManager.class);
}
private IExpectationSetters<ProjectControl> validate()
throws NoSuchProjectException {
return expect(pcf.validateFor(eq(name), //
eq(ProjectControl.OWNER | ProjectControl.VISIBLE)));
}
private void doReplay() {
replay(mockDb, pc, pcf, grm);
}
private void doVerify() {
verify(mockDb, pc, pcf, grm);
}
private void set(String branch, ObjectId id) throws IOException {
final RefUpdate u = realDb.updateRef(R_HEADS + branch);
u.setForceUpdate(true);
u.setNewObjectId(id);
switch (u.update()) {
case NEW:
case FAST_FORWARD:
case FORCED:
break;
default:
fail("unexpected update failure " + branch + " " + u.getResult());
}
}
public void testProjectNotVisible() throws Exception {
final NoSuchProjectException err = new NoSuchProjectException(name);
validate().andThrow(err);
doReplay();
try {
new ListBranches(pcf, grm, name).call();
fail("did not throw when expected not authorized");
} catch (NoSuchProjectException e2) {
assertSame(err, e2);
}
doVerify();
}
private List<Branch> permitted(boolean getFullBranch)
throws NoSuchProjectException, IOException {
validate().andReturn(pc);
expect(grm.openRepository(eq(name.get()))).andReturn(mockDb);
expect(mockDb.getAllRefs()).andDelegateTo(realDb);
if (getFullBranch) {
expect(mockDb.getFullBranch()).andDelegateTo(realDb);
}
mockDb.close();
expectLastCall();
doReplay();
final List<Branch> r = new ListBranches(pcf, grm, name).call();
doVerify();
assertNotNull(r);
return r;
}
public void testEmptyProject() throws Exception {
List<Branch> r = permitted(true);
assertEquals(1, r.size());
Branch b = r.get(0);
assertNotNull(b);
assertNotNull(b.getNameKey());
assertSame(name, b.getNameKey().getParentKey());
assertEquals(HEAD, b.getNameKey().get());
assertEquals(HEAD, b.getName());
assertEquals(HEAD, b.getShortName());
assertNotNull(b.getRevision());
assertEquals("master", b.getRevision().get());
}
public void testMasterBranch() throws Exception {
set("master", idA);
List<Branch> r = permitted(false);
assertEquals(2, r.size());
Branch b = r.get(0);
assertNotNull(b);
assertNotNull(b.getNameKey());
assertSame(name, b.getNameKey().getParentKey());
assertEquals(HEAD, b.getNameKey().get());
assertEquals(HEAD, b.getName());
assertEquals(HEAD, b.getShortName());
assertNotNull(b.getRevision());
assertEquals("master", b.getRevision().get());
b = r.get(1);
assertNotNull(b);
assertNotNull(b.getNameKey());
assertSame(name, b.getNameKey().getParentKey());
assertEquals(R_HEADS + "master", b.getNameKey().get());
assertEquals(R_HEADS + "master", b.getName());
assertEquals("master", b.getShortName());
assertNotNull(b.getRevision());
assertEquals(idA.name(), b.getRevision().get());
}
public void testBranchNotHead() throws Exception {
set("foo", idA);
List<Branch> r = permitted(true);
assertEquals(2, r.size());
Branch b = r.get(0);
assertNotNull(b);
assertNotNull(b.getNameKey());
assertSame(name, b.getNameKey().getParentKey());
assertEquals(HEAD, b.getNameKey().get());
assertEquals(HEAD, b.getName());
assertEquals(HEAD, b.getShortName());
assertNotNull(b.getRevision());
assertEquals("master", b.getRevision().get());
b = r.get(1);
assertNotNull(b);
assertNotNull(b.getNameKey());
assertSame(name, b.getNameKey().getParentKey());
assertEquals(R_HEADS + "foo", b.getNameKey().get());
assertEquals(R_HEADS + "foo", b.getName());
assertEquals("foo", b.getShortName());
assertNotNull(b.getRevision());
assertEquals(idA.name(), b.getRevision().get());
}
public void testSortByName() throws Exception {
Map<String, Ref> u = new LinkedHashMap<String, Ref>();
u.put("foo", new Ref(Ref.Storage.LOOSE, R_HEADS + "foo", idA));
u.put("bar", new Ref(Ref.Storage.LOOSE, R_HEADS + "bar", idA));
u.put(HEAD, new Ref(Ref.Storage.LOOSE, HEAD, R_HEADS + "master", null));
validate().andReturn(pc);
expect(grm.openRepository(eq(name.get()))).andReturn(mockDb);
expect(mockDb.getAllRefs()).andReturn(u);
mockDb.close();
expectLastCall();
doReplay();
final List<Branch> r = new ListBranches(pcf, grm, name).call();
doVerify();
assertNotNull(r);
assertEquals(3, r.size());
assertEquals(HEAD, r.get(0).getShortName());
assertEquals("bar", r.get(1).getShortName());
assertEquals("foo", r.get(2).getShortName());
}
}

View File

@ -38,11 +38,6 @@ limitations under the License.
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>

View File

@ -14,7 +14,7 @@
package com.google.gerrit.pgm;
import static com.google.gerrit.pgm.util.DataSourceProvider.Context.MULTI_USER;
import static com.google.gerrit.server.schema.DataSourceProvider.Context.MULTI_USER;
import com.google.gerrit.httpd.HttpCanonicalWebUrlProvider;
import com.google.gerrit.httpd.WebModule;

View File

@ -14,7 +14,7 @@
package com.google.gerrit.pgm;
import static com.google.gerrit.pgm.util.DataSourceProvider.Context.SINGLE_USER;
import static com.google.gerrit.server.schema.DataSourceProvider.Context.SINGLE_USER;
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.gerrit.pgm.util.RuntimeShutdown;

View File

@ -14,7 +14,7 @@
package com.google.gerrit.pgm;
import static com.google.gerrit.pgm.util.DataSourceProvider.Context.SINGLE_USER;
import static com.google.gerrit.server.schema.DataSourceProvider.Context.SINGLE_USER;
import static com.google.inject.Stage.PRODUCTION;
import com.google.gerrit.common.PageLinks;
@ -30,6 +30,7 @@ import com.google.gerrit.pgm.util.IoUtil;
import com.google.gerrit.pgm.util.SiteProgram;
import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.git.LocalDiskRepositoryManager;
import com.google.gerrit.server.git.GitProjectImporter;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.schema.SchemaUpdater;
@ -176,13 +177,17 @@ public class Init extends SiteProgram {
void importGit() throws OrmException, IOException {
if (flags.importProjects) {
System.err.println("Scanning " + repositoryManager.getBasePath());
gitProjectImporter.run(new GitProjectImporter.Messages() {
@Override
public void warning(String msg) {
public void info(String msg) {
System.err.println(msg);
System.err.flush();
}
@Override
public void warning(String msg) {
info(msg);
}
});
}
}
@ -247,7 +252,7 @@ public class Init extends SiteProgram {
bind(ConsoleUI.class).toInstance(init.ui);
bind(InitFlags.class).toInstance(init.flags);
bind(GitRepositoryManager.class);
bind(GitRepositoryManager.class).to(LocalDiskRepositoryManager.class);
bind(GitProjectImporter.class);
}
});

View File

@ -16,11 +16,11 @@ package com.google.gerrit.pgm.init;
import static com.google.gerrit.pgm.init.InitUtil.die;
import static com.google.gerrit.pgm.init.InitUtil.username;
import static com.google.gerrit.pgm.util.DataSourceProvider.Type.H2;
import static com.google.gerrit.server.schema.DataSourceProvider.Type.H2;
import com.google.gerrit.pgm.util.ConsoleUI;
import com.google.gerrit.pgm.util.DataSourceProvider;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.schema.DataSourceProvider;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@ -82,7 +82,6 @@ class InitDatabase implements InitStep {
break;
}
case POSTGRES:
case POSTGRESQL:
case MYSQL: {
userPassAuth = true;

View File

@ -19,8 +19,8 @@ import static com.google.gerrit.pgm.init.InitUtil.savePublic;
import static com.google.gerrit.pgm.init.InitUtil.saveSecure;
import com.google.gerrit.pgm.util.ConsoleUI;
import com.google.gerrit.pgm.util.DataSourceProvider;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.schema.DataSourceProvider;
import com.google.gerrit.server.util.SocketUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;

View File

@ -96,7 +96,8 @@ public class LogFileCompressor implements Runnable {
private boolean isLive(final File entry) {
final String name = entry.getName();
return ErrorLogFile.LOG_NAME.equals(name);
return ErrorLogFile.LOG_NAME.equals(name) //
|| name.endsWith(".pid");
}
private boolean isCompressed(final File entry) {

View File

@ -21,6 +21,7 @@ import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.server.config.DatabaseModule;
import com.google.gerrit.server.config.GerritServerConfigModule;
import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.schema.DataSourceProvider;
import com.google.gwtorm.client.OrmException;
import com.google.inject.AbstractModule;
import com.google.inject.CreationException;

View File

@ -48,6 +48,11 @@ limitations under the License.
<artifactId>ehcache-core</artifactId>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
</dependency>
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>

View File

@ -37,6 +37,7 @@ import com.google.gerrit.server.account.Realm;
import com.google.gerrit.server.auth.ldap.LdapModule;
import com.google.gerrit.server.cache.CachePool;
import com.google.gerrit.server.git.ChangeMergeQueue;
import com.google.gerrit.server.git.LocalDiskRepositoryManager;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MergeOp;
import com.google.gerrit.server.git.MergeQueue;
@ -111,7 +112,7 @@ public class GerritGlobalModule extends FactoryModule {
factory(AccountInfoCacheFactory.Factory.class);
factory(ProjectState.Factory.class);
bind(GitRepositoryManager.class);
bind(GitRepositoryManager.class).to(LocalDiskRepositoryManager.class);
bind(FileTypeRegistry.class).to(MimeUtilFileTypeRegistry.class);
bind(WorkQueue.class);
@ -141,7 +142,7 @@ public class GerritGlobalModule extends FactoryModule {
install(new LifecycleModule() {
@Override
protected void configure() {
listener().to(GitRepositoryManager.Lifecycle.class);
listener().to(LocalDiskRepositoryManager.Lifecycle.class);
listener().to(CachePool.Lifecycle.class);
listener().to(WorkQueue.Lifecycle.class);
}

View File

@ -32,15 +32,16 @@ import java.util.Set;
/** Imports all projects found within the repository manager. */
public class GitProjectImporter {
public interface Messages {
void info(String msg);
void warning(String msg);
}
private final GitRepositoryManager repositoryManager;
private final LocalDiskRepositoryManager repositoryManager;
private final SchemaFactory<ReviewDb> schema;
private Messages messages;
@Inject
GitProjectImporter(final GitRepositoryManager repositoryManager,
GitProjectImporter(final LocalDiskRepositoryManager repositoryManager,
final SchemaFactory<ReviewDb> schema) {
this.repositoryManager = repositoryManager;
this.schema = schema;
@ -48,6 +49,7 @@ public class GitProjectImporter {
public void run(final Messages msg) throws OrmException, IOException {
messages = msg;
messages.info("Scanning " + repositoryManager.getBasePath());
final ReviewDb db = schema.open();
try {
final HashSet<String> have = new HashSet<String>();

View File

@ -1,4 +1,4 @@
// Copyright (C) 2008 The Android Open Source Project
// 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.
@ -14,75 +14,22 @@
package com.google.gerrit.server.git;
import com.google.gerrit.lifecycle.LifecycleListener;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.LockFile;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryCache;
import org.eclipse.jgit.lib.WindowCache;
import org.eclipse.jgit.lib.WindowCacheConfig;
import org.eclipse.jgit.lib.RepositoryCache.FileKey;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
/** Class managing Git repositories. */
@Singleton
public class GitRepositoryManager {
private static final Logger log =
LoggerFactory.getLogger(GitRepositoryManager.class);
private static final String UNNAMED =
"Unnamed repository; edit this file to name it for gitweb.";
public static class Lifecycle implements LifecycleListener {
private final Config cfg;
@Inject
Lifecycle(@GerritServerConfig final Config cfg) {
this.cfg = cfg;
}
@Override
public void start() {
final WindowCacheConfig c = new WindowCacheConfig();
c.fromConfig(cfg);
WindowCache.reconfigure(c);
}
@Override
public void stop() {
}
}
private final File basePath;
@Inject
GitRepositoryManager(final SitePaths site,
@GerritServerConfig final Config cfg) {
basePath = site.resolve(cfg.getString("gerrit", null, "basePath"));
if (basePath == null) {
throw new IllegalStateException("gerrit.basePath must be configured");
}
}
/** @return base directory under which all projects are stored. */
public File getBasePath() {
return basePath;
}
/**
* Manages Git repositories for the Gerrit server process.
* <p>
* Implementations of this interface should be a {@link Singleton} and
* registered in Guice so they are globally available within the server
* environment.
*/
public interface GitRepositoryManager {
/**
* Get (or open) a repository by name.
*
@ -92,22 +39,8 @@ public class GitRepositoryManager {
* @throws RepositoryNotFoundException the name does not denote an existing
* repository, or the name cannot be read as a repository.
*/
public Repository openRepository(String name)
throws RepositoryNotFoundException {
if (isUnreasonableName(name)) {
throw new RepositoryNotFoundException("Invalid name: " + name);
}
try {
final FileKey loc = FileKey.lenient(new File(basePath, name));
return RepositoryCache.open(loc);
} catch (IOException e1) {
final RepositoryNotFoundException e2;
e2 = new RepositoryNotFoundException("Cannot open repository " + name);
e2.initCause(e1);
throw e2;
}
}
public abstract Repository openRepository(String name)
throws RepositoryNotFoundException;
/**
* Create (and open) a repository by name.
@ -118,25 +51,8 @@ public class GitRepositoryManager {
* @throws RepositoryNotFoundException the name does not denote an existing
* repository, or the name cannot be read as a repository.
*/
public Repository createRepository(String name)
throws RepositoryNotFoundException {
if (isUnreasonableName(name)) {
throw new RepositoryNotFoundException("Invalid name: " + name);
}
try {
if (!name.endsWith(".git")) {
name = name + ".git";
}
final FileKey loc = FileKey.exact(new File(basePath, name));
return RepositoryCache.open(loc, false);
} catch (IOException e1) {
final RepositoryNotFoundException e2;
e2 = new RepositoryNotFoundException("Cannot open repository " + name);
e2.initCause(e1);
throw e2;
}
}
public abstract Repository createRepository(String name)
throws RepositoryNotFoundException;
/**
* Read the {@code GIT_DIR/description} file for gitweb.
@ -150,29 +66,8 @@ public class GitRepositoryManager {
* @throws IOException the description file exists, but is not readable by
* this process.
*/
public String getProjectDescription(final String name)
throws RepositoryNotFoundException, IOException {
final Repository e = openRepository(name);
final File d = new File(e.getDirectory(), "description");
String description;
try {
description = RawParseUtils.decode(IO.readFully(d));
} catch (FileNotFoundException err) {
return null;
}
if (description != null) {
description = description.trim();
if (description.isEmpty()) {
description = null;
}
if (UNNAMED.equals(description)) {
description = null;
}
}
return description;
}
public abstract String getProjectDescription(final String name)
throws RepositoryNotFoundException, IOException;
/**
* Set the {@code GIT_DIR/description} file for gitweb.
@ -183,48 +78,6 @@ public class GitRepositoryManager {
* @param name the repository name, relative to the base directory.
* @param description new description text for the repository.
*/
public void setProjectDescription(final String name, final String description) {
// Update git's description file, in case gitweb is being used
//
try {
final Repository e;
final LockFile f;
e = openRepository(name);
f = new LockFile(new File(e.getDirectory(), "description"));
if (f.lock()) {
String d = description;
if (d != null) {
d = d.trim();
if (d.length() > 0) {
d += "\n";
}
} else {
d = "";
}
f.write(Constants.encode(d));
f.commit();
}
e.close();
} catch (RepositoryNotFoundException e) {
log.error("Cannot update description for " + name, e);
} catch (IOException e) {
log.error("Cannot update description for " + name, e);
}
}
private boolean isUnreasonableName(final String name) {
if (name.length() == 0) return true; // no empty paths
if (name.indexOf('\\') >= 0) return true; // no windows/dos stlye paths
if (name.charAt(0) == '/') return true; // no absolute paths
if (new File(name).isAbsolute()) return true; // no absolute paths
if (name.startsWith("../")) return true; // no "l../etc/passwd"
if (name.contains("/../")) return true; // no "foo/../etc/passwd"
if (name.contains("/./")) return true; // "foo/./foo" is insane to ask
if (name.contains("//")) return true; // windows UNC path can be "//..."
return false; // is a reasonable name
}
public abstract void setProjectDescription(final String name,
final String description);
}

View File

@ -0,0 +1,191 @@
// Copyright (C) 2008 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.git;
import com.google.gerrit.lifecycle.LifecycleListener;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.LockFile;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryCache;
import org.eclipse.jgit.lib.WindowCache;
import org.eclipse.jgit.lib.WindowCacheConfig;
import org.eclipse.jgit.lib.RepositoryCache.FileKey;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
/** Manages Git repositories stored on the local filesystem. */
@Singleton
public class LocalDiskRepositoryManager implements GitRepositoryManager {
private static final Logger log =
LoggerFactory.getLogger(LocalDiskRepositoryManager.class);
private static final String UNNAMED =
"Unnamed repository; edit this file to name it for gitweb.";
public static class Lifecycle implements LifecycleListener {
private final Config cfg;
@Inject
Lifecycle(@GerritServerConfig final Config cfg) {
this.cfg = cfg;
}
@Override
public void start() {
final WindowCacheConfig c = new WindowCacheConfig();
c.fromConfig(cfg);
WindowCache.reconfigure(c);
}
@Override
public void stop() {
}
}
private final File basePath;
@Inject
LocalDiskRepositoryManager(final SitePaths site,
@GerritServerConfig final Config cfg) {
basePath = site.resolve(cfg.getString("gerrit", null, "basePath"));
if (basePath == null) {
throw new IllegalStateException("gerrit.basePath must be configured");
}
}
/** @return base directory under which all projects are stored. */
public File getBasePath() {
return basePath;
}
public Repository openRepository(String name)
throws RepositoryNotFoundException {
if (isUnreasonableName(name)) {
throw new RepositoryNotFoundException("Invalid name: " + name);
}
try {
final FileKey loc = FileKey.lenient(new File(basePath, name));
return RepositoryCache.open(loc);
} catch (IOException e1) {
final RepositoryNotFoundException e2;
e2 = new RepositoryNotFoundException("Cannot open repository " + name);
e2.initCause(e1);
throw e2;
}
}
public Repository createRepository(String name)
throws RepositoryNotFoundException {
if (isUnreasonableName(name)) {
throw new RepositoryNotFoundException("Invalid name: " + name);
}
try {
if (!name.endsWith(".git")) {
name = name + ".git";
}
final FileKey loc = FileKey.exact(new File(basePath, name));
return RepositoryCache.open(loc, false);
} catch (IOException e1) {
final RepositoryNotFoundException e2;
e2 = new RepositoryNotFoundException("Cannot open repository " + name);
e2.initCause(e1);
throw e2;
}
}
public String getProjectDescription(final String name)
throws RepositoryNotFoundException, IOException {
final Repository e = openRepository(name);
final File d = new File(e.getDirectory(), "description");
String description;
try {
description = RawParseUtils.decode(IO.readFully(d));
} catch (FileNotFoundException err) {
return null;
}
if (description != null) {
description = description.trim();
if (description.isEmpty()) {
description = null;
}
if (UNNAMED.equals(description)) {
description = null;
}
}
return description;
}
public void setProjectDescription(final String name, final String description) {
// Update git's description file, in case gitweb is being used
//
try {
final Repository e;
final LockFile f;
e = openRepository(name);
f = new LockFile(new File(e.getDirectory(), "description"));
if (f.lock()) {
String d = description;
if (d != null) {
d = d.trim();
if (d.length() > 0) {
d += "\n";
}
} else {
d = "";
}
f.write(Constants.encode(d));
f.commit();
}
e.close();
} catch (RepositoryNotFoundException e) {
log.error("Cannot update description for " + name, e);
} catch (IOException e) {
log.error("Cannot update description for " + name, e);
}
}
private boolean isUnreasonableName(final String name) {
if (name.length() == 0) return true; // no empty paths
if (name.indexOf('\\') >= 0) return true; // no windows/dos stlye paths
if (name.charAt(0) == '/') return true; // no absolute paths
if (new File(name).isAbsolute()) return true; // no absolute paths
if (name.startsWith("../")) return true; // no "l../etc/passwd"
if (name.contains("/../")) return true; // no "foo/../etc/passwd"
if (name.contains("/./")) return true; // "foo/./foo" is insane to ask
if (name.contains("//")) return true; // windows UNC path can be "//..."
return false; // is a reasonable name
}
}

View File

@ -12,10 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.pgm.util;
package com.google.gerrit.server.schema;
import static com.google.gerrit.server.config.ConfigUtil.getEnum;
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;
@ -71,132 +72,87 @@ public final class DataSourceProvider implements Provider<DataSource>,
}
public static enum Type {
DEFAULT, JDBC, POSTGRES, POSTGRESQL, H2, MYSQL;
H2, POSTGRESQL, MYSQL, JDBC;
}
private DataSource open(final SitePaths site, final Config cfg,
final Context context) {
Type type = ConfigUtil.getEnum(cfg, "database", null, "type", Type.DEFAULT);
Type type = getEnum(cfg, "database", null, "type", Type.values(), null);
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:")) {
if (url == null || url.isEmpty()) {
if (type == null) {
if (url != null && !url.isEmpty()) {
type = Type.JDBC;
} else {
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) {
switch (type) {
case H2: {
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();
url = "jdbc:h2:" + db.toURI().toString();
break;
}
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) {
case POSTGRESQL: {
final StringBuilder b = new StringBuilder();
b.append(pfx);
b.append(hostname);
if (port != null && !port.isEmpty()) {
b.append(":");
b.append(port);
}
b.append("jdbc:postgresql://");
b.append(hostname(optional(cfg, "hostname")));
b.append(port(optional(cfg, "port")));
b.append("/");
b.append(required(cfg, "database"));
url = b.toString();
break;
}
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");
case MYSQL: {
final StringBuilder b = new StringBuilder();
b.append("jdbc:mysql://");
b.append(hostname(optional(cfg, "hostname")));
b.append(port(optional(cfg, "port")));
b.append("/");
b.append(required(cfg, "database"));
url = b.toString();
break;
}
break;
case JDBC:
driver = required(cfg, "driver");
url = required(cfg, "url");
break;
default:
throw new IllegalArgumentException(type + " not supported");
}
}
if (driver == null || driver.isEmpty()) {
if (url.startsWith("jdbc:h2:")) {
driver = "org.h2.Driver";
} else if (url.startsWith("jdbc:postgresql:")) {
driver = "org.postgresql.Driver";
} else if (url.startsWith("jdbc:mysql:")) {
driver = "com.mysql.jdbc.Driver";
} else {
throw new IllegalArgumentException("database.driver must be set");
}
}
boolean usePool;
@ -252,6 +208,23 @@ public final class DataSourceProvider implements Provider<DataSource>,
}
}
private static String hostname(String hostname) {
if (hostname == null || hostname.isEmpty()) {
hostname = "localhost";
} else if (hostname.contains(":") && !hostname.startsWith("[")) {
hostname = "[" + hostname + "]";
}
return hostname;
}
private static String port(String port) {
if (port != null && !port.isEmpty()) {
return ":" + port;
}
return "";
}
private static String optional(final Config config, final String name) {
return config.getString("database", null, name);
}

View File

@ -22,6 +22,7 @@ import com.google.inject.ProvisionException;
import org.apache.sshd.common.KeyPairProvider;
import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
import org.apache.sshd.common.util.SecurityUtils;
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
import java.io.File;
import java.util.ArrayList;
@ -37,33 +38,42 @@ class HostKeyProvider implements Provider<KeyPairProvider> {
@Override
public KeyPairProvider get() {
final File anyKey = site.ssh_key;
final File objKey = site.ssh_key;
final File rsaKey = site.ssh_rsa;
final File dsaKey = site.ssh_dsa;
final List<String> keys = new ArrayList<String>(2);
final List<String> stdKeys = new ArrayList<String>(2);
if (rsaKey.exists()) {
keys.add(rsaKey.getAbsolutePath());
stdKeys.add(rsaKey.getAbsolutePath());
}
if (dsaKey.exists()) {
keys.add(dsaKey.getAbsolutePath());
stdKeys.add(dsaKey.getAbsolutePath());
}
if (anyKey.exists() && !keys.isEmpty()) {
// If both formats of host key exist, we don't know which format
// should be authoritative. Complain and abort.
//
keys.add(anyKey.getAbsolutePath());
throw new ProvisionException("Multiple host keys exist: " + keys);
}
if (objKey.exists()) {
if (stdKeys.isEmpty()) {
SimpleGeneratorHostKeyProvider p = new SimpleGeneratorHostKeyProvider();
p.setPath(objKey.getAbsolutePath());
return p;
if (keys.isEmpty()) {
throw new ProvisionException("No SSH keys under " + site.etc_dir);
} else {
// Both formats of host key exist, we don't know which format
// should be authoritative. Complain and abort.
//
stdKeys.add(objKey.getAbsolutePath());
throw new ProvisionException("Multiple host keys exist: " + stdKeys);
}
} else {
if (stdKeys.isEmpty()) {
throw new ProvisionException("No SSH keys under " + site.etc_dir);
}
if (!SecurityUtils.isBouncyCastleRegistered()) {
throw new ProvisionException("Bouncy Castle Crypto not installed;"
+ " needed to read server host keys: " + stdKeys + "");
}
return new FileKeyPairProvider(stdKeys
.toArray(new String[stdKeys.size()]));
}
if (!SecurityUtils.isBouncyCastleRegistered()) {
throw new ProvisionException("Bouncy Castle Crypto not installed;"
+ " needed to read server host keys: " + keys + "");
}
return new FileKeyPairProvider(keys.toArray(new String[keys.size()]));
}
}

View File

@ -15,18 +15,10 @@
package com.google.gerrit.httpd;
import com.google.gerrit.lifecycle.LifecycleListener;
import com.google.gwtorm.jdbc.SimpleDataSource;
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
import com.google.inject.Singleton;
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;
@ -61,49 +53,10 @@ final class ReviewDbDataSourceProvider implements Provider<DataSource>,
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);
}
throw new ProvisionException("No DataSource " + dsName, namingErr);
}
}
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;
}
private void closeDataSource(final DataSource ds) {
try {
Class<?> type = Class.forName("org.apache.commons.dbcp.BasicDataSource");

View File

@ -27,6 +27,7 @@ import com.google.gerrit.server.config.GerritServerConfigModule;
import com.google.gerrit.server.config.MasterNodeStartup;
import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.config.SitePathFromSystemConfigProvider;
import com.google.gerrit.server.schema.DataSourceProvider;
import com.google.gerrit.sshd.SshModule;
import com.google.gerrit.sshd.commands.MasterCommandModule;
import com.google.inject.AbstractModule;
@ -57,6 +58,7 @@ public class WebAppInitializer extends GuiceServletContextListener {
private static final Logger log =
LoggerFactory.getLogger(WebAppInitializer.class);
private File sitePath;
private Injector dbInjector;
private Injector cfgInjector;
private Injector sysInjector;
@ -66,6 +68,11 @@ public class WebAppInitializer extends GuiceServletContextListener {
private synchronized void init() {
if (manager == null) {
final String path = System.getProperty("gerrit.site_path");
if (path != null) {
sitePath = new File(path);
}
try {
dbInjector = createDbInjector();
} catch (CreationException ce) {
@ -115,28 +122,50 @@ public class WebAppInitializer extends GuiceServletContextListener {
private Injector createDbInjector() {
final List<Module> modules = new ArrayList<Module>();
modules.add(new LifecycleModule() {
@Override
protected void configure() {
bind(Key.get(DataSource.class, Names.named("ReviewDb"))).toProvider(
ReviewDbDataSourceProvider.class).in(SINGLETON);
listener().to(ReviewDbDataSourceProvider.class);
}
});
if (sitePath != null) {
modules.add(new LifecycleModule() {
@Override
protected void configure() {
bind(File.class).annotatedWith(SitePath.class).toInstance(sitePath);
bind(DataSourceProvider.Context.class).toInstance(
DataSourceProvider.Context.MULTI_USER);
bind(Key.get(DataSource.class, Names.named("ReviewDb"))).toProvider(
DataSourceProvider.class).in(SINGLETON);
listener().to(DataSourceProvider.class);
}
});
modules.add(new GerritServerConfigModule());
} else {
modules.add(new LifecycleModule() {
@Override
protected void configure() {
bind(Key.get(DataSource.class, Names.named("ReviewDb"))).toProvider(
ReviewDbDataSourceProvider.class).in(SINGLETON);
listener().to(ReviewDbDataSourceProvider.class);
}
});
}
modules.add(new DatabaseModule());
return Guice.createInjector(PRODUCTION, modules);
}
private Injector createCfgInjector() {
final List<Module> modules = new ArrayList<Module>();
modules.add(new AbstractModule() {
@Override
protected void configure() {
bind(File.class).annotatedWith(SitePath.class).toProvider(
SitePathFromSystemConfigProvider.class).in(SINGLETON);
}
});
modules.add(new GerritServerConfigModule());
if (sitePath == null) {
// If we didn't get the site path from the system property
// we need to get it from the database, as that's our old
// method of locating the site path on disk.
//
modules.add(new AbstractModule() {
@Override
protected void configure() {
bind(File.class).annotatedWith(SitePath.class).toProvider(
SitePathFromSystemConfigProvider.class).in(SINGLETON);
}
});
modules.add(new GerritServerConfigModule());
}
modules.add(new AuthConfigModule());
return dbInjector.createChildInjector(modules);
}

View File

@ -1,32 +0,0 @@
# Any properties starting with "database." will be fed to the JDBC driver,
# after removing the "database." prefix.
#
# The following "special" properties are removed from that set:
#
# database.driver : Class name of the driver to load.
# database.url : The connection URL
#
# H2
# (driver included)
#
# database.driver = org.h2.Driver
# database.url = jdbc:h2:file:ReviewDb
# PostgreSQL
# (driver included)
#
# database.driver = org.postgresql.Driver
# database.url = jdbc:postgresql:reviewdb
# database.user = gerrit2
# database.password = supersecretcode
# MySQL 5.0
# curl -O http://repo1.maven.org/maven2/mysql/mysql-connector-java/5.0.8/mysql-connector-java-5.0.8.jar
#
# database.classpath = mysql-connector-java-5.0.8.jar
# database.driver = com.mysql.jdbc.Driver
# database.url = jdbc:mysql://localhost/reviewdb?user=gerrit2&password=secretcode

View File

@ -31,5 +31,5 @@
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-startupUrl /&#10;-war ${resource_loc:/gerrit-gwtui/target}/gwt-hosted-mode&#10;-server com.google.gerrit.gwtdebug.GerritDebugLauncher&#10;com.google.gerrit.GerritGwtUI"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="gerrit-gwtdbug"/>
<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.maven.ide.eclipse.launchconfig.sourcepathProvider"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx256M&#10;-DGerritServer=${resource_loc:/gerrit-parent/GerritServer.properties}"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx256M&#10;&#10;-Dgerrit.site_path=${resource_loc:/gerrit-parent}/../test_site"/>
</launchConfiguration>