List projects by scanning the managed Git directory

Rather than locating projects in the database table, perform a
recursive scan of the managed repository directory and pull up
anything that we find.  This is a first step towards moving all
the project control metadata directly into Git.

Bug: issue 436
Change-Id: I08e0083f14f5c03eb9e49b4895c265d13b828534
Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce
2011-01-10 10:05:54 -08:00
parent 62defaca1c
commit 13fb707580
13 changed files with 253 additions and 96 deletions

View File

@@ -30,8 +30,9 @@ import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.GroupControl; import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.config.AuthConfig; import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState; import com.google.gerrit.server.project.ProjectControl;
import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwtorm.client.OrmException; import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
@@ -52,6 +53,7 @@ class SuggestServiceImpl extends BaseServiceImplementation implements
private static final String MAX_SUFFIX = "\u9fa5"; private static final String MAX_SUFFIX = "\u9fa5";
private final AuthConfig authConfig; private final AuthConfig authConfig;
private final ProjectControl.Factory projectControlFactory;
private final ProjectCache projectCache; private final ProjectCache projectCache;
private final AccountCache accountCache; private final AccountCache accountCache;
private final GroupControl.Factory groupControlFactory; private final GroupControl.Factory groupControlFactory;
@@ -62,6 +64,7 @@ class SuggestServiceImpl extends BaseServiceImplementation implements
@Inject @Inject
SuggestServiceImpl(final Provider<ReviewDb> schema, SuggestServiceImpl(final Provider<ReviewDb> schema,
final AuthConfig authConfig, final AuthConfig authConfig,
final ProjectControl.Factory projectControlFactory,
final ProjectCache projectCache, final AccountCache accountCache, final ProjectCache projectCache, final AccountCache accountCache,
final GroupControl.Factory groupControlFactory, final GroupControl.Factory groupControlFactory,
final IdentifiedUser.GenericFactory userFactory, final IdentifiedUser.GenericFactory userFactory,
@@ -69,6 +72,7 @@ class SuggestServiceImpl extends BaseServiceImplementation implements
@GerritServerConfig final Config cfg) { @GerritServerConfig final Config cfg) {
super(schema, currentUser); super(schema, currentUser);
this.authConfig = authConfig; this.authConfig = authConfig;
this.projectControlFactory = projectControlFactory;
this.projectCache = projectCache; this.projectCache = projectCache;
this.accountCache = accountCache; this.accountCache = accountCache;
this.groupControlFactory = groupControlFactory; this.groupControlFactory = groupControlFactory;
@@ -80,24 +84,24 @@ class SuggestServiceImpl extends BaseServiceImplementation implements
public void suggestProjectNameKey(final String query, final int limit, public void suggestProjectNameKey(final String query, final int limit,
final AsyncCallback<List<Project.NameKey>> callback) { final AsyncCallback<List<Project.NameKey>> callback) {
run(callback, new Action<List<Project.NameKey>>() { final int max = 10;
public List<Project.NameKey> run(final ReviewDb db) throws OrmException { final int n = limit <= 0 ? max : Math.min(limit, max);
final String a = query;
final String b = a + MAX_SUFFIX;
final int max = 10;
final int n = limit <= 0 ? max : Math.min(limit, max);
final CurrentUser user = currentUser.get(); final List<Project.NameKey> r = new ArrayList<Project.NameKey>(n);
final List<Project.NameKey> r = new ArrayList<Project.NameKey>(); for (final Project.NameKey nameKey : projectCache.byName(query)) {
for (final Project p : db.projects().suggestByName(a, b, n)) { final ProjectControl ctl;
final ProjectState e = projectCache.get(p.getNameKey()); try {
if (e != null && e.controlFor(user).isVisible()) { ctl = projectControlFactory.validateFor(nameKey);
r.add(p.getNameKey()); } catch (NoSuchProjectException e) {
} continue;
}
return r;
} }
});
r.add(ctl.getProject().getNameKey());
if (r.size() == n) {
break;
}
}
callback.onSuccess(r);
} }
public void suggestAccount(final String query, final Boolean active, public void suggestAccount(final String query, final Boolean active,

View File

@@ -17,11 +17,9 @@ package com.google.gerrit.httpd.rpc.project;
import com.google.gerrit.httpd.rpc.Handler; import com.google.gerrit.httpd.rpc.Handler;
import com.google.gerrit.reviewdb.Project; import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.project.NoSuchProjectException; import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectControl; import com.google.gerrit.server.project.ProjectControl;
import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import java.util.ArrayList; import java.util.ArrayList;
@@ -35,33 +33,26 @@ class VisibleProjects extends Handler<List<Project>> {
} }
private final ProjectControl.Factory projectControlFactory; private final ProjectControl.Factory projectControlFactory;
private final CurrentUser user; private final ProjectCache projectCache;
private final ReviewDb db;
@Inject @Inject
VisibleProjects(final ProjectControl.Factory projectControlFactory, VisibleProjects(final ProjectControl.Factory projectControlFactory,
final CurrentUser user, final ReviewDb db) { final ProjectCache projectCache) {
this.projectControlFactory = projectControlFactory; this.projectControlFactory = projectControlFactory;
this.user = user; this.projectCache = projectCache;
this.db = db;
} }
@Override @Override
public List<Project> call() throws OrmException { public List<Project> call() {
final List<Project> result; List<Project> result = new ArrayList<Project>();
if (user.isAdministrator()) { for (Project.NameKey p : projectCache.all()) {
result = db.projects().all().toList(); try {
} else { ProjectControl c = projectControlFactory.controlFor(p);
result = new ArrayList<Project>(); if (c.isVisible() || c.isOwner()) {
for (Project p : db.projects().all().toList()) { result.add(c.getProject());
try {
ProjectControl c = projectControlFactory.controlFor(p.getNameKey());
if (c.isVisible() || c.isOwner()) {
result.add(p);
}
} catch (NoSuchProjectException e) {
continue;
} }
} catch (NoSuchProjectException e) {
continue;
} }
} }
Collections.sort(result, new Comparator<Project>() { Collections.sort(result, new Comparator<Project>() {

View File

@@ -21,7 +21,7 @@ import com.google.gwtorm.client.StringKey;
public final class Project { public final class Project {
/** Project name key */ /** Project name key */
public static class NameKey extends public static class NameKey extends
StringKey<com.google.gwtorm.client.Key<?>> { StringKey<com.google.gwtorm.client.Key<?>> implements Comparable<NameKey> {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Column(id = 1) @Column(id = 1)
@@ -44,6 +44,11 @@ public final class Project {
name = newValue; name = newValue;
} }
@Override
public int compareTo(NameKey other) {
return get().compareTo(other.get());
}
/** Parse a Project.NameKey out of a string representation. */ /** Parse a Project.NameKey out of a string representation. */
public static NameKey parse(final String str) { public static NameKey parse(final String str) {
final NameKey r = new NameKey(); final NameKey r = new NameKey();

View File

@@ -26,8 +26,4 @@ public interface ProjectAccess extends Access<Project, Project.NameKey> {
@Query("ORDER BY name") @Query("ORDER BY name")
ResultSet<Project> all() throws OrmException; ResultSet<Project> all() throws OrmException;
@Query("WHERE name.name >= ? AND name.name <= ? ORDER BY name LIMIT ?")
ResultSet<Project> suggestByName(String nameA, String nameB, int limit)
throws OrmException;
} }

View File

@@ -21,7 +21,7 @@ import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import java.io.IOException; import java.io.IOException;
import java.util.SortedSet;
/** /**
* Manages Git repositories for the Gerrit server process. * Manages Git repositories for the Gerrit server process.
@@ -58,6 +58,9 @@ public interface GitRepositoryManager {
public abstract Repository createRepository(Project.NameKey name) public abstract Repository createRepository(Project.NameKey name)
throws RepositoryNotFoundException; throws RepositoryNotFoundException;
/** @return set of all known projects, sorted by natural NameKey order. */
public abstract SortedSet<Project.NameKey> list();
/** /**
* Read the {@code GIT_DIR/description} file for gitweb. * Read the {@code GIT_DIR/description} file for gitweb.
* <p> * <p>

View File

@@ -41,6 +41,9 @@ import org.slf4j.LoggerFactory;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;
/** Manages Git repositories stored on the local filesystem. */ /** Manages Git repositories stored on the local filesystem. */
@Singleton @Singleton
@@ -227,4 +230,46 @@ public class LocalDiskRepositoryManager implements GitRepositoryManager {
return false; // is a reasonable name return false; // is a reasonable name
} }
@Override
public SortedSet<Project.NameKey> list() {
SortedSet<Project.NameKey> names = new TreeSet<Project.NameKey>();
scanProjects(basePath, "", names);
return Collections.unmodifiableSortedSet(names);
}
private void scanProjects(final File dir, final String prefix,
final SortedSet<Project.NameKey> names) {
final File[] ls = dir.listFiles();
if (ls == null) {
return;
}
for (File f : ls) {
String fileName = f.getName();
if (FileKey.isGitRepository(f, FS.DETECTED)) {
String projectName;
if (fileName.equals(Constants.DOT_GIT)) {
projectName = prefix.substring(0, prefix.length() - 1);
} else if (fileName.endsWith(Constants.DOT_GIT_EXT)) {
int newLen = fileName.length() - Constants.DOT_GIT_EXT.length();
projectName = prefix + fileName.substring(0, newLen);
} else {
projectName = prefix + fileName;
}
Project.NameKey nameKey = new Project.NameKey(projectName);
if (isUnreasonableName(nameKey)) {
log.warn("Ignoring unreasonably named repository " + f.getAbsolutePath());
} else {
names.add(nameKey);
}
} else if (f.isDirectory()) {
scanProjects(f, prefix + f.getName() + "/", names);
}
}
}
} }

View File

@@ -15,10 +15,8 @@
package com.google.gerrit.server.git; package com.google.gerrit.server.git;
import com.google.gerrit.reviewdb.Project; import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.config.WildProjectName; import com.google.gerrit.server.config.WildProjectName;
import com.google.gwtorm.client.OrmException; import com.google.gerrit.server.project.ProjectCache;
import com.google.gwtorm.client.SchemaFactory;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.Assisted;
@@ -37,18 +35,17 @@ public class PushAllProjectsOp extends DefaultQueueOp {
private static final Logger log = private static final Logger log =
LoggerFactory.getLogger(PushAllProjectsOp.class); LoggerFactory.getLogger(PushAllProjectsOp.class);
private final SchemaFactory<ReviewDb> schema; private final ProjectCache projectCache;
private final ReplicationQueue replication; private final ReplicationQueue replication;
private final Project.NameKey wildProject; private final Project.NameKey wildProject;
private final String urlMatch; private final String urlMatch;
@Inject @Inject
public PushAllProjectsOp(final WorkQueue wq, public PushAllProjectsOp(final WorkQueue wq, final ProjectCache projectCache,
final SchemaFactory<ReviewDb> sf, final ReplicationQueue rq, final ReplicationQueue rq, @WildProjectName final Project.NameKey wp,
@WildProjectName final Project.NameKey wp,
@Assisted @Nullable final String urlMatch) { @Assisted @Nullable final String urlMatch) {
super(wq); super(wq);
this.schema = sf; this.projectCache = projectCache;
this.replication = rq; this.replication = rq;
this.wildProject = wp; this.wildProject = wp;
this.urlMatch = urlMatch; this.urlMatch = urlMatch;
@@ -63,17 +60,12 @@ public class PushAllProjectsOp extends DefaultQueueOp {
public void run() { public void run() {
try { try {
final ReviewDb db = schema.open(); for (final Project.NameKey nameKey : projectCache.all()) {
try { if (!nameKey.equals(wildProject)) {
for (final Project project : db.projects().all()) { replication.scheduleFullSync(nameKey, urlMatch);
if (!project.getNameKey().equals(wildProject)) {
replication.scheduleFullSync(project.getNameKey(), urlMatch);
}
} }
} finally {
db.close();
} }
} catch (OrmException e) { } catch (RuntimeException e) {
log.error("Cannot enumerate known projects", e); log.error("Cannot enumerate known projects", e);
} }
} }

View File

@@ -31,4 +31,18 @@ public interface ProjectCache {
/** Invalidate the cached information about all projects. */ /** Invalidate the cached information about all projects. */
public void evictAll(); public void evictAll();
/** @return sorted iteration of projects. */
public abstract Iterable<Project.NameKey> all();
/**
* Filter the set of registered project names by common prefix.
*
* @param prefix common prefix.
* @return sorted iteration of projects sharing the same prefix.
*/
public abstract Iterable<Project.NameKey> byName(String prefix);
/** Notify the cache that a new project was constructed. */
public void onCreateProject(Project.NameKey newProjectName);
} }

View File

@@ -20,6 +20,7 @@ import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.cache.Cache; import com.google.gerrit.server.cache.Cache;
import com.google.gerrit.server.cache.CacheModule; import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.cache.EntryCreator; import com.google.gerrit.server.cache.EntryCreator;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gwtorm.client.SchemaFactory; import com.google.gwtorm.client.SchemaFactory;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Module; import com.google.inject.Module;
@@ -29,19 +30,31 @@ import com.google.inject.name.Named;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/** Cache of project information, including access rights. */ /** Cache of project information, including access rights. */
@Singleton @Singleton
public class ProjectCacheImpl implements ProjectCache { public class ProjectCacheImpl implements ProjectCache {
private static final String CACHE_NAME = "projects"; private static final String CACHE_NAME = "projects";
private static final String CACHE_LIST = "project_list";
public static Module module() { public static Module module() {
return new CacheModule() { return new CacheModule() {
@Override @Override
protected void configure() { protected void configure() {
final TypeLiteral<Cache<Project.NameKey, ProjectState>> type = final TypeLiteral<Cache<Project.NameKey, ProjectState>> nameType =
new TypeLiteral<Cache<Project.NameKey, ProjectState>>() {}; new TypeLiteral<Cache<Project.NameKey, ProjectState>>() {};
core(type, CACHE_NAME).populateWith(Loader.class); core(nameType, CACHE_NAME).populateWith(Loader.class);
final TypeLiteral<Cache<ListKey, SortedSet<Project.NameKey>>> listType =
new TypeLiteral<Cache<ListKey, SortedSet<Project.NameKey>>>() {};
core(listType, CACHE_LIST).populateWith(Lister.class);
bind(ProjectCacheImpl.class); bind(ProjectCacheImpl.class);
bind(ProjectCache.class).to(ProjectCacheImpl.class); bind(ProjectCache.class).to(ProjectCacheImpl.class);
} }
@@ -49,11 +62,16 @@ public class ProjectCacheImpl implements ProjectCache {
} }
private final Cache<Project.NameKey, ProjectState> byName; private final Cache<Project.NameKey, ProjectState> byName;
private final Cache<ListKey,SortedSet<Project.NameKey>> list;
private final Lock listLock;
@Inject @Inject
ProjectCacheImpl( ProjectCacheImpl(
@Named(CACHE_NAME) final Cache<Project.NameKey, ProjectState> byName) { @Named(CACHE_NAME) final Cache<Project.NameKey, ProjectState> byName,
@Named(CACHE_LIST) final Cache<ListKey, SortedSet<Project.NameKey>> list) {
this.byName = byName; this.byName = byName;
this.list = list;
this.listLock = new ReentrantLock(true /* fair */);
} }
/** /**
@@ -78,6 +96,74 @@ public class ProjectCacheImpl implements ProjectCache {
byName.removeAll(); byName.removeAll();
} }
@Override
public void onCreateProject(Project.NameKey newProjectName) {
listLock.lock();
try {
SortedSet<Project.NameKey> n = list.get(ListKey.ALL);
n = new TreeSet<Project.NameKey>(n);
n.add(newProjectName);
list.put(ListKey.ALL, Collections.unmodifiableSortedSet(n));
} finally {
listLock.unlock();
}
}
@Override
public Iterable<Project.NameKey> all() {
return list.get(ListKey.ALL);
}
@Override
public Iterable<Project.NameKey> byName(final String pfx) {
return new Iterable<Project.NameKey>() {
@Override
public Iterator<Project.NameKey> iterator() {
return new Iterator<Project.NameKey>() {
private Project.NameKey next;
private Iterator<Project.NameKey> itr =
list.get(ListKey.ALL).tailSet(new Project.NameKey(pfx)).iterator();
@Override
public boolean hasNext() {
if (next != null) {
return true;
}
if (!itr.hasNext()) {
return false;
}
Project.NameKey r = itr.next();
if (r.get().startsWith(pfx)) {
next = r;
return true;
} else {
itr = Collections.<Project.NameKey> emptyList().iterator();
return false;
}
}
@Override
public Project.NameKey next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
Project.NameKey r = next;
next = null;
return r;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
};
}
static class Loader extends EntryCreator<Project.NameKey, ProjectState> { static class Loader extends EntryCreator<Project.NameKey, ProjectState> {
private final ProjectState.Factory projectStateFactory; private final ProjectState.Factory projectStateFactory;
private final SchemaFactory<ReviewDb> schema; private final SchemaFactory<ReviewDb> schema;
@@ -107,4 +193,25 @@ public class ProjectCacheImpl implements ProjectCache {
} }
} }
} }
static class ListKey {
static final ListKey ALL = new ListKey();
private ListKey() {
}
}
static class Lister extends EntryCreator<ListKey, SortedSet<Project.NameKey>> {
private final GitRepositoryManager mgr;
@Inject
Lister(GitRepositoryManager mgr) {
this.mgr = mgr;
}
@Override
public SortedSet<Project.NameKey> createEntry(ListKey key) throws Exception {
return mgr.list();
}
}
} }

View File

@@ -30,6 +30,7 @@ import com.google.gerrit.server.config.WildProjectName;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.patch.PatchListCache; import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.project.ChangeControl; import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.query.IntPredicate; import com.google.gerrit.server.query.IntPredicate;
import com.google.gerrit.server.query.Predicate; import com.google.gerrit.server.query.Predicate;
import com.google.gerrit.server.query.QueryBuilder; import com.google.gerrit.server.query.QueryBuilder;
@@ -107,6 +108,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
final Project.NameKey wildProjectName; final Project.NameKey wildProjectName;
final PatchListCache patchListCache; final PatchListCache patchListCache;
final GitRepositoryManager repoManager; final GitRepositoryManager repoManager;
final ProjectCache projectCache;
@Inject @Inject
Arguments(Provider<ReviewDb> dbProvider, Arguments(Provider<ReviewDb> dbProvider,
@@ -118,7 +120,8 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
AuthConfig authConfig, ApprovalTypes approvalTypes, AuthConfig authConfig, ApprovalTypes approvalTypes,
@WildProjectName Project.NameKey wildProjectName, @WildProjectName Project.NameKey wildProjectName,
PatchListCache patchListCache, PatchListCache patchListCache,
GitRepositoryManager repoManager) { GitRepositoryManager repoManager,
ProjectCache projectCache) {
this.dbProvider = dbProvider; this.dbProvider = dbProvider;
this.rewriter = rewriter; this.rewriter = rewriter;
this.userFactory = userFactory; this.userFactory = userFactory;
@@ -131,6 +134,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
this.wildProjectName = wildProjectName; this.wildProjectName = wildProjectName;
this.patchListCache = patchListCache; this.patchListCache = patchListCache;
this.repoManager = repoManager; this.repoManager = repoManager;
this.projectCache = projectCache;
} }
} }
@@ -508,23 +512,19 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
// Try to match a project name by substring query. // Try to match a project name by substring query.
final List<ProjectPredicate> predicate = final List<ProjectPredicate> predicate =
new ArrayList<ProjectPredicate>(); new ArrayList<ProjectPredicate>();
try { for (Project.NameKey name : args.projectCache.all()) {
for (final Project p : args.dbProvider.get().projects().all()) { if (name.get().toLowerCase().contains(query.toLowerCase())) {
if (p.getName().toLowerCase().contains(query.toLowerCase())) { predicate.add(new ProjectPredicate(args.dbProvider, name.get()));
predicate.add(new ProjectPredicate(args.dbProvider, p.getName()));
}
} }
}
// If two or more projects contains "query" as substring create an // If two or more projects contains "query" as substring create an
// OrPredicate holding predicates for all these projects, otherwise if // OrPredicate holding predicates for all these projects, otherwise if
// only one contains that, return only that one predicate by itself. // only one contains that, return only that one predicate by itself.
if (predicate.size() == 1) { if (predicate.size() == 1) {
return predicate.get(0); return predicate.get(0);
} else if (predicate.size() > 1) { } else if (predicate.size() > 1) {
return Predicate.or(predicate); return Predicate.or(predicate);
}
} catch (OrmException e) {
throw error("Cannot lookup project.", e);
} }
throw error("Unsupported query:" + query); throw error("Unsupported query:" + query);

View File

@@ -38,7 +38,8 @@ public class ChangeQueryRewriter extends QueryRewriter<ChangeData> {
new ChangeQueryBuilder.Arguments( // new ChangeQueryBuilder.Arguments( //
new InvalidProvider<ReviewDb>(), // new InvalidProvider<ReviewDb>(), //
new InvalidProvider<ChangeQueryRewriter>(), // new InvalidProvider<ChangeQueryRewriter>(), //
null, null, null, null, null, null, null, null, null, null), null)); null, null, null, null, null, null, null, //
null, null, null, null), null));
private final Provider<ReviewDb> dbProvider; private final Provider<ReviewDb> dbProvider;

View File

@@ -27,6 +27,7 @@ import com.google.gerrit.server.config.ProjectCreatorGroups;
import com.google.gerrit.server.config.ProjectOwnerGroups; import com.google.gerrit.server.config.ProjectOwnerGroups;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.ReplicationQueue; import com.google.gerrit.server.git.ReplicationQueue;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectControl; import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.sshd.BaseCommand; import com.google.gerrit.sshd.BaseCommand;
import com.google.gwtorm.client.OrmException; import com.google.gwtorm.client.OrmException;
@@ -101,6 +102,9 @@ final class CreateProject extends BaseCommand {
@Inject @Inject
private GitRepositoryManager repoManager; private GitRepositoryManager repoManager;
@Inject
private ProjectCache projectCache;
@Inject @Inject
@ProjectCreatorGroups @ProjectCreatorGroups
private Set<AccountGroup.Id> projectCreatorGroups; private Set<AccountGroup.Id> projectCreatorGroups;
@@ -223,6 +227,7 @@ final class CreateProject extends BaseCommand {
} }
db.projects().insert(Collections.singleton(newProject)); db.projects().insert(Collections.singleton(newProject));
projectCache.onCreateProject(newProject.getNameKey());
} }
private void validateParameters() throws Failure { private void validateParameters() throws Failure {

View File

@@ -15,7 +15,6 @@
package com.google.gerrit.sshd.commands; package com.google.gerrit.sshd.commands;
import com.google.gerrit.reviewdb.Project; import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.WildProjectName; import com.google.gerrit.server.config.WildProjectName;
import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.GitRepositoryManager;
@@ -23,7 +22,6 @@ import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectControl; import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.ProjectState; import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.sshd.BaseCommand; import com.google.gerrit.sshd.BaseCommand;
import com.google.gwtorm.client.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import org.apache.sshd.server.Environment; import org.apache.sshd.server.Environment;
@@ -43,9 +41,6 @@ final class ListProjects extends BaseCommand {
private static final String DEFAULT_TAB_SEPARATOR = "|"; private static final String DEFAULT_TAB_SEPARATOR = "|";
private static final String NOT_VISIBLE_PROJECT = "(x)"; private static final String NOT_VISIBLE_PROJECT = "(x)";
@Inject
private ReviewDb db;
@Inject @Inject
private IdentifiedUser currentUser; private IdentifiedUser currentUser;
@@ -93,14 +88,14 @@ final class ListProjects extends BaseCommand {
} }
try { try {
for (final Project p : db.projects().all()) { for (final Project.NameKey projectName : projectCache.all()) {
if (p.getNameKey().equals(wildProject)) { if (projectName.equals(wildProject)) {
// This project "doesn't exist". At least not as a repository. // This project "doesn't exist". At least not as a repository.
// //
continue; continue;
} }
final ProjectState e = projectCache.get(p.getNameKey()); final ProjectState e = projectCache.get(projectName);
if (e == null) { if (e == null) {
// If we can't get it from the cache, pretend its not present. // If we can't get it from the cache, pretend its not present.
// //
@@ -118,7 +113,7 @@ final class ListProjects extends BaseCommand {
} }
if (showBranch != null) { if (showBranch != null) {
final Ref ref = getBranchRef(p.getNameKey()); final Ref ref = getBranchRef(projectName);
if (ref == null || ref.getObjectId() == null if (ref == null || ref.getObjectId() == null
|| !pctl.controlForRef(ref.getLeaf().getName()).isVisible()) { || !pctl.controlForRef(ref.getLeaf().getName()).isVisible()) {
// No branch, or the user can't see this branch, so skip it. // No branch, or the user can't see this branch, so skip it.
@@ -130,9 +125,10 @@ final class ListProjects extends BaseCommand {
stdout.print(' '); stdout.print(' ');
} }
stdout.print(p.getName() + "\n"); stdout.print(projectName.get() + "\n");
} else { } else {
treeMap.put(p.getName(), new TreeNode(p, pctl.isVisible())); treeMap.put(projectName.get(),
new TreeNode(pctl.getProject(), pctl.isVisible()));
} }
} }
@@ -144,7 +140,7 @@ final class ListProjects extends BaseCommand {
for (final TreeNode key : treeMap.values()) { for (final TreeNode key : treeMap.values()) {
final String parentName = key.getParentName(); final String parentName = key.getParentName();
if (parentName != null) { if (parentName != null) {
final TreeNode node = treeMap.get((String)parentName); final TreeNode node = treeMap.get(parentName);
if (node != null) { if (node != null) {
node.addChild(key); node.addChild(key);
} else { } else {
@@ -161,8 +157,6 @@ final class ListProjects extends BaseCommand {
printElement(stdout, fakeRoot, -1, false, sortedNodes.get(sortedNodes.size() - 1)); printElement(stdout, fakeRoot, -1, false, sortedNodes.get(sortedNodes.size() - 1));
stdout.flush(); stdout.flush();
} }
} catch (OrmException e) {
throw new Failure(1, "fatal: database error", e);
} finally { } finally {
stdout.flush(); stdout.flush();
} }