Move "projects" table into Git

Project settings are now saved in the project.config file of the
refs/meta/config branch within each Git repository.  This offers
us free version control over the lifespan of the project, and will
help reduce schema version issues as the configuration file is
more free-form.

Project owners can edit the configuration of their project by
hand and push the results back up, possibly even going through
code review, if the proper access rules are assigned in the
project's access panel.  Project users can inspect the history of
the configuration by reading the history of the refs/meta/config
branch with their favorite history browser.

Change-Id: Id63414d86dbfb9033021f76e1d5e782373525a77
Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce 2011-01-10 11:11:55 -08:00
parent 3b0ecf4984
commit a56d834d2b
35 changed files with 1218 additions and 271 deletions

View File

@ -13,7 +13,6 @@ SYNOPSIS
[--branch <REF>] \
[\--owner <GROUP> ...] \
[\--parent <NAME>] \
[\--permissions-only] \
[\--description <DESC>] \
[\--submit-type <TYPE>] \
[\--use-content-merge] \
@ -71,11 +70,6 @@ repository.*.createGroup will be used. If they don't exist,
through. If not specified, the parent is set to the default
project `\-- All Projects \--`.
\--permissions-only::
Create the project only to serve as a parent for other
projects. The new project's Git repository will not be
initialized, and cannot be cloned.
\--description::
Initial description of the project. If not specified,
no description is stored.

View File

