Index project on creation and cache eviction
Change-Id: I5b737803dae56331845c46719dc30de99ad6ff27
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index ad2c50c..9debc2e 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -407,6 +407,10 @@
+
Update of the group secondary index
+* `com.google.gerrit.server.extensions.events.ProjectIndexedListener`:
++
+Update of the project secondary index
+
* `com.google.gerrit.httpd.WebLoginListener`:
+
User login or logout interactively on the Web user interface.
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/ProjectIndexedListener.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/ProjectIndexedListener.java
new file mode 100644
index 0000000..bfbd851
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/ProjectIndexedListener.java
@@ -0,0 +1,28 @@
+// Copyright (C) 2017 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.extensions.events;
+
+import com.google.gerrit.extensions.annotations.ExtensionPoint;
+
+/** Notified whenever a project is indexed */
+@ExtensionPoint
+public interface ProjectIndexedListener {
+ /**
+ * Invoked when a project is indexed
+ *
+ * @param name of the project
+ */
+ void onProjectIndexed(String project);
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
index b7aa416..cff720c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
@@ -2563,7 +2563,11 @@
}
if (isConfig(cmd)) {
logDebug("Reloading project in cache");
- projectCache.evict(project);
+ try {
+ projectCache.evict(project);
+ } catch (IOException e) {
+ log.warn("Cannot evict from project cache, name key: " + project.getName(), e);
+ }
ProjectState ps = projectCache.get(project.getNameKey());
try {
logDebug("Updating project description");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java
index 6854a87..8c9a964 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/IndexModule.java
@@ -45,6 +45,12 @@
import com.google.gerrit.server.index.group.GroupIndexer;
import com.google.gerrit.server.index.group.GroupIndexerImpl;
import com.google.gerrit.server.index.group.GroupSchemaDefinitions;
+import com.google.gerrit.server.index.project.ProjectIndexCollection;
+import com.google.gerrit.server.index.project.ProjectIndexDefinition;
+import com.google.gerrit.server.index.project.ProjectIndexRewriter;
+import com.google.gerrit.server.index.project.ProjectIndexer;
+import com.google.gerrit.server.index.project.ProjectIndexerImpl;
+import com.google.gerrit.server.index.project.ProjectSchemaDefinitions;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Provides;
@@ -70,7 +76,8 @@
ImmutableList.<SchemaDefinitions<?>>of(
AccountSchemaDefinitions.INSTANCE,
ChangeSchemaDefinitions.INSTANCE,
- GroupSchemaDefinitions.INSTANCE);
+ GroupSchemaDefinitions.INSTANCE,
+ ProjectSchemaDefinitions.INSTANCE);
/** Type of secondary index. */
public static IndexType getIndexType(Injector injector) {
@@ -112,14 +119,22 @@
listener().to(GroupIndexCollection.class);
factory(GroupIndexerImpl.Factory.class);
+ bind(ProjectIndexRewriter.class);
+ bind(ProjectIndexCollection.class);
+ listener().to(ProjectIndexCollection.class);
+ factory(ProjectIndexerImpl.Factory.class);
+
DynamicSet.setOf(binder(), OnlineUpgradeListener.class);
}
@Provides
Collection<IndexDefinition<?, ?, ?>> getIndexDefinitions(
- AccountIndexDefinition accounts, ChangeIndexDefinition changes, GroupIndexDefinition groups) {
+ AccountIndexDefinition accounts,
+ ChangeIndexDefinition changes,
+ GroupIndexDefinition groups,
+ ProjectIndexDefinition projects) {
Collection<IndexDefinition<?, ?, ?>> result =
- ImmutableList.<IndexDefinition<?, ?, ?>>of(accounts, groups, changes);
+ ImmutableList.<IndexDefinition<?, ?, ?>>of(accounts, groups, changes, projects);
Set<String> expected =
FluentIterable.from(ALL_SCHEMA_DEFS).transform(SchemaDefinitions::getName).toSet();
Set<String> actual = FluentIterable.from(result).transform(IndexDefinition::getName).toSet();
@@ -156,6 +171,13 @@
@Provides
@Singleton
+ ProjectIndexer getProjectIndexer(
+ ProjectIndexerImpl.Factory factory, ProjectIndexCollection indexes) {
+ return factory.create(indexes);
+ }
+
+ @Provides
+ @Singleton
@IndexExecutor(INTERACTIVE)
ListeningExecutorService getInteractiveIndexExecutor(
@GerritServerConfig Config config, WorkQueue workQueue) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/project/IndexedProjectQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/IndexedProjectQuery.java
new file mode 100644
index 0000000..ede7461
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/IndexedProjectQuery.java
@@ -0,0 +1,34 @@
+// Copyright (C) 2017 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.index.project;
+
+import com.google.gerrit.index.Index;
+import com.google.gerrit.index.IndexedQuery;
+import com.google.gerrit.index.QueryOptions;
+import com.google.gerrit.index.query.DataSource;
+import com.google.gerrit.index.query.Predicate;
+import com.google.gerrit.index.query.QueryParseException;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.project.ProjectState;
+
+public class IndexedProjectQuery extends IndexedQuery<Project.NameKey, ProjectState>
+ implements DataSource<ProjectState> {
+
+ public IndexedProjectQuery(
+ Index<Project.NameKey, ProjectState> index, Predicate<ProjectState> pred, QueryOptions opts)
+ throws QueryParseException {
+ super(index, pred, opts.convertForBackend());
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndexRewriter.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndexRewriter.java
new file mode 100644
index 0000000..e50d08e
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndexRewriter.java
@@ -0,0 +1,43 @@
+// Copyright (C) 2017 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.index.project;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.gerrit.index.IndexRewriter;
+import com.google.gerrit.index.QueryOptions;
+import com.google.gerrit.index.query.Predicate;
+import com.google.gerrit.index.query.QueryParseException;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+@Singleton
+public class ProjectIndexRewriter implements IndexRewriter<ProjectState> {
+ private final ProjectIndexCollection indexes;
+
+ @Inject
+ ProjectIndexRewriter(ProjectIndexCollection indexes) {
+ this.indexes = indexes;
+ }
+
+ @Override
+ public Predicate<ProjectState> rewrite(Predicate<ProjectState> in, QueryOptions opts)
+ throws QueryParseException {
+ ProjectIndex index = indexes.getSearchIndex();
+ checkNotNull(index, "no active search index configured for projects");
+ return new IndexedProjectQuery(index, in, opts);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndexer.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndexer.java
new file mode 100644
index 0000000..e8a8183
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndexer.java
@@ -0,0 +1,28 @@
+// Copyright (C) 2017 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.index.project;
+
+import com.google.gerrit.reviewdb.client.Project;
+import java.io.IOException;
+
+public interface ProjectIndexer {
+
+ /**
+ * Synchronously index a project.
+ *
+ * @param nameKey name key of project to index.
+ */
+ void index(Project.NameKey nameKey) throws IOException;
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java
new file mode 100644
index 0000000..368a056
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java
@@ -0,0 +1,86 @@
+// Copyright (C) 2017 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.index.project;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.extensions.events.ProjectIndexedListener;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.index.Index;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.inject.assistedinject.Assisted;
+import com.google.inject.assistedinject.AssistedInject;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+
+public class ProjectIndexerImpl implements ProjectIndexer {
+ public interface Factory {
+ ProjectIndexerImpl create(ProjectIndexCollection indexes);
+
+ ProjectIndexerImpl create(@Nullable ProjectIndex index);
+ }
+
+ private final ProjectCache projectCache;
+ private final DynamicSet<ProjectIndexedListener> indexedListener;
+ private final ProjectIndexCollection indexes;
+ private final ProjectIndex index;
+
+ @AssistedInject
+ ProjectIndexerImpl(
+ ProjectCache projectCache,
+ DynamicSet<ProjectIndexedListener> indexedListener,
+ @Assisted ProjectIndexCollection indexes) {
+ this.projectCache = projectCache;
+ this.indexedListener = indexedListener;
+ this.indexes = indexes;
+ this.index = null;
+ }
+
+ @AssistedInject
+ ProjectIndexerImpl(
+ ProjectCache projectCache,
+ DynamicSet<ProjectIndexedListener> indexedListener,
+ @Assisted ProjectIndex index) {
+ this.projectCache = projectCache;
+ this.indexedListener = indexedListener;
+ this.indexes = null;
+ this.index = index;
+ }
+
+ @Override
+ public void index(Project.NameKey nameKey) throws IOException {
+ for (Index<?, ProjectState> i : getWriteIndexes()) {
+ i.replace(projectCache.get(nameKey));
+ }
+ fireProjectIndexedEvent(nameKey.get());
+ }
+
+ private void fireProjectIndexedEvent(String name) {
+ for (ProjectIndexedListener listener : indexedListener) {
+ listener.onProjectIndexed(name);
+ }
+ }
+
+ private Collection<ProjectIndex> getWriteIndexes() {
+ if (indexes != null) {
+ return indexes.getWriteIndexes();
+ }
+
+ return index != null ? Collections.singleton(index) : ImmutableSet.of();
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/PerRequestProjectControlCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/PerRequestProjectControlCache.java
index 0f71ac8..b68446f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/PerRequestProjectControlCache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/PerRequestProjectControlCache.java
@@ -18,6 +18,7 @@
import com.google.gerrit.server.CurrentUser;
import com.google.inject.Inject;
import com.google.inject.servlet.RequestScoped;
+import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@@ -48,7 +49,7 @@
return ctl;
}
- public void evict(Project project) {
+ public void evict(Project project) throws IOException {
projectCache.evict(project);
controls.remove(project.getNameKey());
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java
index 65c7315..63052bd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java
@@ -45,17 +45,27 @@
*/
ProjectState checkedGet(Project.NameKey projectName) throws IOException;
- /** Invalidate the cached information about the given project. */
- void evict(Project p);
+ /**
+ * Invalidate the cached information about the given project, and triggers reindexing for it
+ *
+ * @param p project that is being evicted
+ * @throws IOException thrown if the reindexing fails
+ */
+ void evict(Project p) throws IOException;
- /** Invalidate the cached information about the given project. */
- void evict(Project.NameKey p);
+ /**
+ * Invalidate the cached information about the given project, and triggers reindexing for it
+ *
+ * @param p the NameKey of the project that is being evicted
+ * @throws IOException thrown if the reindexing fails
+ */
+ void evict(Project.NameKey p) throws IOException;
/**
* Remove information about the given project from the cache. It will no longer be returned from
* {@link #all()}.
*/
- void remove(Project p);
+ void remove(Project p) throws IOException;
/** @return sorted iteration of projects. */
Iterable<Project.NameKey> all();
@@ -75,5 +85,5 @@
Iterable<Project.NameKey> byName(String prefix);
/** Notify the cache that a new project was constructed. */
- void onCreateProject(Project.NameKey newProjectName);
+ void onCreateProject(Project.NameKey newProjectName) throws IOException;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java
index 6ee143c..2b31ce3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java
@@ -28,8 +28,10 @@
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.index.project.ProjectIndexer;
import com.google.inject.Inject;
import com.google.inject.Module;
+import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.UniqueAnnotations;
@@ -82,6 +84,7 @@
private final LoadingCache<ListKey, SortedSet<Project.NameKey>> list;
private final Lock listLock;
private final ProjectCacheClock clock;
+ private final Provider<ProjectIndexer> indexer;
@Inject
ProjectCacheImpl(
@@ -89,13 +92,15 @@
final AllUsersName allUsersName,
@Named(CACHE_NAME) LoadingCache<String, ProjectState> byName,
@Named(CACHE_LIST) LoadingCache<ListKey, SortedSet<Project.NameKey>> list,
- ProjectCacheClock clock) {
+ ProjectCacheClock clock,
+ Provider<ProjectIndexer> indexer) {
this.allProjectsName = allProjectsName;
this.allUsersName = allUsersName;
this.byName = byName;
this.list = list;
this.listLock = new ReentrantLock(true /* fair */);
this.clock = clock;
+ this.indexer = indexer;
}
@Override
@@ -151,22 +156,20 @@
}
@Override
- public void evict(Project p) {
- if (p != null) {
- byName.invalidate(p.getNameKey().get());
- }
+ public void evict(Project p) throws IOException {
+ evict(p.getNameKey());
}
- /** Invalidate the cached information about the given project. */
@Override
- public void evict(Project.NameKey p) {
+ public void evict(Project.NameKey p) throws IOException {
if (p != null) {
byName.invalidate(p.get());
}
+ indexer.get().index(p);
}
@Override
- public void remove(Project p) {
+ public void remove(Project p) throws IOException {
listLock.lock();
try {
SortedSet<Project.NameKey> n = Sets.newTreeSet(list.get(ListKey.ALL));
@@ -181,7 +184,7 @@
}
@Override
- public void onCreateProject(Project.NameKey newProjectName) {
+ public void onCreateProject(Project.NameKey newProjectName) throws IOException {
listLock.lock();
try {
SortedSet<Project.NameKey> n = Sets.newTreeSet(list.get(ListKey.ALL));
@@ -192,6 +195,7 @@
} finally {
listLock.unlock();
}
+ indexer.get().index(newProjectName);
}
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
index 7a7418c..1166970 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
@@ -390,6 +390,10 @@
}
}
+ public boolean canRead() {
+ return !isHidden() && allRefsAreVisible(Collections.emptySet());
+ }
+
ForProject asForProject() {
return new ForProjectImpl();
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/project/ProjectIsVisibleToPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/project/ProjectIsVisibleToPredicate.java
new file mode 100644
index 0000000..07b1722
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/project/ProjectIsVisibleToPredicate.java
@@ -0,0 +1,48 @@
+// Copyright (C) 2017 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.query.project;
+
+import com.google.gerrit.index.query.IsVisibleToPredicate;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.index.IndexUtils;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.ProjectPermission;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.query.account.AccountQueryBuilder;
+import com.google.gwtorm.server.OrmException;
+
+public class ProjectIsVisibleToPredicate extends IsVisibleToPredicate<ProjectState> {
+ protected final PermissionBackend permissionBackend;
+ protected final CurrentUser user;
+
+ public ProjectIsVisibleToPredicate(PermissionBackend permissionBackend, CurrentUser user) {
+ super(AccountQueryBuilder.FIELD_VISIBLETO, IndexUtils.describe(user));
+ this.permissionBackend = permissionBackend;
+ this.user = user;
+ }
+
+ @Override
+ public boolean match(ProjectState projectState) throws OrmException {
+ return permissionBackend
+ .user(user)
+ .project(projectState.getProject().getNameKey())
+ .testOrFalse(ProjectPermission.READ);
+ }
+
+ @Override
+ public int getCost() {
+ return 1;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java
index f3efdc1..2457f33 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java
@@ -14,14 +14,19 @@
package com.google.gerrit.server.query.project;
+import com.google.common.primitives.Ints;
+import com.google.gerrit.index.query.LimitPredicate;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.index.query.QueryBuilder;
+import com.google.gerrit.index.query.QueryParseException;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.project.ProjectState;
import com.google.inject.Inject;
/** Parses a query string meant to be applied to project objects. */
public class ProjectQueryBuilder extends QueryBuilder<ProjectState> {
+ public static final String FIELD_LIMIT = "limit";
+
private static final QueryBuilder.Definition<ProjectState, ProjectQueryBuilder> mydef =
new QueryBuilder.Definition<>(ProjectQueryBuilder.class);
@@ -34,4 +39,13 @@
public Predicate<ProjectState> name(String name) {
return ProjectPredicates.name(new Project.NameKey(name));
}
+
+ @Operator
+ public Predicate<ProjectState> limit(String query) throws QueryParseException {
+ Integer limit = Ints.tryParse(query);
+ if (limit == null) {
+ throw error("Invalid limit: " + query);
+ }
+ return new LimitPredicate<>(FIELD_LIMIT, limit);
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/project/ProjectQueryProcessor.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/project/ProjectQueryProcessor.java
new file mode 100644
index 0000000..234a67b
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/project/ProjectQueryProcessor.java
@@ -0,0 +1,79 @@
+// Copyright (C) 2017 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.query.project;
+
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.gerrit.server.query.project.ProjectQueryBuilder.FIELD_LIMIT;
+
+import com.google.gerrit.index.IndexConfig;
+import com.google.gerrit.index.query.AndSource;
+import com.google.gerrit.index.query.IndexPredicate;
+import com.google.gerrit.index.query.Predicate;
+import com.google.gerrit.index.query.QueryProcessor;
+import com.google.gerrit.metrics.MetricMaker;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.AccountLimits;
+import com.google.gerrit.server.index.project.ProjectIndexCollection;
+import com.google.gerrit.server.index.project.ProjectIndexRewriter;
+import com.google.gerrit.server.index.project.ProjectSchemaDefinitions;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+/**
+ * Query processor for the project index.
+ *
+ * <p>Instances are one-time-use. Other singleton classes should inject a Provider rather than
+ * holding on to a single instance.
+ */
+public class ProjectQueryProcessor extends QueryProcessor<ProjectState> {
+ private final PermissionBackend permissionBackend;
+ private final Provider<CurrentUser> userProvider;
+
+ static {
+ // It is assumed that basic rewrites do not touch visibleto predicates.
+ checkState(
+ !ProjectIsVisibleToPredicate.class.isAssignableFrom(IndexPredicate.class),
+ "ProjectQueryProcessor assumes visibleto is not used by the index rewriter.");
+ }
+
+ @Inject
+ protected ProjectQueryProcessor(
+ Provider<CurrentUser> userProvider,
+ AccountLimits.Factory limitsFactory,
+ MetricMaker metricMaker,
+ IndexConfig indexConfig,
+ ProjectIndexCollection indexes,
+ ProjectIndexRewriter rewriter,
+ PermissionBackend permissionBackend) {
+ super(
+ metricMaker,
+ ProjectSchemaDefinitions.INSTANCE,
+ indexConfig,
+ indexes,
+ rewriter,
+ FIELD_LIMIT,
+ () -> limitsFactory.create(userProvider.get()).getQueryLimit());
+ this.permissionBackend = permissionBackend;
+ this.userProvider = userProvider;
+ }
+
+ @Override
+ protected Predicate<ProjectState> enforceVisibility(Predicate<ProjectState> pred) {
+ return new AndSource<>(
+ pred, new ProjectIsVisibleToPredicate(permissionBackend, userProvider.get()), start);
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java
index 0d7fa24..6649fcb 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java
@@ -174,7 +174,13 @@
err.append("error: ").append(msg).append("\n");
}
- projectCache.evict(nameKey);
+ try {
+ projectCache.evict(nameKey);
+ } catch (IOException e) {
+ final String msg = "Cannot reindex project: " + name;
+ log.error(msg, e);
+ err.append("error: ").append(msg).append("\n");
+ }
}
if (err.length() > 0) {