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:
@@ -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>>() {
|
|
||||||
public List<Project.NameKey> run(final ReviewDb db) throws OrmException {
|
|
||||||
final String a = query;
|
|
||||||
final String b = a + MAX_SUFFIX;
|
|
||||||
final int max = 10;
|
final int max = 10;
|
||||||
final int n = limit <= 0 ? max : Math.min(limit, max);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
r.add(ctl.getProject().getNameKey());
|
||||||
|
if (r.size() == n) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return r;
|
callback.onSuccess(r);
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void suggestAccount(final String query, final Boolean active,
|
public void suggestAccount(final String query, final Boolean active,
|
||||||
|
|||||||
@@ -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,35 +33,28 @@ 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();
|
|
||||||
} else {
|
|
||||||
result = new ArrayList<Project>();
|
|
||||||
for (Project p : db.projects().all().toList()) {
|
|
||||||
try {
|
try {
|
||||||
ProjectControl c = projectControlFactory.controlFor(p.getNameKey());
|
ProjectControl c = projectControlFactory.controlFor(p);
|
||||||
if (c.isVisible() || c.isOwner()) {
|
if (c.isVisible() || c.isOwner()) {
|
||||||
result.add(p);
|
result.add(c.getProject());
|
||||||
}
|
}
|
||||||
} catch (NoSuchProjectException e) {
|
} catch (NoSuchProjectException e) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Collections.sort(result, new Comparator<Project>() {
|
Collections.sort(result, new Comparator<Project>() {
|
||||||
public int compare(final Project a, final Project b) {
|
public int compare(final Project a, final Project b) {
|
||||||
return a.getName().compareTo(b.getName());
|
return a.getName().compareTo(b.getName());
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
} catch (RuntimeException e) {
|
||||||
db.close();
|
|
||||||
}
|
|
||||||
} catch (OrmException e) {
|
|
||||||
log.error("Cannot enumerate known projects", e);
|
log.error("Cannot enumerate known projects", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,10 +512,9 @@ 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()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -523,9 +526,6 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
|||||||
} 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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user