@ -17,16 +17,21 @@ package com.google.gerrit.httpd.rpc.project;
import com.google.gerrit.common.data.ProjectDetail;
import com.google.gerrit.httpd.rpc.Handler;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gwtorm.client.OrmConcurrencyException;
import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.util.Collections;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import java.io.IOException;
class ChangeProjectSettings extends Handler<ProjectDetail> {
interface Factory {
@ -36,8 +41,8 @@ class ChangeProjectSettings extends Handler<ProjectDetail> {
private final ProjectDetailFactory.Factory projectDetailFactory;
private final ProjectControl.Factory projectControlFactory;
private final ProjectCache projectCache;
private final ReviewDb db;
private final GitRepositoryManager repoManager;
private final GitRepositoryManager mgr;
private final MetaDataUpdate.User metaDataUpdateFactory;
private final Project update;
@ -45,14 +50,14 @@ class ChangeProjectSettings extends Handler<ProjectDetail> {
ChangeProjectSettings(
final ProjectDetailFactory.Factory projectDetailFactory,
final ProjectControl.Factory projectControlFactory,
final ProjectCache projectCache, final ReviewDb db,
final GitRepositoryManager grm,
final ProjectCache projectCache, final GitRepositoryManager mgr,
final MetaDataUpdate.User metaDataUpdateFactory,
@Assisted final Project update) {
this.projectDetailFactory = projectDetailFactory;
this.projectControlFactory = projectControlFactory;
this.projectCache = projectCache;
this.db = db;
this.repoManager = grm;
this.mgr = mgr;
this.metaDataUpdateFactory = metaDataUpdateFactory;
this.update = update;
}
@ -63,17 +68,32 @@ class ChangeProjectSettings extends Handler<ProjectDetail> {
final ProjectControl projectControl =
projectControlFactory.ownerFor(projectName);
final Project proj = db.projects().get(projectName);
if (proj == null) {
final MetaDataUpdate md;
try {
md = metaDataUpdateFactory.create(projectName);
} catch (RepositoryNotFoundException notFound) {
throw new NoSuchProjectException(projectName);
}
try {
// TODO We really should take advantage of the Git commit DAG and
// ensure the current version matches the old version the caller read.
//
ProjectConfig config = ProjectConfig.read(md);
config.getProject().copySettingsFrom(update);
proj.copySettingsFrom(update);
db.projects().update(Collections.singleton(proj));
projectCache.evict(proj);
if (!projectControl.getProjectState().isSpecialWildProject()) {
repoManager.setProjectDescription(projectName, update.getDescription());
md.setMessage("Modified project settings\n");
if (config.commit(md)) {
mgr.setProjectDescription(projectName, update.getDescription());
projectCache.evict(config.getProject());
} else {
throw new OrmConcurrencyException("Cannot update " + projectName);
}
} catch (ConfigInvalidException err) {
throw new OrmException("Cannot read project " + projectName, err);
} catch (IOException err) {
throw new OrmException("Cannot update project " + projectName, err);
} finally {
md.close();
}
return projectDetailFactory.create(projectName).call();

View File

@ -32,7 +32,6 @@ import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.config.SitePath;
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.schema.SchemaUpdater;
import com.google.gerrit.server.schema.UpdateUI;
import com.google.gerrit.server.util.HostPlatform;
@ -292,8 +291,6 @@ public class Init extends SiteProgram {
protected void configure() {
bind(ConsoleUI.class).toInstance(init.ui);
bind(InitFlags.class).toInstance(init.flags);
bind(GitRepositoryManager.class).to(LocalDiskRepositoryManager.class);
}
});
return createDbInjector(SINGLE_USER).createChildInjector(modules);

View File

@ -56,7 +56,6 @@ public class ScanTrackingIds extends SiteProgram {
private List<Change> todo;
private Injector dbInjector;
private Injector gitInjector;
@Inject
private TrackingFooters footers;
@ -74,17 +73,9 @@ public class ScanTrackingIds extends SiteProgram {
}
dbInjector = createDbInjector(MULTI_USER);
gitInjector = dbInjector.createChildInjector(new LifecycleModule() {
@Override
protected void configure() {
bind(GitRepositoryManager.class).to(LocalDiskRepositoryManager.class);
listener().to(LocalDiskRepositoryManager.Lifecycle.class);
}
});
manager.add(dbInjector, gitInjector);
manager.add(dbInjector);
manager.start();
gitInjector.injectMembers(this);
dbInjector.injectMembers(this);
final ReviewDb db = database.open();
try {

View File

@ -22,6 +22,7 @@ import com.google.gerrit.server.config.GerritServerConfigModule;
import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.schema.DataSourceProvider;
import com.google.gerrit.server.schema.DatabaseModule;
import com.google.gerrit.server.schema.SchemaModule;
import com.google.gwtorm.client.OrmException;
import com.google.inject.AbstractModule;
import com.google.inject.CreationException;
@ -162,6 +163,7 @@ public abstract class SiteProgram extends AbstractProgram {
});
modules.add(new GerritServerConfigModule());
modules.add(new DatabaseModule());
modules.add(new SchemaModule());
try {
return Guice.createInjector(PRODUCTION, modules);

View File

@ -58,65 +58,37 @@ public final class Project {
}
public static enum SubmitType {
FAST_FORWARD_ONLY('F'),
FAST_FORWARD_ONLY,
MERGE_IF_NECESSARY('M'),
MERGE_IF_NECESSARY,
MERGE_ALWAYS('A'),
MERGE_ALWAYS,
CHERRY_PICK('C');
private final char code;
private SubmitType(final char c) {
code = c;
}
public char getCode() {
return code;
}
public static SubmitType forCode(final char c) {
for (final SubmitType s : SubmitType.values()) {
if (s.code == c) {
return s;
}
}
return null;
}
CHERRY_PICK;
}
@Column(id = 1)
protected NameKey name;
@Column(id = 2, length = Integer.MAX_VALUE, notNull = false)
protected String description;
@Column(id = 3)
protected boolean useContributorAgreements;
@Column(id = 4)
protected boolean useSignedOffBy;
@Column(id = 5)
protected char submitType;
protected SubmitType submitType;
@Column(id = 6, notNull = false, name = "parent_name")
protected NameKey parent;
@Column(id = 7)
protected boolean requireChangeID;
@Column(id = 8)
protected boolean useContentMerge;
protected Project() {
}
public Project(final Project.NameKey newName) {
name = newName;
useContributorAgreements = true;
setSubmitType(SubmitType.MERGE_IF_NECESSARY);
public Project(Project.NameKey nameKey) {
name = nameKey;
submitType = SubmitType.MERGE_IF_NECESSARY;
}
public Project.NameKey getNameKey() {
@ -124,7 +96,7 @@ public final class Project {
}
public String getName() {
return name.get();
return name != null ? name.get() : null;
}
public String getDescription() {
@ -168,11 +140,11 @@ public final class Project {
}
public SubmitType getSubmitType() {
return SubmitType.forCode(submitType);
return submitType;
}
public void setSubmitType(final SubmitType type) {
submitType = type.getCode();
submitType = type;
}
public void copySettingsFrom(final Project update) {
@ -188,7 +160,11 @@ public final class Project {
return parent;
}
public void setParent(final Project.NameKey parentProjectName) {
parent = parentProjectName;
public String getParentName() {
return parent != null ? parent.get() : null;
}
public void setParentName(String n) {
parent = n != null ? new NameKey(n) : null;
}
}

View File

@ -24,7 +24,6 @@ import com.google.gwtorm.client.Sequence;
* <p>
* Root entities that are at the top level of some important data graph:
* <ul>
* <li>{@link Project}: Configuration for a single Git repository.</li>
* <li>{@link Account}: Per-user account registration, preferences, identity.</li>
* <li>{@link Change}: All review information about a single proposed change.</li>
* <li>{@link SystemConfig}: Server-wide settings, managed by administrator.</li>
@ -93,9 +92,6 @@ public interface ReviewDb extends Schema {
@Relation
AccountPatchReviewAccess accountPatchReviews();
@Relation
ProjectAccess projects();
@Relation
ChangeAccess changes();

View File

@ -23,8 +23,6 @@ import com.google.gerrit.reviewdb.AuthType;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.FileTypeRegistry;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.GerritPersonIdentProvider;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.MimeUtilFileTypeRegistry;
import com.google.gerrit.server.ReplicationUser;
@ -41,8 +39,7 @@ import com.google.gerrit.server.auth.ldap.LdapModule;
import com.google.gerrit.server.cache.CachePool;
import com.google.gerrit.server.events.EventFactory;
import com.google.gerrit.server.git.ChangeMergeQueue;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.LocalDiskRepositoryManager;
import com.google.gerrit.server.git.GitModule;
import com.google.gerrit.server.git.MergeQueue;
import com.google.gerrit.server.git.PushAllProjectsOp;
import com.google.gerrit.server.git.PushReplication;
@ -71,7 +68,6 @@ import com.google.inject.Inject;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.runtime.RuntimeConstants;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.PersonIdent;
import java.util.Properties;
@ -143,9 +139,6 @@ public class GerritGlobalModule extends FactoryModule {
SINGLETON);
bind(AnonymousUser.class);
bind(PersonIdent.class).annotatedWith(GerritPersonIdent.class).toProvider(
GerritPersonIdentProvider.class);
bind(IdGenerator.class);
bind(CachePool.class);
install(AccountByEmailCacheImpl.module());
@ -155,13 +148,13 @@ public class GerritGlobalModule extends FactoryModule {
install(PatchListCacheImpl.module());
install(ProjectCacheImpl.module());
install(new AccessControlModule());
install(new GitModule());
factory(AccountInfoCacheFactory.Factory.class);
factory(GroupInfoCacheFactory.Factory.class);
factory(ProjectState.Factory.class);
factory(RefControl.Factory.class);
bind(GitRepositoryManager.class).to(LocalDiskRepositoryManager.class);
bind(FileTypeRegistry.class).to(MimeUtilFileTypeRegistry.class);
bind(WorkQueue.class);
bind(ToolsCatalog.class);
@ -189,7 +182,6 @@ public class GerritGlobalModule extends FactoryModule {
install(new LifecycleModule() {
@Override
protected void configure() {
listener().to(LocalDiskRepositoryManager.Lifecycle.class);
listener().to(CachePool.Lifecycle.class);
listener().to(WorkQueue.Lifecycle.class);
listener().to(VelocityLifecycle.class);

View File

@ -24,6 +24,7 @@ import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.account.PerformCreateGroup;
import com.google.gerrit.server.git.CreateCodeReviewNotes;
import com.google.gerrit.server.git.MergeOp;
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.ReceiveCommits;
import com.google.gerrit.server.mail.AbandonedSender;
import com.google.gerrit.server.mail.AddReviewerSender;
@ -49,6 +50,7 @@ public class GerritRequestModule extends FactoryModule {
bind(ReviewDb.class).toProvider(RequestScopedReviewDbProvider.class).in(
RequestScoped.class);
bind(IdentifiedUser.RequestFactory.class).in(SINGLETON);
bind(MetaDataUpdate.User.class).in(RequestScoped.class);
bind(AccountResolver.class);
bind(ChangeQueryRewriter.class);

View File

@ -1,37 +0,0 @@
// 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.config;
import com.google.gerrit.reviewdb.SystemConfig;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.File;
/** Provides {@link java.io.File} annotated with {@link SitePath}. */
public class SitePathFromSystemConfigProvider implements Provider<File> {
private final File path;
@Inject
SitePathFromSystemConfigProvider(final SystemConfig config) {
final String p = config.sitePath;
path = new File(p != null && p.length() > 0 ? p : ".").getAbsoluteFile();
}
@Override
public File get() {
return path;
}
}

View File

@ -1,4 +1,4 @@
// Copyright (C) 2008 The Android Open Source Project
// Copyright (C) 2011 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.
@ -12,18 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.reviewdb;
package com.google.gerrit.server.git;
import com.google.gwtorm.client.Access;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.client.PrimaryKey;
import com.google.gwtorm.client.Query;
import com.google.gwtorm.client.ResultSet;
import com.google.gerrit.server.config.FactoryModule;
public interface ProjectAccess extends Access<Project, Project.NameKey> {
@PrimaryKey("name")
Project get(Project.NameKey name) throws OrmException;
@Query("ORDER BY name")
ResultSet<Project> all() throws OrmException;
/** Configures the Git support. */
public class GitModule extends FactoryModule {
@Override
protected void configure() {
factory(MetaDataUpdate.InternalFactory.class);
bind(MetaDataUpdate.Server.class);
}
}

View File

@ -34,6 +34,9 @@ public interface GitRepositoryManager {
/** Note tree listing commits we refuse {@code refs/meta/reject-commits} */
public static final String REF_REJECT_COMMITS = "refs/meta/reject-commits";
/** Configuration settings for a project {@code refs/meta/config} */
public static final String REF_CONFIG = "refs/meta/config";
/**
* Get (or open) a repository by name.
*

View File

@ -882,6 +882,17 @@ public class MergeOp {
private void updateBranch() throws MergeException {
if (mergeTip != null && (branchTip == null || branchTip != mergeTip)) {
if (GitRepositoryManager.REF_CONFIG.equals(branchUpdate.getName())) {
try {
ProjectConfig cfg = new ProjectConfig(destProject.getNameKey());
cfg.load(db, mergeTip);
} catch (Exception e) {
throw new MergeException("Submit would store invalid"
+ " project configuration " + mergeTip.name() + " for "
+ destProject.getName(), e);
}
}
branchUpdate.setForceUpdate(false);
branchUpdate.setNewObjectId(mergeTip);
branchUpdate.setRefLogMessage("merged", true);
@ -889,6 +900,13 @@ public class MergeOp {
switch (branchUpdate.update(rw)) {
case NEW:
case FAST_FORWARD:
if (GitRepositoryManager.REF_CONFIG.equals(branchUpdate.getName())) {
projectCache.evict(destProject);
ProjectState ps = projectCache.get(destProject.getNameKey());
repoManager.setProjectDescription(destProject.getNameKey(), //
ps.getProject().getDescription());
}
replication.scheduleUpdate(destBranch.getParentKey(), branchUpdate
.getName());

View File

@ -0,0 +1,128 @@
// Copyright (C) 2011 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.reviewdb.Project;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
/** Helps with the updating of a {@link VersionedMetaData}. */
public class MetaDataUpdate {
public static class User {
private final InternalFactory factory;
private final GitRepositoryManager mgr;
private final PersonIdent serverIdent;
private final PersonIdent userIdent;
@Inject
User(InternalFactory factory, GitRepositoryManager mgr,
@GerritPersonIdent PersonIdent serverIdent, IdentifiedUser currentUser) {
this.factory = factory;
this.mgr = mgr;
this.serverIdent = serverIdent;
this.userIdent = currentUser.newCommitterIdent( //
serverIdent.getWhen(), //
serverIdent.getTimeZone());
}
public PersonIdent getUserPersonIdent() {
return userIdent;
}
public MetaDataUpdate create(Project.NameKey name)
throws RepositoryNotFoundException {
MetaDataUpdate md = factory.create(name, mgr.openRepository(name));
md.getCommitBuilder().setAuthor(userIdent);
md.getCommitBuilder().setCommitter(serverIdent);
return md;
}
}
public static class Server {
private final InternalFactory factory;
private final GitRepositoryManager mgr;
private final PersonIdent serverIdent;
@Inject
Server(InternalFactory factory, GitRepositoryManager mgr,
@GerritPersonIdent PersonIdent serverIdent) {
this.factory = factory;
this.mgr = mgr;
this.serverIdent = serverIdent;
}
public MetaDataUpdate create(Project.NameKey name)
throws RepositoryNotFoundException {
MetaDataUpdate md = factory.create(name, mgr.openRepository(name));
md.getCommitBuilder().setAuthor(serverIdent);
md.getCommitBuilder().setCommitter(serverIdent);
return md;
}
}
interface InternalFactory {
MetaDataUpdate create(@Assisted Project.NameKey projectName,
@Assisted Repository db);
}
private final ReplicationQueue replication;
private final Project.NameKey projectName;
private final Repository db;
private final CommitBuilder commit;
@Inject
public MetaDataUpdate(ReplicationQueue replication,
@Assisted Project.NameKey projectName, @Assisted Repository db) {
this.replication = replication;
this.projectName = projectName;
this.db = db;
this.commit = new CommitBuilder();
}
/** Set the commit message used when committing the update. */
public void setMessage(String message) {
getCommitBuilder().setMessage(message);
}
/** Close the cached Repository handle. */
public void close() {
getRepository().close();
}
Project.NameKey getProjectName() {
return projectName;
}
Repository getRepository() {
return db;
}
public CommitBuilder getCommitBuilder() {
return commit;
}
void replicate(String ref) {
if (replication.isEnabled()) {
replication.scheduleUpdate(projectName, ref);
}
}
}

View File

@ -0,0 +1,37 @@
// Copyright (C) 2011 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.reviewdb.Project;
/** A disabled {@link ReplicationQueue}. */
public final class NoReplication implements ReplicationQueue {
@Override
public boolean isEnabled() {
return false;
}
@Override
public void scheduleUpdate(Project.NameKey project, String ref) {
}
@Override
public void scheduleFullSync(Project.NameKey project, String urlMatch) {
}
@Override
public void replicateNewProject(Project.NameKey project, String head) {
}
}

View File

@ -0,0 +1,125 @@
// Copyright (C) 2010 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.reviewdb.Project;
import com.google.gerrit.reviewdb.Project.SubmitType;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
import java.io.IOException;
public class ProjectConfig extends VersionedMetaData {
private static final String PROJECT_CONFIG = "project.config";
private static final String PROJECT = "project";
private static final String KEY_DESCRIPTION = "description";
private static final String ACCESS = "access";
private static final String KEY_INHERIT_FROM = "inheritFrom";
private static final String RECEIVE = "receive";
private static final String KEY_REQUIRE_SIGNED_OFF_BY = "requireSignedOffBy";
private static final String KEY_REQUIRE_CHANGE_ID = "requireChangeId";
private static final String KEY_REQUIRE_CONTRIBUTOR_AGREEMENT =
"requireContributorAgreement";
private static final String SUBMIT = "submit";
private static final String KEY_ACTION = "action";
private static final String KEY_MERGE_CONTENT = "mergeContent";
private static final SubmitType defaultSubmitAction =
SubmitType.MERGE_IF_NECESSARY;
private Project.NameKey projectName;
private Project project;
public static ProjectConfig read(MetaDataUpdate update) throws IOException,
ConfigInvalidException {
ProjectConfig r = new ProjectConfig(update.getProjectName());
r.load(update);
return r;
}
public static ProjectConfig read(MetaDataUpdate update, ObjectId id)
throws IOException, ConfigInvalidException {
ProjectConfig r = new ProjectConfig(update.getProjectName());
r.load(update, id);
return r;
}
public ProjectConfig(Project.NameKey projectName) {
this.projectName = projectName;
}
public Project getProject() {
return project;
}
@Override
protected String getRefName() {
return GitRepositoryManager.REF_CONFIG;
}
@Override
protected void onLoad() throws IOException, ConfigInvalidException {
Config rc = readConfig(PROJECT_CONFIG);
project = new Project(projectName);
Project p = project;
p.setDescription(rc.getString(PROJECT, null, KEY_DESCRIPTION));
if (p.getDescription() == null) {
p.setDescription("");
}
p.setParentName(rc.getString(ACCESS, null, KEY_INHERIT_FROM));
p.setUseContributorAgreements(rc.getBoolean(RECEIVE, KEY_REQUIRE_CONTRIBUTOR_AGREEMENT, false));
p.setUseSignedOffBy(rc.getBoolean(RECEIVE, KEY_REQUIRE_SIGNED_OFF_BY, false));
p.setRequireChangeID(rc.getBoolean(RECEIVE, KEY_REQUIRE_CHANGE_ID, false));
p.setSubmitType(rc.getEnum(SUBMIT, null, KEY_ACTION, defaultSubmitAction));
p.setUseContentMerge(rc.getBoolean(SUBMIT, null, KEY_MERGE_CONTENT, false));
}
@Override
protected void onSave(CommitBuilder commit) throws IOException,
ConfigInvalidException {
if (commit.getMessage() == null || "".equals(commit.getMessage())) {
commit.setMessage("Updated project configuration\n");
}
Config rc = readConfig(PROJECT_CONFIG);
Project p = project;
if (p.getDescription() != null && !p.getDescription().isEmpty()) {
rc.setString(PROJECT, null, KEY_DESCRIPTION, p.getDescription());
} else {
rc.unset(PROJECT, null, KEY_DESCRIPTION);
}
set(rc, ACCESS, null, KEY_INHERIT_FROM, p.getParentName());
set(rc, RECEIVE, null, KEY_REQUIRE_CONTRIBUTOR_AGREEMENT, p.isUseContributorAgreements());
set(rc, RECEIVE, null, KEY_REQUIRE_SIGNED_OFF_BY, p.isUseSignedOffBy());
set(rc, RECEIVE, null, KEY_REQUIRE_CHANGE_ID, p.isRequireChangeID());
set(rc, SUBMIT, null, KEY_ACTION, p.getSubmitType(), defaultSubmitAction);
set(rc, SUBMIT, null, KEY_MERGE_CONTENT, p.isUseContentMerge());
saveConfig(PROJECT_CONFIG, rc);
}
}

View File

@ -15,7 +15,6 @@
package com.google.gerrit.server.git;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.server.config.WildProjectName;
import com.google.gerrit.server.project.ProjectCache;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@ -37,17 +36,14 @@ public class PushAllProjectsOp extends DefaultQueueOp {
private final ProjectCache projectCache;
private final ReplicationQueue replication;
private final Project.NameKey wildProject;
private final String urlMatch;
@Inject
public PushAllProjectsOp(final WorkQueue wq, final ProjectCache projectCache,
final ReplicationQueue rq, @WildProjectName final Project.NameKey wp,
@Assisted @Nullable final String urlMatch) {
final ReplicationQueue rq, @Assisted @Nullable final String urlMatch) {
super(wq);
this.projectCache = projectCache;
this.replication = rq;
this.wildProject = wp;
this.urlMatch = urlMatch;
}
@ -61,9 +57,7 @@ public class PushAllProjectsOp extends DefaultQueueOp {
public void run() {
try {
for (final Project.NameKey nameKey : projectCache.all()) {
if (!nameKey.equals(wildProject)) {
replication.scheduleFullSync(nameKey, urlMatch);
}
replication.scheduleFullSync(nameKey, urlMatch);
}
} catch (RuntimeException e) {
log.error("Cannot enumerate known projects", e);

View File

@ -48,7 +48,9 @@ import com.google.gerrit.server.mail.MergedSender;
import com.google.gerrit.server.mail.ReplacePatchSetSender;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.RefControl;
import com.google.gwtorm.client.AtomicUpdate;
import com.google.gwtorm.client.OrmException;
@ -141,6 +143,8 @@ public class ReceiveCommits implements PreReceiveHook, PostReceiveHook {
private final ReplicationQueue replication;
private final PatchSetInfoFactory patchSetInfoFactory;
private final ChangeHookRunner hooks;
private final GitRepositoryManager repoManager;
private final ProjectCache projectCache;
private final String canonicalWebUrl;
private final PersonIdent gerritIdent;
private final TrackingFooters trackingFooters;
@ -175,6 +179,8 @@ public class ReceiveCommits implements PreReceiveHook, PostReceiveHook {
final ReplicationQueue replication,
final PatchSetInfoFactory patchSetInfoFactory,
final ChangeHookRunner hooks,
final ProjectCache projectCache,
final GitRepositoryManager repoManager,
@CanonicalWebUrl @Nullable final String canonicalWebUrl,
@GerritPersonIdent final PersonIdent gerritIdent,
final TrackingFooters trackingFooters,
@ -191,6 +197,8 @@ public class ReceiveCommits implements PreReceiveHook, PostReceiveHook {
this.replication = replication;
this.patchSetInfoFactory = patchSetInfoFactory;
this.hooks = hooks;
this.projectCache = projectCache;
this.repoManager = repoManager;
this.canonicalWebUrl = canonicalWebUrl;
this.gerritIdent = gerritIdent;
this.trackingFooters = trackingFooters;
@ -376,6 +384,13 @@ public class ReceiveCommits implements PreReceiveHook, PostReceiveHook {
}
}
if (GitRepositoryManager.REF_CONFIG.equals(c.getRefName())) {
projectCache.evict(project);
ProjectState ps = projectCache.get(project.getNameKey());
repoManager.setProjectDescription(project.getNameKey(), //
ps.getProject().getDescription());
}
if (!c.getRefName().startsWith(NEW_CHANGE)) {
// We only schedule direct refs updates for replication.
// Change refs are scheduled when they are created.
@ -555,24 +570,59 @@ public class ReceiveCommits implements PreReceiveHook, PostReceiveHook {
switch (cmd.getType()) {
case CREATE:
parseCreate(cmd);
continue;
break;
case UPDATE:
parseUpdate(cmd);
continue;
break;
case DELETE:
parseDelete(cmd);
continue;
break;
case UPDATE_NONFASTFORWARD:
parseRewind(cmd);
break;
default:
reject(cmd);
continue;
}
// Everything else is bogus as far as we are concerned.
//
reject(cmd);
if (cmd.getResult() != ReceiveCommand.Result.NOT_ATTEMPTED){
continue;
}
if (GitRepositoryManager.REF_CONFIG.equals(cmd.getRefName())) {
if (!projectControl.isOwner()) {
reject(cmd, "not project owner");
continue;
}
switch (cmd.getType()) {
case CREATE:
case UPDATE:
case UPDATE_NONFASTFORWARD:
try {
ProjectConfig cfg = new ProjectConfig(project.getNameKey());
cfg.load(repo, cmd.getNewId());
} catch (Exception e) {
reject(cmd, "invalid project configuration");
log.error("User " + currentUser.getUserName()
+ " tried to push invalid project configuration "
+ cmd.getNewId().name() + " for " + project.getName(), e);
continue;
}
break;
case DELETE:
break;
default:
reject(cmd);
continue;
}
}
}
}

View File

@ -0,0 +1,306 @@
// Copyright (C) 2010 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 org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEditor;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.dircache.DirCacheEditor.DeletePath;
import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.UnmergedPathException;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.util.RawParseUtils;
import java.io.IOException;
/**
* Support for metadata stored within a version controlled branch.
* <p>
* Implementors are responsible for supplying implementations of the onLoad and
* onSave methods to read from the repository, or format an update that can
* later be written back to the repository.
*/
public abstract class VersionedMetaData {
private RevCommit revision;
private ObjectReader reader;
private ObjectInserter inserter;
private DirCache newTree;
/** @return name of the reference storing this configuration. */
protected abstract String getRefName();
protected abstract void onLoad() throws IOException, ConfigInvalidException;
protected abstract void onSave(CommitBuilder commit) throws IOException,
ConfigInvalidException;
/** @return revision of the metadata that was loaded. */
public ObjectId getRevision() {
return revision.copy();
}
/**
* Load the current version from the branch.
* <p>
* The repository is not held after the call completes, allowing the
* application to retain this object for long periods of time.
*
* @param db repository to access.
* @throws IOException
* @throws ConfigInvalidException
*/
public void load(Repository db) throws IOException, ConfigInvalidException {
Ref ref = db.getRef(getRefName());
load(db, ref != null ? ref.getObjectId() : null);
}
/**
* Load a specific version from the repository.
* <p>
* This method is primarily useful for applying updates to a specific revision
* that was shown to an end-user in the user interface. If there are conflicts
* with another user's concurrent changes, these will be automatically
* detected at commit time.
* <p>
* The repository is not held after the call completes, allowing the
* application to retain this object for long periods of time.
*
* @param db repository to access.
* @param id revision to load.
* @throws IOException
* @throws ConfigInvalidException
*/
public void load(Repository db, ObjectId id) throws IOException,
ConfigInvalidException {
if (id != null) {
reader = db.newObjectReader();
try {
revision = new RevWalk(reader).parseCommit(id);
onLoad();
} finally {
reader.release();
reader = null;
}
} else {
// The branch does not yet exist.
revision = null;
onLoad();
}
}
public void load(MetaDataUpdate update) throws IOException,
ConfigInvalidException {
load(update.getRepository());
}
public void load(MetaDataUpdate update, ObjectId id) throws IOException,
ConfigInvalidException {
load(update.getRepository(), id);
}
/**
* Update this metadata branch, recording a new commit on its reference.
*
* @param update helper information to define the update that will occur.
* @return true if the update was successful, false if it failed because of a
* concurrent update to the same reference.
* @throws IOException if there is a storage problem and the update cannot be
* executed as requested.
*/
public boolean commit(MetaDataUpdate update) throws IOException {
final Repository db = update.getRepository();
final CommitBuilder commit = update.getCommitBuilder();
reader = db.newObjectReader();
inserter = db.newObjectInserter();
try {
final RevWalk rw = new RevWalk(reader);
final RevTree src = revision != null ? rw.parseTree(revision) : null;
final ObjectId res = writeTree(src, commit);
if (res.equals(src)) {
// If there are no changes to the content, don't create the commit.
return true;
}
commit.setTreeId(res);
if (revision != null) {
commit.setParentId(revision);
}
RefUpdate ru = db.updateRef(getRefName());
if (revision != null) {
ru.setExpectedOldObjectId(revision);
} else {
ru.setExpectedOldObjectId(ObjectId.zeroId());
}
ru.setNewObjectId(inserter.insert(commit));
ru.disableRefLog();
inserter.flush();
switch (ru.update(rw)) {
case NEW:
case FAST_FORWARD:
revision = rw.parseCommit(ru.getNewObjectId());
update.replicate(ru.getName());
return true;
case LOCK_FAILURE:
return false;
default:
throw new IOException("Cannot update " + ru.getName() + " in "
+ db.getDirectory() + ": " + ru.getResult());
}
} catch (ConfigInvalidException e) {
throw new IOException("Cannot update " + getRefName() + " in "
+ db.getDirectory() + ": " + e.getMessage(), e);
} finally {
inserter.release();
inserter = null;
reader.release();
reader = null;
}
}
private ObjectId writeTree(RevTree srcTree, CommitBuilder commit)
throws IOException, MissingObjectException, IncorrectObjectTypeException,
UnmergedPathException, ConfigInvalidException {
try {
newTree = readTree(srcTree);
onSave(commit);
return newTree.writeTree(inserter);
} finally {
newTree = null;
}
}
private DirCache readTree(RevTree tree) throws IOException,
MissingObjectException, IncorrectObjectTypeException {
DirCache dc = DirCache.newInCore();
if (tree != null) {
DirCacheBuilder b = dc.builder();
b.addTree(new byte[0], DirCacheEntry.STAGE_0, reader, tree);
b.finish();
}
return dc;
}
protected Config readConfig(String fileName) throws IOException,
ConfigInvalidException {
Config rc = new Config();
String text = readUTF8(fileName);
if (!text.isEmpty()) {
try {
rc.fromText(text);
} catch (ConfigInvalidException err) {
throw new ConfigInvalidException("Invalid config file " + fileName
+ " in commit" + revision.name(), err);
}
}
return rc;
}
protected String readUTF8(String fileName) throws IOException {
byte[] raw = readFile(fileName);
return raw.length != 0 ? RawParseUtils.decode(raw) : "";
}
protected byte[] readFile(String fileName) throws IOException {
if (revision == null) {
return new byte[] {};
}
TreeWalk tw = TreeWalk.forPath(reader, fileName, revision.getTree());
if (tw != null) {
ObjectLoader obj = reader.open(tw.getObjectId(0), Constants.OBJ_BLOB);
return obj.getCachedBytes(Integer.MAX_VALUE);
} else {
return new byte[] {};
}
}
protected static void set(Config rc, String section, String subsection,
String name, String value) {
if (value != null) {
rc.setString(section, subsection, name, value);
} else {
rc.unset(section, subsection, name);
}
}
protected static void set(Config rc, String section, String subsection,
String name, boolean value) {
if (value) {
rc.setBoolean(section, subsection, name, value);
} else {
rc.unset(section, subsection, name);
}
}
protected static <E extends Enum<?>> void set(Config rc, String section,
String subsection, String name, E value, E defaultValue) {
if (value != defaultValue) {
rc.setEnum(section, subsection, name, value);
} else {
rc.unset(section, subsection, name);
}
}
protected void saveConfig(String fileName, Config cfg) throws IOException {
saveUTF8(fileName, cfg.toText());
}
protected void saveUTF8(String fileName, String text) throws IOException {
saveFile(fileName, text != null ? Constants.encode(text) : null);
}
protected void saveFile(String fileName, byte[] raw) throws IOException {
DirCacheEditor editor = newTree.editor();
if (raw != null && 0 < raw.length) {
final ObjectId blobId = inserter.insert(Constants.OBJ_BLOB, raw);
editor.add(new PathEdit(fileName) {
@Override
public void apply(DirCacheEntry ent) {
ent.setFileMode(FileMode.REGULAR_FILE);
ent.setObjectId(blobId);
}
});
} else {
editor.add(new DeletePath(fileName));
}
editor.finish();
}
}

View File

@ -21,6 +21,7 @@ import com.google.gerrit.server.cache.Cache;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.cache.EntryCreator;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gwtorm.client.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Module;
@ -28,6 +29,9 @@ import com.google.inject.Singleton;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Named;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Repository;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
@ -167,27 +171,37 @@ public class ProjectCacheImpl implements ProjectCache {
static class Loader extends EntryCreator<Project.NameKey, ProjectState> {
private final ProjectState.Factory projectStateFactory;
private final SchemaFactory<ReviewDb> schema;
private final GitRepositoryManager mgr;
@Inject
Loader(ProjectState.Factory psf, SchemaFactory<ReviewDb> sf) {
Loader(ProjectState.Factory psf, SchemaFactory<ReviewDb> sf,
GitRepositoryManager g) {
projectStateFactory = psf;
schema = sf;
mgr = g;
}
@Override
public ProjectState createEntry(Project.NameKey key) throws Exception {
final ReviewDb db = schema.open();
try {
final Project p = db.projects().get(key);
if (p == null) {
return null;
Repository git = mgr.openRepository(key);
try {
final ProjectConfig cfg = new ProjectConfig(key);
cfg.load(git);
final Project p = cfg.getProject();
final Collection<RefRight> rights =
Collections.unmodifiableCollection(db.refRights().byProject(key)
.toList());
return projectStateFactory.create(p, rights);
} finally {
git.close();
}
final Collection<RefRight> rights =
Collections.unmodifiableCollection(db.refRights().byProject(
p.getNameKey()).toList());
} catch (RepositoryNotFoundException notFound) {
return null;
return projectStateFactory.create(p, rights);
} finally {
db.close();
}

View File

@ -26,8 +26,6 @@ import com.google.inject.TypeLiteral;
public class DatabaseModule extends FactoryModule {
@Override
protected void configure() {
install(new SchemaVersion.Module());
bind(new TypeLiteral<SchemaFactory<ReviewDb>>() {}).to(
new TypeLiteral<Database<ReviewDb>>() {}).in(SINGLETON);
bind(new TypeLiteral<Database<ReviewDb>>() {}).toProvider(

View File

@ -23,8 +23,13 @@ import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.reviewdb.RefRight;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.reviewdb.SystemConfig;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.NoReplication;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.workflow.NoOpFunction;
import com.google.gerrit.server.workflow.SubmitFunction;
import com.google.gwtjsonrpc.server.SignedToken;
@ -37,6 +42,11 @@ import com.google.gwtorm.schema.sql.DialectPostgreSQL;
import com.google.gwtorm.schema.sql.SqlDialect;
import com.google.inject.Inject;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@ -50,6 +60,9 @@ public class SchemaCreator {
private final @SitePath
File site_path;
private final GitRepositoryManager mgr;
private final PersonIdent serverUser;
private final int versionNbr;
private final ScriptRunner index_generic;
private final ScriptRunner index_postgres;
@ -57,20 +70,25 @@ public class SchemaCreator {
@Inject
public SchemaCreator(final SitePaths site,
@Current final SchemaVersion version) {
this(site.site_path, version);
@Current final SchemaVersion version, final GitRepositoryManager mgr,
@GerritPersonIdent final PersonIdent au) {
this(site.site_path, version, mgr, au);
}
public SchemaCreator(final @SitePath File site,
@Current final SchemaVersion version) {
@Current final SchemaVersion version, final GitRepositoryManager gitMgr,
final @GerritPersonIdent PersonIdent au) {
site_path = site;
mgr = gitMgr;
serverUser = au;
versionNbr = version.getVersionNbr();
index_generic = new ScriptRunner("index_generic.sql");
index_postgres = new ScriptRunner("index_postgres.sql");
mysql_nextval = new ScriptRunner("mysql_nextval.sql");
}
public void create(final ReviewDb db) throws OrmException {
public void create(final ReviewDb db) throws OrmException, IOException,
ConfigInvalidException {
final JdbcSchema jdbc = (JdbcSchema) db;
final JdbcExecutor e = new JdbcExecutor(jdbc);
try {
@ -92,7 +110,11 @@ public class SchemaCreator {
initPushTagCategory(db);
initPushUpdateBranchCategory(db);
initForgeIdentityCategory(db, sConfig);
initWildCardProject(db);
if (mgr != null) {
// TODO This should never be null when initializing a site.
initWildCardProject();
}
final SqlDialect d = jdbc.getDialect();
if (d instanceof DialectH2) {
@ -177,13 +199,38 @@ public class SchemaCreator {
return s;
}
private void initWildCardProject(final ReviewDb c) throws OrmException {
final Project p;
private void initWildCardProject() throws IOException, ConfigInvalidException {
Repository git;
try {
git = mgr.openRepository(DEFAULT_WILD_NAME);
} catch (RepositoryNotFoundException notFound) {
// A repository may be missing if this project existed only to store
// inheritable permissions. For example '-- All Projects --'.
try {
git = mgr.createRepository(DEFAULT_WILD_NAME);
} catch (RepositoryNotFoundException err) {
final String name = DEFAULT_WILD_NAME.get();
throw new IOException("Cannot create repository " + name, err);
}
}
try {
MetaDataUpdate md =
new MetaDataUpdate(new NoReplication(), DEFAULT_WILD_NAME, git);
md.getCommitBuilder().setAuthor(serverUser);
md.getCommitBuilder().setCommitter(serverUser);
p = new Project(DEFAULT_WILD_NAME);
p.setDescription("Rights inherited by all other projects");
p.setUseContributorAgreements(false);
c.projects().insert(Collections.singleton(p));
ProjectConfig config = ProjectConfig.read(md);
Project p = config.getProject();
p.setDescription("Rights inherited by all other projects");
p.setUseContributorAgreements(false);
md.setMessage("Created project\n");
if (!config.commit(md)) {
throw new IOException("Cannot create " + DEFAULT_WILD_NAME.get());
}
} finally {
git.close();
}
}
private void initVerifiedCategory(final ReviewDb c) throws OrmException {

View File

@ -0,0 +1,43 @@
// 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.LifecycleModule;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.GerritPersonIdentProvider;
import com.google.gerrit.server.config.FactoryModule;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.LocalDiskRepositoryManager;
import org.eclipse.jgit.lib.PersonIdent;
/** Validate the schema and connect to Git. */
public class SchemaModule extends FactoryModule {
@Override
protected void configure() {
install(new SchemaVersion.Module());
bind(PersonIdent.class).annotatedWith(GerritPersonIdent.class).toProvider(
GerritPersonIdentProvider.class);
bind(GitRepositoryManager.class).to(LocalDiskRepositoryManager.class);
install(new LifecycleModule() {
@Override
protected void configure() {
listener().to(LocalDiskRepositoryManager.Lifecycle.class);
}
});
}
}

View File

@ -23,6 +23,8 @@ import com.google.gwtorm.client.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Provider;
import org.eclipse.jgit.errors.ConfigInvalidException;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Collections;
@ -49,7 +51,13 @@ public class SchemaUpdater {
final SchemaVersion u = updater.get();
final CurrentSchemaVersion version = getSchemaVersion(db);
if (version == null) {
creator.create(db);
try {
creator.create(db);
} catch (IOException e) {
throw new OrmException("Cannot initialize schema", e);
} catch (ConfigInvalidException e) {
throw new OrmException("Cannot initialize schema", e);
}
} else {
try {

View File

@ -32,7 +32,7 @@ import java.util.List;
/** A version of the database schema. */
public abstract class SchemaVersion {
/** The current schema version. */
private static final Class<? extends SchemaVersion> C = Schema_52.class;
private static final Class<? extends SchemaVersion> C = Schema_53.class;
public static class Module extends AbstractModule {
@Override

View File

@ -0,0 +1,127 @@
// Copyright (C) 2010 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.reviewdb.Project;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.NoReplication;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.jdbc.JdbcSchema;
import com.google.inject.Inject;
import com.google.inject.Provider;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
class Schema_53 extends SchemaVersion {
private final GitRepositoryManager mgr;
private final PersonIdent serverUser;
@Inject
Schema_53(Provider<Schema_52> prior, GitRepositoryManager mgr,
@GerritPersonIdent PersonIdent serverUser) {
super(prior);
this.mgr = mgr;
this.serverUser = serverUser;
}
@Override
protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException,
SQLException {
Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM projects ORDER BY name");
while (rs.next()) {
final String name = rs.getString("name");
final Project.NameKey nameKey = new Project.NameKey(name);
Repository git;
try {
git = mgr.openRepository(nameKey);
} catch (RepositoryNotFoundException notFound) {
// A repository may be missing if this project existed only to store
// inheritable permissions. For example '-- All Projects --'.
try {
git = mgr.createRepository(nameKey);
} catch (RepositoryNotFoundException err) {
throw new OrmException("Cannot create repository " + name, err);
}
}
try {
MetaDataUpdate md =
new MetaDataUpdate(new NoReplication(), nameKey, git);
md.getCommitBuilder().setAuthor(serverUser);
md.getCommitBuilder().setCommitter(serverUser);
ProjectConfig config = ProjectConfig.read(md);
loadProject(rs, config.getProject());
md.setMessage("Import project configuration from SQL\n");
if (!config.commit(md)) {
throw new OrmException("Cannot export project " + name);
}
} catch (ConfigInvalidException err) {
throw new OrmException("Cannot read project " + name, err);
} catch (IOException err) {
throw new OrmException("Cannot export project " + name, err);
} finally {
git.close();
}
}
rs.close();
stmt.close();
}
private void loadProject(ResultSet rs, Project project) throws SQLException,
OrmException {
project.setDescription(rs.getString("description"));
project.setUseContributorAgreements("Y".equals(rs
.getString("use_contributor_agreements")));
switch (rs.getString("submit_type").charAt(0)) {
case 'F':
project.setSubmitType(Project.SubmitType.FAST_FORWARD_ONLY);
break;
case 'M':
project.setSubmitType(Project.SubmitType.MERGE_IF_NECESSARY);
break;
case 'A':
project.setSubmitType(Project.SubmitType.MERGE_ALWAYS);
break;
case 'C':
project.setSubmitType(Project.SubmitType.CHERRY_PICK);
break;
default:
throw new OrmException("Unsupported submit_type="
+ rs.getString("submit_type") + " on project " + project.getName());
}
project.setUseSignedOffBy("Y".equals(rs.getString("use_signed_off_by")));
project.setRequireChangeID("Y".equals(rs.getString("require_change_id")));
project.setUseContentMerge("Y".equals(rs.getString("use_content_merge")));
project.setParentName(rs.getString("parent_name"));
}
}

View File

@ -286,9 +286,10 @@ public class RefControlTest extends TestCase {
ProjectCache projectCache = null;
Project.NameKey wildProject = new Project.NameKey("-- All Projects --");
ProjectControl.AssistedFactory projectControlFactory = null;
Project project = new Project(parent);
ProjectState ps =
new ProjectState(anonymousUser, projectCache, wildProject,
projectControlFactory, new Project(parent), localRights);
projectControlFactory, project, localRights);
ps.setInheritedRights(inheritedRights);
return ps;
}

View File

@ -17,7 +17,6 @@ package com.google.gerrit.server.schema;
import com.google.gerrit.reviewdb.AccountGroup;
import com.google.gerrit.reviewdb.ApprovalCategory;
import com.google.gerrit.reviewdb.ApprovalCategoryValue;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.reviewdb.RefRight;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.reviewdb.SystemConfig;
@ -151,24 +150,6 @@ public class SchemaCreatorTest extends TestCase {
}
}
public void testCreateSchema_WildCardProject() throws OrmException {
final ReviewDb c = db.create().open();
try {
final SystemConfig cfg;
final Project all;
cfg = c.systemConfig().get(new SystemConfig.Key());
all = c.projects().get(cfg.wildProjectName);
assertNotNull(all);
assertEquals("-- All Projects --", all.getName());
assertFalse(all.isUseContributorAgreements());
assertFalse(all.isUseSignedOffBy());
assertFalse(all.isRequireChangeID());
} finally {
c.close();
}
}
public void testCreateSchema_ApprovalCategory_CodeReview()
throws OrmException {
final ReviewDb c = db.create().open();
@ -347,18 +328,16 @@ public class SchemaCreatorTest extends TestCase {
final ReviewDb c = db.open();
try {
final SystemConfig cfg;
final Project all;
final RefRight right;
cfg = c.systemConfig().get(new SystemConfig.Key());
all = c.projects().get(cfg.wildProjectName);
right =
c.refRights().get(
new RefRight.Key(all.getNameKey(), new RefRight.RefPattern(
new RefRight.Key(cfg.wildProjectName, new RefRight.RefPattern(
pattern), category, group));
assertNotNull(right);
assertEquals(all.getNameKey(), right.getProjectNameKey());
assertEquals(cfg.wildProjectName, right.getProjectNameKey());
assertEquals(group, right.getAccountGroupId());
assertEquals(category, right.getApprovalCategoryId());
assertEquals(min, right.getMinValue());

View File

@ -16,7 +16,12 @@ package com.google.gerrit.server.schema;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.reviewdb.SystemConfig;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.GerritPersonIdentProvider;
import com.google.gerrit.server.config.GerritServerConfig;
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.testutil.InMemoryDatabase;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.client.SchemaFactory;
@ -27,6 +32,9 @@ import com.google.inject.TypeLiteral;
import junit.framework.TestCase;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.PersonIdent;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.List;
@ -58,6 +66,22 @@ public class SchemaUpdaterTest extends TestCase {
bind(new TypeLiteral<SchemaFactory<ReviewDb>>() {}).toInstance(db);
bind(SitePaths.class).toInstance(paths);
install(new SchemaVersion.Module());
Config cfg = new Config();
cfg.setString("gerrit", null, "basePath", "git");
cfg.setString("user", null, "name", "Gerrit Code Review");
cfg.setString("user", null, "email", "gerrit@localhost");
bind(Config.class) //
.annotatedWith(GerritServerConfig.class) //
.toInstance(cfg);
bind(PersonIdent.class) //
.annotatedWith(GerritPersonIdent.class) //
.toProvider(GerritPersonIdentProvider.class);
bind(GitRepositoryManager.class) //
.to(LocalDiskRepositoryManager.class);
}
}).getInstance(SchemaUpdater.class);

View File

@ -17,7 +17,13 @@ package com.google.gerrit.testutil;
import com.google.gerrit.reviewdb.CurrentSchemaVersion;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.reviewdb.SystemConfig;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.GerritPersonIdentProvider;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.config.SystemConfigProvider;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.LocalDiskRepositoryManager;
import com.google.gerrit.server.schema.Current;
import com.google.gerrit.server.schema.SchemaCreator;
import com.google.gerrit.server.schema.SchemaVersion;
@ -25,13 +31,19 @@ import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.client.SchemaFactory;
import com.google.gwtorm.jdbc.Database;
import com.google.gwtorm.jdbc.SimpleDataSource;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Key;
import com.google.inject.Provider;
import junit.framework.TestCase;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.PersonIdent;
import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
@ -84,8 +96,33 @@ public class InMemoryDatabase implements SchemaFactory<ReviewDb> {
database = new Database<ReviewDb>(dataSource, ReviewDb.class);
schemaVersion =
Guice.createInjector(new SchemaVersion.Module()).getBinding(
Key.get(SchemaVersion.class, Current.class)).getProvider().get();
Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
install(new SchemaVersion.Module());
bind(File.class) //
.annotatedWith(SitePath.class) //
.toInstance(new File("."));
Config cfg = new Config();
cfg.setString("gerrit", null, "basePath", "git");
cfg.setString("user", null, "name", "Gerrit Code Review");
cfg.setString("user", null, "email", "gerrit@localhost");
bind(Config.class) //
.annotatedWith(GerritServerConfig.class) //
.toInstance(cfg);
bind(PersonIdent.class) //
.annotatedWith(GerritPersonIdent.class) //
.toProvider(GerritPersonIdentProvider.class);
bind(GitRepositoryManager.class) //
.to(LocalDiskRepositoryManager.class);
}
}).getBinding(Key.get(SchemaVersion.class, Current.class))
.getProvider().get();
} catch (SQLException e) {
throw new OrmException(e);
}
@ -106,7 +143,14 @@ public class InMemoryDatabase implements SchemaFactory<ReviewDb> {
created = true;
final ReviewDb c = open();
try {
new SchemaCreator(new File("."), schemaVersion).create(c);
try {
new SchemaCreator(new File("."), schemaVersion, null,
new PersonIdent("name", "email@site")).create(c);
} catch (IOException e) {
throw new OrmException("Cannot create in-memory database", e);
} catch (ConfigInvalidException e) {
throw new OrmException("Cannot create in-memory database", e);
}
} finally {
c.close();
}

View File

@ -15,22 +15,24 @@
package com.google.gerrit.sshd.commands;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.config.WildProjectName;
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.sshd.AdminCommand;
import com.google.gerrit.sshd.BaseCommand;
import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
import org.apache.sshd.server.Environment;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@ -44,10 +46,10 @@ final class AdminSetParent extends BaseCommand {
private List<ProjectControl> children = new ArrayList<ProjectControl>();
@Inject
private ReviewDb db;
private ProjectCache projectCache;
@Inject
private ProjectCache projectCache;
private MetaDataUpdate.User metaDataUpdateFactory;
@Inject
@WildProjectName
@ -64,7 +66,7 @@ final class AdminSetParent extends BaseCommand {
});
}
private void updateParents() throws OrmException, UnloggedFailure {
private void updateParents() throws Failure {
final StringBuilder err = new StringBuilder();
final Set<Project.NameKey> grandParents = new HashSet<Project.NameKey>();
Project.NameKey newParentKey;
@ -112,16 +114,25 @@ final class AdminSetParent extends BaseCommand {
continue;
}
final Project child = db.projects().get(key);
if (child == null) {
// Race condition? Its in the cache, but not the database.
//
err.append("error: Project '" + name + "' not found\n");
continue;
try {
MetaDataUpdate md = metaDataUpdateFactory.create(key);
try {
ProjectConfig config = ProjectConfig.read(md);
config.getProject().setParentName(newParentKey.get());
md.setMessage("Inherit access from " + newParentKey.get() + "\n");
if (!config.commit(md)) {
err.append("error: Could not update project " + name + "\n");
}
} finally {
md.close();
}
} catch (RepositoryNotFoundException notFound) {
err.append("error: Project " + name + " not found\n");
} catch (IOException e) {
throw new Failure(1, "Cannot update project " + name, e);
} catch (ConfigInvalidException e) {
throw new Failure(1, "Cannot update project " + name, e);
}
child.setParent(newParentKey);
db.projects().update(Collections.singleton(child));
}
// Invalidate all projects in cache since inherited rights were changed.

View File

@ -26,6 +26,8 @@ import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.ProjectCreatorGroups;
import com.google.gerrit.server.config.ProjectOwnerGroups;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.git.ReplicationQueue;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectControl;
@ -34,6 +36,7 @@ import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject;
import org.apache.sshd.server.Environment;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
@ -49,7 +52,6 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@ -67,9 +69,6 @@ final class CreateProject extends BaseCommand {
@Option(name = "--parent", aliases = {"-p"}, metaVar = "NAME", usage = "parent project")
private ProjectControl newParent;
@Option(name = "--permissions-only", usage = "create project for use only as parent")
private boolean permissionsOnly;
@Option(name = "--description", aliases = {"-d"}, metaVar = "DESC", usage = "description of project")
private String projectDescription = "";
@ -123,6 +122,9 @@ final class CreateProject extends BaseCommand {
@GerritPersonIdent
private PersonIdent serverIdent;
@Inject
MetaDataUpdate.User metaDataUpdateFactory;
private Project.NameKey nameKey;
@Override
@ -138,34 +140,28 @@ final class CreateProject extends BaseCommand {
validateParameters();
nameKey = new Project.NameKey(projectName);
if (!permissionsOnly) {
final Repository repo = repoManager.createRepository(nameKey);
try {
RefUpdate u = repo.updateRef(Constants.HEAD);
u.disableRefLog();
u.link(branch);
final Repository repo = repoManager.createRepository(nameKey);
try {
RefUpdate u = repo.updateRef(Constants.HEAD);
u.disableRefLog();
u.link(branch);
repoManager.setProjectDescription(nameKey, projectDescription);
createProject();
rq.replicateNewProject(nameKey, branch);
if (createEmptyCommit) {
createEmptyCommit(repo, nameKey, branch);
}
} finally {
repo.close();
}
} else {
createProject();
repoManager.setProjectDescription(nameKey, projectDescription);
if (createEmptyCommit) {
createEmptyCommit(repo, nameKey, branch);
}
rq.replicateNewProject(nameKey, branch);
} finally {
repo.close();
}
} catch (Exception e) {
p.print("Error when trying to create project: " + e.getMessage()
+ "\n");
p.flush();
}
}
});
}
@ -176,9 +172,9 @@ final class CreateProject extends BaseCommand {
try {
CommitBuilder cb = new CommitBuilder();
cb.setTreeId(oi.insert(Constants.OBJ_TREE, new byte[] {}));
cb.setAuthor(metaDataUpdateFactory.getUserPersonIdent());
cb.setCommitter(serverIdent);
cb.setAuthor(cb.getCommitter());
cb.setMessage("Initial empty repository");
cb.setMessage("Initial empty repository\n");
ObjectId id = oi.insert(cb);
oi.flush();
@ -202,7 +198,9 @@ final class CreateProject extends BaseCommand {
}
}
private void createProject() throws OrmException {
private void createProject() throws OrmException, IOException,
ConfigInvalidException {
List<RefRight> access = new ArrayList<RefRight>();
for (AccountGroup.Id ownerId : ownerIds) {
final RefRight.Key prk =
@ -215,19 +213,30 @@ final class CreateProject extends BaseCommand {
}
db.refRights().insert(access);
final Project newProject = new Project(nameKey);
newProject.setDescription(projectDescription);
newProject.setSubmitType(submitType);
newProject.setUseContributorAgreements(contributorAgreements);
newProject.setUseSignedOffBy(signedOffBy);
newProject.setUseContentMerge(contentMerge);
newProject.setRequireChangeID(requireChangeID);
if (newParent != null) {
newProject.setParent(newParent.getProject().getNameKey());
}
Project.NameKey nameKey = new Project.NameKey(projectName);
MetaDataUpdate md = metaDataUpdateFactory.create(nameKey);
try {
ProjectConfig config = ProjectConfig.read(md);
db.projects().insert(Collections.singleton(newProject));
projectCache.onCreateProject(newProject.getNameKey());
Project newProject = config.getProject();
newProject.setDescription(projectDescription);
newProject.setSubmitType(submitType);
newProject.setUseContributorAgreements(contributorAgreements);
newProject.setUseSignedOffBy(signedOffBy);
newProject.setUseContentMerge(contentMerge);
newProject.setRequireChangeID(requireChangeID);
if (newParent != null) {
newProject.setParentName(newParent.getProject().getName());
}
md.setMessage("Created project\n");
if (!config.commit(md)) {
throw new IOException("Cannot create " + projectName);
}
} finally {
md.close();
}
projectCache.onCreateProject(nameKey);
}
private void validateParameters() throws Failure {

View File

@ -16,7 +16,6 @@ package com.google.gerrit.sshd.commands;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.WildProjectName;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectControl;
@ -50,10 +49,6 @@ final class ListProjects extends BaseCommand {
@Inject
private GitRepositoryManager repoManager;
@Inject
@WildProjectName
private Project.NameKey wildProject;
@Option(name = "--show-branch", aliases = {"-b"}, usage = "displays the sha of each project in the specified branch")
private String showBranch;
@ -89,12 +84,6 @@ final class ListProjects extends BaseCommand {
try {
for (final Project.NameKey projectName : projectCache.all()) {
if (projectName.equals(wildProject)) {
// This project "doesn't exist". At least not as a repository.
//
continue;
}
final ProjectState e = projectCache.get(projectName);
if (e == null) {
// If we can't get it from the cache, pretend its not present.

View File

@ -0,0 +1,61 @@
// 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;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.reviewdb.SystemConfig;
import com.google.gerrit.server.config.SitePath;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.client.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.File;
import java.util.List;
/** Provides {@link java.io.File} annotated with {@link SitePath}. */
class SitePathFromSystemConfigProvider implements Provider<File> {
private final File path;
@Inject
SitePathFromSystemConfigProvider(SchemaFactory<ReviewDb> schemaFactory)
throws OrmException {
path = read(schemaFactory);
}
@Override
public File get() {
return path;
}
private static File read(SchemaFactory<ReviewDb> schemaFactory)
throws OrmException {
ReviewDb db = schemaFactory.open();
try {
List<SystemConfig> all = db.systemConfig().all().toList();
switch (all.size()) {
case 1:
return new File(all.get(0).sitePath);
case 0:
throw new OrmException("system_config table is empty");
default:
throw new OrmException("system_config must have exactly 1 row;"
+ " found " + all.size() + " rows instead");
}
} finally {
db.close();
}
}
}

View File

@ -25,9 +25,9 @@ import com.google.gerrit.server.config.GerritGlobalModule;
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.server.schema.DatabaseModule;
import com.google.gerrit.server.schema.SchemaModule;
import com.google.gerrit.sshd.SshModule;
import com.google.gerrit.sshd.commands.MasterCommandModule;
import com.google.inject.AbstractModule;
@ -166,6 +166,7 @@ public class WebAppInitializer extends GuiceServletContextListener {
});
modules.add(new GerritServerConfigModule());
}
modules.add(new SchemaModule());
modules.add(new AuthConfigModule());
return dbInjector.createChildInjector(modules);
}