Merge "Change style of commit message box display and use UiBinder."
diff --git a/Documentation/cmd-create-project.txt b/Documentation/cmd-create-project.txt
index 85d1a92..02aa078 100644
--- a/Documentation/cmd-create-project.txt
+++ b/Documentation/cmd-create-project.txt
@@ -19,7 +19,7 @@
[--use-signed-off-by | --so]
[--use-content-merge]
[--require-change-id | --id]
- [--branch <REF> | -b <REF>]
+ [[--branch <REF> | -b <REF>] ...]
[--empty-commit]
{ <NAME> | --name <NAME> }
@@ -59,8 +59,11 @@
--branch::
-b::
- Name of the initial branch in the newly created project.
- Defaults to 'master'.
+ Name of the initial branch(es) in the newly created project.
+ Several branches can be specified on the command line.
+ If several branches are specified then the first one becomes HEAD
+ of the project. If none branches are specified then default value
+ ('master') is used.
--owner::
-o::
diff --git a/Documentation/cmd-ls-projects.txt b/Documentation/cmd-ls-projects.txt
index c1b37fa..25cd9a9 100644
--- a/Documentation/cmd-ls-projects.txt
+++ b/Documentation/cmd-ls-projects.txt
@@ -46,8 +46,11 @@
Allows listing of projects together with their respective
description.
+
-Line-feeds are escaped to allow ls-project to keep the
-"one project per line"-style.
+For text format output, all non-printable characters (ASCII value 31 or
+less) are escaped according to the conventions used in languages like C,
+Python, and Perl, employing standard sequences like `\n` and `\t`, and
+`\xNN` for all others. In shell scripts, the `printf` command can be
+used to unescape the output.
--tree::
-t::
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 2c050d4..6e50ef4 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -354,8 +354,8 @@
[[cache.name.maxAge]]cache.<name>.maxAge::
+
-Maximum age to keep an entry in the cache. If an entry has not
-been accessed in this period of time, it is removed from the cache.
+Maximum age to keep an entry in the cache. Entries are removed from
+the cache and refreshed from source data every maxAge interval.
Values should use common unit suffixes to express their setting:
+
* s, sec, second, seconds
@@ -371,7 +371,7 @@
supplied, the maximum age is infinite and items are never purged
except when the cache is full.
+
-Default is `90 days` for most caches, except:
+Default is `0`, meaning store forever with no expire, except:
+
* `"adv_bases"`: default is `10 minutes`
* `"ldap_groups"`: default is `1 hour`
@@ -379,33 +379,42 @@
[[cache.name.memoryLimit]]cache.<name>.memoryLimit::
+
-Maximum number of cache items to retain in memory. Keep in mind
-this is total number of items, not bytes of heap used.
+The total cost of entries to retain in memory. The cost computation
+varies by the cache. For most caches where the in-memory size of each
+entry is relatively the same, memoryLimit is currently defined to be
+the number of entries held by the cache (each entry costs 1).
++
+For caches where the size of an entry can vary significantly between
+individual entries (notably `"diff"`, `"diff_intraline"`), memoryLimit
+is an approximation of the total number of bytes stored by the cache.
+Larger entries that represent bigger patch sets or longer source files
+will consume a bigger portion of the memoryLimit. For these caches the
+memoryLimit should be set to roughly the amount of RAM (in bytes) the
+administrator can dedicate to the cache.
+
Default is 1024 for most caches, except:
+
* `"adv_bases"`: default is `4096`
-* `"diff"`: default is `128`
-* `"diff_intraline"`: default is `128`
+* `"diff"`: default is `10m` (10 MiB of memory)
+* `"diff_intraline"`: default is `10m` (10 MiB of memory)
+* `"plugin_resources"`: default is 2m (2 MiB of memory)
+
++
+If set to 0 the cache is disabled. Entries are removed immediately
+after being stored by the cache. This is primarily useful for testing.
[[cache.name.diskLimit]]cache.<name>.diskLimit::
+
-Maximum number of cache items to retain on disk, if this cache
-supports storing its items to disk. Like memoryLimit, this is
-total number of items, not bytes of disk used. If 0, disk storage
-for this cache is disabled.
+Total size in bytes of the keys and values stored on disk. Caches that
+have grown bigger than this size are scanned daily at 1 AM local
+server time to trim the cache. Entries are removed in least recently
+accessed order until the cache fits within this limit. Caches may
+grow larger than this during the day, as the size check is only
+performed once every 24 hours.
+
-Default is 16384.
-
-[[cache.name.diskBuffer]]cache.<name>.diskBuffer::
+Default is 128 MiB per cache.
+
-Number of bytes to buffer in memory before writing less frequently
-accessed cache items to disk, if this cache supports storing its
-items to disk.
-+
-Default is 5 MiB.
-+
-Common unit suffixes of 'k', 'm', or 'g' are supported.
+If 0, disk storage for the cache is disabled.
[[cache_names]]Standard Caches
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -447,14 +456,10 @@
directory and file levels. Gerrit uses this cache to accelerate
the display of affected file names, as well as file contents.
+
-Entries in this cache are relatively large, so the memory limit
-should not be set incredibly high. Administrators should try to
-target cache.diff.memoryLimit to be roughly the number of changes
-which their users will process in a 1 or 2 day span.
-+
-Keeping entries for 90 days gives sufficient time for most changes
-to be submitted or abandoned before their relevant difference items
-expire out.
+Entries in this cache are relatively large, so memoryLimit is an
+estimate in bytes of memory used. Administrators should try to target
+cache.diff.memoryLimit to fit all changes users will view in a 1 or 2
+day span.
cache `"diff_intraline"`::
+
@@ -462,14 +467,10 @@
between two commits. Gerrit uses this cache to accelerate display of
intraline differences when viewing a file.
+
-Entries in this cache are relatively large, so the memory limit
-should not be set incredibly high. Administrators should try to
-target cache.diff.memoryLimit to be roughly the number of changes
-which their users will process in a 1 or 2 day span.
-+
-Keeping entries for 90 days gives sufficient time for most changes
-to be submitted or abandoned before their relevant difference items
-expire out.
+Entries in this cache are relatively large, so memoryLimit is an
+estimate in bytes of memory used. Administrators should try to target
+cache.diff.memoryLimit to fit all files users will view in a 1 or 2
+day span.
cache `"git_tags"`::
+
@@ -517,6 +518,12 @@
expressions are used, so this cache remembers the ordering for
each branch.
+cache `"plugin_resources"`::
++
+Caches formatted plugin resources, such as plugin documentation that
+has been converted from Markdown to HTML. The memoryLimit refers to
+the bytes of memory dedicated to storing the documentation.
+
cache `"projects"`::
+
Caches the project description records, from the `projects` table
@@ -550,8 +557,8 @@
unable to persist the session information. Enabling a disk cache
is strongly recommended.
+
-Session storage is relatively inexpensive, the average entry in
-this cache is approximately 248 bytes, depending on the JVM.
+Session storage is relatively inexpensive. The average entry in
+this cache is approximately 346 bytes.
See also link:cmd-flush-caches.html[gerrit flush-caches].
@@ -598,13 +605,6 @@
+
Default is true, enabled.
-cache.plugin_resources.memoryLimit::
-+
-Number of bytes of memory to use to cache formatted plugin resources,
-such as plugin documentation that has been converted from Markdown to
-HTML. Default is 2 MiB. Common unit suffixes of 'k', 'm', or 'g' are
-supported.
-
cache.projects.checkFrequency::
+
How often project configuration should be checked for update from Git.
diff --git a/Documentation/dev-release-subproject.txt b/Documentation/dev-release-subproject.txt
index a9d0553..b0607fd 100644
--- a/Documentation/dev-release-subproject.txt
+++ b/Documentation/dev-release-subproject.txt
@@ -4,43 +4,38 @@
Preparing a New Gerrit Subproject Snapshot for Publishing
---------------------------------------------------------
-* You will need to have the following in the pom.xml to make it deployable to:
-gerrit-maven-repository.googlecode.com
+* You will need to have the following in the pom.xml to make it
+ deployable to the gerrit-maven storage bucket:
+
----
<distributionManagement>
- <snapshotRepository>
- <id>gerrit-snapshot-repository</id>
- <name>gerrit Snapshot Repository</name>
- <url>dav:https://gerrit-maven-repository.googlecode.com/svn/</url>
- <uniqueVersion>true</uniqueVersion>
- </snapshotRepository>
-
<repository>
- <id>gerrit-maven-repository</id>
+ <id>gerrit-maven</id>
<name>gerrit Maven Repository</name>
- <url>dav:https://gerrit-maven-repository.googlecode.com/svn/</url>
+ <url>s3://gerrit-maven@commondatastorage.googleapis.com</url>
<uniqueVersion>true</uniqueVersion>
</repository>
</distributionManagement>
----
-* Since ubuntu maven is incomplete, also add this to the pom.xml:
+* Add this to the pom.xml to enable the wagon provider:
----
<build>
- <extensions>
- <extension>
- <groupId>org.apache.maven.wagon</groupId>
- <artifactId>wagon-webdav-jackrabbit</artifactId>
- <version>1.0-beta-6</version>
- </extension>
+ <extensions>
+ <extension>
+ <groupId>net.anzix.aws</groupId>
+ <artifactId>s3-maven-wagon</artifactId>
+ <version>3.2</version>
+ </extension>
</extensions>
</build>
----
-* Add your username and password to your ~/.m2/settings.xml file:
+* Add your username and password to your ~/.m2/settings.xml file.
+ These need to come from the link:https://code.google.com/apis/console/[API Console].
----
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
@@ -48,15 +43,9 @@
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<servers>
<server>
- <id>gerrit-maven-repository</id>
- <username>JohnDoe@example.com</username>
- <password>OpenSessame</password>
- </server>
-
- <server>
- <id>gerrit-snapshot-repository</id>
- <username>JohnDoe@example.com</username>
- <password>OpenSessame</password>
+ <id>gerrit-maven</id>
+ <username>GOOG..EXAMPLE.....EXAMPLE</username>
+ <password>EXAMPLE..EXAMPLE..EXAMPLE</password>
</server>
</servers>
</settings>
diff --git a/Documentation/dev-release.txt b/Documentation/dev-release.txt
index 7484795..f65f7fc 100644
--- a/Documentation/dev-release.txt
+++ b/Documentation/dev-release.txt
@@ -64,6 +64,9 @@
Create the Actual Release
---------------------------
+In the example commands below we assume that the last release was '2.4' and that
+we are preparing '2.5' release.
+
Prepare the Subprojects
~~~~~~~~~~~~~~~~~~~~~~~
@@ -75,20 +78,21 @@
Prepare Gerrit
~~~~~~~~~~~~~~
-* Update the top level pom in Gerrit to ensure that none of the Subprojects
- point to snapshot releases
+* In the 'stable-2.5' branch: Update the top level pom in Gerrit to ensure that
+none of the Subprojects point to snapshot releases
-* Update the poms for the Gerrit version, push for review, get merged
+* In the 'master' branch: Update the poms for the Gerrit version, push for
+review, get merged
====
- tools/version.sh --snapshot=2.3
+ tools/version.sh --snapshot=2.5
====
* Tag
====
- git tag -a -m "gerrit 2.2.2-rc0" v2.2.2-rc0
- git tag -a -m "gerrit 2.2.2.1" v2.2.2.1
+ git tag -a -m "gerrit 2.5-rc0" v2.5-rc0
+ git tag -a -m "gerrit 2.5" v2.5
====
* Build
@@ -126,7 +130,8 @@
* Push the New Tag
====
- git push google refs/tags/v2.2.2.1:refs/tags/v2.2.2.1
+ git push google refs/tags/v2.5-rc0:refs/tags/v2.5-rc0
+ git push google refs/tags/v2.5:refs/tags/v2.5
====
@@ -134,7 +139,7 @@
~~~~
====
- make -C Documentation PRIOR=2.2.2 update
+ make -C Documentation PRIOR=2.4 update
make -C ReleaseNotes update
====
@@ -142,7 +147,9 @@
* Update Google Code project links
** Go to http://code.google.com/p/gerrit/admin
-** Point the main page to the new docs
+** Point the main page to the new docs. The link to the documentation has to be
+updated at two places: in the project description and also in the Links
+section.
** Point the main page to the new release notes
[NOTE]
diff --git a/Documentation/licenses.txt b/Documentation/licenses.txt
index e50979a..4186026 100644
--- a/Documentation/licenses.txt
+++ b/Documentation/licenses.txt
@@ -18,6 +18,7 @@
|Google Gson | <<apache2,Apache License 2.0>>
|Google Web Toolkit | <<apache2,Apache License 2.0>>
|Guice | <<apache2,Apache License 2.0>>
+|Guava Libraries | <<apache2,Apache License 2.0>>
|Apache Commons Codec | <<apache2,Apache License 2.0>>
|Apache Commons DBCP | <<apache2,Apache License 2.0>>
|Apache Commons Http Client | <<apache2,Apache License 2.0>>
@@ -33,7 +34,6 @@
|Apache Xerces | <<apache2,Apache License 2.0>>
|OpenID4Java | <<apache2,Apache License 2.0>>
|Neko HTML | <<apache2,Apache License 2.0>>
-|Ehcache | <<apache2,Apache License 2.0>>
|mime-util | <<apache2,Apache License 2.0>>
|Jetty | <<apache2,Apache License 2.0>>, or link:http://www.eclipse.org/legal/epl-v10.html[EPL]
|Prolog Cafe | <<prolog_cafe,EPL or GPL>>
diff --git a/ReleaseNotes/ReleaseNotes-2.4.txt b/ReleaseNotes/ReleaseNotes-2.4.txt
index 4f144f3..82f3ed4 100644
--- a/ReleaseNotes/ReleaseNotes-2.4.txt
+++ b/ReleaseNotes/ReleaseNotes-2.4.txt
@@ -70,6 +70,11 @@
of the change we depend upon. A new patch set containing
the rebased commit will be produced and added to the
change.
++
+Rebasing of a change in web UI is restricted to change owner, submitter or
+those with the (new) 'rebase' permission.
+
+* Add a new permission 'rebase' to permit rebasing changes in the web UI
* Make a user's dashboard visible if any of the changes are visible to the
current user.
@@ -219,6 +224,7 @@
* issue 1353 Fix case check for project name so that symlinks work again
* Fix merging of access sections
* Fix inconsistent behaviour when replicating refs/meta/config
+* Fix duplicated results on status:open project:P branch:B
Documentation
-------------
diff --git a/ReleaseNotes/ReleaseNotes-2.5.txt b/ReleaseNotes/ReleaseNotes-2.5.txt
index 34af3dd..60c4f08 100644
--- a/ReleaseNotes/ReleaseNotes-2.5.txt
+++ b/ReleaseNotes/ReleaseNotes-2.5.txt
@@ -14,3 +14,42 @@
Gerrit 2.5 no longer includes replication support out of the box.
Servers that reply upon `replication.config` to copy Git repository
data to other locations must also install the replication plugin.
+
+Cache Configuration
+~~~~~~~~~~~~~~~~~~~
+
+Disk caches are now backed by individual H2 databases, rather than
+Ehcache's own private format. Administrators are encouraged to clear
+the `'$site_path'/cache` directory before starting the new server.
+
+The `cache.NAME.diskLimit` configuration variable is now expressed in
+bytes of disk used. This is a change from previous versions of Gerrit,
+which expressed the limit as the number of entries rather than bytes.
+Bytes of disk is a more accurate way to size what is held. Admins that
+set this variable must update their configurations, as the old values
+are too small. For example a setting of `diskLimit = 65535` will only
+store 64 KiB worth of data on disk and can no longer hold 65,000 patch
+sets. It is recommended to delete the diskLimit variable (if set) and
+rely on the built-in default of `128m`.
+
+The `cache.diff.memoryLimit` and `cache.diff_intraline.memoryLimit`
+configuration variables are now expressed in bytes of memory used,
+rather than number of entries in the cache. This is a change from
+previous versions of Gerrit and gives administrators more control over
+how memory is partioned within a server. Admins that set this variable
+must update their configurations, as the old values are too small.
+For example a setting of `memoryLimit = 1024` now means only 1 KiB of
+data (which may not even hold 1 patch set), not 1024 patch sets. It
+is recommended to set these to `10m` for 10 MiB of memory, and
+increase as necessary.
+
+The `cache.NAME.maxAge` variable now means the maximum amount of time
+that can elapse between reads of the source data into the cache, no
+matter how often it is being accessed. In prior versions it meant how
+long an item could be held without being requested by a client before
+it was discarded. The new meaning of elapsed time before consulting
+the source data is more useful, as it enables a strict bound on how
+stale the cached data can be. This is especially useful for slave
+servers account and permission data, or the `ldap_groups` cache, where
+updates are often made to the source without telling Gerrit to reload
+the cache.
diff --git a/gerrit-ehcache/.gitignore b/gerrit-cache-h2/.gitignore
similarity index 83%
copy from gerrit-ehcache/.gitignore
copy to gerrit-cache-h2/.gitignore
index fe190c9..cb430b8 100644
--- a/gerrit-ehcache/.gitignore
+++ b/gerrit-cache-h2/.gitignore
@@ -1,6 +1,6 @@
/target
/.classpath
/.project
-/.settings/org.eclipse.m2e.core.prefs
/.settings/org.maven.ide.eclipse.prefs
-/gerrit-ehcache.iml
\ No newline at end of file
+/.settings/org.eclipse.m2e.core.prefs
+/gerrit-cache-h2.iml
diff --git a/gerrit-cache-h2/.settings/org.eclipse.core.resources.prefs b/gerrit-cache-h2/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..fc11c3f
--- /dev/null
+++ b/gerrit-cache-h2/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,5 @@
+#Thu Jul 28 11:02:36 PDT 2011
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/test/java=UTF-8
+encoding/<project>=UTF-8
diff --git a/gerrit-ehcache/.settings/org.eclipse.core.runtime.prefs b/gerrit-cache-h2/.settings/org.eclipse.core.runtime.prefs
similarity index 100%
rename from gerrit-ehcache/.settings/org.eclipse.core.runtime.prefs
rename to gerrit-cache-h2/.settings/org.eclipse.core.runtime.prefs
diff --git a/gerrit-ehcache/.settings/org.eclipse.jdt.core.prefs b/gerrit-cache-h2/.settings/org.eclipse.jdt.core.prefs
similarity index 99%
rename from gerrit-ehcache/.settings/org.eclipse.jdt.core.prefs
rename to gerrit-cache-h2/.settings/org.eclipse.jdt.core.prefs
index e89c048..470942d 100644
--- a/gerrit-ehcache/.settings/org.eclipse.jdt.core.prefs
+++ b/gerrit-cache-h2/.settings/org.eclipse.jdt.core.prefs
@@ -1,4 +1,4 @@
-#Thu Jan 19 12:55:44 PST 2012
+#Thu Jul 28 11:02:36 PDT 2011
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
diff --git a/gerrit-ehcache/.settings/org.eclipse.jdt.ui.prefs b/gerrit-cache-h2/.settings/org.eclipse.jdt.ui.prefs
similarity index 100%
rename from gerrit-ehcache/.settings/org.eclipse.jdt.ui.prefs
rename to gerrit-cache-h2/.settings/org.eclipse.jdt.ui.prefs
diff --git a/gerrit-ehcache/pom.xml b/gerrit-cache-h2/pom.xml
similarity index 76%
rename from gerrit-ehcache/pom.xml
rename to gerrit-cache-h2/pom.xml
index f9117b9..4d4303c 100644
--- a/gerrit-ehcache/pom.xml
+++ b/gerrit-cache-h2/pom.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
-Copyright (C) 2010 The Android Open Source Project
+Copyright (C) 2012 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.
@@ -25,23 +25,28 @@
<version>2.5-SNAPSHOT</version>
</parent>
- <artifactId>gerrit-ehcache</artifactId>
- <name>Gerrit Code Review - Ehcache Bindings</name>
+ <artifactId>gerrit-cache-h2</artifactId>
+ <name>Gerrit Code Review - Guava + H2 caching</name>
<description>
- Bindings to Ehcache
+ Implementation of caching backed by Guava and H2
</description>
<dependencies>
<dependency>
- <groupId>net.sf.ehcache</groupId>
- <artifactId>ehcache-core</artifactId>
- </dependency>
-
- <dependency>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-server</artifactId>
<version>${project.version}</version>
</dependency>
+
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.h2database</groupId>
+ <artifactId>h2</artifactId>
+ </dependency>
</dependencies>
</project>
diff --git a/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/DefaultCacheFactory.java b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/DefaultCacheFactory.java
new file mode 100644
index 0000000..8bb0709
--- /dev/null
+++ b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/DefaultCacheFactory.java
@@ -0,0 +1,121 @@
+// Copyright (C) 2012 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.cache.h2;
+
+import com.google.common.base.Strings;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.cache.Weigher;
+import com.google.gerrit.lifecycle.LifecycleModule;
+import com.google.gerrit.server.cache.CacheBinding;
+import com.google.gerrit.server.cache.MemoryCacheFactory;
+import com.google.gerrit.server.cache.PersistentCacheFactory;
+import com.google.gerrit.server.cache.h2.H2CacheImpl.ValueHolder;
+import com.google.gerrit.server.config.ConfigUtil;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.inject.Inject;
+
+import org.eclipse.jgit.lib.Config;
+
+import java.util.concurrent.TimeUnit;
+
+public class DefaultCacheFactory implements MemoryCacheFactory {
+ public static class Module extends LifecycleModule {
+ @Override
+ protected void configure() {
+ bind(DefaultCacheFactory.class);
+ bind(MemoryCacheFactory.class).to(DefaultCacheFactory.class);
+ bind(PersistentCacheFactory.class).to(H2CacheFactory.class);
+ listener().to(H2CacheFactory.class);
+ }
+ }
+
+ private final Config cfg;
+
+ @Inject
+ public DefaultCacheFactory(@GerritServerConfig Config config) {
+ this.cfg = config;
+ }
+
+ @Override
+ public <K, V> Cache<K, V> build(CacheBinding<K, V> def) {
+ return create(def, false).build();
+ }
+
+ @Override
+ public <K, V> LoadingCache<K, V> build(
+ CacheBinding<K, V> def,
+ CacheLoader<K, V> loader) {
+ return create(def, false).build(loader);
+ }
+
+ @SuppressWarnings("unchecked")
+ <K, V> CacheBuilder<K, V> create(
+ CacheBinding<K, V> def,
+ boolean unwrapValueHolder) {
+ CacheBuilder<K,V> builder = newCacheBuilder();
+ builder.recordStats();
+ builder.maximumWeight(cfg.getLong(
+ "cache", def.name(), "memoryLimit",
+ def.maximumWeight()));
+
+ Weigher<K, V> weigher = def.weigher();
+ if (weigher != null && unwrapValueHolder) {
+ final Weigher<K, V> impl = weigher;
+ weigher = (Weigher<K, V>) new Weigher<K, ValueHolder<V>> () {
+ @Override
+ public int weigh(K key, ValueHolder<V> value) {
+ return impl.weigh(key, value.value);
+ }
+ };
+ } else if (weigher == null) {
+ weigher = unitWeight();
+ }
+ builder.weigher(weigher);
+
+ Long age = def.expireAfterWrite(TimeUnit.SECONDS);
+ if (has(def.name(), "maxAge")) {
+ builder.expireAfterWrite(ConfigUtil.getTimeUnit(cfg,
+ "cache", def.name(), "maxAge",
+ age != null ? age : 0,
+ TimeUnit.SECONDS), TimeUnit.SECONDS);
+ } else if (age != null) {
+ builder.expireAfterWrite(age, TimeUnit.SECONDS);
+ }
+
+ return builder;
+ }
+
+ private boolean has(String name, String var) {
+ return !Strings.isNullOrEmpty(cfg.getString("cache", name, var));
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ private static <K, V> CacheBuilder<K, V> newCacheBuilder() {
+ CacheBuilder builder = CacheBuilder.newBuilder();
+ return builder;
+ }
+
+ private static <K, V> Weigher<K, V> unitWeight() {
+ return new Weigher<K, V>() {
+ @Override
+ public int weigh(K key, V value) {
+ return 1;
+ }
+ };
+ }
+}
diff --git a/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java
new file mode 100644
index 0000000..27da20f
--- /dev/null
+++ b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java
@@ -0,0 +1,198 @@
+// Copyright (C) 2012 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.cache.h2;
+
+import com.google.common.base.Preconditions;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import com.google.gerrit.extensions.events.LifecycleListener;
+import com.google.gerrit.server.cache.CacheBinding;
+import com.google.gerrit.server.cache.PersistentCacheFactory;
+import com.google.gerrit.server.cache.h2.H2CacheImpl.SqlStore;
+import com.google.gerrit.server.cache.h2.H2CacheImpl.ValueHolder;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.config.SitePaths;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.google.inject.TypeLiteral;
+
+import org.eclipse.jgit.lib.Config;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+@Singleton
+class H2CacheFactory implements PersistentCacheFactory, LifecycleListener {
+ static final Logger log = LoggerFactory.getLogger(H2CacheFactory.class);
+
+ private final DefaultCacheFactory defaultFactory;
+ private final Config config;
+ private final File cacheDir;
+ private final List<H2CacheImpl<?, ?>> caches;
+ private final ExecutorService executor;
+ private final ScheduledExecutorService cleanup;
+ private volatile boolean started;
+
+ @Inject
+ H2CacheFactory(
+ DefaultCacheFactory defaultCacheFactory,
+ @GerritServerConfig Config cfg,
+ SitePaths site) {
+ defaultFactory = defaultCacheFactory;
+ config = cfg;
+
+ File loc = site.resolve(cfg.getString("cache", null, "directory"));
+ if (loc == null) {
+ cacheDir = null;
+ } else if (loc.exists() || loc.mkdirs()) {
+ if (loc.canWrite()) {
+ log.info("Enabling disk cache " + loc.getAbsolutePath());
+ cacheDir = loc;
+ } else {
+ log.warn("Can't write to disk cache: " + loc.getAbsolutePath());
+ cacheDir = null;
+ }
+ } else {
+ log.warn("Can't create disk cache: " + loc.getAbsolutePath());
+ cacheDir = null;
+ }
+
+ caches = Lists.newLinkedList();
+
+ if (cacheDir != null) {
+ executor = Executors.newFixedThreadPool(
+ 1,
+ new ThreadFactoryBuilder()
+ .setNameFormat("DiskCache-Store-%d")
+ .build());
+ cleanup = Executors.newScheduledThreadPool(
+ 1,
+ new ThreadFactoryBuilder()
+ .setNameFormat("DiskCache-Prune-%d")
+ .setDaemon(true)
+ .build());
+ } else {
+ executor = null;
+ cleanup = null;
+ }
+ }
+
+ @Override
+ public void start() {
+ started = true;
+ if (executor != null) {
+ for (final H2CacheImpl<?, ?> cache : caches) {
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ cache.start();
+ }
+ });
+
+ cleanup.schedule(new Runnable() {
+ @Override
+ public void run() {
+ cache.prune(cleanup);
+ }
+ }, 30, TimeUnit.SECONDS);
+ }
+ }
+ }
+
+ @Override
+ public void stop() {
+ if (executor != null) {
+ try {
+ cleanup.shutdownNow();
+
+ List<Runnable> pending = executor.shutdownNow();
+ if (executor.awaitTermination(15, TimeUnit.MINUTES)) {
+ if (pending != null && !pending.isEmpty()) {
+ log.info(String.format("Finishing %d disk cache updates", pending.size()));
+ for (Runnable update : pending) {
+ update.run();
+ }
+ }
+ } else {
+ log.info("Timeout waiting for disk cache to close");
+ }
+ } catch (InterruptedException e) {
+ log.warn("Interrupted waiting for disk cache to shutdown");
+ }
+ }
+ for (H2CacheImpl<?, ?> cache : caches) {
+ cache.stop();
+ }
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes", "cast"})
+ @Override
+ public <K, V> Cache<K, V> build(CacheBinding<K, V> def) {
+ Preconditions.checkState(!started, "cache must be built before start");
+ long limit = config.getLong("cache", def.name(), "diskLimit", 128 << 20);
+
+ if (cacheDir == null || limit <= 0) {
+ return defaultFactory.build(def);
+ }
+
+ SqlStore<K, V> store = newSqlStore(def.name(), def.keyType(), limit);
+ H2CacheImpl<K, V> cache = new H2CacheImpl<K, V>(
+ executor, store, def.keyType(),
+ (Cache<K, ValueHolder<V>>) defaultFactory.create(def, true).build());
+ caches.add(cache);
+ return cache;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <K, V> LoadingCache<K, V> build(
+ CacheBinding<K, V> def,
+ CacheLoader<K, V> loader) {
+ Preconditions.checkState(!started, "cache must be built before start");
+ long limit = config.getLong("cache", def.name(), "diskLimit", 128 << 20);
+
+ if (cacheDir == null || limit <= 0) {
+ return defaultFactory.build(def, loader);
+ }
+
+ SqlStore<K, V> store = newSqlStore(def.name(), def.keyType(), limit);
+ Cache<K, ValueHolder<V>> mem = (Cache<K, ValueHolder<V>>)
+ defaultFactory.create(def, true)
+ .build((CacheLoader<K, V>) new H2CacheImpl.Loader<K, V>(
+ executor, store, loader));
+ H2CacheImpl<K, V> cache = new H2CacheImpl<K, V>(
+ executor, store, def.keyType(), mem);
+ caches.add(cache);
+ return cache;
+ }
+
+ private <V, K> SqlStore<K, V> newSqlStore(
+ String name,
+ TypeLiteral<K> keyType,
+ long maxSize) {
+ File db = new File(cacheDir, name).getAbsoluteFile();
+ String url = "jdbc:h2:" + db.toURI().toString();
+ return new SqlStore<K, V>(url, keyType, maxSize);
+ }
+}
diff --git a/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java
new file mode 100644
index 0000000..ad437b7
--- /dev/null
+++ b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/H2CacheImpl.java
@@ -0,0 +1,709 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+
+package com.google.gerrit.server.cache.h2;
+
+import com.google.common.cache.AbstractLoadingCache;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.CacheStats;
+import com.google.common.cache.LoadingCache;
+import com.google.common.hash.BloomFilter;
+import com.google.common.hash.Funnel;
+import com.google.common.hash.Funnels;
+import com.google.common.hash.PrimitiveSink;
+import com.google.inject.TypeLiteral;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Timestamp;
+import java.util.Calendar;
+import java.util.Map;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Hybrid in-memory and database backed cache built on H2.
+ * <p>
+ * This cache can be used as either a recall cache, or a loading cache if a
+ * CacheLoader was supplied to its constructor at build time. Before creating an
+ * entry the in-memory cache is checked for the item, then the database is
+ * checked, and finally the CacheLoader is used to construct the item. This is
+ * mostly useful for CacheLoaders that are computationally intensive, such as
+ * the PatchListCache.
+ * <p>
+ * Cache stores and invalidations are performed on a background thread, hiding
+ * the latency associated with serializing the key and value pairs and writing
+ * them to the database log.
+ * <p>
+ * A BloomFilter is used around the database to reduce the number of SELECTs
+ * issued against the database for new cache items that have not been seen
+ * before, a common operation for the PatchListCache. The BloomFilter is sized
+ * when the cache starts to be 64,000 entries or double the number of items
+ * currently in the database table.
+ * <p>
+ * This cache does not export its items as a ConcurrentMap.
+ *
+ * @see H2CacheFactory
+ */
+public class H2CacheImpl<K, V> extends AbstractLoadingCache<K, V> {
+ private static final Logger log = LoggerFactory.getLogger(H2CacheImpl.class);
+
+ private final Executor executor;
+ private final SqlStore<K, V> store;
+ private final TypeLiteral<K> keyType;
+ private final Cache<K, ValueHolder<V>> mem;
+
+ H2CacheImpl(Executor executor,
+ SqlStore<K, V> store,
+ TypeLiteral<K> keyType,
+ Cache<K, ValueHolder<V>> mem) {
+ this.executor = executor;
+ this.store = store;
+ this.keyType = keyType;
+ this.mem = mem;
+ }
+
+ @Override
+ public V getIfPresent(Object objKey) {
+ if (!keyType.getRawType().isInstance(objKey)) {
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ K key = (K) objKey;
+
+ ValueHolder<V> h = mem.getIfPresent(key);
+ if (h != null) {
+ return h.value;
+ }
+
+ if (store.mightContain(key)) {
+ h = store.getIfPresent(key);
+ if (h != null) {
+ mem.put(key, h);
+ return h.value;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public V get(K key) throws ExecutionException {
+ if (mem instanceof LoadingCache) {
+ return ((LoadingCache<K, ValueHolder<V>>) mem).get(key).value;
+ }
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void put(final K key, V val) {
+ final ValueHolder<V> h = new ValueHolder<V>(val);
+ h.created = System.currentTimeMillis();
+ mem.put(key, h);
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ store.put(key, h);
+ }
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void invalidate(final Object key) {
+ if (keyType.getRawType().isInstance(key) && store.mightContain((K) key)) {
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ store.invalidate((K) key);
+ }
+ });
+ }
+ mem.invalidate(key);
+ }
+
+ @Override
+ public void invalidateAll() {
+ store.invalidateAll();
+ mem.invalidateAll();
+ }
+
+ @Override
+ public long size() {
+ return mem.size();
+ }
+
+ @Override
+ public CacheStats stats() {
+ return mem.stats();
+ }
+
+ public DiskStats diskStats() {
+ return store.diskStats();
+ }
+
+ void start() {
+ store.open();
+ }
+
+ void stop() {
+ for (Map.Entry<K, ValueHolder<V>> e : mem.asMap().entrySet()) {
+ ValueHolder<V> h = e.getValue();
+ if (!h.clean) {
+ store.put(e.getKey(), h);
+ }
+ }
+ store.close();
+ }
+
+ void prune(final ScheduledExecutorService service) {
+ store.prune(mem);
+
+ Calendar cal = Calendar.getInstance();
+ cal.set(Calendar.HOUR_OF_DAY, 01);
+ cal.set(Calendar.MINUTE, 0);
+ cal.set(Calendar.SECOND, 0);
+ cal.set(Calendar.MILLISECOND, 0);
+ cal.add(Calendar.DAY_OF_MONTH, 1);
+
+ long delay = cal.getTimeInMillis() - System.currentTimeMillis();
+ service.schedule(new Runnable() {
+ @Override
+ public void run() {
+ prune(service);
+ }
+ }, delay, TimeUnit.MILLISECONDS);
+ }
+
+ public static class DiskStats {
+ long size;
+ long space;
+ long hitCount;
+ long missCount;
+
+ public long size() {
+ return size;
+ }
+
+ public long space() {
+ return space;
+ }
+
+ public long hitCount() {
+ return hitCount;
+ }
+
+ public long requestCount() {
+ return hitCount + missCount;
+ }
+ }
+
+ static class ValueHolder<V> {
+ final V value;
+ long created;
+ volatile boolean clean;
+
+ ValueHolder(V value) {
+ this.value = value;
+ }
+ }
+
+ static class Loader<K, V> extends CacheLoader<K, ValueHolder<V>> {
+ private final Executor executor;
+ private final SqlStore<K, V> store;
+ private final CacheLoader<K, V> loader;
+
+ Loader(Executor executor, SqlStore<K, V> store, CacheLoader<K, V> loader) {
+ this.executor = executor;
+ this.store = store;
+ this.loader = loader;
+ }
+
+ @Override
+ public ValueHolder<V> load(final K key) throws Exception {
+ if (store.mightContain(key)) {
+ ValueHolder<V> h = store.getIfPresent(key);
+ if (h != null) {
+ return h;
+ }
+ }
+
+ final ValueHolder<V> h = new ValueHolder<V>(loader.load(key));
+ h.created = System.currentTimeMillis();
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ store.put(key, h);
+ }
+ });
+ return h;
+ }
+ }
+
+ private static class KeyType<K> {
+ String columnType() {
+ return "OTHER";
+ }
+
+ @SuppressWarnings("unchecked")
+ K get(ResultSet rs, int col) throws SQLException {
+ return (K) rs.getObject(col);
+ }
+
+ void set(PreparedStatement ps, int col, K value) throws SQLException {
+ ps.setObject(col, value);
+ }
+
+ Funnel<K> funnel() {
+ return new Funnel<K>() {
+ @Override
+ public void funnel(K from, PrimitiveSink into) {
+ try {
+ ObjectOutputStream ser =
+ new ObjectOutputStream(new SinkOutputStream(into));
+ ser.writeObject(from);
+ ser.flush();
+ } catch (IOException err) {
+ throw new RuntimeException("Cannot hash as Serializable", err);
+ }
+ }
+ };
+ }
+
+ @SuppressWarnings("unchecked")
+ static <K> KeyType<K> create(TypeLiteral<K> type) {
+ if (type.getRawType() == String.class) {
+ return (KeyType<K>) STRING;
+ }
+ return (KeyType<K>) OTHER;
+ }
+
+ static final KeyType<?> OTHER = new KeyType<Object>();
+ static final KeyType<String> STRING = new KeyType<String>() {
+ @Override
+ String columnType() {
+ return "VARCHAR(4096)";
+ }
+
+ @Override
+ String get(ResultSet rs, int col) throws SQLException {
+ return rs.getString(col);
+ }
+
+ @Override
+ void set(PreparedStatement ps, int col, String value)
+ throws SQLException {
+ ps.setString(col, value);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ Funnel<String> funnel() {
+ Funnel<?> s = Funnels.stringFunnel();
+ return (Funnel<String>) s;
+ }
+ };
+ }
+
+ static class SqlStore<K, V> {
+ private final String url;
+ private final KeyType<K> keyType;
+ private final long maxSize;
+ private final BlockingQueue<SqlHandle> handles;
+ private final AtomicLong hitCount = new AtomicLong();
+ private final AtomicLong missCount = new AtomicLong();
+ private volatile BloomFilter<K> bloomFilter;
+ private int estimatedSize;
+
+ SqlStore(String jdbcUrl, TypeLiteral<K> keyType, long maxSize) {
+ this.url = jdbcUrl;
+ this.keyType = KeyType.create(keyType);
+ this.maxSize = maxSize;
+
+ int cores = Runtime.getRuntime().availableProcessors();
+ int keep = Math.min(cores, 16);
+ this.handles = new ArrayBlockingQueue<SqlHandle>(keep);
+ }
+
+ synchronized void open() {
+ if (bloomFilter == null) {
+ bloomFilter = buildBloomFilter();
+ }
+ }
+
+ void close() {
+ SqlHandle h;
+ while ((h = handles.poll()) != null) {
+ h.close();
+ }
+ }
+
+ boolean mightContain(K key) {
+ BloomFilter<K> b = bloomFilter;
+ if (b == null) {
+ synchronized (this) {
+ b = bloomFilter;
+ if (b == null) {
+ b = buildBloomFilter();
+ bloomFilter = b;
+ }
+ }
+ }
+ return b == null || b.mightContain(key);
+ }
+
+ private BloomFilter<K> buildBloomFilter() {
+ SqlHandle c = null;
+ try {
+ c = acquire();
+ Statement s = c.conn.createStatement();
+ try {
+ ResultSet r;
+ if (estimatedSize <= 0) {
+ r = s.executeQuery("SELECT COUNT(*) FROM data");
+ try {
+ estimatedSize = r.next() ? r.getInt(1) : 0;
+ } finally {
+ r.close();
+ }
+ }
+
+ BloomFilter<K> b = newBloomFilter();
+ r = s.executeQuery("SELECT k FROM data");
+ try {
+ while (r.next()) {
+ b.put(keyType.get(r, 1));
+ }
+ } finally {
+ r.close();
+ }
+ return b;
+ } finally {
+ s.close();
+ }
+ } catch (SQLException e) {
+ log.warn("Cannot build BloomFilter for " + url, e);
+ c = close(c);
+ return null;
+ } finally {
+ release(c);
+ }
+ }
+
+ ValueHolder<V> getIfPresent(K key) {
+ SqlHandle c = null;
+ try {
+ c = acquire();
+ if (c.get == null) {
+ c.get = c.conn.prepareStatement("SELECT v FROM data WHERE k=?");
+ }
+ keyType.set(c.get, 1, key);
+ ResultSet r = c.get.executeQuery();
+ try {
+ if (!r.next()) {
+ missCount.incrementAndGet();
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ V val = (V) r.getObject(1);
+ ValueHolder<V> h = new ValueHolder<V>(val);
+ h.clean = true;
+ hitCount.incrementAndGet();
+ touch(c, key);
+ return h;
+ } finally {
+ r.close();
+ c.get.clearParameters();
+ }
+ } catch (SQLException e) {
+ log.warn("Cannot read cache " + url + " for " + key, e);
+ c = close(c);
+ return null;
+ } finally {
+ release(c);
+ }
+ }
+
+ private void touch(SqlHandle c, K key) throws SQLException {
+ if (c.touch == null) {
+ c.touch =c.conn.prepareStatement("UPDATE data SET accessed=? WHERE k=?");
+ }
+ try {
+ c.touch.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
+ keyType.set(c.touch, 2, key);
+ c.touch.executeUpdate();
+ } finally {
+ c.touch.clearParameters();
+ }
+ }
+
+ void put(K key, ValueHolder<V> holder) {
+ if (holder.clean) {
+ return;
+ }
+
+ BloomFilter<K> b = bloomFilter;
+ if (b != null) {
+ b.put(key);
+ bloomFilter = b;
+ }
+
+ SqlHandle c = null;
+ try {
+ c = acquire();
+ if (c.put == null) {
+ c.put = c.conn.prepareStatement("MERGE INTO data VALUES(?,?,?,?)");
+ }
+ try {
+ keyType.set(c.put, 1, key);
+ c.put.setObject(2, holder.value);
+ c.put.setTimestamp(3, new Timestamp(holder.created));
+ c.put.setTimestamp(4, new Timestamp(System.currentTimeMillis()));
+ c.put.executeUpdate();
+ holder.clean = true;
+ } finally {
+ c.put.clearParameters();
+ }
+ } catch (SQLException e) {
+ log.warn("Cannot put into cache " + url, e);
+ c = close(c);
+ } finally {
+ release(c);
+ }
+ }
+
+ void invalidate(K key) {
+ SqlHandle c = null;
+ try {
+ c = acquire();
+ invalidate(c, key);
+ } catch (SQLException e) {
+ log.warn("Cannot invalidate cache " + url, e);
+ c = close(c);
+ } finally {
+ release(c);
+ }
+ }
+
+ private void invalidate(SqlHandle c, K key) throws SQLException {
+ if (c.invalidate == null) {
+ c.invalidate = c.conn.prepareStatement("DELETE FROM data WHERE k=?");
+ }
+ try {
+ keyType.set(c.invalidate, 1, key);
+ c.invalidate.executeUpdate();
+ } finally {
+ c.invalidate.clearParameters();
+ }
+ }
+
+ void invalidateAll() {
+ SqlHandle c = null;
+ try {
+ c = acquire();
+ Statement s = c.conn.createStatement();
+ try {
+ s.executeUpdate("DELETE FROM data");
+ } finally {
+ s.close();
+ }
+ bloomFilter = newBloomFilter();
+ } catch (SQLException e) {
+ log.warn("Cannot invalidate cache " + url, e);
+ c = close(c);
+ } finally {
+ release(c);
+ }
+ }
+
+ void prune(Cache<K, ?> mem) {
+ SqlHandle c = null;
+ try {
+ c = acquire();
+ Statement s = c.conn.createStatement();
+ try {
+ long used = 0;
+ ResultSet r = s.executeQuery("SELECT"
+ + " SUM(OCTET_LENGTH(k) + OCTET_LENGTH(v))"
+ + " FROM data");
+ try {
+ used = r.next() ? r.getLong(1) : 0;
+ } finally {
+ r.close();
+ }
+ if (used <= maxSize) {
+ return;
+ }
+
+ r = s.executeQuery("SELECT"
+ + " k"
+ + ",OCTET_LENGTH(k) + OCTET_LENGTH(v)"
+ + " FROM data"
+ + " ORDER BY accessed");
+ try {
+ while (maxSize < used && r.next()) {
+ K key = keyType.get(r, 1);
+ if (mem.getIfPresent(key) != null) {
+ touch(c, key);
+ } else {
+ invalidate(c, key);
+ used -= r.getLong(2);
+ }
+ }
+ } finally {
+ r.close();
+ }
+ } finally {
+ s.close();
+ }
+ } catch (SQLException e) {
+ log.warn("Cannot prune cache " + url, e);
+ c = close(c);
+ } finally {
+ release(c);
+ }
+ }
+
+ DiskStats diskStats() {
+ DiskStats d = new DiskStats();
+ d.hitCount = hitCount.get();
+ d.missCount = missCount.get();
+ SqlHandle c = null;
+ try {
+ c = acquire();
+ Statement s = c.conn.createStatement();
+ try {
+ ResultSet r = s.executeQuery("SELECT"
+ + " COUNT(*)"
+ + ",SUM(OCTET_LENGTH(k) + OCTET_LENGTH(v))"
+ + " FROM data");
+ try {
+ if (r.next()) {
+ d.size = r.getLong(1);
+ d.space = r.getLong(2);
+ }
+ } finally {
+ r.close();
+ }
+ } finally {
+ s.close();
+ }
+ } catch (SQLException e) {
+ log.warn("Cannot get DiskStats for " + url, e);
+ c = close(c);
+ } finally {
+ release(c);
+ }
+ return d;
+ }
+
+ private SqlHandle acquire() throws SQLException {
+ SqlHandle h = handles.poll();
+ return h != null ? h : new SqlHandle(url, keyType);
+ }
+
+ private void release(SqlHandle h) {
+ if (h != null && !handles.offer(h)) {
+ h.close();
+ }
+ }
+
+ private SqlHandle close(SqlHandle h) {
+ if (h != null) {
+ h.close();
+ }
+ return null;
+ }
+
+ private BloomFilter<K> newBloomFilter() {
+ int cnt = Math.max(64 * 1024, 2 * estimatedSize);
+ return BloomFilter.create(keyType.funnel(), cnt);
+ }
+ }
+
+ static class SqlHandle {
+ private final String url;
+ Connection conn;
+ PreparedStatement get;
+ PreparedStatement put;
+ PreparedStatement touch;
+ PreparedStatement invalidate;
+
+ SqlHandle(String url, KeyType<?> type) throws SQLException {
+ this.url = url;
+ this.conn = org.h2.Driver.load().connect(url, null);
+ Statement stmt = conn.createStatement();
+ try {
+ stmt.execute("CREATE TABLE IF NOT EXISTS data"
+ + "(k " + type.columnType() + " NOT NULL PRIMARY KEY HASH"
+ + ",v OTHER NOT NULL"
+ + ",created TIMESTAMP NOT NULL"
+ + ",accessed TIMESTAMP NOT NULL"
+ + ")");
+ } finally {
+ stmt.close();
+ }
+ }
+
+ void close() {
+ get = closeStatement(get);
+ put = closeStatement(put);
+ touch = closeStatement(touch);
+ invalidate = closeStatement(invalidate);
+
+ if (conn != null) {
+ try {
+ conn.close();
+ } catch (SQLException e) {
+ log.warn("Cannot close connection to " + url, e);
+ } finally {
+ conn = null;
+ }
+ }
+ }
+
+ private PreparedStatement closeStatement(PreparedStatement ps) {
+ if (ps != null) {
+ try {
+ ps.close();
+ } catch (SQLException e) {
+ log.warn("Cannot close statement for " + url, e);
+ }
+ }
+ return null;
+ }
+ }
+
+ private static class SinkOutputStream extends OutputStream {
+ private final PrimitiveSink sink;
+
+ SinkOutputStream(PrimitiveSink sink) {
+ this.sink = sink;
+ }
+
+ @Override
+ public void write(int b) {
+ sink.putByte((byte)b);
+ }
+
+ @Override
+ public void write(byte[] b, int p, int n) {
+ sink.putBytes(b, p, n);
+ }
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java b/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
index c0c317c..10b1924 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
@@ -40,6 +40,7 @@
public static final String ADMIN_GROUPS = "/admin/groups/";
public static final String ADMIN_PROJECTS = "/admin/projects/";
public static final String ADMIN_CREATE_PROJECT = "/admin/create-project/";
+ public static final String ADMIN_PLUGINS = "/admin/plugins/";
public static String toChange(final ChangeInfo c) {
return toChange(c.getId());
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeListService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeListService.java
index f646bc6..0ddd239 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeListService.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeListService.java
@@ -15,33 +15,14 @@
package com.google.gerrit.common.data;
import com.google.gerrit.common.auth.SignInRequired;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
import com.google.gwtjsonrpc.common.AsyncCallback;
import com.google.gwtjsonrpc.common.RemoteJsonService;
import com.google.gwtjsonrpc.common.RpcImpl;
import com.google.gwtjsonrpc.common.VoidResult;
import com.google.gwtjsonrpc.common.RpcImpl.Version;
-import java.util.Set;
-
@RpcImpl(version = Version.V2_0)
public interface ChangeListService extends RemoteJsonService {
- /** Get all changes which match an arbitrary query string. */
- void allQueryPrev(String query, String pos, int limit,
- AsyncCallback<SingleListChangeInfo> callback);
-
- /** Get all changes which match an arbitrary query string. */
- void allQueryNext(String query, String pos, int limit,
- AsyncCallback<SingleListChangeInfo> callback);
-
- /** Get the data to show AccountDashboardScreen for an account. */
- void forAccount(Account.Id id, AsyncCallback<AccountDashboardInfo> callback);
-
- /** Get the ids of all changes starred by the caller. */
- @SignInRequired
- void myStarredChangeIds(AsyncCallback<Set<Change.Id>> callback);
-
/**
* Add and/or remove changes from the set of starred changes of the caller.
*
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupAdminService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupAdminService.java
index f385e27..ffa1e3e 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupAdminService.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupAdminService.java
@@ -24,7 +24,6 @@
import com.google.gwtjsonrpc.common.VoidResult;
import com.google.gwtjsonrpc.common.RpcImpl.Version;
-import java.util.List;
import java.util.Set;
@RpcImpl(version = Version.V2_0)
@@ -60,14 +59,6 @@
AsyncCallback<VoidResult> callback);
@SignInRequired
- void changeExternalGroup(AccountGroup.Id groupId,
- AccountGroup.ExternalNameKey bindTo, AsyncCallback<VoidResult> callback);
-
- @SignInRequired
- void searchExternalGroups(String searchFilter,
- AsyncCallback<List<AccountGroup.ExternalNameKey>> callback);
-
- @SignInRequired
void addGroupMember(AccountGroup.Id groupId, String nameOrEmail,
AsyncCallback<GroupDetail> callback);
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDescription.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDescription.java
new file mode 100644
index 0000000..828bf24
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDescription.java
@@ -0,0 +1,48 @@
+// Copyright (C) 2012 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.common.data;
+
+import com.google.gerrit.reviewdb.client.AccountGroup;
+
+/**
+ * Group methods exposed by the GroupBackend.
+ */
+public class GroupDescription {
+ /**
+ * The Basic information required to be exposed by any Group.
+ */
+ public interface Basic {
+ /** @return the non-null UUID of the group. */
+ AccountGroup.UUID getGroupUUID();
+
+ /** @return the non-null name of the group. */
+ String getName();
+
+ /** @return whether the group is visible to all accounts. */
+ boolean isVisibleToAll();
+ }
+
+ /**
+ * The extended information exposed by internal groups backed by an
+ * AccountGroup.
+ */
+ public interface Internal extends Basic {
+ /** @return the backing AccountGroup. */
+ AccountGroup getAccountGroup();
+ }
+
+ private GroupDescription() {
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDescriptions.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDescriptions.java
new file mode 100644
index 0000000..e0bc7d8
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDescriptions.java
@@ -0,0 +1,60 @@
+// Copyright (C) 2012 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.common.data;
+
+import com.google.gerrit.reviewdb.client.AccountGroup;
+
+import javax.annotation.Nullable;
+
+/**
+ * Utility class for building GroupDescription objects.
+ */
+public class GroupDescriptions {
+
+ @Nullable
+ public static AccountGroup toAccountGroup(GroupDescription.Basic group) {
+ if (group instanceof GroupDescription.Internal) {
+ return ((GroupDescription.Internal) group).getAccountGroup();
+ }
+ return null;
+ }
+
+ public static GroupDescription.Internal forAccountGroup(final AccountGroup group) {
+ return new GroupDescription.Internal() {
+ @Override
+ public AccountGroup.UUID getGroupUUID() {
+ return group.getGroupUUID();
+ }
+
+ @Override
+ public String getName() {
+ return group.getName();
+ }
+
+ @Override
+ public boolean isVisibleToAll() {
+ return group.isVisibleToAll();
+ }
+
+ @Override
+ public AccountGroup getAccountGroup() {
+ return group;
+ }
+ };
+ }
+
+ private GroupDescriptions() {
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupReference.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupReference.java
index f05d1b9..c261fdd 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupReference.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupReference.java
@@ -23,6 +23,10 @@
return new GroupReference(group.getGroupUUID(), group.getName());
}
+ public static GroupReference forGroup(GroupDescription.Basic group) {
+ return new GroupReference(group.getGroupUUID(), group.getName());
+ }
+
protected String uuid;
protected String name;
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ParameterizedString.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ParameterizedString.java
index 68676cf..2a70d6c 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ParameterizedString.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ParameterizedString.java
@@ -15,6 +15,7 @@
package com.google.gerrit.common.data;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -62,28 +63,9 @@
raw.append(pattern.substring(i, b));
ops.add(new Constant(pattern.substring(i, b)));
- String expr = pattern.substring(b + 2, e);
- String parameterName = "";
- List<Function> functions = new ArrayList<Function>();
- if (!expr.contains(".")) {
- parameterName = expr;
- } else {
- int firstDot = expr.indexOf('.');
- parameterName = expr.substring(0, firstDot);
- String actionsStr = expr.substring(firstDot + 1);
- String[] actions = actionsStr.split("\\.");
+ // "${parameter[.functions...]}" -> "parameter[.functions...]"
+ final Parameter p = new Parameter(pattern.substring(b + 2, e));
- for (String action : actions) {
- Function function = FUNCTIONS.get(action);
- if (function == null) {
- function = NOOP;
- }
- functions.add(function);
- }
- }
-
- final Parameter p =
- new Parameter(parameterName, Collections.unmodifiableList(functions));
raw.append("{" + prs.size() + "}");
prs.add(p);
ops.add(p);
@@ -184,9 +166,25 @@
private final String name;
private final List<Function> functions;
- Parameter(final String name, final List<Function> functions) {
- this.name = name;
- this.functions = functions;
+ Parameter(final String parameter) {
+ // "parameter[.functions...]" -> (parameter, functions...)
+ final List<String> names = Arrays.asList(parameter.split("\\."));
+ final List<Function> functs = new ArrayList<Function>(names.size());
+
+ if (names.isEmpty()) {
+ name = "";
+ } else {
+ name = names.get(0);
+
+ for (String fname : names.subList(1, names.size())) {
+ final Function function = FUNCTIONS.get(fname);
+ if (function != null) {
+ functs.add(function);
+ }
+ }
+ }
+
+ functions = Collections.unmodifiableList(functs);
}
@Override
@@ -207,12 +205,6 @@
}
private static final Map<String, Function> FUNCTIONS = initFunctions();
- private static final Function NOOP = new Function() {
- @Override
- String apply(String a) {
- return a;
- }
- };
private static Map<String, Function> initFunctions() {
final HashMap<String, Function> m = new HashMap<String, Function>();
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAdminService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAdminService.java
index 1b504b0..df6728e 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAdminService.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAdminService.java
@@ -28,10 +28,7 @@
@RpcImpl(version = Version.V2_0)
public interface ProjectAdminService extends RemoteJsonService {
- void visibleProjects(AsyncCallback<ProjectList> callback);
-
void visibleProjectDetails(AsyncCallback<List<ProjectDetail>> callback);
- void suggestParentCandidates(AsyncCallback<List<Project>> callback);
void projectDetail(Project.NameKey projectName,
AsyncCallback<ProjectDetail> callback);
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectList.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectList.java
deleted file mode 100644
index 8511460..0000000
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectList.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (C) 2011 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License
-
-package com.google.gerrit.common.data;
-
-import com.google.gerrit.reviewdb.client.Project;
-
-import java.util.List;
-
-public class ProjectList {
- protected List<Project> projects;
- protected boolean canCreateProject;
-
- public ProjectList() {
- }
-
- public List<Project> getProjects() {
- return projects;
- }
-
- public void setProjects(List<Project> projects) {
- this.projects = projects;
- }
-
- public boolean canCreateProject() {
- return canCreateProject;
- }
-
- public void setCanCreateProject(boolean canCreateProject) {
- this.canCreateProject = canCreateProject;
- }
-}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/SubmitRecord.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/SubmitRecord.java
index 5049ba4..365f6a9 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/SubmitRecord.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/SubmitRecord.java
@@ -68,6 +68,12 @@
NEED,
/**
+ * The label may be set, but it's neither necessary for submission
+ * nor does it block submission if set.
+ */
+ MAY,
+
+ /**
* The label is required for submission, but is impossible to complete.
* The likely cause is access has not been granted correctly by the
* project owner or site administrator.
@@ -78,5 +84,34 @@
public String label;
public Status status;
public Account.Id appliedBy;
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(label).append(": ").append(status);
+ if (appliedBy != null) {
+ sb.append(" by ").append(appliedBy);
+ }
+ return sb.toString();
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(status);
+ if (status == Status.RULE_ERROR && errorMessage != null) {
+ sb.append('(').append(errorMessage).append(')');
+ }
+ sb.append('[');
+ if (labels != null) {
+ String delimiter = "";
+ for (Label label : labels) {
+ sb.append(delimiter).append(label);
+ delimiter = ", ";
+ }
+ }
+ sb.append(']');
+ return sb.toString();
}
}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/SuggestService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/SuggestService.java
index d52a724..7205b74 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/SuggestService.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/SuggestService.java
@@ -29,9 +29,16 @@
void suggestAccount(String query, Boolean enabled, int limit,
AsyncCallback<List<AccountInfo>> callback);
+ /**
+ * @see #suggestAccountGroup(com.google.gerrit.reviewdb.client.Project.NameKey, String, int, AsyncCallback)
+ */
+ @Deprecated
void suggestAccountGroup(String query, int limit,
AsyncCallback<List<GroupReference>> callback);
+ void suggestAccountGroupForProject(Project.NameKey project, String query,
+ int limit, AsyncCallback<List<GroupReference>> callback);
+
/**
* @see #suggestChangeReviewer(com.google.gerrit.reviewdb.client.Change.Id, String, int, AsyncCallback)
*/
diff --git a/gerrit-ehcache/.settings/org.eclipse.core.resources.prefs b/gerrit-ehcache/.settings/org.eclipse.core.resources.prefs
deleted file mode 100644
index 97e731b..0000000
--- a/gerrit-ehcache/.settings/org.eclipse.core.resources.prefs
+++ /dev/null
@@ -1,4 +0,0 @@
-#Tue May 15 09:21:09 PDT 2012
-eclipse.preferences.version=1
-encoding//src/main/java=UTF-8
-encoding/<project>=UTF-8
diff --git a/gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/EhcachePoolImpl.java b/gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/EhcachePoolImpl.java
deleted file mode 100644
index db421ea..0000000
--- a/gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/EhcachePoolImpl.java
+++ /dev/null
@@ -1,272 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.ehcache;
-
-import static java.util.concurrent.TimeUnit.MINUTES;
-import static java.util.concurrent.TimeUnit.SECONDS;
-
-import com.google.gerrit.extensions.events.LifecycleListener;
-import com.google.gerrit.lifecycle.LifecycleModule;
-import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.cache.CachePool;
-import com.google.gerrit.server.cache.CacheProvider;
-import com.google.gerrit.server.cache.EntryCreator;
-import com.google.gerrit.server.cache.EvictionPolicy;
-import com.google.gerrit.server.cache.ProxyCache;
-import com.google.gerrit.server.config.ConfigUtil;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-
-import net.sf.ehcache.CacheManager;
-import net.sf.ehcache.Ehcache;
-import net.sf.ehcache.config.CacheConfiguration;
-import net.sf.ehcache.config.Configuration;
-import net.sf.ehcache.config.DiskStoreConfiguration;
-import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
-
-import org.eclipse.jgit.lib.Config;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.util.HashMap;
-import java.util.Map;
-
-/** Pool of all declared caches created by {@link CacheModule}s. */
-@Singleton
-public class EhcachePoolImpl implements CachePool {
- private static final Logger log =
- LoggerFactory.getLogger(EhcachePoolImpl.class);
-
- public static class Module extends LifecycleModule {
- @Override
- protected void configure() {
- bind(CachePool.class).to(EhcachePoolImpl.class);
- bind(EhcachePoolImpl.class);
- listener().to(EhcachePoolImpl.Lifecycle.class);
- }
- }
-
- public static class Lifecycle implements LifecycleListener {
- private final EhcachePoolImpl cachePool;
-
- @Inject
- Lifecycle(final EhcachePoolImpl cachePool) {
- this.cachePool = cachePool;
- }
-
- @Override
- public void start() {
- cachePool.start();
- }
-
- @Override
- public void stop() {
- cachePool.stop();
- }
- }
-
- private final Config config;
- private final SitePaths site;
-
- private final Object lock = new Object();
- private final Map<String, CacheProvider<?, ?>> caches;
- private CacheManager manager;
-
- @Inject
- EhcachePoolImpl(@GerritServerConfig final Config cfg, final SitePaths site) {
- this.config = cfg;
- this.site = site;
- this.caches = new HashMap<String, CacheProvider<?, ?>>();
- }
-
- @SuppressWarnings({"rawtypes", "unchecked"})
- private void start() {
- synchronized (lock) {
- if (manager != null) {
- throw new IllegalStateException("Cache pool has already been started");
- }
-
- try {
- System.setProperty("net.sf.ehcache.skipUpdateCheck", "" + true);
- } catch (SecurityException e) {
- // Ignore it, the system is just going to ping some external page
- // using a background thread and there's not much we can do about
- // it now.
- }
-
- manager = new CacheManager(new Factory().toConfiguration());
- for (CacheProvider<?, ?> p : caches.values()) {
- Ehcache eh = manager.getEhcache(p.getName());
- EntryCreator<?, ?> c = p.getEntryCreator();
- if (c != null) {
- p.bind(new PopulatingCache(eh, c));
- } else {
- p.bind(new SimpleCache(eh));
- }
- }
- }
- }
-
- private void stop() {
- synchronized (lock) {
- if (manager != null) {
- manager.shutdown();
- }
- }
- }
-
- /** <i>Discouraged</i> Get the underlying cache descriptions, for statistics. */
- public CacheManager getCacheManager() {
- synchronized (lock) {
- return manager;
- }
- }
-
- public <K, V> ProxyCache<K, V> register(final CacheProvider<K, V> provider) {
- synchronized (lock) {
- if (manager != null) {
- throw new IllegalStateException("Cache pool has already been started");
- }
-
- final String n = provider.getName();
- if (caches.containsKey(n) && caches.get(n) != provider) {
- throw new IllegalStateException("Cache \"" + n + "\" already defined");
- }
- caches.put(n, provider);
- return new ProxyCache<K, V>();
- }
- }
-
- private class Factory {
- private static final int MB = 1024 * 1024;
- private final Configuration mgr = new Configuration();
-
- Configuration toConfiguration() {
- configureDiskStore();
- configureDefaultCache();
-
- for (CacheProvider<?, ?> p : caches.values()) {
- final String name = p.getName();
- final CacheConfiguration c = newCache(name);
- c.setMemoryStoreEvictionPolicyFromObject(toPolicy(p.evictionPolicy()));
-
- c.setMaxElementsInMemory(getInt(name, "memorylimit", p.memoryLimit()));
-
- c.setTimeToIdleSeconds(0);
- c.setTimeToLiveSeconds(getSeconds(name, "maxage", p.maxAge()));
- c.setEternal(c.getTimeToLiveSeconds() == 0);
-
- if (p.disk() && mgr.getDiskStoreConfiguration() != null) {
- c.setMaxElementsOnDisk(getInt(name, "disklimit", p.diskLimit()));
-
- int v = c.getDiskSpoolBufferSizeMB() * MB;
- v = getInt(name, "diskbuffer", v) / MB;
- c.setDiskSpoolBufferSizeMB(Math.max(1, v));
- c.setOverflowToDisk(c.getMaxElementsOnDisk() > 0);
- c.setDiskPersistent(c.getMaxElementsOnDisk() > 0);
- }
-
- mgr.addCache(c);
- }
-
- return mgr;
- }
-
- private MemoryStoreEvictionPolicy toPolicy(final EvictionPolicy policy) {
- switch (policy) {
- case LFU:
- return MemoryStoreEvictionPolicy.LFU;
-
- case LRU:
- return MemoryStoreEvictionPolicy.LRU;
-
- default:
- throw new IllegalArgumentException("Unsupported " + policy);
- }
- }
-
- private int getInt(String n, String s, int d) {
- return config.getInt("cache", n, s, d);
- }
-
- private long getSeconds(String n, String s, long d) {
- d = MINUTES.convert(d, SECONDS);
- long m = ConfigUtil.getTimeUnit(config, "cache", n, s, d, MINUTES);
- return SECONDS.convert(m, MINUTES);
- }
-
- private void configureDiskStore() {
- boolean needDisk = false;
- for (CacheProvider<?, ?> p : caches.values()) {
- if (p.disk()) {
- needDisk = true;
- break;
- }
- }
- if (!needDisk) {
- return;
- }
-
- File loc = site.resolve(config.getString("cache", null, "directory"));
- if (loc == null) {
- } else if (loc.exists() || loc.mkdirs()) {
- if (loc.canWrite()) {
- final DiskStoreConfiguration c = new DiskStoreConfiguration();
- c.setPath(loc.getAbsolutePath());
- mgr.addDiskStore(c);
- log.info("Enabling disk cache " + loc.getAbsolutePath());
- } else {
- log.warn("Can't write to disk cache: " + loc.getAbsolutePath());
- }
- } else {
- log.warn("Can't create disk cache: " + loc.getAbsolutePath());
- }
- }
-
- private CacheConfiguration newConfiguration() {
- CacheConfiguration c = new CacheConfiguration();
-
- c.setMaxElementsInMemory(1024);
- c.setMemoryStoreEvictionPolicyFromObject(MemoryStoreEvictionPolicy.LFU);
-
- c.setTimeToIdleSeconds(0);
- c.setTimeToLiveSeconds(0 /* infinite */);
- c.setEternal(true);
-
- if (mgr.getDiskStoreConfiguration() != null) {
- c.setMaxElementsOnDisk(16384);
- c.setOverflowToDisk(false);
- c.setDiskPersistent(false);
-
- c.setDiskSpoolBufferSizeMB(5);
- c.setDiskExpiryThreadIntervalSeconds(60 * 60);
- }
- return c;
- }
-
- private void configureDefaultCache() {
- mgr.setDefaultCacheConfiguration(newConfiguration());
- }
-
- private CacheConfiguration newCache(final String name) {
- CacheConfiguration c = newConfiguration();
- c.setName(name);
- return c;
- }
- }
-}
diff --git a/gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/PopulatingCache.java b/gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/PopulatingCache.java
deleted file mode 100644
index f5c6c45..0000000
--- a/gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/PopulatingCache.java
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.ehcache;
-
-import com.google.gerrit.server.cache.Cache;
-import com.google.gerrit.server.cache.EntryCreator;
-
-import net.sf.ehcache.CacheException;
-import net.sf.ehcache.Ehcache;
-import net.sf.ehcache.Element;
-import net.sf.ehcache.constructs.blocking.CacheEntryFactory;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * A decorator for {@link Cache} which automatically constructs missing entries.
- * <p>
- * On a cache miss {@link EntryCreator#createEntry(Object)} is invoked, allowing
- * the application specific subclass to compute the entry and return it for
- * caching. During a miss the cache takes a lock related to the missing key,
- * ensuring that at most one thread performs the creation work, and other
- * threads wait for the result. Concurrent creations are possible if two
- * different keys miss and hash to different locks in the internal lock table.
- *
- * @param <K> type of key used to name cache entries.
- * @param <V> type of value stored within a cache entry.
- */
-class PopulatingCache<K, V> implements Cache<K, V> {
- private static final Logger log =
- LoggerFactory.getLogger(PopulatingCache.class);
-
- private final net.sf.ehcache.constructs.blocking.SelfPopulatingCache self;
- private final EntryCreator<K, V> creator;
-
- PopulatingCache(Ehcache s, EntryCreator<K, V> entryCreator) {
- creator = entryCreator;
- final CacheEntryFactory f = new CacheEntryFactory() {
- @SuppressWarnings("unchecked")
- @Override
- public Object createEntry(Object key) throws Exception {
- return creator.createEntry((K) key);
- }
- };
- self = new net.sf.ehcache.constructs.blocking.SelfPopulatingCache(s, f);
- }
-
- /**
- * Get the element from the cache, or {@link EntryCreator#missing(Object)} if not found.
- * <p>
- * The {@link EntryCreator#missing(Object)} method is only invoked if:
- * <ul>
- * <li>{@code key == null}, in which case the application should return a
- * suitable return value that callers can accept, or throw a RuntimeException.
- * <li>{@code createEntry(key)} threw an exception, in which case the entry
- * was not stored in the cache. An entry was recorded in the application log,
- * but a return value is still required.
- * <li>The cache has been shutdown, and access is forbidden.
- * </ul>
- *
- * @param key key to locate.
- * @return either the cached entry, or {@code missing(key)} if not found.
- */
- @SuppressWarnings("unchecked")
- public V get(final K key) {
- if (key == null) {
- return creator.missing(key);
- }
-
- final Element m;
- try {
- m = self.get(key);
- } catch (IllegalStateException err) {
- log.error("Cannot lookup " + key + " in \"" + self.getName() + "\"", err);
- return creator.missing(key);
- } catch (CacheException err) {
- log.error("Cannot lookup " + key + " in \"" + self.getName() + "\"", err);
- return creator.missing(key);
- }
- return m != null ? (V) m.getObjectValue() : creator.missing(key);
- }
-
- public void remove(final K key) {
- if (key != null) {
- self.remove(key);
- }
- }
-
- /** Remove all cached items, forcing them to be created again on demand. */
- public void removeAll() {
- self.removeAll();
- }
-
- public void put(K key, V value) {
- self.put(new Element(key, value));
- }
-
- @Override
- public String toString() {
- return "Cache[" + self.getName() + "]";
- }
-}
diff --git a/gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/SimpleCache.java b/gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/SimpleCache.java
deleted file mode 100644
index e4428e3..0000000
--- a/gerrit-ehcache/src/main/java/com/google/gerrit/ehcache/SimpleCache.java
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.ehcache;
-
-import com.google.gerrit.server.cache.Cache;
-
-import net.sf.ehcache.CacheException;
-import net.sf.ehcache.Ehcache;
-import net.sf.ehcache.Element;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * A fast in-memory and/or on-disk based cache.
- *
- * @type <K> type of key used to lookup entries in the cache.
- * @type <V> type of value stored within each cache entry.
- */
-final class SimpleCache<K, V> implements Cache<K, V> {
- private static final Logger log = LoggerFactory.getLogger(SimpleCache.class);
-
- private final Ehcache self;
-
- SimpleCache(final Ehcache self) {
- this.self = self;
- }
-
- Ehcache getEhcache() {
- return self;
- }
-
- @SuppressWarnings("unchecked")
- public V get(final K key) {
- if (key == null) {
- return null;
- }
- final Element m;
- try {
- m = self.get(key);
- } catch (IllegalStateException err) {
- log.error("Cannot lookup " + key + " in \"" + self.getName() + "\"", err);
- return null;
- } catch (CacheException err) {
- log.error("Cannot lookup " + key + " in \"" + self.getName() + "\"", err);
- return null;
- }
- return m != null ? (V) m.getObjectValue() : null;
- }
-
- public void put(final K key, final V value) {
- self.put(new Element(key, value));
- }
-
- public void remove(final K key) {
- if (key != null) {
- self.remove(key);
- }
- }
-
- public void removeAll() {
- self.removeAll();
- }
-
- @Override
- public String toString() {
- return "Cache[" + self.getName() + "]";
- }
-}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/RequiresCapability.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/RequiresCapability.java
similarity index 85%
rename from gerrit-sshd/src/main/java/com/google/gerrit/sshd/RequiresCapability.java
rename to gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/RequiresCapability.java
index cc41a79..382f4ea 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/RequiresCapability.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/annotations/RequiresCapability.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.sshd;
+package com.google.gerrit.extensions.annotations;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@@ -21,7 +21,8 @@
import java.lang.annotation.Target;
/**
- * Annotation on {@link SshCommand} declaring a capability must be granted.
+ * Annotation on {@link SshCommand} or {@link RestApiServlet} declaring a
+ * capability must be granted.
*/
@Target({ElementType.TYPE})
@Retention(RUNTIME)
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
index 40ffc7d..2e33fd8 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
@@ -17,6 +17,7 @@
import static com.google.gerrit.common.PageLinks.ADMIN_CREATE_PROJECT;
import static com.google.gerrit.common.PageLinks.ADMIN_GROUPS;
import static com.google.gerrit.common.PageLinks.ADMIN_PROJECTS;
+import static com.google.gerrit.common.PageLinks.ADMIN_PLUGINS;
import static com.google.gerrit.common.PageLinks.MINE;
import static com.google.gerrit.common.PageLinks.REGISTER;
import static com.google.gerrit.common.PageLinks.SETTINGS;
@@ -48,6 +49,7 @@
import com.google.gerrit.client.admin.AccountGroupScreen;
import com.google.gerrit.client.admin.CreateProjectScreen;
import com.google.gerrit.client.admin.GroupListScreen;
+import com.google.gerrit.client.admin.PluginListScreen;
import com.google.gerrit.client.admin.ProjectAccessScreen;
import com.google.gerrit.client.admin.ProjectBranchesScreen;
import com.google.gerrit.client.admin.ProjectInfoScreen;
@@ -621,6 +623,10 @@
} else if (matchPrefix("/admin/projects/", token)) {
Gerrit.display(token, selectProject());
+ } else if (matchPrefix(ADMIN_PLUGINS, token)
+ || matchExact("/admin/plugins", token)) {
+ Gerrit.display(token, new PluginListScreen());
+
} else if (matchExact(ADMIN_CREATE_PROJECT, token)
|| matchExact("/admin/create-project", token)) {
Gerrit.display(token, new CreateProjectScreen());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
index 701e1fc..5c2a3190 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
@@ -577,6 +577,7 @@
m = new LinkMenuBar();
addLink(m, C.menuGroups(), PageLinks.ADMIN_GROUPS);
addLink(m, C.menuProjects(), PageLinks.ADMIN_PROJECTS);
+ addLink(m, C.menuPlugins(), PageLinks.ADMIN_PLUGINS);
menuLeft.add(m, C.menuAdmin());
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java
index f716814..43afe1e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java
@@ -72,6 +72,7 @@
String menuPeople();
String menuGroups();
String menuProjects();
+ String menuPlugins();
String menuDocumentation();
String menuDocumentationIndex();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties
index 8e3ca6c..277c380 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties
@@ -55,6 +55,7 @@
menuPeople = People
menuGroups = Groups
menuProjects = Projects
+menuPlugins = Plugins
menuDocumentation = Documentation
menuDocumentationIndex = Index
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java
index 574f58e..490db59 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java
@@ -169,6 +169,7 @@
String patchSetUserIdentity();
String patchSizeCell();
String permalink();
+ String pluginsTable();
String posscore();
String projectAdminApprovalCategoryRangeLine();
String projectAdminApprovalCategoryValue();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java
index 72ac1a4..93739c2 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java
@@ -276,7 +276,8 @@
private class PermissionEditorSource extends EditorSource<PermissionEditor> {
@Override
public PermissionEditor create(int index) {
- PermissionEditor subEditor = new PermissionEditor(readOnly, value);
+ PermissionEditor subEditor =
+ new PermissionEditor(projectAccess.getProjectName(), readOnly, value);
permissionContainer.insert(subEditor, index);
return subEditor;
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupInfoScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupInfoScreen.java
index 130241e..ea7c04b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupInfoScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupInfoScreen.java
@@ -27,17 +27,10 @@
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
-import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.SuggestBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwtexpui.clippy.client.CopyableLabel;
@@ -45,8 +38,6 @@
import com.google.gwtexpui.globalkey.client.NpTextBox;
import com.google.gwtjsonrpc.common.VoidResult;
-import java.util.List;
-
public class AccountGroupInfoScreen extends AccountGroupScreen {
private CopyableLabel groupUUIDLabel;
@@ -64,12 +55,6 @@
private ListBox typeSelect;
private Button saveType;
- private Panel externalPanel;
- private Label externalName;
- private NpTextBox externalNameFilter;
- private Button externalNameSearch;
- private Grid externalMatches;
-
private CheckBox visibleToAllCheckBox;
private Button saveGroupOptions;
@@ -86,8 +71,6 @@
initDescription();
initGroupOptions();
initGroupType();
-
- initExternal();
}
private void enableForm(final boolean canModify) {
@@ -95,8 +78,6 @@
ownerTxtBox.setEnabled(canModify);
descTxt.setEnabled(canModify);
typeSelect.setEnabled(canModify);
- externalNameFilter.setEnabled(canModify);
- externalNameSearch.setEnabled(canModify);
visibleToAllCheckBox.setEnabled(canModify);
}
@@ -243,7 +224,6 @@
typeSelect = new ListBox();
typeSelect.setStyleName(Gerrit.RESOURCES.css().groupTypeSelectListBox());
typeSelect.addItem(Util.C.groupType_INTERNAL(), AccountGroup.Type.INTERNAL.name());
- typeSelect.addItem(Util.C.groupType_LDAP(), AccountGroup.Type.LDAP.name());
typeSelect.addChangeHandler(new ChangeHandler() {
@Override
public void onChange(ChangeEvent event) {
@@ -279,54 +259,12 @@
add(fp);
}
- private void initExternal() {
- externalName = new Label();
-
- externalNameFilter = new NpTextBox();
- externalNameFilter.setStyleName(Gerrit.RESOURCES.css()
- .groupExternalNameFilterTextBox());
- externalNameFilter.setVisibleLength(30);
- externalNameFilter.addKeyPressHandler(new KeyPressHandler() {
- @Override
- public void onKeyPress(final KeyPressEvent event) {
- if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) {
- doExternalSearch();
- }
- }
- });
-
- externalNameSearch = new Button(Gerrit.C.searchButton());
- externalNameSearch.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- doExternalSearch();
- }
- });
-
- externalMatches = new Grid();
- externalMatches.setStyleName(Gerrit.RESOURCES.css().infoTable());
- externalMatches.setVisible(false);
-
- final FlowPanel searchLine = new FlowPanel();
- searchLine.add(externalNameFilter);
- searchLine.add(externalNameSearch);
-
- externalPanel = new VerticalPanel();
- externalPanel.add(new SmallHeading(Util.C.headingExternalGroup()));
- externalPanel.add(externalName);
- externalPanel.add(searchLine);
- externalPanel.add(externalMatches);
- add(externalPanel);
- }
-
private void setType(final AccountGroup.Type newType) {
final boolean system = newType == AccountGroup.Type.SYSTEM;
typeSystem.setVisible(system);
typeSelect.setVisible(!system);
saveType.setVisible(!system);
- externalPanel.setVisible(newType == AccountGroup.Type.LDAP);
- externalNameFilter.setText(groupNameTxt.getText());
if (!system) {
for (int i = 0; i < typeSelect.getItemCount(); i++) {
@@ -367,77 +305,6 @@
});
}
- private void doExternalSearch() {
- externalNameFilter.setEnabled(false);
- externalNameSearch.setEnabled(false);
- Util.GROUP_SVC.searchExternalGroups(externalNameFilter.getText(),
- new GerritCallback<List<AccountGroup.ExternalNameKey>>() {
- @Override
- public void onSuccess(List<AccountGroup.ExternalNameKey> result) {
- try {
- final CellFormatter fmt = externalMatches.getCellFormatter();
-
- if (result.isEmpty()) {
- externalMatches.resize(1, 1);
- externalMatches.setText(0, 0, Util.C.errorNoMatchingGroups());
- fmt.setStyleName(0, 0, Gerrit.RESOURCES.css().header());
- return;
- }
-
- externalMatches.resize(1 + result.size(), 2);
-
- externalMatches.setText(0, 0, Util.C.columnGroupName());
- externalMatches.setText(0, 1, "");
- fmt.setStyleName(0, 0, Gerrit.RESOURCES.css().header());
- fmt.setStyleName(0, 1, Gerrit.RESOURCES.css().header());
-
- for (int row = 0; row < result.size(); row++) {
- final AccountGroup.ExternalNameKey key = result.get(row);
- final Button b = new Button(Util.C.buttonSelectGroup());
- b.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- setExternalGroup(key);
- }
- });
- externalMatches.setText(1 + row, 0, key.get());
- externalMatches.setWidget(1 + row, 1, b);
- fmt.setStyleName(1 + row, 1, Gerrit.RESOURCES.css().rightmost());
- }
- } finally {
- externalMatches.setVisible(true);
- externalNameFilter.setEnabled(true);
- externalNameSearch.setEnabled(true);
- }
- }
-
- @Override
- public void onFailure(Throwable caught) {
- externalNameFilter.setEnabled(true);
- externalNameSearch.setEnabled(true);
- super.onFailure(caught);
- }
- });
- }
-
- private void setExternalGroup(final AccountGroup.ExternalNameKey key) {
- externalMatches.setVisible(false);
-
- Util.GROUP_SVC.changeExternalGroup(getGroupId(), key,
- new GerritCallback<VoidResult>() {
- @Override
- public void onSuccess(VoidResult result) {
- externalName.setText(key.get());
- }
-
- @Override
- public void onFailure(Throwable caught) {
- externalMatches.setVisible(true);
- super.onFailure(caught);
- }
- });
- }
-
@Override
protected void display(final GroupDetail groupDetail) {
final AccountGroup group = groupDetail.group;
@@ -452,13 +319,6 @@
visibleToAllCheckBox.setValue(group.isVisibleToAll());
- switch (group.getType()) {
- case LDAP:
- externalName.setText(group.getExternalNameKey() != null ? group
- .getExternalNameKey().get() : Util.C.noGroupSelected());
- break;
- }
-
setType(group.getType());
enableForm(groupDetail.canModify);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
index d049ff6..49bb5dc3 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
@@ -104,6 +104,12 @@
String projectAdminTabBranches();
String projectAdminTabAccess();
+ String plugins();
+ String pluginTabInstalled();
+
+ String columnPluginName();
+ String columnPluginVersion();
+
String noGroupSelected();
String errorNoMatchingGroups();
String errorNoGitRepository();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
index 4330513..3b3d0bb 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
@@ -84,6 +84,11 @@
projectAdminTabBranches = Branches
projectAdminTabAccess = Access
+plugins = Plugins
+pluginTabInstalled = Installed
+columnPluginName = Plugin Name
+columnPluginVersion = Version
+
noGroupSelected = (No group selected)
errorNoMatchingGroups = No Matching Groups
errorNoGitRepository = No Git Repository
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupReferenceBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupReferenceBox.java
index 9da9c22..4ddc9a8 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupReferenceBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupReferenceBox.java
@@ -17,6 +17,7 @@
import com.google.gerrit.client.ui.AccountGroupSuggestOracle;
import com.google.gerrit.client.ui.RPCSuggestOracle;
import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gwt.editor.client.LeafValueEditor;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyPressEvent;
@@ -140,4 +141,8 @@
public void setAccessKey(char key) {
suggestBox.setAccessKey(key);
}
+
+ public void setProject(Project.NameKey projectName) {
+ oracle.setProject(projectName);
+ }
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.java
index d10afd1..2c43233 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.java
@@ -25,6 +25,7 @@
import com.google.gerrit.common.data.PermissionRange;
import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.common.data.RefConfigSection;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
@@ -98,20 +99,25 @@
@UiField
DivElement deleted;
+ private final Project.NameKey projectName;
private final boolean readOnly;
private final AccessSection section;
private Permission value;
private PermissionRange.WithDefaults validRange;
private boolean isDeleted;
- public PermissionEditor(boolean readOnly, AccessSection section) {
+ public PermissionEditor(Project.NameKey projectName,
+ boolean readOnly,
+ AccessSection section) {
this.readOnly = readOnly;
this.section = section;
+ this.projectName = projectName;
normalName = new ValueLabel<String>(PermissionNameRenderer.INSTANCE);
deletedName = new ValueLabel<String>(PermissionNameRenderer.INSTANCE);
initWidget(uiBinder.createAndBindUi(this));
+ groupToAdd.setProject(projectName);
rules = ListEditor.of(new RuleEditorSource());
exclusiveGroup.setEnabled(!readOnly);
@@ -223,7 +229,8 @@
// If the oracle didn't get to complete a UUID, resolve it now.
//
addRule.setEnabled(false);
- SuggestUtil.SVC.suggestAccountGroup(ref.getName(), 1,
+ SuggestUtil.SVC.suggestAccountGroupForProject(
+ projectName, ref.getName(), 1,
new GerritCallback<List<GroupReference>>() {
@Override
public void onSuccess(List<GroupReference> result) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.java
index e4cced7..5dd8b3c 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.java
@@ -25,6 +25,7 @@
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRange;
import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.SpanElement;
@@ -178,16 +179,21 @@
@Override
public void setValue(PermissionRule value) {
GroupReference ref = value.getGroup();
- if (ref.getUUID() != null) {
+
+ boolean link;
+ if (ref.getUUID() != null && AccountGroup.isInternalGroup(ref.getUUID())) {
+ groupNameLink.setText(ref.getName());
groupNameLink.setTargetHistoryToken(Dispatcher.toGroup(ref.getUUID()));
+ link = true;
+ } else {
+ groupNameSpan.setInnerText(ref.getName());
+ groupNameSpan.setTitle(ref.getUUID() != null ? ref.getUUID().get() : "");
+ link = false;
}
- groupNameLink.setText(ref.getName());
- groupNameSpan.setInnerText(ref.getName());
deletedGroupName.setInnerText(ref.getName());
-
- groupNameLink.setVisible(ref.getUUID() != null);
- UIObject.setVisible(groupNameSpan, ref.getUUID() == null);
+ groupNameLink.setVisible(link);
+ UIObject.setVisible(groupNameSpan, !link);
}
@Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PluginListScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PluginListScreen.java
new file mode 100644
index 0000000..7019a06
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PluginListScreen.java
@@ -0,0 +1,92 @@
+// Copyright (C) 2012 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.client.admin;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.plugins.PluginInfo;
+import com.google.gerrit.client.plugins.PluginMap;
+import com.google.gerrit.client.rpc.ScreenLoadCallback;
+import com.google.gerrit.client.ui.FancyFlexTable;
+import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Panel;
+
+public class PluginListScreen extends PluginScreen {
+
+ private Panel pluginPanel;
+ private PluginTable pluginTable;
+
+ @Override
+ protected void onInitUI() {
+ super.onInitUI();
+ initPluginList();
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ PluginMap.all(new ScreenLoadCallback<PluginMap>(this) {
+ @Override
+ protected void preDisplay(final PluginMap result) {
+ pluginTable.display(result);
+ }
+ });
+ }
+
+ private void initPluginList() {
+ pluginTable = new PluginTable();
+ pluginTable.addStyleName(Gerrit.RESOURCES.css().pluginsTable());
+
+ pluginPanel = new FlowPanel();
+ pluginPanel.setWidth("500px");
+ pluginPanel.add(pluginTable);
+ add(pluginPanel);
+ }
+
+ private class PluginTable extends FancyFlexTable<PluginInfo> {
+ PluginTable() {
+ table.setText(0, 1, Util.C.columnPluginName());
+ table.setText(0, 2, Util.C.columnPluginVersion());
+
+ final FlexCellFormatter fmt = table.getFlexCellFormatter();
+ fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().dataHeader());
+ fmt.addStyleName(0, 2, Gerrit.RESOURCES.css().dataHeader());
+ }
+
+ void display(final PluginMap plugins) {
+ while (1 < table.getRowCount()) {
+ table.removeRow(table.getRowCount() - 1);
+ }
+
+ for (final PluginInfo p : plugins.values().asList()) {
+ final int row = table.getRowCount();
+ table.insertRow(row);
+ applyDataRowStyle(row);
+ populate(row, p);
+ }
+ }
+
+ void populate(final int row, final PluginInfo plugin) {
+ table.setText(row, 1, plugin.name());
+ table.setText(row, 2, plugin.version());
+
+ final FlexCellFormatter fmt = table.getFlexCellFormatter();
+ fmt.addStyleName(row, 1, Gerrit.RESOURCES.css().dataCell());
+ fmt.addStyleName(row, 2, Gerrit.RESOURCES.css().dataCell());
+
+ setRowItem(row, plugin);
+ }
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PluginScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PluginScreen.java
new file mode 100644
index 0000000..72cd7f9
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PluginScreen.java
@@ -0,0 +1,35 @@
+// Copyright (C) 2012 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.client.admin;
+
+import static com.google.gerrit.common.PageLinks.ADMIN_PLUGINS;
+
+import com.google.gerrit.client.ui.MenuScreen;
+
+public abstract class PluginScreen extends MenuScreen {
+
+ public PluginScreen() {
+ setRequiresSignIn(true);
+
+ link(Util.C.pluginTabInstalled(), ADMIN_PLUGINS);
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ setPageTitle(Util.C.plugins());
+ display();
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.java
index 1ed919b..923a63e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.java
@@ -18,6 +18,7 @@
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.client.rpc.ScreenLoadCallback;
import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.ProjectAccess;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gwt.core.client.GWT;
@@ -33,6 +34,8 @@
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwtexpui.globalkey.client.NpTextArea;
+import java.util.Collections;
+
public class ProjectAccessScreen extends ProjectScreen {
interface Binder extends UiBinder<HTMLPanel, ProjectAccessScreen> {
}
@@ -110,6 +113,8 @@
@UiHandler("edit")
void onEdit(ClickEvent event) {
+ resetEditors();
+
edit.setEnabled(false);
cancel1.setVisible(true);
UIObject.setVisible(commitTools, true);
@@ -117,6 +122,18 @@
driver.edit(access);
}
+ private void resetEditors() {
+ // Push an empty instance through the driver before pushing the real
+ // data. This will force GWT to delete and recreate the editors, which
+ // is required to build initialize them as editable vs. read-only.
+ ProjectAccess mock = new ProjectAccess();
+ mock.setProjectName(access.getProjectName());
+ mock.setRevision(access.getRevision());
+ mock.setLocal(Collections.<AccessSection> emptyList());
+ mock.setOwnerOf(Collections.<String> emptySet());
+ driver.edit(mock);
+ }
+
@UiHandler(value={"cancel1", "cancel2"})
void onCancel(ClickEvent event) {
Gerrit.display(PageLinks.toProjectAcceess(getProjectKey()));
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java
index dead713..9efea22 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java
@@ -111,8 +111,7 @@
}
}
- Collections.sort(out.asList(), compare());
- Collections.sort(in.asList(), compare());
+ Collections.sort(out.asList(), outComparator());
table.updateColumnsForLabels(out, in, done);
outgoing.display(out);
@@ -121,18 +120,11 @@
table.finishDisplay();
}
- private Comparator<ChangeInfo> compare() {
+ private Comparator<ChangeInfo> outComparator() {
return new Comparator<ChangeInfo>() {
@Override
public int compare(ChangeInfo a, ChangeInfo b) {
- int cmp = a.project().compareTo(b.project());
- if (cmp != 0) return cmp;
- cmp = a.branch().compareTo(b.branch());
- if (cmp != 0) return cmp;
-
- String at = a.topic() != null ? a.topic() : "";
- String bt = b.topic() != null ? b.topic() : "";
- cmp = at.compareTo(bt);
+ int cmp = a.created().compareTo(b.created());
if (cmp != 0) return cmp;
return a._number() - b._number();
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ApprovalTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ApprovalTable.java
index c0c9ce8..20df1df 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ApprovalTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ApprovalTable.java
@@ -182,6 +182,9 @@
break;
}
+ case MAY:
+ break;
+
case NEED:
case IMPOSSIBLE:
if (reportedMissing.add(lbl.label)) {
@@ -204,10 +207,12 @@
continue;
}
String labelName = legacyType.getCategory().getLabelName();
- if (psa.getValue() == legacyType.getMax().getValue()) {
- ad.approved(labelName);
- } else if (psa.getValue() == legacyType.getMin().getValue()) {
- ad.rejected(labelName);
+ if (psa.getValue() != 0 ) {
+ if (psa.getValue() == legacyType.getMax().getValue()) {
+ ad.approved(labelName);
+ } else if (psa.getValue() == legacyType.getMin().getValue()) {
+ ad.rejected(labelName);
+ }
}
if (!columns.contains(labelName)) {
columns.add(labelName);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfo.java
index 06c8e61..52e3bd2 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfo.java
@@ -33,6 +33,18 @@
return new Change.Id(_number());
}
+ public final Timestamp created() {
+ Timestamp ts = _get_cts();
+ if (ts == null) {
+ ts = JavaSqlTimestamp_JsonSerializer.parseTimestamp(createdRaw());
+ _set_cts(ts);
+ }
+ return ts;
+ }
+
+ private final native Timestamp _get_cts() /*-{ return this._cts; }-*/;
+ private final native void _set_cts(Timestamp ts) /*-{ this._cts = ts; }-*/;
+
public final Timestamp updated() {
return JavaSqlTimestamp_JsonSerializer.parseTimestamp(updatedRaw());
}
@@ -56,6 +68,7 @@
private final native String statusRaw() /*-{ return this.status; }-*/;
public final native String subject() /*-{ return this.subject; }-*/;
public final native AccountInfo owner() /*-{ return this.owner; }-*/;
+ private final native String createdRaw() /*-{ return this.created; }-*/;
private final native String updatedRaw() /*-{ return this.updated; }-*/;
public final native boolean starred() /*-{ return this.starred ? true : false; }-*/;
public final native String _sortkey() /*-{ return this._sortkey; }-*/;
@@ -81,6 +94,8 @@
return SubmitRecord.Label.Status.OK;
} else if (rejected() != null) {
return SubmitRecord.Label.Status.REJECT;
+ } else if (optional()) {
+ return SubmitRecord.Label.Status.MAY;
} else {
return SubmitRecord.Label.Status.NEED;
}
@@ -92,6 +107,7 @@
public final native AccountInfo recommended() /*-{ return this.recommended; }-*/;
public final native AccountInfo disliked() /*-{ return this.disliked; }-*/;
+ public final native boolean optional() /*-{ return this.optional ? true : false; }-*/;
final native short _value()
/*-{
if (this.value) return this.value;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
index 9b53c73..00baf28 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
@@ -27,7 +27,6 @@
import com.google.gerrit.common.PageLinks;
import com.google.gerrit.common.data.ChangeDetail;
import com.google.gerrit.common.data.PatchSetDetail;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountDiffPreference;
import com.google.gerrit.reviewdb.client.AccountGeneralPreferences;
import com.google.gerrit.reviewdb.client.AuthType;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
index f588044..9a1ca43 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
@@ -1386,3 +1386,7 @@
font-style: italic;
padding: 2px 6px 1px;
}
+
+/** PluginListScreen **/
+.pluginsTable {
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/IncompleteUserInfoException.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/plugins/PluginInfo.java
similarity index 65%
copy from gerrit-server/src/main/java/com/google/gerrit/server/git/IncompleteUserInfoException.java
copy to gerrit-gwtui/src/main/java/com/google/gerrit/client/plugins/PluginInfo.java
index 204d777..454c97b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/IncompleteUserInfoException.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/plugins/PluginInfo.java
@@ -12,12 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.git;
+package com.google.gerrit.client.plugins;
-public class IncompleteUserInfoException extends Exception {
- private static final long serialVersionUID = 1L;
+import com.google.gwt.core.client.JavaScriptObject;
- public IncompleteUserInfoException(final String userName, final String missingInfo) {
- super("For the user \"" + userName + "\" " + missingInfo + " is not set.");
+public class PluginInfo extends JavaScriptObject {
+
+ public final native String name() /*-{ return this.name; }-*/;
+ public final native String version() /*-{ return this.version; }-*/;
+
+ protected PluginInfo() {
}
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/plugins/PluginMap.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/plugins/PluginMap.java
new file mode 100644
index 0000000..0f2ab4c
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/plugins/PluginMap.java
@@ -0,0 +1,30 @@
+// Copyright (C) 2012 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.client.plugins;
+
+import com.google.gerrit.client.rpc.NativeMap;
+import com.google.gerrit.client.rpc.RestApi;
+import com.google.gwtjsonrpc.common.AsyncCallback;
+
+/** Plugins available from {@code /plugins/}. */
+public class PluginMap extends NativeMap<PluginInfo> {
+ public static void all(AsyncCallback<PluginMap> callback) {
+ new RestApi("/plugins/")
+ .send(NativeMap.copyKeysIntoChildren(callback));
+ }
+
+ protected PluginMap() {
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/Natives.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/Natives.java
index 3d99c9e..a6c609c 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/Natives.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/Natives.java
@@ -36,7 +36,8 @@
if (parser == null) {
parser = bestJsonParser();
}
- return parse0(parser, json);
+ // javac generics bug
+ return Natives.<T>parse0(parser, json);
}
private static native <T extends JavaScriptObject>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RestApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RestApi.java
index bd69092..e1fb883 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RestApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RestApi.java
@@ -124,7 +124,8 @@
T data;
try {
- data = Natives.parseJSON(json.substring(JSON_MAGIC.length()));
+ // javac generics bug
+ data = Natives.<T>parseJSON(json.substring(JSON_MAGIC.length()));
} catch (RuntimeException e) {
cb.onFailure(new RemoteJsonException("Invalid JSON"));
return;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountGroupSuggestOracle.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountGroupSuggestOracle.java
index 885f53b..5da00cd 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountGroupSuggestOracle.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountGroupSuggestOracle.java
@@ -18,6 +18,7 @@
import com.google.gerrit.client.rpc.GerritCallback;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gwt.user.client.ui.SuggestOracle;
import com.google.gwtexpui.safehtml.client.HighlightSuggestOracle;
@@ -31,11 +32,14 @@
private Map<String, AccountGroup.UUID> priorResults =
new HashMap<String, AccountGroup.UUID>();
+ private Project.NameKey projectName;
+
@Override
public void onRequestSuggestions(final Request req, final Callback callback) {
RpcStatus.hide(new Runnable() {
public void run() {
- SuggestUtil.SVC.suggestAccountGroup(req.getQuery(), req.getLimit(),
+ SuggestUtil.SVC.suggestAccountGroupForProject(
+ projectName, req.getQuery(), req.getLimit(),
new GerritCallback<List<GroupReference>>() {
public void onSuccess(final List<GroupReference> result) {
priorResults.clear();
@@ -52,6 +56,10 @@
});
}
+ public void setProject(Project.NameKey projectName) {
+ this.projectName = projectName;
+ }
+
private static class AccountGroupSuggestion implements
SuggestOracle.Suggestion {
private final GroupReference info;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/CacheBasedWebSession.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/CacheBasedWebSession.java
index 7914991..151a6d9 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/CacheBasedWebSession.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/CacheBasedWebSession.java
@@ -26,14 +26,11 @@
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AuthResult;
-import com.google.gerrit.server.cache.Cache;
import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.cache.EvictionPolicy;
import com.google.gerrit.server.config.AuthConfig;
import com.google.inject.Inject;
import com.google.inject.Module;
import com.google.inject.Provider;
-import com.google.inject.TypeLiteral;
import com.google.inject.servlet.RequestScoped;
import javax.servlet.http.Cookie;
@@ -49,13 +46,9 @@
return new CacheModule() {
@Override
protected void configure() {
- final String cacheName = WebSessionManager.CACHE_NAME;
- final TypeLiteral<Cache<Key, Val>> type =
- new TypeLiteral<Cache<Key, Val>>() {};
- disk(type, cacheName) //
- .memoryLimit(1024) // reasonable default for many sites
- .maxAge(MAX_AGE_MINUTES, MINUTES) // expire sessions if they are inactive
- .evictionPolicy(EvictionPolicy.LRU) // keep most recently used
+ persist(WebSessionManager.CACHE_NAME, String.class, Val.class)
+ .maximumWeight(1024) // reasonable default for many sites
+ .expireAfterWrite(MAX_AGE_MINUTES, MINUTES) // expire sessions if they are inactive
;
bind(WebSessionManager.class);
bind(WebSession.class)
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpServlet.java
index 1f26227..1c9c521 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpServlet.java
@@ -14,13 +14,13 @@
package com.google.gerrit.httpd;
+import com.google.common.cache.Cache;
import com.google.gerrit.common.data.Capable;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.AccessPath;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.cache.Cache;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.git.AsyncReceiveCommits;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -99,11 +99,11 @@
install(new CacheModule() {
@Override
protected void configure() {
- TypeLiteral<Cache<AdvertisedObjectsCacheKey, Set<ObjectId>>> cache =
- new TypeLiteral<Cache<AdvertisedObjectsCacheKey, Set<ObjectId>>>() {};
- core(cache, ID_CACHE)
- .memoryLimit(4096)
- .maxAge(10, TimeUnit.MINUTES);
+ cache(ID_CACHE,
+ AdvertisedObjectsCacheKey.class,
+ new TypeLiteral<Set<ObjectId>>() {})
+ .maximumWeight(4096)
+ .expireAfterWrite(10, TimeUnit.MINUTES);
}
});
}
@@ -320,12 +320,12 @@
if (isGet) {
rc.advertiseHistory();
- cache.remove(cacheKey);
+ cache.invalidate(cacheKey);
} else {
- Set<ObjectId> ids = cache.get(cacheKey);
+ Set<ObjectId> ids = cache.getIfPresent(cacheKey);
if (ids != null) {
rp.getAdvertisedObjects().addAll(ids);
- cache.remove(cacheKey);
+ cache.invalidate(cacheKey);
}
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpRequestContext.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpRequestContext.java
index 4c88240..8ef826b 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpRequestContext.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpRequestContext.java
@@ -17,7 +17,6 @@
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.util.RequestContext;
import com.google.inject.Inject;
-import com.google.inject.Provider;
class HttpRequestContext implements RequestContext {
private final WebSession session;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RestApiServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RestApiServlet.java
index 8105e25..ff41ad8 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RestApiServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RestApiServlet.java
@@ -15,10 +15,14 @@
package com.google.gerrit.httpd;
import com.google.common.base.Strings;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.CapabilityControl;
import com.google.gerrit.util.cli.CmdLineParser;
-import com.google.gwtjsonrpc.server.RPCServletUtils;
import com.google.gwtjsonrpc.common.JsonConstants;
+import com.google.gwtjsonrpc.server.RPCServletUtils;
import com.google.inject.Inject;
+import com.google.inject.Provider;
import org.kohsuke.args4j.CmdLineException;
import org.slf4j.Logger;
@@ -62,12 +66,24 @@
}
}
+ private final Provider<CurrentUser> currentUser;
+
+ @Inject
+ protected RestApiServlet(final Provider<CurrentUser> currentUser) {
+ this.currentUser = currentUser;
+ }
+
@Override
protected void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
noCache(res);
try {
+ checkRequiresCapability();
super.service(req, res);
+ } catch (RequireCapabilityException err) {
+ res.setStatus(HttpServletResponse.SC_FORBIDDEN);
+ noCache(res);
+ sendText(req, res, err.getMessage());
} catch (Error err) {
handleError(err, req, res);
} catch (RuntimeException err) {
@@ -75,6 +91,20 @@
}
}
+ private void checkRequiresCapability() throws RequireCapabilityException {
+ RequiresCapability rc = getClass().getAnnotation(RequiresCapability.class);
+ if (rc != null) {
+ CurrentUser user = currentUser.get();
+ CapabilityControl ctl = user.getCapabilities();
+ if (!ctl.canPerform(rc.value()) && !ctl.canAdministrateServer()) {
+ String msg = String.format(
+ "fatal: %s does not have \"%s\" capability.",
+ user.getUserName(), rc.value());
+ throw new RequireCapabilityException(msg);
+ }
+ }
+ }
+
private static void noCache(HttpServletResponse res) {
res.setHeader("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
res.setHeader("Pragma", "no-cache");
@@ -175,4 +205,11 @@
return true;
}
}
+
+ @SuppressWarnings("serial") // Never serialized or thrown out of this class.
+ private static class RequireCapabilityException extends Exception {
+ public RequireCapabilityException(String msg) {
+ super(msg);
+ }
+ }
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java
index 9e69946..a712f9b 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java
@@ -26,6 +26,7 @@
import com.google.gerrit.httpd.rpc.account.AccountCapabilitiesServlet;
import com.google.gerrit.httpd.rpc.change.DeprecatedChangeQueryServlet;
import com.google.gerrit.httpd.rpc.change.ListChangesServlet;
+import com.google.gerrit.httpd.rpc.plugin.ListPluginsServlet;
import com.google.gerrit.httpd.rpc.project.ListProjectsServlet;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
@@ -95,6 +96,7 @@
filter("/a/*").through(RequireIdentifiedUserFilter.class);
serveRegex("^/(?:a/)?accounts/self/capabilities$").with(AccountCapabilitiesServlet.class);
serveRegex("^/(?:a/)?changes/$").with(ListChangesServlet.class);
+ serveRegex("^/(?:a/)?plugins/$").with(ListPluginsServlet.class);
serveRegex("^/(?:a/)?projects/(.*)?$").with(ListProjectsServlet.class);
if (cfg.deprecatedQuery) {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java
index 852caae..0d14b79 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java
@@ -25,6 +25,7 @@
import com.google.gerrit.httpd.gitweb.GitWebModule;
import com.google.gerrit.httpd.rpc.UiRpcModule;
import com.google.gerrit.lifecycle.LifecycleModule;
+import com.google.gerrit.server.CmdLineParserModule;
import com.google.gerrit.server.RemotePeer;
import com.google.gerrit.server.account.AccountManager;
import com.google.gerrit.server.account.ChangeUserName;
@@ -38,7 +39,6 @@
import com.google.gerrit.server.contact.ContactStoreProvider;
import com.google.gerrit.server.util.GuiceRequestScopePropagator;
import com.google.gerrit.server.util.RequestScopePropagator;
-import com.google.gerrit.util.cli.CmdLineParser;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.Injector;
@@ -134,7 +134,7 @@
bind(ChangeUserName.CurrentUser.class);
factory(ChangeUserName.Factory.class);
factory(ClearPassword.Factory.class);
- factory(CmdLineParser.Factory.class);
+ install(new CmdLineParserModule());
factory(GeneratePassword.Factory.class);
bind(SocketAddress.class).annotatedWith(RemotePeer.class).toProvider(
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebSessionManager.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebSessionManager.java
index ee02ef4..4b4edf4 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebSessionManager.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebSessionManager.java
@@ -26,9 +26,9 @@
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.MINUTES;
+import com.google.common.cache.Cache;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountExternalId;
-import com.google.gerrit.server.cache.Cache;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.inject.Inject;
@@ -55,11 +55,11 @@
private final long sessionMaxAgeMillis;
private final SecureRandom prng;
- private final Cache<Key, Val> self;
+ private final Cache<String, Val> self;
@Inject
WebSessionManager(@GerritServerConfig Config cfg,
- @Named(CACHE_NAME) final Cache<Key, Val> cache) {
+ @Named(CACHE_NAME) final Cache<String, Val> cache) {
prng = new SecureRandom();
self = cache;
@@ -76,7 +76,7 @@
prng.nextBytes(rnd);
buf = new ByteArrayOutputStream(3 + nonceLen);
- writeVarInt32(buf, (int) Key.serialVersionUID);
+ writeVarInt32(buf, (int) Val.serialVersionUID);
writeVarInt32(buf, who.get());
writeBytes(buf, rnd);
@@ -120,7 +120,7 @@
Val val = new Val(who, refreshCookieAt, remember,
lastLogin, xsrfToken, expiresAt);
- self.put(key, val);
+ self.put(key.token, val);
return val;
}
@@ -141,21 +141,19 @@
}
Val get(final Key key) {
- Val val = self.get(key);
+ Val val = self.getIfPresent(key.token);
if (val != null && val.expiresAt <= now()) {
- self.remove(key);
+ self.invalidate(key.token);
return null;
}
return val;
}
void destroy(final Key key) {
- self.remove(key);
+ self.invalidate(key.token);
}
- static final class Key implements Serializable {
- static final long serialVersionUID = 2L;
-
+ static final class Key {
private transient String token;
Key(final String t) {
@@ -175,18 +173,10 @@
public boolean equals(Object obj) {
return obj instanceof Key && token.equals(((Key) obj).token);
}
-
- private void writeObject(final ObjectOutputStream out) throws IOException {
- writeString(out, token);
- }
-
- private void readObject(final ObjectInputStream in) throws IOException {
- token = readString(in);
- }
}
static final class Val implements Serializable {
- static final long serialVersionUID = Key.serialVersionUID;
+ static final long serialVersionUID = 2L;
private transient Account.Id accountId;
private transient long refreshCookieAt;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginModule.java
index 2e5001b..bb47b8b 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginModule.java
@@ -14,6 +14,7 @@
package com.google.gerrit.httpd.plugins;
+import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.plugins.ModuleGenerator;
import com.google.gerrit.server.plugins.ReloadPluginListener;
import com.google.gerrit.server.plugins.StartPluginListener;
@@ -21,6 +22,8 @@
import com.google.inject.servlet.ServletModule;
public class HttpPluginModule extends ServletModule {
+ static final String PLUGIN_RESOURCES = "plugin_resources";
+
@Override
protected void configureServlets() {
bind(HttpPluginServlet.class);
@@ -36,5 +39,14 @@
bind(ModuleGenerator.class)
.to(HttpAutoRegisterModuleGenerator.class);
+
+ install(new CacheModule() {
+ @Override
+ protected void configure() {
+ cache(PLUGIN_RESOURCES, ResourceKey.class, Resource.class)
+ .maximumWeight(2 << 20)
+ .weigher(ResourceWeigher.class);
+ }
+ });
}
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java
index b5c228e..79f9011 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java
@@ -16,8 +16,6 @@
import com.google.common.base.Strings;
import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.Weigher;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gerrit.extensions.registration.RegistrationHandle;
@@ -32,6 +30,7 @@
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
+import com.google.inject.name.Named;
import com.google.inject.servlet.GuiceFilter;
import org.eclipse.jgit.lib.Config;
@@ -57,7 +56,6 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import javax.annotation.Nullable;
import javax.servlet.FilterChain;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
@@ -90,22 +88,12 @@
@Inject
HttpPluginServlet(MimeUtilFileTypeRegistry mimeUtil,
@CanonicalWebUrl Provider<String> webUrl,
+ @Named(HttpPluginModule.PLUGIN_RESOURCES) Cache<ResourceKey, Resource> cache,
@GerritServerConfig Config cfg,
SshInfo sshInfo) {
this.mimeUtil = mimeUtil;
this.webUrl = webUrl;
-
- this.resourceCache = CacheBuilder.newBuilder()
- .maximumWeight(cfg.getInt(
- "cache", "plugin_resources", "memoryLimit",
- 2 * 1024 * 1024))
- .weigher(new Weigher<ResourceKey, Resource>() {
- @Override
- public int weigh(ResourceKey key, Resource value) {
- return key.weight() + value.weight();
- }
- })
- .build();
+ this.resourceCache = cache;
String sshHost = "review.example.com";
int sshPort = 29418;
@@ -247,8 +235,8 @@
if (exists(entry)) {
sendResource(jar, entry, key, res);
} else {
- resourceCache.put(key, NOT_FOUND);
- NOT_FOUND.send(req, res);
+ resourceCache.put(key, Resource.NOT_FOUND);
+ Resource.NOT_FOUND.send(req, res);
}
} else if (file.equals("Documentation")) {
res.sendRedirect(uri + "/index.html");
@@ -268,12 +256,12 @@
} else if (exists(entry)) {
sendResource(jar, entry, key, res);
} else {
- resourceCache.put(key, NOT_FOUND);
- NOT_FOUND.send(req, res);
+ resourceCache.put(key, Resource.NOT_FOUND);
+ Resource.NOT_FOUND.send(req, res);
}
} else {
- resourceCache.put(key, NOT_FOUND);
- NOT_FOUND.send(req, res);
+ resourceCache.put(key, Resource.NOT_FOUND);
+ Resource.NOT_FOUND.send(req, res);
}
}
@@ -559,7 +547,7 @@
return 0 <= s ? path.substring(1, s) : path.substring(1);
}
- private static void noCache(HttpServletResponse res) {
+ static void noCache(HttpServletResponse res) {
res.setHeader("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
res.setHeader("Pragma", "no-cache");
res.setHeader("Cache-Control", "no-cache, must-revalidate");
@@ -576,99 +564,6 @@
}
}
- private static final class ResourceKey {
- private final Plugin.CacheKey plugin;
- private final String resource;
-
- ResourceKey(Plugin p, String r) {
- this.plugin = p.getCacheKey();
- this.resource = r;
- }
-
- int weight() {
- return 28 + resource.length();
- }
-
- @Override
- public int hashCode() {
- return plugin.hashCode() * 31 + resource.hashCode();
- }
-
- @Override
- public boolean equals(Object other) {
- if (other instanceof ResourceKey) {
- ResourceKey rk = (ResourceKey) other;
- return plugin == rk.plugin && resource.equals(rk.resource);
- }
- return false;
- }
- }
-
- private static abstract class Resource {
- abstract int weight();
- abstract void send(HttpServletRequest req, HttpServletResponse res)
- throws IOException;
- }
-
- private static final class SmallResource extends Resource {
- private final byte[] data;
- private String contentType;
- private String characterEncoding;
- private long lastModified;
-
- SmallResource(byte[] data) {
- this.data = data;
- }
-
- SmallResource setLastModified(long when) {
- this.lastModified = when;
- return this;
- }
-
- SmallResource setContentType(String contentType) {
- this.contentType = contentType;
- return this;
- }
-
- SmallResource setCharacterEncoding(@Nullable String enc) {
- this.characterEncoding = enc;
- return this;
- }
-
- @Override
- int weight() {
- return data.length;
- }
-
- @Override
- void send(HttpServletRequest req, HttpServletResponse res)
- throws IOException {
- if (0 < lastModified) {
- res.setDateHeader("Last-Modified", lastModified);
- }
- res.setContentType(contentType);
- if (characterEncoding != null) {
- res.setCharacterEncoding(characterEncoding);
- }
- res.setContentLength(data.length);
- res.getOutputStream().write(data);
- }
- }
-
- private static final Resource NOT_FOUND = new Resource() {
- @Override
- int weight() {
- return 4;
- }
-
- @Override
- void send(HttpServletRequest req, HttpServletResponse res)
- throws IOException {
- noCache(res);
- res.sendError(HttpServletResponse.SC_NOT_FOUND);
- }
- };
-
private static class WrappedRequest extends HttpServletRequestWrapper {
private final String contextPath;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/Resource.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/Resource.java
new file mode 100644
index 0000000..05970af
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/Resource.java
@@ -0,0 +1,40 @@
+// Copyright (C) 2012 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.httpd.plugins;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+abstract class Resource {
+ static final Resource NOT_FOUND = new Resource() {
+ @Override
+ int weigh() {
+ return 0;
+ }
+
+ @Override
+ void send(HttpServletRequest req, HttpServletResponse res)
+ throws IOException {
+ HttpPluginServlet.noCache(res);
+ res.sendError(HttpServletResponse.SC_NOT_FOUND);
+ }
+ };
+
+ abstract int weigh();
+ abstract void send(HttpServletRequest req, HttpServletResponse res)
+ throws IOException;
+}
\ No newline at end of file
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ResourceKey.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ResourceKey.java
new file mode 100644
index 0000000..068d6b4
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ResourceKey.java
@@ -0,0 +1,45 @@
+// Copyright (C) 2012 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.httpd.plugins;
+
+import com.google.gerrit.server.plugins.Plugin;
+
+final class ResourceKey {
+ private final Plugin.CacheKey plugin;
+ private final String resource;
+
+ ResourceKey(Plugin p, String r) {
+ this.plugin = p.getCacheKey();
+ this.resource = r;
+ }
+
+ int weigh() {
+ return resource.length() * 2;
+ }
+
+ @Override
+ public int hashCode() {
+ return plugin.hashCode() * 31 + resource.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof ResourceKey) {
+ ResourceKey rk = (ResourceKey) other;
+ return plugin == rk.plugin && resource.equals(rk.resource);
+ }
+ return false;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/IncompleteUserInfoException.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ResourceWeigher.java
similarity index 65%
copy from gerrit-server/src/main/java/com/google/gerrit/server/git/IncompleteUserInfoException.java
copy to gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ResourceWeigher.java
index 204d777..2514272 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/IncompleteUserInfoException.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/ResourceWeigher.java
@@ -12,12 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.git;
+package com.google.gerrit.httpd.plugins;
-public class IncompleteUserInfoException extends Exception {
- private static final long serialVersionUID = 1L;
+import com.google.common.cache.Weigher;
- public IncompleteUserInfoException(final String userName, final String missingInfo) {
- super("For the user \"" + userName + "\" " + missingInfo + " is not set.");
+class ResourceWeigher implements Weigher<ResourceKey, Resource> {
+ @Override
+ public int weigh(ResourceKey key, Resource value) {
+ return key.weigh() + value.weigh();
}
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/SmallResource.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/SmallResource.java
new file mode 100644
index 0000000..e408f72
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/SmallResource.java
@@ -0,0 +1,66 @@
+// Copyright (C) 2012 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.httpd.plugins;
+
+import java.io.IOException;
+
+import javax.annotation.Nullable;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+final class SmallResource extends Resource {
+ private final byte[] data;
+ private String contentType;
+ private String characterEncoding;
+ private long lastModified;
+
+ SmallResource(byte[] data) {
+ this.data = data;
+ }
+
+ SmallResource setLastModified(long when) {
+ this.lastModified = when;
+ return this;
+ }
+
+ SmallResource setContentType(String contentType) {
+ this.contentType = contentType;
+ return this;
+ }
+
+ SmallResource setCharacterEncoding(@Nullable String enc) {
+ this.characterEncoding = enc;
+ return this;
+ }
+
+ @Override
+ int weigh() {
+ return contentType.length() * 2 + data.length;
+ }
+
+ @Override
+ void send(HttpServletRequest req, HttpServletResponse res)
+ throws IOException {
+ if (0 < lastModified) {
+ res.setDateHeader("Last-Modified", lastModified);
+ }
+ res.setContentType(contentType);
+ if (characterEncoding != null) {
+ res.setCharacterEncoding(characterEncoding);
+ }
+ res.setContentLength(data.length);
+ res.getOutputStream().write(data);
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/BaseServiceImplementation.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/BaseServiceImplementation.java
index 26db6f9..62506f0 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/BaseServiceImplementation.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/BaseServiceImplementation.java
@@ -26,6 +26,7 @@
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gwtjsonrpc.common.AsyncCallback;
import com.google.gwtorm.server.OrmException;
+import com.google.gwtorm.server.OrmRuntimeException;
import com.google.inject.Provider;
/** Support for services which require a {@link ReviewDb} instance. */
@@ -70,20 +71,14 @@
callback.onFailure(new NoSuchEntityException());
} catch (NoSuchGroupException e) {
callback.onFailure(new NoSuchEntityException());
-
- } catch (OrmException e) {
- if (e.getCause() instanceof Failure) {
- callback.onFailure(e.getCause().getCause());
-
- } else if (e.getCause() instanceof CorruptEntityException) {
- callback.onFailure(e.getCause());
-
- } else if (e.getCause() instanceof NoSuchEntityException) {
- callback.onFailure(e.getCause());
-
- } else {
- callback.onFailure(e);
+ } catch (OrmRuntimeException e) {
+ Exception ex = e;
+ if (e.getCause() instanceof OrmException) {
+ ex = (OrmException) e.getCause();
}
+ handleOrmException(callback, ex);
+ } catch (OrmException e) {
+ handleOrmException(callback, e);
} catch (Failure e) {
if (e.getCause() instanceof NoSuchProjectException
|| e.getCause() instanceof NoSuchChangeException) {
@@ -95,6 +90,19 @@
}
}
+ private static <T> void handleOrmException(
+ final AsyncCallback<T> callback, Exception e) {
+ if (e.getCause() instanceof Failure) {
+ callback.onFailure(e.getCause().getCause());
+ } else if (e.getCause() instanceof CorruptEntityException) {
+ callback.onFailure(e.getCause());
+ } else if (e.getCause() instanceof NoSuchEntityException) {
+ callback.onFailure(e.getCause());
+ } else {
+ callback.onFailure(e);
+ }
+ }
+
/** Exception whose cause is passed into onFailure. */
public static class Failure extends Exception {
private static final long serialVersionUID = 1L;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/ChangeListServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/ChangeListServiceImpl.java
index 66b2dc0..0b54db1 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/ChangeListServiceImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/ChangeListServiceImpl.java
@@ -14,258 +14,32 @@
package com.google.gerrit.httpd.rpc;
-import com.google.gerrit.common.data.AccountDashboardInfo;
-import com.google.gerrit.common.data.ChangeInfo;
import com.google.gerrit.common.data.ChangeListService;
-import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.common.data.SingleListChangeInfo;
import com.google.gerrit.common.data.ToggleStarRequest;
-import com.google.gerrit.common.errors.InvalidQueryException;
-import com.google.gerrit.common.errors.NoSuchEntityException;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.client.StarredChange;
-import com.google.gerrit.reviewdb.server.ChangeAccess;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.account.AccountInfoCacheFactory;
-import com.google.gerrit.server.project.ChangeControl;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gerrit.server.query.Predicate;
-import com.google.gerrit.server.query.QueryParseException;
-import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gerrit.server.query.change.ChangeDataSource;
-import com.google.gerrit.server.query.change.ChangeQueryBuilder;
-import com.google.gerrit.server.query.change.ChangeQueryRewriter;
import com.google.gwtjsonrpc.common.AsyncCallback;
import com.google.gwtjsonrpc.common.VoidResult;
-import com.google.gwtorm.server.ListResultSet;
import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.ResultSet;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class ChangeListServiceImpl extends BaseServiceImplementation implements
ChangeListService {
- private static final Comparator<ChangeInfo> ID_COMP =
- new Comparator<ChangeInfo>() {
- public int compare(final ChangeInfo o1, final ChangeInfo o2) {
- return o1.getId().get() - o2.getId().get();
- }
- };
- private static final Comparator<ChangeInfo> SORT_KEY_COMP =
- new Comparator<ChangeInfo>() {
- public int compare(final ChangeInfo o1, final ChangeInfo o2) {
- return o2.getSortKey().compareTo(o1.getSortKey());
- }
- };
- private static final Comparator<Change> QUERY_PREV =
- new Comparator<Change>() {
- public int compare(final Change a, final Change b) {
- return a.getSortKey().compareTo(b.getSortKey());
- }
- };
- private static final Comparator<Change> QUERY_NEXT =
- new Comparator<Change>() {
- public int compare(final Change a, final Change b) {
- return b.getSortKey().compareTo(a.getSortKey());
- }
- };
-
private final Provider<CurrentUser> currentUser;
- private final ChangeControl.Factory changeControlFactory;
- private final AccountInfoCacheFactory.Factory accountInfoCacheFactory;
-
- private final ChangeQueryBuilder.Factory queryBuilder;
- private final Provider<ChangeQueryRewriter> queryRewriter;
@Inject
ChangeListServiceImpl(final Provider<ReviewDb> schema,
- final Provider<CurrentUser> currentUser,
- final ChangeControl.Factory changeControlFactory,
- final AccountInfoCacheFactory.Factory accountInfoCacheFactory,
- final ChangeQueryBuilder.Factory queryBuilder,
- final Provider<ChangeQueryRewriter> queryRewriter) {
+ final Provider<CurrentUser> currentUser) {
super(schema, currentUser);
this.currentUser = currentUser;
- this.changeControlFactory = changeControlFactory;
- this.accountInfoCacheFactory = accountInfoCacheFactory;
- this.queryBuilder = queryBuilder;
- this.queryRewriter = queryRewriter;
- }
-
- private boolean canRead(final Change c, final ReviewDb db) throws OrmException {
- try {
- return changeControlFactory.controlFor(c).isVisible(db);
- } catch (NoSuchChangeException e) {
- return false;
- }
- }
-
- @Override
- public void allQueryPrev(final String query, final String pos,
- final int pageSize, final AsyncCallback<SingleListChangeInfo> callback) {
- try {
- run(callback, new QueryPrev(pageSize, pos) {
- @Override
- ResultSet<Change> query(ReviewDb db, int lim, String key)
- throws OrmException, InvalidQueryException {
- return searchQuery(db, query, lim, key, QUERY_PREV);
- }
- });
- } catch (InvalidQueryException e) {
- callback.onFailure(e);
- }
- }
-
- @Override
- public void allQueryNext(final String query, final String pos,
- final int pageSize, final AsyncCallback<SingleListChangeInfo> callback) {
- try {
- run(callback, new QueryNext(pageSize, pos) {
- @Override
- ResultSet<Change> query(ReviewDb db, int lim, String key)
- throws OrmException, InvalidQueryException {
- return searchQuery(db, query, lim, key, QUERY_NEXT);
- }
- });
- } catch (InvalidQueryException e) {
- callback.onFailure(e);
- }
- }
-
- @SuppressWarnings("unchecked")
- private ResultSet<Change> searchQuery(final ReviewDb db, String query,
- final int limit, final String key, final Comparator<Change> cmp)
- throws OrmException, InvalidQueryException {
- try {
- final ChangeQueryBuilder builder = queryBuilder.create(currentUser.get());
- final Predicate<ChangeData> visibleToMe = builder.is_visible();
- Predicate<ChangeData> q = builder.parse(query);
- q = Predicate.and(q, //
- cmp == QUERY_PREV //
- ? builder.sortkey_after(key) //
- : builder.sortkey_before(key), //
- builder.limit(limit), //
- visibleToMe //
- );
-
- ChangeQueryRewriter rewriter = queryRewriter.get();
- Predicate<ChangeData> s = rewriter.rewrite(q);
- if (!(s instanceof ChangeDataSource)) {
- s = rewriter.rewrite(Predicate.and(builder.status_open(), q));
- }
-
- if (s instanceof ChangeDataSource) {
- ArrayList<Change> r = new ArrayList<Change>();
- HashSet<Change.Id> want = new HashSet<Change.Id>();
- for (ChangeData d : ((ChangeDataSource) s).read()) {
- if (d.hasChange()) {
- // Checking visibleToMe here should be unnecessary, the
- // query should have already performed it. But we don't
- // want to trust the query rewriter that much yet.
- //
- if (visibleToMe.match(d)) {
- r.add(d.getChange());
- }
- } else {
- want.add(d.getId());
- }
- }
-
- // Here we have to check canRead. Its impossible to
- // do that test without the change object, and it being
- // missing above means we have to compute it ourselves.
- //
- if (!want.isEmpty()) {
- for (Change c : db.changes().get(want)) {
- if (canRead(c, db)) {
- r.add(c);
- }
- }
- }
-
- Collections.sort(r, cmp);
- return new ListResultSet<Change>(r);
- } else {
- throw new InvalidQueryException("Not Supported", s.toString());
- }
- } catch (QueryParseException e) {
- throw new InvalidQueryException(e.getMessage(), query);
- }
- }
-
- public void forAccount(final Account.Id id,
- final AsyncCallback<AccountDashboardInfo> callback) {
- final Account.Id me = getAccountId();
- final Account.Id target = id != null ? id : me;
- if (target == null) {
- callback.onFailure(new NoSuchEntityException());
- return;
- }
-
- run(callback, new Action<AccountDashboardInfo>() {
- public AccountDashboardInfo run(final ReviewDb db) throws OrmException,
- Failure {
- final AccountInfoCacheFactory ac = accountInfoCacheFactory.create();
- final Account user = ac.get(target);
- if (user == null) {
- throw new Failure(new NoSuchEntityException());
- }
-
- final Set<Change.Id> stars = currentUser.get().getStarredChanges();
- final ChangeAccess changes = db.changes();
- final AccountDashboardInfo d;
-
- final Set<Change.Id> openReviews = new HashSet<Change.Id>();
- final Set<Change.Id> closedReviews = new HashSet<Change.Id>();
- for (final PatchSetApproval ca : db.patchSetApprovals().openByUser(id)) {
- openReviews.add(ca.getPatchSetId().getParentKey());
- }
- for (final PatchSetApproval ca : db.patchSetApprovals()
- .closedByUser(id)) {
- closedReviews.add(ca.getPatchSetId().getParentKey());
- }
-
- d = new AccountDashboardInfo(target);
- d.setByOwner(filter(changes.byOwnerOpen(target), stars, ac, db));
- d.setClosed(filter(changes.byOwnerClosed(target), stars, ac, db));
-
- for (final ChangeInfo c : d.getByOwner()) {
- openReviews.remove(c.getId());
- }
- d.setForReview(filter(changes.get(openReviews), stars, ac, db));
- Collections.sort(d.getForReview(), ID_COMP);
-
- for (final ChangeInfo c : d.getClosed()) {
- closedReviews.remove(c.getId());
- }
- if (!closedReviews.isEmpty()) {
- d.getClosed().addAll(filter(changes.get(closedReviews), stars, ac, db));
- Collections.sort(d.getClosed(), SORT_KEY_COMP);
- }
-
- // User dashboards are visible to other users, if the current user
- // can see any of the changes in the dashboard.
- if (!target.equals(me)
- && d.getByOwner().isEmpty()
- && d.getClosed().isEmpty()
- && d.getForReview().isEmpty()) {
- throw new Failure(new NoSuchEntityException());
- }
-
- d.setAccounts(ac.create());
- return d;
- }
- });
}
public void toggleStars(final ToggleStarRequest req,
@@ -297,97 +71,4 @@
}
});
}
-
- public void myStarredChangeIds(final AsyncCallback<Set<Change.Id>> callback) {
- callback.onSuccess(currentUser.get().getStarredChanges());
- }
-
- private int safePageSize(final int pageSize) throws InvalidQueryException {
- int maxLimit = currentUser.get().getCapabilities()
- .getRange(GlobalCapability.QUERY_LIMIT)
- .getMax();
- if (maxLimit <= 0) {
- throw new InvalidQueryException("Search Disabled");
- }
- return 0 < pageSize && pageSize <= maxLimit ? pageSize : maxLimit;
- }
-
- private List<ChangeInfo> filter(final ResultSet<Change> rs,
- final Set<Change.Id> starred, final AccountInfoCacheFactory accts,
- final ReviewDb db) throws OrmException {
- final ArrayList<ChangeInfo> r = new ArrayList<ChangeInfo>();
- for (final Change c : rs) {
- if (canRead(c, db)) {
- final ChangeInfo ci = new ChangeInfo(c);
- accts.want(ci.getOwner());
- ci.setStarred(starred.contains(ci.getId()));
- r.add(ci);
- }
- }
- return r;
- }
-
- private abstract class QueryNext implements Action<SingleListChangeInfo> {
- protected final String pos;
- protected final int limit;
- protected final int slim;
-
- QueryNext(final int pageSize, final String pos) throws InvalidQueryException {
- this.pos = pos;
- this.limit = safePageSize(pageSize);
- this.slim = limit + 1;
- }
-
- public SingleListChangeInfo run(final ReviewDb db) throws OrmException,
- InvalidQueryException {
- final AccountInfoCacheFactory ac = accountInfoCacheFactory.create();
- final SingleListChangeInfo d = new SingleListChangeInfo();
- final Set<Change.Id> starred = currentUser.get().getStarredChanges();
-
- final ArrayList<ChangeInfo> list = new ArrayList<ChangeInfo>();
- final ResultSet<Change> rs = query(db, slim, pos);
- for (final Change c : rs) {
- if (!canRead(c, db)) {
- continue;
- }
- final ChangeInfo ci = new ChangeInfo(c);
- ac.want(ci.getOwner());
- ci.setStarred(starred.contains(ci.getId()));
- list.add(ci);
- if (list.size() == slim) {
- rs.close();
- break;
- }
- }
-
- final boolean atEnd = finish(list);
- d.setChanges(list, atEnd);
- d.setAccounts(ac.create());
- return d;
- }
-
- boolean finish(final ArrayList<ChangeInfo> list) {
- final boolean atEnd = list.size() <= limit;
- if (list.size() == slim) {
- list.remove(limit);
- }
- return atEnd;
- }
-
- abstract ResultSet<Change> query(final ReviewDb db, final int slim,
- String sortKey) throws OrmException, InvalidQueryException;
- }
-
- private abstract class QueryPrev extends QueryNext {
- QueryPrev(int pageSize, String pos) throws InvalidQueryException {
- super(pageSize, pos);
- }
-
- @Override
- boolean finish(final ArrayList<ChangeInfo> list) {
- final boolean atEnd = super.finish(list);
- Collections.reverse(list);
- return atEnd;
- }
- }
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SuggestServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SuggestServiceImpl.java
index c1c9169..1baa49b 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SuggestServiceImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SuggestServiceImpl.java
@@ -14,6 +14,8 @@
package com.google.gerrit.httpd.rpc;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
import com.google.gerrit.common.data.AccountInfo;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.ReviewerInfo;
@@ -21,8 +23,6 @@
import com.google.gerrit.common.errors.NoSuchGroupException;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountExternalId;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupName;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
@@ -31,14 +31,14 @@
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountControl;
import com.google.gerrit.server.account.AccountVisibility;
-import com.google.gerrit.server.account.GroupCache;
-import com.google.gerrit.server.account.GroupControl;
+import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.account.GroupMembers;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.patch.AddReviewer;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.project.ProjectControl;
import com.google.gwtjsonrpc.common.AsyncCallback;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
@@ -53,41 +53,43 @@
import java.util.Map;
import java.util.Set;
+import javax.annotation.Nullable;
+
class SuggestServiceImpl extends BaseServiceImplementation implements
SuggestService {
private static final String MAX_SUFFIX = "\u9fa5";
private final Provider<ReviewDb> reviewDbProvider;
private final AccountCache accountCache;
- private final GroupControl.Factory groupControlFactory;
private final GroupMembers.Factory groupMembersFactory;
private final IdentifiedUser.GenericFactory identifiedUserFactory;
private final AccountControl.Factory accountControlFactory;
private final ChangeControl.Factory changeControlFactory;
+ private final ProjectControl.Factory projectControlFactory;
private final Config cfg;
- private final GroupCache groupCache;
+ private final GroupBackend groupBackend;
private final boolean suggestAccounts;
@Inject
SuggestServiceImpl(final Provider<ReviewDb> schema,
final AccountCache accountCache,
- final GroupControl.Factory groupControlFactory,
final GroupMembers.Factory groupMembersFactory,
final Provider<CurrentUser> currentUser,
final IdentifiedUser.GenericFactory identifiedUserFactory,
final AccountControl.Factory accountControlFactory,
final ChangeControl.Factory changeControlFactory,
- @GerritServerConfig final Config cfg, final GroupCache groupCache) {
+ final ProjectControl.Factory projectControlFactory,
+ @GerritServerConfig final Config cfg, final GroupBackend groupBackend) {
super(schema, currentUser);
this.reviewDbProvider = schema;
this.accountCache = accountCache;
- this.groupControlFactory = groupControlFactory;
this.groupMembersFactory = groupMembersFactory;
this.identifiedUserFactory = identifiedUserFactory;
this.accountControlFactory = accountControlFactory;
this.changeControlFactory = changeControlFactory;
+ this.projectControlFactory = projectControlFactory;
this.cfg = cfg;
- this.groupCache = groupCache;
+ this.groupBackend = groupBackend;
if ("OFF".equals(cfg.getString("suggest", null, "accounts"))) {
this.suggestAccounts = false;
@@ -176,33 +178,31 @@
public void suggestAccountGroup(final String query, final int limit,
final AsyncCallback<List<GroupReference>> callback) {
+ suggestAccountGroupForProject(null, query, limit, callback);
+ }
+
+ public void suggestAccountGroupForProject(final Project.NameKey project,
+ final String query, final int limit,
+ final AsyncCallback<List<GroupReference>> callback) {
run(callback, new Action<List<GroupReference>>() {
- public List<GroupReference> run(final ReviewDb db) throws OrmException {
- return suggestAccountGroup(db, query, limit);
+ public List<GroupReference> run(final ReviewDb db) {
+ ProjectControl projectControl = null;
+ if (project != null) {
+ try {
+ projectControl = projectControlFactory.controlFor(project);
+ } catch (NoSuchProjectException e) {
+ return Collections.emptyList();
+ }
+ }
+ return suggestAccountGroup(projectControl, query, limit);
}
});
}
- private List<GroupReference> suggestAccountGroup(final ReviewDb db,
- final String query, final int limit) throws OrmException {
- final String a = query;
- final String b = a + MAX_SUFFIX;
- final int max = 10;
- final int n = limit <= 0 ? max : Math.min(limit, max);
- List<GroupReference> r = new ArrayList<GroupReference>(n);
- for (AccountGroupName group : db.accountGroupNames().suggestByName(a, b, n)) {
- try {
- if (groupControlFactory.controlFor(group.getId()).isVisible()) {
- AccountGroup g = groupCache.get(group.getId());
- if (g != null && g.getGroupUUID() != null) {
- r.add(GroupReference.forGroup(g));
- }
- }
- } catch (NoSuchGroupException e) {
- continue;
- }
- }
- return r;
+ private List<GroupReference> suggestAccountGroup(
+ @Nullable final ProjectControl projectControl, final String query, final int limit) {
+ final int n = limit <= 0 ? 10 : Math.min(limit, 10);
+ return Lists.newArrayList(Iterables.limit(groupBackend.suggest(query), n));
}
@Override
@@ -243,7 +243,7 @@
reviewer.add(new ReviewerInfo(a));
}
final List<GroupReference> suggestedAccountGroups =
- suggestAccountGroup(db, query, limit);
+ suggestAccountGroup(changeControl.getProjectControl(), query, limit);
for (final GroupReference g : suggestedAccountGroups) {
if (suggestGroupAsReviewer(changeControl.getProject().getNameKey(), g)) {
reviewer.add(new ReviewerInfo(g));
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountCapabilitiesServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountCapabilitiesServlet.java
index 0d0ffe7..a33c209 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountCapabilitiesServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountCapabilitiesServlet.java
@@ -58,8 +58,9 @@
private final Provider<Impl> factory;
@Inject
- AccountCapabilitiesServlet(
+ AccountCapabilitiesServlet(final Provider<CurrentUser> currentUser,
ParameterParser paramParser, Provider<Impl> factory) {
+ super(currentUser);
this.paramParser = paramParser;
this.factory = factory;
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupAdminServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupAdminServiceImpl.java
index d9f327d..c7b4c79 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupAdminServiceImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupAdminServiceImpl.java
@@ -18,6 +18,7 @@
import com.google.gerrit.common.data.GroupDetail;
import com.google.gerrit.common.data.GroupList;
import com.google.gerrit.common.data.GroupOptions;
+import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.errors.InactiveAccountException;
import com.google.gerrit.common.errors.NameAlreadyUsedException;
import com.google.gerrit.common.errors.NoSuchAccountException;
@@ -34,29 +35,27 @@
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountResolver;
+import com.google.gerrit.server.account.GroupBackend;
+import com.google.gerrit.server.account.GroupBackends;
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.account.GroupIncludeCache;
-import com.google.gerrit.server.account.Realm;
import com.google.gwtjsonrpc.common.AsyncCallback;
import com.google.gwtjsonrpc.common.VoidResult;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
-import java.util.ArrayList;
import java.util.Collections;
-import java.util.Comparator;
import java.util.HashSet;
-import java.util.List;
import java.util.Set;
class GroupAdminServiceImpl extends BaseServiceImplementation implements
GroupAdminService {
private final AccountCache accountCache;
private final AccountResolver accountResolver;
- private final Realm accountRealm;
private final GroupCache groupCache;
+ private final GroupBackend groupBackend;
private final GroupIncludeCache groupIncludeCache;
private final GroupControl.Factory groupControlFactory;
@@ -70,8 +69,9 @@
final Provider<IdentifiedUser> currentUser,
final AccountCache accountCache,
final GroupIncludeCache groupIncludeCache,
- final AccountResolver accountResolver, final Realm accountRealm,
+ final AccountResolver accountResolver,
final GroupCache groupCache,
+ final GroupBackend groupBackend,
final GroupControl.Factory groupControlFactory,
final CreateGroup.Factory createGroupFactory,
final RenameGroup.Factory renameGroupFactory,
@@ -81,8 +81,8 @@
this.accountCache = accountCache;
this.groupIncludeCache = groupIncludeCache;
this.accountResolver = accountResolver;
- this.accountRealm = accountRealm;
this.groupCache = groupCache;
+ this.groupBackend = groupBackend;
this.groupControlFactory = groupControlFactory;
this.createGroupFactory = createGroupFactory;
this.renameGroupFactory = renameGroupFactory;
@@ -145,13 +145,13 @@
final AccountGroup group = db.accountGroups().get(groupId);
assertAmGroupOwner(db, group);
- AccountGroup owner =
- groupCache.get(new AccountGroup.NameKey(newOwnerName));
+ GroupReference owner =
+ GroupBackends.findExactSuggestion(groupBackend, newOwnerName);
if (owner == null) {
throw new Failure(new NoSuchEntityException());
}
- group.setOwnerGroupUUID(owner.getGroupUUID());
+ group.setOwnerGroupUUID(owner.getUUID());
db.accountGroups().update(Collections.singleton(group));
groupCache.evict(group);
return VoidResult.INSTANCE;
@@ -178,43 +178,13 @@
});
}
- public void changeExternalGroup(final AccountGroup.Id groupId,
- final AccountGroup.ExternalNameKey bindTo,
- final AsyncCallback<VoidResult> callback) {
- run(callback, new Action<VoidResult>() {
- public VoidResult run(final ReviewDb db) throws OrmException, Failure {
- final AccountGroup group = db.accountGroups().get(groupId);
- assertAmGroupOwner(db, group);
- group.setExternalNameKey(bindTo);
- db.accountGroups().update(Collections.singleton(group));
- groupCache.evict(group);
- return VoidResult.INSTANCE;
- }
- });
- }
-
- public void searchExternalGroups(final String searchFilter,
- final AsyncCallback<List<AccountGroup.ExternalNameKey>> callback) {
- final ArrayList<AccountGroup.ExternalNameKey> matches =
- new ArrayList<AccountGroup.ExternalNameKey>(
- accountRealm.lookupGroups(searchFilter));
- Collections.sort(matches, new Comparator<AccountGroup.ExternalNameKey>() {
- @Override
- public int compare(AccountGroup.ExternalNameKey a,
- AccountGroup.ExternalNameKey b) {
- return a.get().compareTo(b.get());
- }
- });
- callback.onSuccess(matches);
- }
-
public void addGroupMember(final AccountGroup.Id groupId,
final String nameOrEmail, final AsyncCallback<GroupDetail> callback) {
run(callback, new Action<GroupDetail>() {
public GroupDetail run(ReviewDb db) throws OrmException, Failure,
NoSuchGroupException {
final GroupControl control = groupControlFactory.validateFor(groupId);
- if (control.getAccountGroup().getType() != AccountGroup.Type.INTERNAL) {
+ if (groupCache.get(groupId).getType() != AccountGroup.Type.INTERNAL) {
throw new Failure(new NameAlreadyUsedException());
}
@@ -249,7 +219,7 @@
public GroupDetail run(ReviewDb db) throws OrmException, Failure,
NoSuchGroupException {
final GroupControl control = groupControlFactory.validateFor(groupId);
- if (control.getAccountGroup().getType() != AccountGroup.Type.INTERNAL) {
+ if (groupCache.get(groupId).getType() != AccountGroup.Type.INTERNAL) {
throw new Failure(new NameAlreadyUsedException());
}
@@ -282,7 +252,7 @@
public VoidResult run(final ReviewDb db) throws OrmException,
NoSuchGroupException, Failure {
final GroupControl control = groupControlFactory.validateFor(groupId);
- if (control.getAccountGroup().getType() != AccountGroup.Type.INTERNAL) {
+ if (groupCache.get(groupId).getType() != AccountGroup.Type.INTERNAL) {
throw new Failure(new NameAlreadyUsedException());
}
@@ -336,7 +306,7 @@
public VoidResult run(final ReviewDb db) throws OrmException,
NoSuchGroupException, Failure {
final GroupControl control = groupControlFactory.validateFor(groupId);
- if (control.getAccountGroup().getType() != AccountGroup.Type.INTERNAL) {
+ if (groupCache.get(groupId).getType() != AccountGroup.Type.INTERNAL) {
throw new Failure(new NameAlreadyUsedException());
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/change/ListChangesServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/change/ListChangesServlet.java
index 91fc5b0..b501d43 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/change/ListChangesServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/change/ListChangesServlet.java
@@ -15,6 +15,7 @@
package com.google.gerrit.httpd.rpc.change;
import com.google.gerrit.httpd.RestApiServlet;
+import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.OutputFormat;
import com.google.gerrit.server.query.QueryParseException;
import com.google.gerrit.server.query.change.ListChanges;
@@ -43,7 +44,9 @@
private final Provider<ListChanges> factory;
@Inject
- ListChangesServlet(ParameterParser paramParser, Provider<ListChanges> ls) {
+ ListChangesServlet(final Provider<CurrentUser> currentUser,
+ ParameterParser paramParser, Provider<ListChanges> ls) {
+ super(currentUser);
this.paramParser = paramParser;
this.factory = ls;
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java
index d765f39..6b5299a 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java
@@ -143,20 +143,20 @@
detail.setCanEdit(control.getRefControl().canWrite());
- if (detail.getChange().getStatus().isOpen()) {
- List<SubmitRecord> submitRecords = control.canSubmit(db, patch);
- for (SubmitRecord rec : submitRecords) {
- if (rec.labels != null) {
- for (SubmitRecord.Label lbl : rec.labels) {
- aic.want(lbl.appliedBy);
- }
- }
- if (rec.status == SubmitRecord.Status.OK && control.getRefControl().canSubmit()) {
- detail.setCanSubmit(true);
+ List<SubmitRecord> submitRecords = control.getSubmitRecords(db, patch);
+ for (SubmitRecord rec : submitRecords) {
+ if (rec.labels != null) {
+ for (SubmitRecord.Label lbl : rec.labels) {
+ aic.want(lbl.appliedBy);
}
}
- detail.setSubmitRecords(submitRecords);
+ if (detail.getChange().getStatus().isOpen()
+ && rec.status == SubmitRecord.Status.OK
+ && control.getRefControl().canSubmit()) {
+ detail.setCanSubmit(true);
+ }
}
+ detail.setSubmitRecords(submitRecords);
patchsetsById = new HashMap<PatchSet.Id, PatchSet>();
loadPatchSets();
@@ -274,13 +274,16 @@
}
}
- final RevId cprev = loader.patchSet.getRevision();
final Set<Change.Id> descendants = new HashSet<Change.Id>();
- if (cprev != null) {
- for (PatchSetAncestor a : db.patchSetAncestors().descendantsOf(cprev)) {
- final Change.Id ck = a.getPatchSet().getParentKey();
- if (descendants.add(ck)) {
- changesToGet.add(a.getPatchSet().getParentKey());
+ RevId cprev;
+ for (PatchSet p : detail.getPatchSets()) {
+ cprev = p.getRevision();
+ if (cprev != null) {
+ for (PatchSetAncestor a : db.patchSetAncestors().descendantsOf(cprev)) {
+ final Change.Id ck = a.getPatchSet().getParentKey();
+ if (descendants.add(ck)) {
+ changesToGet.add(ck);
+ }
}
}
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetDetailFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetDetailFactory.java
index de3ff2f..95a8e26 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetDetailFactory.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetDetailFactory.java
@@ -31,6 +31,7 @@
import com.google.gerrit.server.patch.PatchList;
import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.patch.PatchListKey;
+import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
import com.google.gerrit.server.project.ChangeControl;
@@ -108,18 +109,19 @@
final PatchList list;
- if (psIdBase != null) {
- oldId = toObjectId(psIdBase);
- newId = toObjectId(psIdNew);
+ try {
+ if (psIdBase != null) {
+ oldId = toObjectId(psIdBase);
+ newId = toObjectId(psIdNew);
- projectKey = control.getProject().getNameKey();
+ projectKey = control.getProject().getNameKey();
- list = listFor(keyFor(diffPrefs.getIgnoreWhitespace()));
- } else { // OK, means use base to compare
- list = patchListCache.get(control.getChange(), patchSet);
- if (list == null) {
- throw new NoSuchEntityException();
+ list = listFor(keyFor(diffPrefs.getIgnoreWhitespace()));
+ } else { // OK, means use base to compare
+ list = patchListCache.get(control.getChange(), patchSet);
}
+ } catch (PatchListNotAvailableException e) {
+ throw new NoSuchEntityException();
}
final List<Patch> patches = list.toPatchList(patchSet.getId());
@@ -185,7 +187,8 @@
return new PatchListKey(projectKey, oldId, newId, whitespace);
}
- private PatchList listFor(final PatchListKey key) {
+ private PatchList listFor(PatchListKey key)
+ throws PatchListNotAvailableException {
return patchListCache.get(key);
}
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetPublishDetailFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetPublishDetailFactory.java
index 183b5f6..37a1c92 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetPublishDetailFactory.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetPublishDetailFactory.java
@@ -145,6 +145,7 @@
switch (lbl.status) {
case OK:
+ case MAY:
ok++;
break;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchScriptFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchScriptFactory.java
index ac00b8d..8ad9a10 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchScriptFactory.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchScriptFactory.java
@@ -35,6 +35,7 @@
import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.patch.PatchListEntry;
import com.google.gerrit.server.patch.PatchListKey;
+import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gwtorm.server.OrmException;
@@ -154,12 +155,12 @@
content.getOldName(), //
content.getNewName());
- try {
return b.toPatchScript(content, comments, history);
- } catch (IOException e) {
- log.error("File content unavailable", e);
- throw new NoSuchChangeException(changeId, e);
- }
+ } catch (PatchListNotAvailableException e) {
+ throw new NoSuchChangeException(changeId, e);
+ } catch (IOException e) {
+ log.error("File content unavailable", e);
+ throw new NoSuchChangeException(changeId, e);
} finally {
git.close();
}
@@ -169,7 +170,8 @@
return new PatchListKey(projectKey, aId, bId, whitespace);
}
- private PatchList listFor(final PatchListKey key) {
+ private PatchList listFor(final PatchListKey key)
+ throws PatchListNotAvailableException {
return patchListCache.get(key);
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/plugin/ListPluginsServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/plugin/ListPluginsServlet.java
new file mode 100644
index 0000000..5e8145c
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/plugin/ListPluginsServlet.java
@@ -0,0 +1,68 @@
+// Copyright (C) 2012 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.httpd.rpc.plugin;
+
+import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
+import com.google.gerrit.httpd.RestApiServlet;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.OutputFormat;
+import com.google.gerrit.server.plugins.ListPlugins;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@Singleton
+@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
+public class ListPluginsServlet extends RestApiServlet {
+ private static final long serialVersionUID = 1L;
+ private final ParameterParser paramParser;
+ private final Provider<ListPlugins> factory;
+
+ @Inject
+ ListPluginsServlet(final Provider<CurrentUser> currentUser,
+ ParameterParser paramParser, Provider<ListPlugins> ls) {
+ super(currentUser);
+ this.paramParser = paramParser;
+ this.factory = ls;
+ }
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse res)
+ throws IOException {
+ ListPlugins impl = factory.get();
+ if (acceptsJson(req)) {
+ impl.setFormat(OutputFormat.JSON_COMPACT);
+ }
+ if (paramParser.parse(impl, req, res)) {
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ if (impl.getFormat().isJson()) {
+ res.setContentType(JSON_TYPE);
+ buf.write(JSON_MAGIC);
+ } else {
+ res.setContentType("text/plain");
+ }
+ impl.display(buf);
+ res.setCharacterEncoding("UTF-8");
+ send(req, res, buf.toByteArray());
+ }
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java
index 777329b..c991c47 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java
@@ -22,9 +22,9 @@
import com.google.gerrit.common.errors.InvalidNameException;
import com.google.gerrit.common.errors.NoSuchGroupException;
import com.google.gerrit.httpd.rpc.Handler;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.account.GroupCache;
+import com.google.gerrit.server.account.GroupBackend;
+import com.google.gerrit.server.account.GroupBackends;
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.project.NoSuchProjectException;
@@ -60,7 +60,7 @@
private final ProjectAccessFactory.Factory projectAccessFactory;
private final ProjectControl.Factory projectControlFactory;
private final ProjectCache projectCache;
- private final GroupCache groupCache;
+ private final GroupBackend groupBackend;
private final MetaDataUpdate.User metaDataUpdateFactory;
private final Project.NameKey projectName;
@@ -71,7 +71,7 @@
@Inject
ChangeProjectAccess(final ProjectAccessFactory.Factory projectAccessFactory,
final ProjectControl.Factory projectControlFactory,
- final ProjectCache projectCache, final GroupCache groupCache,
+ final ProjectCache projectCache, final GroupBackend groupBackend,
final MetaDataUpdate.User metaDataUpdateFactory,
@Assisted final Project.NameKey projectName,
@@ -81,7 +81,7 @@
this.projectAccessFactory = projectAccessFactory;
this.projectControlFactory = projectControlFactory;
this.projectCache = projectCache;
- this.groupCache = groupCache;
+ this.groupBackend = groupBackend;
this.metaDataUpdateFactory = metaDataUpdateFactory;
this.projectName = projectName;
@@ -198,12 +198,12 @@
private void lookupGroup(PermissionRule rule) throws NoSuchGroupException {
GroupReference ref = rule.getGroup();
if (ref.getUUID() == null) {
- AccountGroup.NameKey name = new AccountGroup.NameKey(ref.getName());
- AccountGroup group = groupCache.get(name);
+ final GroupReference group =
+ GroupBackends.findBestSuggestion(groupBackend, ref.getName());
if (group == null) {
- throw new NoSuchGroupException(name);
+ throw new NoSuchGroupException(ref.getName());
}
- ref.setUUID(group.getGroupUUID());
+ ref.setUUID(group.getUUID());
}
}
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/CreateProjectHandler.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/CreateProjectHandler.java
index 039a301..e0a2f9c 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/CreateProjectHandler.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/CreateProjectHandler.java
@@ -28,6 +28,8 @@
import org.eclipse.jgit.lib.Constants;
+import java.util.Collections;
+
public class CreateProjectHandler extends Handler<VoidResult> {
interface Factory {
@@ -74,7 +76,7 @@
}
args.projectDescription = "";
args.submitType = SubmitType.MERGE_IF_NECESSARY;
- args.branch = Constants.MASTER;
+ args.branch = Collections.emptyList();
args.createEmptyCommit = emptyCommit;
args.permissionsOnly = permissionsOnly;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ListProjectsServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ListProjectsServlet.java
index 2757640..d327d35 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ListProjectsServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ListProjectsServlet.java
@@ -16,6 +16,7 @@
import com.google.common.base.Strings;
import com.google.gerrit.httpd.RestApiServlet;
+import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.OutputFormat;
import com.google.gerrit.server.project.ListProjects;
import com.google.inject.Inject;
@@ -36,7 +37,9 @@
private final Provider<ListProjects> factory;
@Inject
- ListProjectsServlet(ParameterParser paramParser, Provider<ListProjects> ls) {
+ ListProjectsServlet(final Provider<CurrentUser> currentUser,
+ ParameterParser paramParser, Provider<ListProjects> ls) {
+ super(currentUser);
this.paramParser = paramParser;
this.factory = ls;
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java
index 7ac4ec3..f934d11 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java
@@ -23,7 +23,7 @@
import com.google.gerrit.httpd.rpc.Handler;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.account.GroupCache;
+import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -51,7 +51,7 @@
ProjectAccessFactory create(@Assisted Project.NameKey name);
}
- private final GroupCache groupCache;
+ private final GroupBackend groupBackend;
private final ProjectCache projectCache;
private final ProjectControl.Factory projectControlFactory;
private final GroupControl.Factory groupControlFactory;
@@ -62,7 +62,7 @@
private ProjectControl pc;
@Inject
- ProjectAccessFactory(final GroupCache groupCache,
+ ProjectAccessFactory(final GroupBackend groupBackend,
final ProjectCache projectCache,
final ProjectControl.Factory projectControlFactory,
final GroupControl.Factory groupControlFactory,
@@ -70,7 +70,7 @@
final AllProjectsName allProjectsName,
@Assisted final Project.NameKey name) {
- this.groupCache = groupCache;
+ this.groupBackend = groupBackend;
this.projectCache = projectCache;
this.projectControlFactory = projectControlFactory;
this.groupControlFactory = groupControlFactory;
@@ -94,7 +94,7 @@
try {
config = ProjectConfig.read(md);
- if (config.updateGroupNames(groupCache)) {
+ if (config.updateGroupNames(groupBackend)) {
md.setMessage("Update group names\n");
if (config.commit(md)) {
projectCache.evict(config.getProject());
@@ -110,6 +110,7 @@
md.close();
}
+ final RefControl metaConfigControl = pc.controlForRef(GitRepositoryManager.REF_CONFIG);
List<AccessSection> local = new ArrayList<AccessSection>();
Set<String> ownerOf = new HashSet<String>();
Map<AccountGroup.UUID, Boolean> visibleGroups =
@@ -125,7 +126,7 @@
} else if (RefConfigSection.isValid(name)) {
RefControl rc = pc.controlForRef(name);
- if (rc.isOwner()) {
+ if (rc.isOwner() || metaConfigControl.isVisible()) {
local.add(section);
ownerOf.add(name);
@@ -195,8 +196,7 @@
detail.setLocal(local);
detail.setOwnerOf(ownerOf);
- detail.setConfigVisible(pc.isOwner()
- || pc.controlForRef(GitRepositoryManager.REF_CONFIG).isVisible());
+ detail.setConfigVisible(pc.isOwner() || metaConfigControl.isVisible());
return detail;
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java
index a6bb74f..ca7f448 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java
@@ -19,7 +19,6 @@
import com.google.gerrit.common.data.ProjectAccess;
import com.google.gerrit.common.data.ProjectAdminService;
import com.google.gerrit.common.data.ProjectDetail;
-import com.google.gerrit.common.data.ProjectList;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gwtjsonrpc.common.AsyncCallback;
@@ -37,12 +36,10 @@
private final ChangeProjectSettings.Factory changeProjectSettingsFactory;
private final DeleteBranches.Factory deleteBranchesFactory;
private final ListBranches.Factory listBranchesFactory;
- private final VisibleProjects.Factory visibleProjectsFactory;
private final VisibleProjectDetails.Factory visibleProjectDetailsFactory;
private final ProjectAccessFactory.Factory projectAccessFactory;
private final CreateProjectHandler.Factory createProjectHandlerFactory;
private final ProjectDetailFactory.Factory projectDetailFactory;
- private final SuggestParentCandidatesHandler.Factory suggestParentCandidatesHandlerFactory;
@Inject
ProjectAdminServiceImpl(final AddBranch.Factory addBranchFactory,
@@ -50,28 +47,19 @@
final ChangeProjectSettings.Factory changeProjectSettingsFactory,
final DeleteBranches.Factory deleteBranchesFactory,
final ListBranches.Factory listBranchesFactory,
- final VisibleProjects.Factory visibleProjectsFactory,
final VisibleProjectDetails.Factory visibleProjectDetailsFactory,
final ProjectAccessFactory.Factory projectAccessFactory,
final ProjectDetailFactory.Factory projectDetailFactory,
- final SuggestParentCandidatesHandler.Factory parentCandidatesFactory,
final CreateProjectHandler.Factory createNewProjectFactory) {
this.addBranchFactory = addBranchFactory;
this.changeProjectAccessFactory = changeProjectAccessFactory;
this.changeProjectSettingsFactory = changeProjectSettingsFactory;
this.deleteBranchesFactory = deleteBranchesFactory;
this.listBranchesFactory = listBranchesFactory;
- this.visibleProjectsFactory = visibleProjectsFactory;
this.visibleProjectDetailsFactory = visibleProjectDetailsFactory;
this.projectAccessFactory = projectAccessFactory;
this.projectDetailFactory = projectDetailFactory;
this.createProjectHandlerFactory = createNewProjectFactory;
- this.suggestParentCandidatesHandlerFactory = parentCandidatesFactory;
- }
-
- @Override
- public void visibleProjects(final AsyncCallback<ProjectList> callback) {
- visibleProjectsFactory.create().to(callback);
}
@Override
@@ -80,11 +68,6 @@
}
@Override
- public void suggestParentCandidates(AsyncCallback<List<Project>> callback) {
- suggestParentCandidatesHandlerFactory.create().to(callback);
- }
-
- @Override
public void projectDetail(final Project.NameKey projectName,
final AsyncCallback<ProjectDetail> callback) {
projectDetailFactory.create(projectName).to(callback);
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java
index 2eb55b3..efcc22f 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java
@@ -34,11 +34,9 @@
factory(ChangeProjectSettings.Factory.class);
factory(DeleteBranches.Factory.class);
factory(ListBranches.Factory.class);
- factory(VisibleProjects.Factory.class);
factory(VisibleProjectDetails.Factory.class);
factory(ProjectAccessFactory.Factory.class);
factory(ProjectDetailFactory.Factory.class);
- factory(SuggestParentCandidatesHandler.Factory.class);
}
});
rpc(ProjectAdminServiceImpl.class);
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/SuggestParentCandidatesHandler.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/SuggestParentCandidatesHandler.java
deleted file mode 100644
index ba0e4cd..0000000
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/SuggestParentCandidatesHandler.java
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (C) 2011 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License
-
-package com.google.gerrit.httpd.rpc.project;
-
-import com.google.gerrit.httpd.rpc.Handler;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.project.SuggestParentCandidates;
-import com.google.inject.Inject;
-
-import java.util.List;
-
-public class SuggestParentCandidatesHandler extends Handler<List<Project>> {
- interface Factory {
- SuggestParentCandidatesHandler create();
- }
-
- private final SuggestParentCandidates suggestParentCandidates;
-
- @Inject
- SuggestParentCandidatesHandler(final SuggestParentCandidates suggestParentCandidates) {
- this.suggestParentCandidates = suggestParentCandidates;
- }
-
- @Override
- public List<Project> call() throws Exception {
- return suggestParentCandidates.getProjects();
- }
-}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/VisibleProjects.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/VisibleProjects.java
deleted file mode 100644
index ba65617..0000000
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/VisibleProjects.java
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.httpd.rpc.project;
-
-import com.google.gerrit.common.data.ProjectList;
-import com.google.gerrit.httpd.rpc.Handler;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectControl;
-import com.google.inject.Inject;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-class VisibleProjects extends Handler<ProjectList> {
- interface Factory {
- VisibleProjects create();
- }
-
- private final ProjectControl.Factory projectControlFactory;
- private final ProjectCache projectCache;
- private final CurrentUser user;
-
- @Inject
- VisibleProjects(final ProjectControl.Factory projectControlFactory,
- final ProjectCache projectCache, final CurrentUser user) {
- this.projectControlFactory = projectControlFactory;
- this.projectCache = projectCache;
- this.user = user;
- }
-
- @Override
- public ProjectList call() {
- ProjectList result = new ProjectList();
- result.setProjects(getProjects());
- result.setCanCreateProject(user.getCapabilities().canCreateProject());
- return result;
- }
-
- private List<Project> getProjects() {
- List<Project> result = new ArrayList<Project>();
- for (Project.NameKey p : projectCache.all()) {
- try {
- ProjectControl c = projectControlFactory.controlFor(p);
- if (c.isVisible() || c.isOwner()) {
- result.add(c.getProject());
- }
- } catch (NoSuchProjectException e) {
- continue;
- }
- }
- Collections.sort(result, new Comparator<Project>() {
- public int compare(final Project a, final Project b) {
- return a.getName().compareTo(b.getName());
- }
- });
- return result;
- }
-}
diff --git a/gerrit-patch-commonsnet/src/main/java/org/apache/commons/net/smtp/AuthSMTPClient.java b/gerrit-patch-commonsnet/src/main/java/org/apache/commons/net/smtp/AuthSMTPClient.java
index 7d7bc49..1f08a81 100644
--- a/gerrit-patch-commonsnet/src/main/java/org/apache/commons/net/smtp/AuthSMTPClient.java
+++ b/gerrit-patch-commonsnet/src/main/java/org/apache/commons/net/smtp/AuthSMTPClient.java
@@ -18,7 +18,11 @@
import org.apache.commons.codec.binary.Base64;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.SocketException;
import java.security.InvalidKeyException;
@@ -50,7 +54,22 @@
}
_socket_ = sslFactory(verify).createSocket(_socket_, hostname, port, true);
- _connectAction_();
+
+ // XXX: Can't call _connectAction_() because SMTP server doesn't
+ // give banner information again after STARTTLS, thus SMTP._connectAction_()
+ // will wait on __getReply() forever, see source code of commons-net-2.2.
+ //
+ // The lines below are copied from SocketClient._connectAction_() and
+ // SMTP._connectAction_() in commons-net-2.2.
+ _socket_.setSoTimeout(_timeout_);
+ _input_ = _socket_.getInputStream();
+ _output_ = _socket_.getOutputStream();
+ _reader =
+ new BufferedReader(new InputStreamReader(_input_,
+ UTF_8));
+ _writer =
+ new BufferedWriter(new OutputStreamWriter(_output_,
+ UTF_8));
return true;
}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
index c05948d..ff5ee17 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
@@ -17,7 +17,6 @@
import static com.google.gerrit.server.schema.DataSourceProvider.Context.MULTI_USER;
import com.google.gerrit.common.ChangeHookRunner;
-import com.google.gerrit.ehcache.EhcachePoolImpl;
import com.google.gerrit.httpd.CacheBasedWebSession;
import com.google.gerrit.httpd.GitOverHttpModule;
import com.google.gerrit.httpd.HttpCanonicalWebUrlProvider;
@@ -36,6 +35,7 @@
import com.google.gerrit.pgm.util.RuntimeShutdown;
import com.google.gerrit.pgm.util.SiteProgram;
import com.google.gerrit.reviewdb.client.AuthType;
+import com.google.gerrit.server.cache.h2.DefaultCacheFactory;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.AuthConfigModule;
import com.google.gerrit.server.config.CanonicalWebUrlModule;
@@ -157,6 +157,7 @@
manager.start();
RuntimeShutdown.add(new Runnable() {
+ @Override
public void run() {
log.info("caught shutdown, cleaning up");
if (runId != null) {
@@ -209,7 +210,7 @@
modules.add(new ChangeHookRunner.Module());
modules.add(new ReceiveCommitsExecutorModule());
modules.add(cfgInjector.getInstance(GerritGlobalModule.class));
- modules.add(new EhcachePoolImpl.Module());
+ modules.add(new DefaultCacheFactory.Module());
modules.add(new SmtpEmailSender.Module());
modules.add(new SignedTokenEmailTokenVerifier.Module());
modules.add(new PluginModule());
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ExportReviewNotes.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ExportReviewNotes.java
index 5f0bc80..f100372 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ExportReviewNotes.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ExportReviewNotes.java
@@ -17,7 +17,6 @@
import static com.google.gerrit.server.schema.DataSourceProvider.Context.MULTI_USER;
import com.google.gerrit.common.data.ApprovalTypes;
-import com.google.gerrit.ehcache.EhcachePoolImpl;
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.pgm.util.SiteProgram;
@@ -27,7 +26,7 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.account.AccountCacheImpl;
import com.google.gerrit.server.account.GroupCacheImpl;
-import com.google.gerrit.server.cache.CachePool;
+import com.google.gerrit.server.cache.h2.DefaultCacheFactory;
import com.google.gerrit.server.config.ApprovalTypesProvider;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.CanonicalWebUrlProvider;
@@ -100,7 +99,7 @@
install(AccountCacheImpl.module());
install(GroupCacheImpl.module());
- install(new EhcachePoolImpl.Module());
+ install(new DefaultCacheFactory.Module());
install(new FactoryModule() {
@Override
protected void configure() {
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Gsql.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Gsql.java
index d967969..a5ce908 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Gsql.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Gsql.java
@@ -47,6 +47,7 @@
manager.add(dbInjector);
manager.start();
RuntimeShutdown.add(new Runnable() {
+ @Override
public void run() {
try {
System.in.close();
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
index f06946f..2d56453 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
@@ -191,6 +191,11 @@
}
@Override
+ public boolean isBatch() {
+ return ui.isBatch();
+ }
+
+ @Override
public void pruneSchema(StatementExecutor e, List<String> prune) {
for (String p : prune) {
if (!pruneList.contains(p)) {
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Rulec.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Rulec.java
index 451ed30..cabdc64 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Rulec.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Rulec.java
@@ -43,7 +43,7 @@
@Option(name = "--all", usage = "recompile all rules")
private boolean all;
- @Option(name = "--quiet", usage = "supress some messsages")
+ @Option(name = "--quiet", usage = "suppress some messages")
private boolean quiet;
@Argument(index = 0, multiValued = true, metaVar = "PROJECT", usage = "project to compile rules for")
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitSendEmail.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitSendEmail.java
index 7a3556e..c5732e9 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitSendEmail.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitSendEmail.java
@@ -28,12 +28,14 @@
class InitSendEmail implements InitStep {
private final ConsoleUI ui;
private final Section sendemail;
+ private final SitePaths site;
@Inject
InitSendEmail(final ConsoleUI ui, final SitePaths site,
final Section.Factory sections) {
this.ui = ui;
this.sendemail = sections.get("sendemail");
+ this.site = site;
}
public void run() {
@@ -49,7 +51,9 @@
true);
String username = null;
- if ((enc != null && enc != Encryption.NONE) || !isLocal(hostname)) {
+ if (site.gerrit_config.exists()) {
+ username = sendemail.get("smtpUser");
+ } else if ((enc != null && enc != Encryption.NONE) || !isLocal(hostname)) {
username = username();
}
sendemail.string("SMTP username", "smtpUser", username);
diff --git a/gerrit-plugin-api/pom.xml b/gerrit-plugin-api/pom.xml
index 5c4ca3449..84f6f7b 100644
--- a/gerrit-plugin-api/pom.xml
+++ b/gerrit-plugin-api/pom.xml
@@ -62,7 +62,6 @@
<excludes>
<exclude>gwtexpui:gwtexpui</exclude>
<exclude>gwtjsonrpc:gwtjsonrpc</exclude>
- <exclude>com.google.gerrit:gerrit-ehcache</exclude>
<exclude>com.google.gerrit:gerrit-prettify</exclude>
<exclude>com.google.gerrit:gerrit-patch-commonsnet</exclude>
<exclude>com.google.gerrit:gerrit-patch-jgit</exclude>
@@ -82,7 +81,6 @@
<exclude>asm:asm</exclude>
<exclude>eu.medsea.mimeutil:mime-util</exclude>
- <exclude>net.sf.ehcache:ehcache-core</exclude>
<exclude>org.antlr:antlr</exclude>
<exclude>org.antlr:antlr-runtime</exclude>
<exclude>org.apache.mina:mina-core</exclude>
diff --git a/gerrit-ehcache/.gitignore b/gerrit-plugin-archetype/.gitignore
similarity index 84%
rename from gerrit-ehcache/.gitignore
rename to gerrit-plugin-archetype/.gitignore
index fe190c9..80d6257 100644
--- a/gerrit-ehcache/.gitignore
+++ b/gerrit-plugin-archetype/.gitignore
@@ -1,6 +1,5 @@
/target
/.classpath
/.project
-/.settings/org.eclipse.m2e.core.prefs
/.settings/org.maven.ide.eclipse.prefs
-/gerrit-ehcache.iml
\ No newline at end of file
+/.settings/org.eclipse.m2e.core.prefs
diff --git a/gerrit-plugin-archetype/pom.xml b/gerrit-plugin-archetype/pom.xml
new file mode 100644
index 0000000..dd1794b
--- /dev/null
+++ b/gerrit-plugin-archetype/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2012 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-parent</artifactId>
+ <version>2.5-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>gerrit-plugin-archetype</artifactId>
+ <name>Gerrit Code Review - Plugin Archetype</name>
+
+ <properties>
+ <defaultGerritApiVersion>${project.version}</defaultGerritApiVersion>
+ </properties>
+
+ <build>
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <filtering>true</filtering>
+ <includes>
+ <include>META-INF/maven/archetype-metadata.xml</include>
+ </includes>
+ </resource>
+ <resource>
+ <directory>src/main/resources</directory>
+ <filtering>false</filtering>
+ <excludes>
+ <exclude>META-INF/maven/archetype-metadata.xml</exclude>
+ </excludes>
+ </resource>
+ </resources>
+ </build>
+
+</project>
diff --git a/gerrit-plugin-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml b/gerrit-plugin-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml
new file mode 100644
index 0000000..88328be
--- /dev/null
+++ b/gerrit-plugin-archetype/src/main/resources/META-INF/maven/archetype-metadata.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2012 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.
+-->
+<archetype-descriptor name="com.sap.ldi.qi.itests">
+ <requiredProperties>
+ <requiredProperty key="pluginName"/>
+
+ <requiredProperty key="Gerrit-Module">
+ <defaultValue>Y</defaultValue>
+ </requiredProperty>
+ <requiredProperty key="Gerrit-SshModule">
+ <defaultValue>Y</defaultValue>
+ </requiredProperty>
+ <requiredProperty key="Gerrit-HttpModule">
+ <defaultValue>Y</defaultValue>
+ </requiredProperty>
+
+ <requiredProperty key="Implementation-Vendor"/>
+ <requiredProperty key="Implementation-Url"/>
+
+ <requiredProperty key="gerritApiType">
+ <defaultValue>plugin</defaultValue>
+ </requiredProperty>
+ <requiredProperty key="gerritApiVersion">
+ <defaultValue>${defaultGerritApiVersion}</defaultValue>
+ </requiredProperty>
+ </requiredProperties>
+
+ <fileSets>
+ <fileSet filtered="true" packaged="true">
+ <directory>src/main/java</directory>
+ <includes>
+ <include>**/*.java</include>
+ </includes>
+ </fileSet>
+
+ <fileSet filtered="true">
+ <directory>src/main/resources/Documentation</directory>
+ <includes>
+ <include>**/*.md</include>
+ </includes>
+ </fileSet>
+
+ <fileSet>
+ <directory></directory>
+ <includes>
+ <include>.gitignore</include>
+ <include>LICENSE</include>
+ </includes>
+ </fileSet>
+ </fileSets>
+</archetype-descriptor>
diff --git a/gerrit-ehcache/.gitignore b/gerrit-plugin-archetype/src/main/resources/archetype-resources/.gitignore
similarity index 84%
copy from gerrit-ehcache/.gitignore
copy to gerrit-plugin-archetype/src/main/resources/archetype-resources/.gitignore
index fe190c9..80d6257 100644
--- a/gerrit-ehcache/.gitignore
+++ b/gerrit-plugin-archetype/src/main/resources/archetype-resources/.gitignore
@@ -1,6 +1,5 @@
/target
/.classpath
/.project
-/.settings/org.eclipse.m2e.core.prefs
/.settings/org.maven.ide.eclipse.prefs
-/gerrit-ehcache.iml
\ No newline at end of file
+/.settings/org.eclipse.m2e.core.prefs
diff --git a/gerrit-plugin-archetype/src/main/resources/archetype-resources/LICENSE b/gerrit-plugin-archetype/src/main/resources/archetype-resources/LICENSE
new file mode 100644
index 0000000..11069ed
--- /dev/null
+++ b/gerrit-plugin-archetype/src/main/resources/archetype-resources/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+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.
diff --git a/gerrit-plugin-archetype/src/main/resources/archetype-resources/pom.xml b/gerrit-plugin-archetype/src/main/resources/archetype-resources/pom.xml
new file mode 100644
index 0000000..92099fa
--- /dev/null
+++ b/gerrit-plugin-archetype/src/main/resources/archetype-resources/pom.xml
@@ -0,0 +1,103 @@
+<!--
+Copyright (C) 2012 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>${groupId}</groupId>
+ <artifactId>${artifactId}</artifactId>
+ <packaging>jar</packaging>
+ <version>${version}</version>
+ <name>${pluginName}</name>
+
+ <properties>
+ <Gerrit-ApiType>${gerritApiType}</Gerrit-ApiType>
+ <Gerrit-ApiVersion>${gerritApiVersion}</Gerrit-ApiVersion>
+ </properties>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>2.4</version>
+ <configuration>
+ <archive>
+ <manifestEntries>
+#if ($Gerrit-Module.equalsIgnoreCase("Y"))
+ <Gerrit-Module>${package}.Module</Gerrit-Module>
+#end
+#if ($Gerrit-SshModule.equalsIgnoreCase("Y"))
+ <Gerrit-SshModule>${package}.SshModule</Gerrit-SshModule>
+#end
+#if ($Gerrit-HttpModule.equalsIgnoreCase("Y"))
+ <Gerrit-HttpModule>${package}.HttpModule</Gerrit-HttpModule>
+#end
+
+ <Implementation-Vendor>${Implementation-Vendor}</Implementation-Vendor>
+ <Implementation-URL>${Implementation-Url}</Implementation-URL>
+
+ <Implementation-Title>${Gerrit-ApiType} ${project.artifactId}</Implementation-Title>
+ <Implementation-Version>${project.version}</Implementation-Version>
+
+ <Gerrit-ApiType>${Gerrit-ApiType}</Gerrit-ApiType>
+ <Gerrit-ApiVersion>${Gerrit-ApiVersion}</Gerrit-ApiVersion>
+ </manifestEntries>
+ </archive>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.3.2</version>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ <encoding>UTF-8</encoding>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-${Gerrit-ApiType}-api</artifactId>
+ <version>${Gerrit-ApiVersion}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.8.1</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <repositories>
+ <repository>
+ <id>gerrit-api-repository</id>
+#if ($gerritApiVersion.endsWith("SNAPSHOT"))
+ <url>https://gerrit-api.commondatastorage.googleapis.com/snapshot/</url>
+#else
+ <url>https://gerrit-api.commondatastorage.googleapis.com/release/</url>
+#end
+ </repository>
+ </repositories>
+</project>
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/cache/CachePool.java b/gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/java/HttpModule.java
similarity index 70%
copy from gerrit-server/src/main/java/com/google/gerrit/server/cache/CachePool.java
copy to gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/java/HttpModule.java
index 3370b08..2840112 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/cache/CachePool.java
+++ b/gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/java/HttpModule.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2010 The Android Open Source Project
+// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.cache;
+package ${package};
-public interface CachePool {
- public <K, V> ProxyCache<K, V> register(CacheProvider<K, V> provider);
+import com.google.inject.servlet.ServletModule;
+
+class HttpModule extends ServletModule {
+ @Override
+ protected void configureServlets() {
+ // TODO
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/cache/CachePool.java b/gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/java/Module.java
similarity index 71%
rename from gerrit-server/src/main/java/com/google/gerrit/server/cache/CachePool.java
rename to gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/java/Module.java
index 3370b08..0d28349 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/cache/CachePool.java
+++ b/gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/java/Module.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2010 The Android Open Source Project
+// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.cache;
+package ${package};
-public interface CachePool {
- public <K, V> ProxyCache<K, V> register(CacheProvider<K, V> provider);
+import com.google.inject.AbstractModule;
+
+class Module extends AbstractModule {
+ @Override
+ protected void configure() {
+ // TODO
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/cache/CachePool.java b/gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/java/SshModule.java
similarity index 66%
copy from gerrit-server/src/main/java/com/google/gerrit/server/cache/CachePool.java
copy to gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/java/SshModule.java
index 3370b08..aa15ca5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/cache/CachePool.java
+++ b/gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/java/SshModule.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2010 The Android Open Source Project
+// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,8 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.cache;
+package ${package};
-public interface CachePool {
- public <K, V> ProxyCache<K, V> register(CacheProvider<K, V> provider);
+import com.google.gerrit.sshd.PluginCommandModule;
+
+class SshModule extends PluginCommandModule {
+ @Override
+ protected void configureCommands() {
+ // command("my-command").to(MyCommand.class);
+ }
}
diff --git a/gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/resources/Documentation/cmd-start.md b/gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/resources/Documentation/cmd-start.md
new file mode 100644
index 0000000..beecb90
--- /dev/null
+++ b/gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/resources/Documentation/cmd-start.md
@@ -0,0 +1 @@
+TODO: command documentation
diff --git a/gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/resources/Documentation/config.md b/gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/resources/Documentation/config.md
new file mode 100644
index 0000000..bde3084
--- /dev/null
+++ b/gerrit-plugin-archetype/src/main/resources/archetype-resources/src/main/resources/Documentation/config.md
@@ -0,0 +1 @@
+TODO: config documentation
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroup.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroup.java
index 1f244bb..2ea659d 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroup.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AccountGroup.java
@@ -79,30 +79,10 @@
}
}
- /** Distinguished name, within organization directory server. */
- public static class ExternalNameKey extends
- StringKey<com.google.gwtorm.client.Key<?>> {
- private static final long serialVersionUID = 1L;
-
- @Column(id = 1)
- protected String name;
-
- protected ExternalNameKey() {
- }
-
- public ExternalNameKey(final String n) {
- name = n;
- }
-
- @Override
- public String get() {
- return name;
- }
-
- @Override
- protected void set(String newValue) {
- name = newValue;
- }
+ /** @return true if the UUID is for a group managed within Gerrit. */
+ public static boolean isInternalGroup(AccountGroup.UUID uuid) {
+ return uuid.get().startsWith("global:")
+ || uuid.get().matches("[0-9a-f]{40}");
}
/** Synthetic key to link to within the database */
@@ -157,20 +137,7 @@
* who is a member of the owner group. These groups are not treated special
* in the code.
*/
- INTERNAL,
-
- /**
- * Group defined by external LDAP database.
- * <p>
- * A group whose membership is determined by the LDAP directory that we
- * connect to for user and group information. In UI contexts the membership
- * of the group is not displayed, as it may be exceedingly large, or might
- * contain users who have never logged into this server before (and thus
- * have no matching account record). Adding or removing users from an LDAP
- * group requires making edits through the LDAP directory, and cannot be
- * done through our UI.
- */
- LDAP;
+ INTERNAL;
}
/** Common UUID assigned to the "Project Owners" placeholder group. */
@@ -201,10 +168,6 @@
@Column(id = 5, length = 8)
protected String groupType;
- /** Distinguished name in the directory server. */
- @Column(id = 6, notNull = false)
- protected ExternalNameKey externalName;
-
@Column(id = 7)
protected boolean visibleToAll;
@@ -273,14 +236,6 @@
groupType = t.name();
}
- public ExternalNameKey getExternalNameKey() {
- return externalName;
- }
-
- public void setExternalNameKey(final ExternalNameKey k) {
- externalName = k;
- }
-
public void setVisibleToAll(final boolean visibleToAll) {
this.visibleToAll = visibleToAll;
}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountGroupAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountGroupAccess.java
index 9e88244..1de80f3 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountGroupAccess.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountGroupAccess.java
@@ -29,10 +29,6 @@
@Query("WHERE groupUUID = ?")
ResultSet<AccountGroup> byUUID(AccountGroup.UUID uuid) throws OrmException;
- @Query("WHERE externalName = ?")
- ResultSet<AccountGroup> byExternalName(AccountGroup.ExternalNameKey name)
- throws OrmException;
-
@Query
ResultSet<AccountGroup> all() throws OrmException;
}
diff --git a/gerrit-server/pom.xml b/gerrit-server/pom.xml
index ceb3c55..af18173 100644
--- a/gerrit-server/pom.xml
+++ b/gerrit-server/pom.xml
@@ -110,6 +110,11 @@
</dependency>
<dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+
+ <dependency>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-antlr</artifactId>
<version>${project.version}</version>
diff --git a/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java b/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java
index 2ba1304..1185fd3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java
@@ -30,6 +30,7 @@
import com.google.gerrit.server.patch.PatchList;
import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.patch.PatchListKey;
+import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
import com.google.gerrit.server.project.ChangeControl;
@@ -80,8 +81,10 @@
ObjectId b = ObjectId.fromString(psInfo.getRevId());
Whitespace ws = Whitespace.IGNORE_NONE;
PatchListKey plKey = new PatchListKey(projectKey, a, b, ws);
- PatchList patchList = plCache.get(plKey);
- if (patchList == null) {
+ PatchList patchList;
+ try {
+ patchList = plCache.get(plKey);
+ } catch (PatchListNotAvailableException e) {
throw new SystemException("Cannot create " + plKey);
}
return patchList;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/CmdLineParserModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/CmdLineParserModule.java
new file mode 100644
index 0000000..e64533c
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/CmdLineParserModule.java
@@ -0,0 +1,62 @@
+// Copyright (C) 2012 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;
+
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.server.config.FactoryModule;
+import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.args4j.AccountGroupIdHandler;
+import com.google.gerrit.server.args4j.AccountGroupUUIDHandler;
+import com.google.gerrit.server.args4j.AccountIdHandler;
+import com.google.gerrit.server.args4j.ChangeIdHandler;
+import com.google.gerrit.server.args4j.ObjectIdHandler;
+import com.google.gerrit.server.args4j.PatchSetIdHandler;
+import com.google.gerrit.server.args4j.ProjectControlHandler;
+import com.google.gerrit.server.args4j.SocketAddressHandler;
+import com.google.gerrit.util.cli.CmdLineParser;
+import com.google.gerrit.util.cli.OptionHandlerUtil;
+
+import org.eclipse.jgit.lib.ObjectId;
+
+import org.kohsuke.args4j.spi.OptionHandler;
+
+import java.net.SocketAddress;
+
+public class CmdLineParserModule extends FactoryModule {
+ public CmdLineParserModule() {
+ }
+
+ @Override
+ protected void configure() {
+ factory(CmdLineParser.Factory.class);
+
+ registerOptionHandler(Account.Id.class, AccountIdHandler.class);
+ registerOptionHandler(AccountGroup.Id.class, AccountGroupIdHandler.class);
+ registerOptionHandler(AccountGroup.UUID.class, AccountGroupUUIDHandler.class);
+ registerOptionHandler(Change.Id.class, ChangeIdHandler.class);
+ registerOptionHandler(ObjectId.class, ObjectIdHandler.class);
+ registerOptionHandler(PatchSet.Id.class, PatchSetIdHandler.class);
+ registerOptionHandler(ProjectControl.class, ProjectControlHandler.class);
+ registerOptionHandler(SocketAddress.class, SocketAddressHandler.class);
+ }
+
+ private <T> void registerOptionHandler(Class<T> type,
+ Class<? extends OptionHandler<T>> impl) {
+ install(OptionHandlerUtil.moduleFor(type, impl));
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java b/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java
index 6e519c4..050f7e1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server;
+import com.google.common.collect.ImmutableSet;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountDiffPreference;
import com.google.gerrit.reviewdb.client.AccountGroup;
@@ -24,8 +25,9 @@
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.CapabilityControl;
+import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.account.GroupMembership;
-import com.google.gerrit.server.account.MaterializedGroupMembership;
+import com.google.gerrit.server.account.ListGroupMembership;
import com.google.gerrit.server.account.Realm;
import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.config.AuthConfig;
@@ -46,13 +48,10 @@
import java.net.MalformedURLException;
import java.net.SocketAddress;
import java.net.URL;
-import java.util.AbstractSet;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TimeZone;
@@ -70,7 +69,7 @@
private final Provider<String> canonicalUrl;
private final Realm realm;
private final AccountCache accountCache;
- private final MaterializedGroupMembership.Factory groupMembershipFactory;
+ private final GroupBackend groupBackend;
@Inject
GenericFactory(
@@ -79,14 +78,14 @@
final @AnonymousCowardName String anonymousCowardName,
final @CanonicalWebUrl Provider<String> canonicalUrl,
final Realm realm, final AccountCache accountCache,
- final MaterializedGroupMembership.Factory groupMembershipFactory) {
+ final GroupBackend groupBackend) {
this.capabilityControlFactory = capabilityControlFactory;
this.authConfig = authConfig;
this.anonymousCowardName = anonymousCowardName;
this.canonicalUrl = canonicalUrl;
this.realm = realm;
this.accountCache = accountCache;
- this.groupMembershipFactory = groupMembershipFactory;
+ this.groupBackend = groupBackend;
}
public IdentifiedUser create(final Account.Id id) {
@@ -96,14 +95,14 @@
public IdentifiedUser create(Provider<ReviewDb> db, Account.Id id) {
return new IdentifiedUser(capabilityControlFactory, AccessPath.UNKNOWN,
authConfig, anonymousCowardName, canonicalUrl, realm, accountCache,
- groupMembershipFactory, null, db, id);
+ groupBackend, null, db, id);
}
public IdentifiedUser create(AccessPath accessPath,
Provider<SocketAddress> remotePeerProvider, Account.Id id) {
return new IdentifiedUser(capabilityControlFactory, accessPath,
authConfig, anonymousCowardName, canonicalUrl, realm, accountCache,
- groupMembershipFactory, remotePeerProvider, null, id);
+ groupBackend, remotePeerProvider, null, id);
}
}
@@ -121,7 +120,7 @@
private final Provider<String> canonicalUrl;
private final Realm realm;
private final AccountCache accountCache;
- private final MaterializedGroupMembership.Factory groupMembershipFactory;
+ private final GroupBackend groupBackend;
private final Provider<SocketAddress> remotePeerProvider;
private final Provider<ReviewDb> dbProvider;
@@ -133,7 +132,7 @@
final @AnonymousCowardName String anonymousCowardName,
final @CanonicalWebUrl Provider<String> canonicalUrl,
final Realm realm, final AccountCache accountCache,
- final MaterializedGroupMembership.Factory groupMembershipFactory,
+ final GroupBackend groupBackend,
final @RemotePeer Provider<SocketAddress> remotePeerProvider,
final Provider<ReviewDb> dbProvider) {
@@ -143,7 +142,7 @@
this.canonicalUrl = canonicalUrl;
this.realm = realm;
this.accountCache = accountCache;
- this.groupMembershipFactory = groupMembershipFactory;
+ this.groupBackend = groupBackend;
this.remotePeerProvider = remotePeerProvider;
this.dbProvider = dbProvider;
@@ -153,40 +152,22 @@
final Account.Id id) {
return new IdentifiedUser(capabilityControlFactory, accessPath,
authConfig, anonymousCowardName, canonicalUrl, realm, accountCache,
- groupMembershipFactory, remotePeerProvider, dbProvider, id);
+ groupBackend, remotePeerProvider, dbProvider, id);
}
}
private static final Logger log =
LoggerFactory.getLogger(IdentifiedUser.class);
- private static final Set<AccountGroup.UUID> registeredGroups =
- new AbstractSet<AccountGroup.UUID>() {
- private final List<AccountGroup.UUID> groups =
- Collections.unmodifiableList(Arrays.asList(new AccountGroup.UUID[] {
- AccountGroup.ANONYMOUS_USERS, AccountGroup.REGISTERED_USERS}));
-
- @Override
- public boolean contains(Object o) {
- return groups.contains(o);
- }
-
- @Override
- public Iterator<AccountGroup.UUID> iterator() {
- return groups.iterator();
- }
-
- @Override
- public int size() {
- return groups.size();
- }
- };
+ private static final GroupMembership registeredGroups =
+ new ListGroupMembership(ImmutableSet.of(
+ AccountGroup.ANONYMOUS_USERS,
+ AccountGroup.REGISTERED_USERS));
private final Provider<String> canonicalUrl;
- private final Realm realm;
private final AccountCache accountCache;
- private final MaterializedGroupMembership.Factory groupMembershipFactory;
private final AuthConfig authConfig;
+ private final GroupBackend groupBackend;
private final String anonymousCowardName;
@Nullable
@@ -210,14 +191,13 @@
final String anonymousCowardName,
final Provider<String> canonicalUrl,
final Realm realm, final AccountCache accountCache,
- final MaterializedGroupMembership.Factory groupMembershipFactory,
+ final GroupBackend groupBackend,
@Nullable final Provider<SocketAddress> remotePeerProvider,
@Nullable final Provider<ReviewDb> dbProvider, final Account.Id id) {
super(capabilityControlFactory, accessPath);
this.canonicalUrl = canonicalUrl;
- this.realm = realm;
this.accountCache = accountCache;
- this.groupMembershipFactory = groupMembershipFactory;
+ this.groupBackend = groupBackend;
this.authConfig = authConfig;
this.anonymousCowardName = anonymousCowardName;
this.remotePeerProvider = remotePeerProvider;
@@ -225,7 +205,8 @@
this.accountId = id;
}
- private AccountState state() {
+ // TODO(cranger): maybe get the state through the accountCache instead.
+ public AccountState state() {
if (state == null) {
state = accountCache.get(getAccountId());
}
@@ -272,12 +253,11 @@
public GroupMembership getEffectiveGroups() {
if (effectiveGroups == null) {
if (authConfig.isIdentityTrustable(state().getExternalIds())) {
- effectiveGroups = realm.groups(state());
+ effectiveGroups = groupBackend.membershipsOf(this);
} else {
- effectiveGroups = groupMembershipFactory.create(registeredGroups);
+ effectiveGroups = registeredGroups;
}
}
-
return effectiveGroups;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/StringUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/StringUtil.java
new file mode 100644
index 0000000..fe1072d
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/StringUtil.java
@@ -0,0 +1,54 @@
+// Copyright (C) 2012 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;
+
+public class StringUtil {
+ /**
+ * An array of the string representations that should be used in place
+ * of the non-printable characters in the beginning of the ASCII table
+ * when escaping a string. The index of each element in the array
+ * corresponds to its ASCII value, i.e. the string representation of
+ * ASCII 0 is found in the first element of this array.
+ */
+ static String[] NON_PRINTABLE_CHARS =
+ { "\\x00", "\\x01", "\\x02", "\\x03", "\\x04", "\\x05", "\\x06", "\\a",
+ "\\b", "\\t", "\\n", "\\v", "\\f", "\\r", "\\x0e", "\\x0f",
+ "\\x10", "\\x11", "\\x12", "\\x13", "\\x14", "\\x15", "\\x16", "\\x17",
+ "\\x18", "\\x19", "\\x1a", "\\x1b", "\\x1c", "\\x1d", "\\x1e", "\\x1f" };
+
+ /**
+ * Escapes the input string so that all non-printable characters
+ * (0x00-0x1f) are represented as a hex escape (\x00, \x01, ...)
+ * or as a C-style escape sequence (\a, \b, \t, \n, \v, \f, or \r).
+ * Backslashes in the input string are doubled (\\).
+ */
+ public static String escapeString(final String str) {
+ // Allocate a buffer big enough to cover the case with a string needed
+ // very excessive escaping without having to reallocate the buffer.
+ final StringBuilder result = new StringBuilder(3 * str.length());
+
+ for (int i = 0; i < str.length(); i++) {
+ char c = str.charAt(i);
+ if (c < NON_PRINTABLE_CHARS.length) {
+ result.append(NON_PRINTABLE_CHARS[c]);
+ } else if (c == '\\') {
+ result.append("\\\\");
+ } else {
+ result.append(c);
+ }
+ }
+ return result.toString();
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountByEmailCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountByEmailCacheImpl.java
index 72fb2e8..4827ed5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountByEmailCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountByEmailCacheImpl.java
@@ -14,12 +14,14 @@
package com.google.gerrit.server.account;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountExternalId;
import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.cache.Cache;
import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.cache.EntryCreator;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Module;
@@ -27,45 +29,58 @@
import com.google.inject.TypeLiteral;
import com.google.inject.name.Named;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import java.util.Collections;
-import java.util.HashSet;
import java.util.Set;
+import java.util.concurrent.ExecutionException;
/** Translates an email address to a set of matching accounts. */
@Singleton
public class AccountByEmailCacheImpl implements AccountByEmailCache {
+ private static final Logger log = LoggerFactory
+ .getLogger(AccountByEmailCacheImpl.class);
private static final String CACHE_NAME = "accounts_byemail";
public static Module module() {
return new CacheModule() {
@Override
protected void configure() {
- final TypeLiteral<Cache<String, Set<Account.Id>>> type =
- new TypeLiteral<Cache<String, Set<Account.Id>>>() {};
- core(type, CACHE_NAME).populateWith(Loader.class);
+ cache(CACHE_NAME,
+ String.class,
+ new TypeLiteral<Set<Account.Id>>() {})
+ .loader(Loader.class);
bind(AccountByEmailCacheImpl.class);
bind(AccountByEmailCache.class).to(AccountByEmailCacheImpl.class);
}
};
}
- private final Cache<String, Set<Account.Id>> cache;
+ private final LoadingCache<String, Set<Account.Id>> cache;
@Inject
AccountByEmailCacheImpl(
- @Named(CACHE_NAME) final Cache<String, Set<Account.Id>> cache) {
+ @Named(CACHE_NAME) LoadingCache<String, Set<Account.Id>> cache) {
this.cache = cache;
}
public Set<Account.Id> get(final String email) {
- return cache.get(email);
+ try {
+ return cache.get(email);
+ } catch (ExecutionException e) {
+ log.warn("Cannot resolve accounts by email", e);
+ return Collections.emptySet();
+ }
}
public void evict(final String email) {
- cache.remove(email);
+ if (email != null) {
+ cache.invalidate(email);
+ }
}
- static class Loader extends EntryCreator<String, Set<Account.Id>> {
+ static class Loader extends CacheLoader<String, Set<Account.Id>> {
private final SchemaFactory<ReviewDb> schema;
@Inject
@@ -74,10 +89,10 @@
}
@Override
- public Set<Account.Id> createEntry(final String email) throws Exception {
+ public Set<Account.Id> load(String email) throws Exception {
final ReviewDb db = schema.open();
try {
- final HashSet<Account.Id> r = new HashSet<Account.Id>();
+ Set<Account.Id> r = Sets.newHashSet();
for (Account a : db.accounts().byPreferredEmail(email)) {
r.add(a.getId());
}
@@ -85,30 +100,10 @@
.byEmailAddress(email)) {
r.add(a.getAccountId());
}
- return pack(r);
+ return ImmutableSet.copyOf(r);
} finally {
db.close();
}
}
-
- @Override
- public Set<Account.Id> missing(final String key) {
- return Collections.emptySet();
- }
-
- private static Set<Account.Id> pack(final Set<Account.Id> c) {
- switch (c.size()) {
- case 0:
- return Collections.emptySet();
- case 1:
- return one(c);
- default:
- return Collections.unmodifiableSet(new HashSet<Account.Id>(c));
- }
- }
-
- private static <T> Set<T> one(final Set<T> c) {
- return Collections.singleton(c.iterator().next());
- }
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java
index 819ec31..4217f9f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java
@@ -14,14 +14,16 @@
package com.google.gerrit.server.account;
+import com.google.common.base.Optional;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableSet;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountExternalId;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.AccountGroupMember;
import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.cache.Cache;
import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.cache.EntryCreator;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
@@ -30,14 +32,21 @@
import com.google.inject.TypeLiteral;
import com.google.inject.name.Named;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
+import java.util.concurrent.ExecutionException;
/** Caches important (but small) account state to avoid database hits. */
@Singleton
public class AccountCacheImpl implements AccountCache {
+ private static final Logger log = LoggerFactory
+ .getLogger(AccountCacheImpl.class);
+
private static final String BYID_NAME = "accounts";
private static final String BYUSER_NAME = "accounts_byname";
@@ -45,13 +54,13 @@
return new CacheModule() {
@Override
protected void configure() {
- final TypeLiteral<Cache<Account.Id, AccountState>> byIdType =
- new TypeLiteral<Cache<Account.Id, AccountState>>() {};
- core(byIdType, BYID_NAME).populateWith(ByIdLoader.class);
+ cache(BYID_NAME, Account.Id.class, AccountState.class)
+ .loader(ByIdLoader.class);
- final TypeLiteral<Cache<String, Account.Id>> byUsernameType =
- new TypeLiteral<Cache<String, Account.Id>>() {};
- core(byUsernameType, BYUSER_NAME).populateWith(ByNameLoader.class);
+ cache(BYUSER_NAME,
+ String.class,
+ new TypeLiteral<Optional<Account.Id>>() {})
+ .loader(ByNameLoader.class);
bind(AccountCacheImpl.class);
bind(AccountCache.class).to(AccountCacheImpl.class);
@@ -59,54 +68,76 @@
};
}
- private final Cache<Account.Id, AccountState> byId;
- private final Cache<String, Account.Id> byName;
+ private final LoadingCache<Account.Id, AccountState> byId;
+ private final LoadingCache<String, Optional<Account.Id>> byName;
@Inject
- AccountCacheImpl(@Named(BYID_NAME) Cache<Account.Id, AccountState> byId,
- @Named(BYUSER_NAME) Cache<String, Account.Id> byUsername) {
+ AccountCacheImpl(@Named(BYID_NAME) LoadingCache<Account.Id, AccountState> byId,
+ @Named(BYUSER_NAME) LoadingCache<String, Optional<Account.Id>> byUsername) {
this.byId = byId;
this.byName = byUsername;
}
- public AccountState get(final Account.Id accountId) {
- return byId.get(accountId);
+ public AccountState get(Account.Id accountId) {
+ try {
+ return byId.get(accountId);
+ } catch (ExecutionException e) {
+ log.warn("Cannot load AccountState for " + accountId, e);
+ return missing(accountId);
+ }
}
@Override
public AccountState getByUsername(String username) {
- Account.Id id = byName.get(username);
- return id != null ? byId.get(id) : null;
+ try {
+ Optional<Account.Id> id = byName.get(username);
+ return id != null && id.isPresent() ? byId.get(id.get()) : null;
+ } catch (ExecutionException e) {
+ log.warn("Cannot load AccountState for " + username, e);
+ return null;
+ }
}
- public void evict(final Account.Id accountId) {
- byId.remove(accountId);
+ public void evict(Account.Id accountId) {
+ if (accountId != null) {
+ byId.invalidate(accountId);
+ }
}
public void evictByUsername(String username) {
- byName.remove(username);
+ if (username != null) {
+ byName.invalidate(username);
+ }
}
- static class ByIdLoader extends EntryCreator<Account.Id, AccountState> {
+ private static AccountState missing(Account.Id accountId) {
+ Account account = new Account(accountId);
+ Collection<AccountExternalId> ids = Collections.emptySet();
+ Set<AccountGroup.UUID> anon = ImmutableSet.of(AccountGroup.ANONYMOUS_USERS);
+ return new AccountState(account, anon, ids);
+ }
+
+ static class ByIdLoader extends CacheLoader<Account.Id, AccountState> {
private final SchemaFactory<ReviewDb> schema;
private final GroupCache groupCache;
- private final Cache<String, Account.Id> byName;
+ private final LoadingCache<String, Optional<Account.Id>> byName;
@Inject
ByIdLoader(SchemaFactory<ReviewDb> sf, GroupCache groupCache,
- @Named(BYUSER_NAME) Cache<String, Account.Id> byUsername) {
+ @Named(BYUSER_NAME) LoadingCache<String, Optional<Account.Id>> byUsername) {
this.schema = sf;
this.groupCache = groupCache;
this.byName = byUsername;
}
@Override
- public AccountState createEntry(final Account.Id key) throws Exception {
+ public AccountState load(Account.Id key) throws Exception {
final ReviewDb db = schema.open();
try {
final AccountState state = load(db, key);
- if (state.getUserName() != null) {
- byName.put(state.getUserName(), state.getAccount().getId());
+ String user = state.getUserName();
+ if (user != null) {
+ byName.put(user, Optional.of(state.getAccount().getId()));
}
return state;
} finally {
@@ -142,18 +173,9 @@
return new AccountState(account, internalGroups, externalIds);
}
-
- @Override
- public AccountState missing(final Account.Id accountId) {
- final Account account = new Account(accountId);
- final Collection<AccountExternalId> ids = Collections.emptySet();
- final Set<AccountGroup.UUID> anonymous =
- Collections.singleton(AccountGroup.ANONYMOUS_USERS);
- return new AccountState(account, anonymous, ids);
- }
}
- static class ByNameLoader extends EntryCreator<String, Account.Id> {
+ static class ByNameLoader extends CacheLoader<String, Optional<Account.Id>> {
private final SchemaFactory<ReviewDb> schema;
@Inject
@@ -162,14 +184,17 @@
}
@Override
- public Account.Id createEntry(final String username) throws Exception {
+ public Optional<Account.Id> load(String username) throws Exception {
final ReviewDb db = schema.open();
try {
final AccountExternalId.Key key = new AccountExternalId.Key( //
AccountExternalId.SCHEME_USERNAME, //
username);
final AccountExternalId id = db.accountExternalIds().get(key);
- return id != null ? id.getAccountId() : null;
+ if (id != null) {
+ return Optional.of(id.getAccountId());
+ }
+ return Optional.absent();
} finally {
db.close();
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountControl.java
index e297ad7..32b4e2c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountControl.java
@@ -83,10 +83,21 @@
* effective groups.
*/
public boolean canSee(final Account otherUser) {
+ return canSee(otherUser.getId());
+ }
+
+ /**
+ * Returns true if the otherUser is allowed to see the current user, based
+ * on the account visibility policy. Depending on the group membership
+ * realms supported, this may not be able to determine SAME_GROUP or
+ * VISIBLE_GROUP correctly (defaulting to not being visible). This is because
+ * {@link GroupMembership#getKnownGroups()} may only return a subset of the
+ * effective groups.
+ */
+ public boolean canSee(final Account.Id otherUser) {
// Special case: I can always see myself.
if (currentUser instanceof IdentifiedUser
- && ((IdentifiedUser) currentUser).getAccountId()
- .equals(otherUser.getId())) {
+ && ((IdentifiedUser) currentUser).getAccountId().equals(otherUser)) {
return true;
}
@@ -131,7 +142,7 @@
return false;
}
- private Set<AccountGroup.UUID> groupsOf(Account account) {
- return userFactory.create(account.getId()).getEffectiveGroups().getKnownGroups();
+ private Set<AccountGroup.UUID> groupsOf(Account.Id account) {
+ return userFactory.create(account).getEffectiveGroups().getKnownGroups();
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/DefaultRealm.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/DefaultRealm.java
index 844e604..c90f3e9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/DefaultRealm.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/DefaultRealm.java
@@ -15,25 +15,20 @@
package com.google.gerrit.server.account;
import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.inject.Inject;
-import java.util.Collections;
import java.util.Set;
public class DefaultRealm implements Realm {
private final EmailExpander emailExpander;
private final AccountByEmailCache byEmail;
- private final MaterializedGroupMembership.Factory groupMembershipFactory;
@Inject
DefaultRealm(final EmailExpander emailExpander,
- final AccountByEmailCache byEmail,
- final MaterializedGroupMembership.Factory groupMembershipFactory) {
+ final AccountByEmailCache byEmail) {
this.emailExpander = emailExpander;
this.byEmail = byEmail;
- this.groupMembershipFactory = groupMembershipFactory;
}
@Override
@@ -65,11 +60,6 @@
}
@Override
- public GroupMembership groups(final AccountState who) {
- return groupMembershipFactory.create(who.getInternalGroups());
- }
-
- @Override
public Account.Id lookup(final String accountName) {
if (emailExpander.canExpand(accountName)) {
final Set<Account.Id> c = byEmail.get(emailExpander.expand(accountName));
@@ -79,9 +69,4 @@
}
return null;
}
-
- @Override
- public Set<AccountGroup.ExternalNameKey> lookupGroups(String name) {
- return Collections.emptySet();
- }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupBackend.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupBackend.java
new file mode 100644
index 0000000..b4e770f
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupBackend.java
@@ -0,0 +1,51 @@
+// Copyright (C) 2012 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.account;
+
+import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.extensions.annotations.ExtensionPoint;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.server.IdentifiedUser;
+
+import java.util.Collection;
+
+import javax.annotation.Nullable;
+
+/**
+ * Implementations of GroupBackend provide lookup and membership accessors
+ * to a group system.
+ */
+@ExtensionPoint
+public interface GroupBackend {
+ /** @return {@code true} if the backend can operate on the UUID. */
+ boolean handles(AccountGroup.UUID uuid);
+
+ /**
+ * Looks up a group in the backend. If the group does not exist, null is
+ * returned.
+ *
+ * @param uuid the group identifier
+ * @return the group
+ */
+ @Nullable
+ GroupDescription.Basic get(AccountGroup.UUID uuid);
+
+ /** @return suggestions for the group name sorted by name. */
+ Collection<GroupReference> suggest(String name);
+
+ /** @return the group membership checker for the backend. */
+ GroupMembership membershipsOf(IdentifiedUser user);
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupBackends.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupBackends.java
new file mode 100644
index 0000000..cdbb0e4
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupBackends.java
@@ -0,0 +1,89 @@
+// Copyright (C) 2012 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.account;
+
+import com.google.common.collect.Iterables;
+import com.google.gerrit.common.data.GroupReference;
+
+import java.util.Collection;
+import java.util.Comparator;
+
+import javax.annotation.Nullable;
+
+/**
+ * Utility class for dealing with a GroupBackend.
+ */
+public class GroupBackends {
+
+ public static final Comparator<GroupReference> GROUP_REF_NAME_COMPARATOR =
+ new Comparator<GroupReference>() {
+ @Override
+ public int compare(GroupReference a, GroupReference b) {
+ return a.getName().compareTo(b.getName());
+ }
+ };
+
+ /**
+ * Runs {@link GroupBackend#suggest(String)} and filters the result to return
+ * the best suggestion, or null if one does not exist.
+ *
+ * @param groupBackend the group backend
+ * @param name the name for which to suggest groups
+ * @return the best single GroupReference suggestion
+ */
+ @Nullable
+ public static GroupReference findBestSuggestion(
+ GroupBackend groupBackend, String name) {
+ Collection<GroupReference> refs = groupBackend.suggest(name);
+ if (refs.size() == 1) {
+ return Iterables.getOnlyElement(refs);
+ }
+
+ for (GroupReference ref : refs) {
+ if (isExactSuggestion(ref, name)) {
+ return ref;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Runs {@link GroupBackend#suggest(String)} and filters the result to return
+ * the exact suggestion, or null if one does not exist.
+ *
+ * @param groupBackend the group backend
+ * @param name the name for which to suggest groups
+ * @return the exact single GroupReference suggestion
+ */
+ @Nullable
+ public static GroupReference findExactSuggestion(
+ GroupBackend groupBackend, String name) {
+ Collection<GroupReference> refs = groupBackend.suggest(name);
+ for (GroupReference ref : refs) {
+ if (isExactSuggestion(ref, name)) {
+ return ref;
+ }
+ }
+ return null;
+ }
+
+ /** Returns whether the GroupReference is an exact suggestion for the name. */
+ public static boolean isExactSuggestion(GroupReference ref, String name) {
+ return ref.getName().equalsIgnoreCase(name) || ref.getUUID().get().equals(name);
+ }
+
+ private GroupBackends() {
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCache.java
index b092ac4..3b9e85f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCache.java
@@ -16,8 +16,6 @@
import com.google.gerrit.reviewdb.client.AccountGroup;
-import java.util.Collection;
-
import javax.annotation.Nullable;
/** Tracks group objects in memory for efficient access. */
@@ -34,8 +32,6 @@
@Nullable
public AccountGroup get(AccountGroup.UUID uuid);
- public Collection<AccountGroup> get(AccountGroup.ExternalNameKey externalName);
-
/** @return sorted iteration of groups. */
public abstract Iterable<AccountGroup> all();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCacheImpl.java
index d29a5e5..b301839 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCacheImpl.java
@@ -14,12 +14,15 @@
package com.google.gerrit.server.account;
+import com.google.common.base.Optional;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.AccountGroupName;
import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.cache.Cache;
import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.cache.EntryCreator;
+import com.google.gwtorm.server.OrmDuplicateKeyException;
+import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Module;
@@ -27,48 +30,41 @@
import com.google.inject.TypeLiteral;
import com.google.inject.name.Named;
-import java.util.ArrayList;
-import java.util.Collection;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import java.util.Collections;
import java.util.List;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.ExecutionException;
/** Tracks group objects in memory for efficient access. */
@Singleton
public class GroupCacheImpl implements GroupCache {
+ private static final Logger log = LoggerFactory
+ .getLogger(GroupCacheImpl.class);
+
private static final String BYID_NAME = "groups";
private static final String BYNAME_NAME = "groups_byname";
private static final String BYUUID_NAME = "groups_byuuid";
- private static final String BYEXT_NAME = "groups_byext";
- private static final String BYNAME_LIST = "groups_byname_list";
public static Module module() {
return new CacheModule() {
@Override
protected void configure() {
- final TypeLiteral<Cache<AccountGroup.Id, AccountGroup>> byId =
- new TypeLiteral<Cache<AccountGroup.Id, AccountGroup>>() {};
- core(byId, BYID_NAME).populateWith(ByIdLoader.class);
+ cache(BYID_NAME,
+ AccountGroup.Id.class,
+ new TypeLiteral<Optional<AccountGroup>>() {})
+ .loader(ByIdLoader.class);
- final TypeLiteral<Cache<AccountGroup.NameKey, AccountGroup>> byName =
- new TypeLiteral<Cache<AccountGroup.NameKey, AccountGroup>>() {};
- core(byName, BYNAME_NAME).populateWith(ByNameLoader.class);
+ cache(BYNAME_NAME,
+ String.class,
+ new TypeLiteral<Optional<AccountGroup>>() {})
+ .loader(ByNameLoader.class);
- final TypeLiteral<Cache<AccountGroup.UUID, AccountGroup>> byUUID =
- new TypeLiteral<Cache<AccountGroup.UUID, AccountGroup>>() {};
- core(byUUID, BYUUID_NAME).populateWith(ByUUIDLoader.class);
-
- final TypeLiteral<Cache<AccountGroup.ExternalNameKey, Collection<AccountGroup>>> byExternalName =
- new TypeLiteral<Cache<AccountGroup.ExternalNameKey, Collection<AccountGroup>>>() {};
- core(byExternalName, BYEXT_NAME) //
- .populateWith(ByExternalNameLoader.class);
-
- final TypeLiteral<Cache<ListKey, SortedSet<AccountGroup.NameKey>>> listType =
- new TypeLiteral<Cache<ListKey, SortedSet<AccountGroup.NameKey>>>() {};
- core(listType, BYNAME_LIST).populateWith(Lister.class);
+ cache(BYUUID_NAME,
+ String.class,
+ new TypeLiteral<Optional<AccountGroup>>() {})
+ .loader(ByUUIDLoader.class);
bind(GroupCacheImpl.class);
bind(GroupCache.class).to(GroupCacheImpl.class);
@@ -76,94 +72,113 @@
};
}
- private final Cache<AccountGroup.Id, AccountGroup> byId;
- private final Cache<AccountGroup.NameKey, AccountGroup> byName;
- private final Cache<AccountGroup.UUID, AccountGroup> byUUID;
- private final Cache<AccountGroup.ExternalNameKey, Collection<AccountGroup>> byExternalName;
- private final Cache<ListKey,SortedSet<AccountGroup.NameKey>> list;
- private final Lock listLock;
+ private final LoadingCache<AccountGroup.Id, Optional<AccountGroup>> byId;
+ private final LoadingCache<String, Optional<AccountGroup>> byName;
+ private final LoadingCache<String, Optional<AccountGroup>> byUUID;
+ private final SchemaFactory<ReviewDb> schema;
@Inject
GroupCacheImpl(
- @Named(BYID_NAME) Cache<AccountGroup.Id, AccountGroup> byId,
- @Named(BYNAME_NAME) Cache<AccountGroup.NameKey, AccountGroup> byName,
- @Named(BYUUID_NAME) Cache<AccountGroup.UUID, AccountGroup> byUUID,
- @Named(BYEXT_NAME) Cache<AccountGroup.ExternalNameKey, Collection<AccountGroup>> byExternalName,
- @Named(BYNAME_LIST) final Cache<ListKey, SortedSet<AccountGroup.NameKey>> list) {
+ @Named(BYID_NAME) LoadingCache<AccountGroup.Id, Optional<AccountGroup>> byId,
+ @Named(BYNAME_NAME) LoadingCache<String, Optional<AccountGroup>> byName,
+ @Named(BYUUID_NAME) LoadingCache<String, Optional<AccountGroup>> byUUID,
+ SchemaFactory<ReviewDb> schema) {
this.byId = byId;
this.byName = byName;
this.byUUID = byUUID;
- this.byExternalName = byExternalName;
- this.list = list;
- this.listLock = new ReentrantLock(true /* fair */);
+ this.schema = schema;
}
+ @Override
public AccountGroup get(final AccountGroup.Id groupId) {
- return byId.get(groupId);
+ try {
+ Optional<AccountGroup> g = byId.get(groupId);
+ return g.isPresent() ? g.get() : missing(groupId);
+ } catch (ExecutionException e) {
+ log.warn("Cannot load group "+groupId, e);
+ return missing(groupId);
+ }
}
+ @Override
public void evict(final AccountGroup group) {
- byId.remove(group.getId());
- byName.remove(group.getNameKey());
- byUUID.remove(group.getGroupUUID());
- byExternalName.remove(group.getExternalNameKey());
+ if (group.getId() != null) {
+ byId.invalidate(group.getId());
+ }
+ if (group.getNameKey() != null) {
+ byName.invalidate(group.getNameKey().get());
+ }
+ if (group.getGroupUUID() != null) {
+ byUUID.invalidate(group.getGroupUUID().get());
+ }
}
+ @Override
public void evictAfterRename(final AccountGroup.NameKey oldName,
final AccountGroup.NameKey newName) {
- byName.remove(oldName);
- updateGroupList(oldName, newName);
+ if (oldName != null) {
+ byName.invalidate(oldName.get());
+ }
+ if (newName != null) {
+ byName.invalidate(newName.get());
+ }
}
- public AccountGroup get(final AccountGroup.NameKey name) {
- return byName.get(name);
+ @Override
+ public AccountGroup get(AccountGroup.NameKey name) {
+ if (name == null) {
+ return null;
+ }
+ try {
+ return byName.get(name.get()).orNull();
+ } catch (ExecutionException e) {
+ log.warn(String.format("Cannot lookup group %s by name", name.get()), e);
+ return null;
+ }
}
- public AccountGroup get(final AccountGroup.UUID uuid) {
- return byUUID.get(uuid);
- }
-
- public Collection<AccountGroup> get(
- final AccountGroup.ExternalNameKey externalName) {
- return byExternalName.get(externalName);
+ @Override
+ public AccountGroup get(AccountGroup.UUID uuid) {
+ if (uuid == null) {
+ return null;
+ }
+ try {
+ return byUUID.get(uuid.get()).orNull();
+ } catch (ExecutionException e) {
+ log.warn(String.format("Cannot lookup group %s by name", uuid.get()), e);
+ return null;
+ }
}
@Override
public Iterable<AccountGroup> all() {
- final List<AccountGroup> groups = new ArrayList<AccountGroup>();
- for (final AccountGroup.NameKey groupName : list.get(ListKey.ALL)) {
- final AccountGroup group = get(groupName);
- if (group != null) {
- groups.add(group);
+ try {
+ ReviewDb db = schema.open();
+ try {
+ return Collections.unmodifiableList(db.accountGroups().all().toList());
+ } finally {
+ db.close();
}
+ } catch (OrmException e) {
+ log.warn("Cannot list internal groups", e);
+ return Collections.emptyList();
}
- return Collections.unmodifiableList(groups);
}
@Override
- public void onCreateGroup(final AccountGroup.NameKey newGroupName) {
- updateGroupList(null, newGroupName);
+ public void onCreateGroup(AccountGroup.NameKey newGroupName) {
+ byName.invalidate(newGroupName.get());
}
- private void updateGroupList(final AccountGroup.NameKey nameToRemove,
- final AccountGroup.NameKey nameToAdd) {
- listLock.lock();
- try {
- SortedSet<AccountGroup.NameKey> n = list.get(ListKey.ALL);
- n = new TreeSet<AccountGroup.NameKey>(n);
- if (nameToRemove != null) {
- n.remove(nameToRemove);
- }
- if (nameToAdd != null) {
- n.add(nameToAdd);
- }
- list.put(ListKey.ALL, Collections.unmodifiableSortedSet(n));
- } finally {
- listLock.unlock();
- }
+ private static AccountGroup missing(AccountGroup.Id key) {
+ AccountGroup.NameKey name = new AccountGroup.NameKey("Deleted Group" + key);
+ AccountGroup g = new AccountGroup(name, key, null);
+ g.setType(AccountGroup.Type.SYSTEM);
+ return g;
}
- static class ByIdLoader extends EntryCreator<AccountGroup.Id, AccountGroup> {
+ static class ByIdLoader extends
+ CacheLoader<AccountGroup.Id, Optional<AccountGroup>> {
private final SchemaFactory<ReviewDb> schema;
@Inject
@@ -172,32 +187,18 @@
}
@Override
- public AccountGroup createEntry(final AccountGroup.Id key) throws Exception {
+ public Optional<AccountGroup> load(final AccountGroup.Id key)
+ throws Exception {
final ReviewDb db = schema.open();
try {
- final AccountGroup group = db.accountGroups().get(key);
- if (group != null) {
- return group;
- } else {
- return missing(key);
- }
+ return Optional.fromNullable(db.accountGroups().get(key));
} finally {
db.close();
}
}
-
- @Override
- public AccountGroup missing(final AccountGroup.Id key) {
- final AccountGroup.NameKey name =
- new AccountGroup.NameKey("Deleted Group" + key.toString());
- final AccountGroup g = new AccountGroup(name, key, null);
- g.setType(AccountGroup.Type.SYSTEM);
- return g;
- }
}
- static class ByNameLoader extends
- EntryCreator<AccountGroup.NameKey, AccountGroup> {
+ static class ByNameLoader extends CacheLoader<String, Optional<AccountGroup>> {
private final SchemaFactory<ReviewDb> schema;
@Inject
@@ -206,25 +207,23 @@
}
@Override
- public AccountGroup createEntry(final AccountGroup.NameKey key)
+ public Optional<AccountGroup> load(String name)
throws Exception {
- final AccountGroupName r;
final ReviewDb db = schema.open();
try {
- r = db.accountGroupNames().get(key);
+ AccountGroup.NameKey key = new AccountGroup.NameKey(name);
+ AccountGroupName r = db.accountGroupNames().get(key);
if (r != null) {
- return db.accountGroups().get(r.getId());
- } else {
- return null;
+ return Optional.fromNullable(db.accountGroups().get(r.getId()));
}
+ return Optional.absent();
} finally {
db.close();
}
}
}
- static class ByUUIDLoader extends
- EntryCreator<AccountGroup.UUID, AccountGroup> {
+ static class ByUUIDLoader extends CacheLoader<String, Optional<AccountGroup>> {
private final SchemaFactory<ReviewDb> schema;
@Inject
@@ -233,74 +232,23 @@
}
@Override
- public AccountGroup createEntry(final AccountGroup.UUID uuid)
+ public Optional<AccountGroup> load(String uuid)
throws Exception {
final ReviewDb db = schema.open();
try {
- List<AccountGroup> r = db.accountGroups().byUUID(uuid).toList();
+ List<AccountGroup> r;
+
+ r = db.accountGroups().byUUID(new AccountGroup.UUID(uuid)).toList();
if (r.size() == 1) {
- return r.get(0);
+ return Optional.of(r.get(0));
+ } else if (r.size() == 0) {
+ return Optional.absent();
} else {
- return null;
+ throw new OrmDuplicateKeyException("Duplicate group UUID " + uuid);
}
} finally {
db.close();
}
}
}
-
- static class ByExternalNameLoader extends
- EntryCreator<AccountGroup.ExternalNameKey, Collection<AccountGroup>> {
- private final SchemaFactory<ReviewDb> schema;
-
- @Inject
- ByExternalNameLoader(final SchemaFactory<ReviewDb> sf) {
- schema = sf;
- }
-
- @Override
- public Collection<AccountGroup> createEntry(
- final AccountGroup.ExternalNameKey key) throws Exception {
- final ReviewDb db = schema.open();
- try {
- return db.accountGroups().byExternalName(key).toList();
- } finally {
- db.close();
- }
- }
- }
-
- static class ListKey {
- static final ListKey ALL = new ListKey();
-
- private ListKey() {
- }
- }
-
- static class Lister extends EntryCreator<ListKey, SortedSet<AccountGroup.NameKey>> {
- private final SchemaFactory<ReviewDb> schema;
-
- @Inject
- Lister(final SchemaFactory<ReviewDb> sf) {
- schema = sf;
- }
-
- @Override
- public SortedSet<AccountGroup.NameKey> createEntry(ListKey key)
- throws Exception {
- final ReviewDb db = schema.open();
- try {
- final List<AccountGroupName> groupNames =
- db.accountGroupNames().all().toList();
- final SortedSet<AccountGroup.NameKey> groups =
- new TreeSet<AccountGroup.NameKey>();
- for (final AccountGroupName groupName : groupNames) {
- groups.add(groupName.getNameKey());
- }
- return Collections.unmodifiableSortedSet(groups);
- } finally {
- db.close();
- }
- }
- }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java
index ea388c7..d9b12ac 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.account;
+import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.common.data.GroupDescriptions;
import com.google.gerrit.common.errors.NoSuchGroupException;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
@@ -27,11 +29,14 @@
public static class Factory {
private final GroupCache groupCache;
private final Provider<CurrentUser> user;
+ private final GroupBackend groupBackend;
@Inject
- Factory(final GroupCache gc, final Provider<CurrentUser> cu) {
+ Factory(final GroupCache gc, final Provider<CurrentUser> cu,
+ final GroupBackend gb) {
groupCache = gc;
user = cu;
+ groupBackend = gb;
}
public GroupControl controlFor(final AccountGroup.Id groupId)
@@ -45,7 +50,7 @@
public GroupControl controlFor(final AccountGroup.UUID groupId)
throws NoSuchGroupException {
- final AccountGroup group = groupCache.get(groupId);
+ final GroupDescription.Basic group = groupBackend.get(groupId);
if (group == null) {
throw new NoSuchGroupException(groupId);
}
@@ -67,22 +72,22 @@
}
private final CurrentUser user;
- private final AccountGroup group;
+ private final GroupDescription.Basic group;
private Boolean isOwner;
- GroupControl(CurrentUser who, AccountGroup gc) {
+ GroupControl(CurrentUser who, GroupDescription.Basic gd) {
user = who;
- group = gc;
+ group = gd;
+ }
+
+ GroupControl(CurrentUser who, AccountGroup ag) {
+ this(who, GroupDescriptions.forAccountGroup(ag));
}
public CurrentUser getCurrentUser() {
return user;
}
- public AccountGroup getAccountGroup() {
- return group;
- }
-
/** Can this user see this group exists? */
public boolean isVisible() {
return group.isVisibleToAll()
@@ -91,8 +96,11 @@
}
public boolean isOwner() {
- if (isOwner == null) {
- AccountGroup.UUID ownerUUID = group.getOwnerGroupUUID();
+ AccountGroup accountGroup = GroupDescriptions.toAccountGroup(group);
+ if (accountGroup == null) {
+ isOwner = false;
+ } else if (isOwner == null) {
+ AccountGroup.UUID ownerUUID = accountGroup.getOwnerGroupUUID();
isOwner = getCurrentUser().getEffectiveGroups().contains(ownerUUID)
|| getCurrentUser().getCapabilities().canAdministrateServer();
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupDetailFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupDetailFactory.java
index 2d455e9..2e500bd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupDetailFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupDetailFactory.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.account;
+import com.google.gerrit.common.data.GroupDescription;
import com.google.gerrit.common.data.GroupDetail;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.errors.NoSuchGroupException;
@@ -40,6 +41,7 @@
private final ReviewDb db;
private final GroupControl.Factory groupControl;
private final GroupCache groupCache;
+ private final GroupBackend groupBackend;
private final AccountInfoCacheFactory aic;
private final GroupInfoCacheFactory gic;
@@ -49,12 +51,14 @@
@Inject
GroupDetailFactory(final ReviewDb db,
final GroupControl.Factory groupControl, final GroupCache groupCache,
+ final GroupBackend groupBackend,
final AccountInfoCacheFactory.Factory accountInfoCacheFactory,
final GroupInfoCacheFactory.Factory groupInfoCacheFactory,
@Assisted final AccountGroup.Id groupId) {
this.db = db;
this.groupControl = groupControl;
this.groupCache = groupCache;
+ this.groupBackend = groupBackend;
this.aic = accountInfoCacheFactory.create();
this.gic = groupInfoCacheFactory.create();
@@ -64,10 +68,10 @@
@Override
public GroupDetail call() throws OrmException, NoSuchGroupException {
control = groupControl.validateFor(groupId);
- final AccountGroup group = control.getAccountGroup();
+ final AccountGroup group = groupCache.get(groupId);
final GroupDetail detail = new GroupDetail();
detail.setGroup(group);
- AccountGroup ownerGroup = groupCache.get(group.getOwnerGroupUUID());
+ GroupDescription.Basic ownerGroup = groupBackend.get(group.getOwnerGroupUUID());
if (ownerGroup != null) {
detail.setOwnerGroup(GroupReference.forGroup(ownerGroup));
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java
index 791d0f5..7fbba45 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java
@@ -14,12 +14,14 @@
package com.google.gerrit.server.account;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.AccountGroupInclude;
import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.cache.Cache;
import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.cache.EntryCreator;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Module;
@@ -27,24 +29,30 @@
import com.google.inject.TypeLiteral;
import com.google.inject.name.Named;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import java.util.Collection;
import java.util.Collections;
-import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.ExecutionException;
/** Tracks group inclusions in memory for efficient access. */
@Singleton
public class GroupIncludeCacheImpl implements GroupIncludeCache {
+ private static final Logger log = LoggerFactory
+ .getLogger(GroupIncludeCacheImpl.class);
private static final String BYINCLUDE_NAME = "groups_byinclude";
public static Module module() {
return new CacheModule() {
@Override
protected void configure() {
- final TypeLiteral<Cache<AccountGroup.UUID, Collection<AccountGroup.UUID>>> byInclude =
- new TypeLiteral<Cache<AccountGroup.UUID, Collection<AccountGroup.UUID>>>() {};
- core(byInclude, BYINCLUDE_NAME).populateWith(ByIncludeLoader.class);
+ cache(BYINCLUDE_NAME,
+ AccountGroup.UUID.class,
+ new TypeLiteral<Set<AccountGroup.UUID>>() {})
+ .loader(ByIncludeLoader.class);
bind(GroupIncludeCacheImpl.class);
bind(GroupIncludeCache.class).to(GroupIncludeCacheImpl.class);
@@ -52,24 +60,31 @@
};
}
- private final Cache<AccountGroup.UUID, Collection<AccountGroup.UUID>> byInclude;
+ private final LoadingCache<AccountGroup.UUID, Set<AccountGroup.UUID>> byInclude;
@Inject
GroupIncludeCacheImpl(
- @Named(BYINCLUDE_NAME) Cache<AccountGroup.UUID, Collection<AccountGroup.UUID>> byInclude) {
+ @Named(BYINCLUDE_NAME) LoadingCache<AccountGroup.UUID, Set<AccountGroup.UUID>> byInclude) {
this.byInclude = byInclude;
}
public Collection<AccountGroup.UUID> getByInclude(AccountGroup.UUID groupId) {
- return byInclude.get(groupId);
+ try {
+ return byInclude.get(groupId);
+ } catch (ExecutionException e) {
+ log.warn("Cannot load included groups", e);
+ return Collections.emptySet();
+ }
}
public void evictInclude(AccountGroup.UUID groupId) {
- byInclude.remove(groupId);
+ if (groupId != null) {
+ byInclude.invalidate(groupId);
+ }
}
static class ByIncludeLoader extends
- EntryCreator<AccountGroup.UUID, Collection<AccountGroup.UUID>> {
+ CacheLoader<AccountGroup.UUID, Set<AccountGroup.UUID>> {
private final SchemaFactory<ReviewDb> schema;
@Inject
@@ -78,32 +93,28 @@
}
@Override
- public Collection<AccountGroup.UUID> createEntry(final AccountGroup.UUID key) throws Exception {
+ public Set<AccountGroup.UUID> load(AccountGroup.UUID key) throws Exception {
final ReviewDb db = schema.open();
try {
List<AccountGroup> group = db.accountGroups().byUUID(key).toList();
if (group.size() != 1) {
- return Collections.emptyList();
+ return Collections.emptySet();
}
- Set<AccountGroup.Id> ids = new HashSet<AccountGroup.Id>();
- for (AccountGroupInclude agi : db.accountGroupIncludes().byInclude(group.get(0).getId())) {
+ Set<AccountGroup.Id> ids = Sets.newHashSet();
+ for (AccountGroupInclude agi : db.accountGroupIncludes()
+ .byInclude(group.get(0).getId())) {
ids.add(agi.getGroupId());
}
- Set<AccountGroup.UUID> groupArray = new HashSet<AccountGroup.UUID> ();
+ Set<AccountGroup.UUID> groupArray = Sets.newHashSet();
for (AccountGroup g : db.accountGroups().get(ids)) {
groupArray.add(g.getGroupUUID());
}
- return Collections.unmodifiableCollection(groupArray);
+ return ImmutableSet.copyOf(groupArray);
} finally {
db.close();
}
}
-
- @Override
- public Collection<AccountGroup.UUID> missing(final AccountGroup.UUID key) {
- return Collections.emptyList();
- }
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupMembership.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupMembership.java
index 9bb571e..d536c09 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupMembership.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupMembership.java
@@ -24,7 +24,6 @@
* the presence of a user in a particular group.
*/
public interface GroupMembership {
-
public static final GroupMembership EMPTY =
new ListGroupMembership(Collections.<AccountGroup.UUID>emptySet());
@@ -45,7 +44,7 @@
* This may not return all groups the {@link #contains(AccountGroup.UUID)}
* would return {@code true} for, but will at least contain all top level
* groups. This restriction stems from the API of some group systems, which
- * make it expensive to enumate the members of a group.
+ * make it expensive to enumerate the members of a group.
*/
Set<AccountGroup.UUID> getKnownGroups();
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/MaterializedGroupMembership.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/IncludingGroupMembership.java
similarity index 89%
rename from gerrit-server/src/main/java/com/google/gerrit/server/account/MaterializedGroupMembership.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/account/IncludingGroupMembership.java
index 81ff656..d448fff 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/MaterializedGroupMembership.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/IncludingGroupMembership.java
@@ -25,11 +25,12 @@
import java.util.Set;
/**
- * Creates a GroupMembership object from materialized collection of groups.
+ * Creates a GroupMembership checker for the internal group system, which
+ * starts with the seed groups and includes all child groups.
*/
-public class MaterializedGroupMembership implements GroupMembership {
+public class IncludingGroupMembership implements GroupMembership {
public interface Factory {
- MaterializedGroupMembership create(Iterable<AccountGroup.UUID> groupIds);
+ IncludingGroupMembership create(Iterable<AccountGroup.UUID> groupIds);
}
private final GroupIncludeCache groupIncludeCache;
@@ -37,7 +38,7 @@
private final Queue<AccountGroup.UUID> groupQueue;
@Inject
- MaterializedGroupMembership(
+ IncludingGroupMembership(
GroupIncludeCache groupIncludeCache,
@Assisted Iterable<AccountGroup.UUID> seedGroups) {
this.groupIncludeCache = groupIncludeCache;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/InternalGroupBackend.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/InternalGroupBackend.java
new file mode 100644
index 0000000..ad65499
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/InternalGroupBackend.java
@@ -0,0 +1,94 @@
+// Copyright (C) 2012 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.account;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.common.data.GroupDescriptions;
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import java.util.Collection;
+
+/**
+ * Implementation of GroupBackend for the internal group system.
+ */
+@Singleton
+public class InternalGroupBackend implements GroupBackend {
+ private static final Function<AccountGroup, GroupReference> ACT_GROUP_TO_GROUP_REF =
+ new Function<AccountGroup, GroupReference>() {
+ @Override
+ public GroupReference apply(AccountGroup group) {
+ return GroupReference.forGroup(group);
+ }
+ };
+
+ private final GroupControl.Factory groupControlFactory;
+ private final GroupCache groupCache;
+ private final IncludingGroupMembership.Factory groupMembershipFactory;
+
+
+ @Inject
+ InternalGroupBackend(GroupControl.Factory groupControlFactory,
+ GroupCache groupCache,
+ IncludingGroupMembership.Factory groupMembershipFactory) {
+ this.groupControlFactory = groupControlFactory;
+ this.groupCache = groupCache;
+ this.groupMembershipFactory = groupMembershipFactory;
+ }
+
+ @Override
+ public boolean handles(AccountGroup.UUID uuid) {
+ return AccountGroup.isInternalGroup(uuid);
+ }
+
+ @Override
+ public GroupDescription.Internal get(AccountGroup.UUID uuid) {
+ if (!handles(uuid)) {
+ return null;
+ }
+
+ AccountGroup g = groupCache.get(uuid);
+ if (g == null) {
+ return null;
+ }
+ return GroupDescriptions.forAccountGroup(g);
+ }
+
+ @Override
+ public Collection<GroupReference> suggest(final String name) {
+ Iterable<AccountGroup> filtered = Iterables.filter(groupCache.all(),
+ new Predicate<AccountGroup>() {
+ @Override
+ public boolean apply(AccountGroup group) {
+ // startsWithIgnoreCase && isVisible
+ return group.getName().regionMatches(true, 0, name, 0, name.length())
+ && groupControlFactory.controlFor(group).isVisible();
+ }
+ });
+ return Lists.newArrayList(Iterables.transform(filtered, ACT_GROUP_TO_GROUP_REF));
+ }
+
+ @Override
+ public GroupMembership membershipsOf(IdentifiedUser user) {
+ return groupMembershipFactory.create(user.state().getInternalGroups());
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/Realm.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/Realm.java
index 2ebd0e5..e44d46e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/Realm.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/Realm.java
@@ -15,11 +15,8 @@
package com.google.gerrit.server.account;
import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.server.ReviewDb;
-import java.util.Set;
-
public interface Realm {
/** Can the end-user modify this field of their own account? */
public boolean allowsEdit(Account.FieldName field);
@@ -34,8 +31,6 @@
public void onCreateAccount(AuthRequest who, Account account);
- public GroupMembership groups(AccountState who);
-
/**
* Locate an account whose local username is the given account name.
* <p>
@@ -45,9 +40,4 @@
* user by that email address.
*/
public Account.Id lookup(String accountName);
-
- /**
- * Search for matching external groups.
- */
- public Set<AccountGroup.ExternalNameKey> lookupGroups(String name);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/UniversalGroupBackend.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/UniversalGroupBackend.java
new file mode 100644
index 0000000..1974961
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/UniversalGroupBackend.java
@@ -0,0 +1,161 @@
+// Copyright (C) 2012 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.account;
+
+import static com.google.gerrit.server.account.GroupBackends.GROUP_REF_NAME_COMPARATOR;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+/**
+ * Universal implementation of the GroupBackend that works with the injected
+ * set of GroupBackends.
+ */
+@Singleton
+public class UniversalGroupBackend implements GroupBackend {
+ private static final Logger log =
+ LoggerFactory.getLogger(UniversalGroupBackend.class);
+
+ private final DynamicSet<GroupBackend> backends;
+
+ @Inject
+ UniversalGroupBackend(DynamicSet<GroupBackend> backends) {
+ this.backends = backends;
+ }
+
+ @Nullable
+ private GroupBackend backend(AccountGroup.UUID uuid) {
+ if (uuid != null) {
+ for (GroupBackend g : backends) {
+ if (g.handles(uuid)) {
+ return g;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean handles(AccountGroup.UUID uuid) {
+ return backend(uuid) != null;
+ }
+
+ @Override
+ public GroupDescription.Basic get(AccountGroup.UUID uuid) {
+ GroupBackend b = backend(uuid);
+ if (b == null) {
+ log.warn("Unknown GroupBackend for UUID: " + uuid);
+ return null;
+ }
+ return b.get(uuid);
+ }
+
+ @Override
+ public Collection<GroupReference> suggest(String name) {
+ Set<GroupReference> groups = Sets.newTreeSet(GROUP_REF_NAME_COMPARATOR);
+ for (GroupBackend g : backends) {
+ groups.addAll(g.suggest(name));
+ }
+ return groups;
+ }
+
+ @Override
+ public GroupMembership membershipsOf(IdentifiedUser user) {
+ return new UniversalGroupMembership(user);
+ }
+
+ private class UniversalGroupMembership implements GroupMembership {
+ private final Map<GroupBackend, GroupMembership> memberships;
+
+ private UniversalGroupMembership(IdentifiedUser user) {
+ ImmutableMap.Builder<GroupBackend, GroupMembership> builder =
+ ImmutableMap.builder();
+ for (GroupBackend g : backends) {
+ builder.put(g, g.membershipsOf(user));
+ }
+ this.memberships = builder.build();
+ }
+
+ @Nullable
+ private GroupMembership membership(AccountGroup.UUID uuid) {
+ if (uuid != null) {
+ for (Map.Entry<GroupBackend, GroupMembership> m : memberships.entrySet()) {
+ if (m.getKey().handles(uuid)) {
+ return m.getValue();
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean contains(AccountGroup.UUID uuid) {
+ GroupMembership m = membership(uuid);
+ if (m == null) {
+ log.warn("Unknown GroupMembership for UUID: " + uuid);
+ return false;
+ }
+ return m.contains(uuid);
+ }
+
+ @Override
+ public boolean containsAnyOf(Iterable<AccountGroup.UUID> uuids) {
+ Multimap<GroupMembership, AccountGroup.UUID> lookups =
+ ArrayListMultimap.create();
+ for (AccountGroup.UUID uuid : uuids) {
+ GroupMembership m = membership(uuid);
+ if (m == null) {
+ log.warn("Unknown GroupMembership for UUID: " + uuid);
+ continue;
+ }
+ lookups.put(m, uuid);
+ }
+ for (Map.Entry<GroupMembership, Collection<AccountGroup.UUID>> entry :
+ lookups.asMap().entrySet()) {
+ if (entry.getKey().containsAnyOf(entry.getValue())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Set<AccountGroup.UUID> getKnownGroups() {
+ Set<AccountGroup.UUID> groups = Sets.newHashSet();
+ for (GroupMembership m : memberships.values()) {
+ groups.addAll(m.getKnownGroups());
+ }
+ return groups;
+ }
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/AccountGroupIdHandler.java b/gerrit-server/src/main/java/com/google/gerrit/server/args4j/AccountGroupIdHandler.java
similarity index 97%
rename from gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/AccountGroupIdHandler.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/args4j/AccountGroupIdHandler.java
index 307a10a..bf74a4a 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/AccountGroupIdHandler.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/args4j/AccountGroupIdHandler.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.sshd.args4j;
+package com.google.gerrit.server.args4j;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.account.GroupCache;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/AccountGroupUUIDHandler.java b/gerrit-server/src/main/java/com/google/gerrit/server/args4j/AccountGroupUUIDHandler.java
similarity index 77%
rename from gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/AccountGroupUUIDHandler.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/args4j/AccountGroupUUIDHandler.java
index 49bf695..406ca58 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/AccountGroupUUIDHandler.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/args4j/AccountGroupUUIDHandler.java
@@ -12,10 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.sshd.args4j;
+package com.google.gerrit.server.args4j;
+import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.server.account.GroupCache;
+import com.google.gerrit.server.account.GroupBackend;
+import com.google.gerrit.server.account.GroupBackends;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -27,25 +29,25 @@
import org.kohsuke.args4j.spi.Setter;
public class AccountGroupUUIDHandler extends OptionHandler<AccountGroup.UUID> {
- private final GroupCache groupCache;
+ private final GroupBackend groupBackend;
@Inject
- public AccountGroupUUIDHandler(final GroupCache groupCache,
+ public AccountGroupUUIDHandler(final GroupBackend groupBackend,
@Assisted final CmdLineParser parser, @Assisted final OptionDef option,
@Assisted final Setter<AccountGroup.UUID> setter) {
super(parser, option, setter);
- this.groupCache = groupCache;
+ this.groupBackend = groupBackend;
}
@Override
public final int parseArguments(final Parameters params)
throws CmdLineException {
final String n = params.getParameter(0);
- final AccountGroup group = groupCache.get(new AccountGroup.NameKey(n));
+ final GroupReference group = GroupBackends.findBestSuggestion(groupBackend, n);
if (group == null) {
throw new CmdLineException(owner, "Group \"" + n + "\" does not exist");
}
- setter.addValue(group.getGroupUUID());
+ setter.addValue(group.getUUID());
return 1;
}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/AccountIdHandler.java b/gerrit-server/src/main/java/com/google/gerrit/server/args4j/AccountIdHandler.java
similarity index 98%
rename from gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/AccountIdHandler.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/args4j/AccountIdHandler.java
index d54ae34..8e71b88 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/AccountIdHandler.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/args4j/AccountIdHandler.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.sshd.args4j;
+package com.google.gerrit.server.args4j;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AuthType;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/ChangeIdHandler.java b/gerrit-server/src/main/java/com/google/gerrit/server/args4j/ChangeIdHandler.java
similarity index 94%
rename from gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/ChangeIdHandler.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/args4j/ChangeIdHandler.java
index 0194b91..9c3d052 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/ChangeIdHandler.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/args4j/ChangeIdHandler.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.sshd.args4j;
+package com.google.gerrit.server.args4j;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
@@ -56,7 +56,8 @@
try {
final Change.Key key = Change.Key.parse(tokens[2]);
final Project.NameKey project = new Project.NameKey(tokens[0]);
- final Branch.NameKey branch = new Branch.NameKey(project, tokens[1]);
+ final Branch.NameKey branch =
+ new Branch.NameKey(project, "refs/heads/" + tokens[1]);
for (final Change change : db.changes().byBranchKey(branch, key)) {
setter.addValue(change.getId());
return 1;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/ObjectIdHandler.java b/gerrit-server/src/main/java/com/google/gerrit/server/args4j/ObjectIdHandler.java
similarity index 96%
rename from gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/ObjectIdHandler.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/args4j/ObjectIdHandler.java
index adb5ad6..b7f2fb9 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/ObjectIdHandler.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/args4j/ObjectIdHandler.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.sshd.args4j;
+package com.google.gerrit.server.args4j;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/PatchSetIdHandler.java b/gerrit-server/src/main/java/com/google/gerrit/server/args4j/PatchSetIdHandler.java
similarity index 97%
rename from gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/PatchSetIdHandler.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/args4j/PatchSetIdHandler.java
index 2d6a4df..a48568f 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/PatchSetIdHandler.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/args4j/PatchSetIdHandler.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.sshd.args4j;
+package com.google.gerrit.server.args4j;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.inject.Inject;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/ProjectControlHandler.java b/gerrit-server/src/main/java/com/google/gerrit/server/args4j/ProjectControlHandler.java
similarity index 98%
rename from gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/ProjectControlHandler.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/args4j/ProjectControlHandler.java
index e0f7c4c..c1c9c50 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/ProjectControlHandler.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/args4j/ProjectControlHandler.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.sshd.args4j;
+package com.google.gerrit.server.args4j;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.project.NoSuchProjectException;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/SocketAddressHandler.java b/gerrit-server/src/main/java/com/google/gerrit/server/args4j/SocketAddressHandler.java
similarity index 97%
rename from gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/SocketAddressHandler.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/args4j/SocketAddressHandler.java
index 454a084..0c20b2d 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/SocketAddressHandler.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/args4j/SocketAddressHandler.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.sshd.args4j;
+package com.google.gerrit.server.args4j;
import com.google.gerrit.server.util.SocketUtil;
import com.google.inject.Inject;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/SubcommandHandler.java b/gerrit-server/src/main/java/com/google/gerrit/server/args4j/SubcommandHandler.java
similarity index 96%
rename from gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/SubcommandHandler.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/args4j/SubcommandHandler.java
index 3df73a8..619ec1f 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/SubcommandHandler.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/args4j/SubcommandHandler.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.sshd.args4j;
+package com.google.gerrit.server.args4j;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java
index e81bfc2..2265bc2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java
@@ -17,7 +17,6 @@
import com.google.gerrit.common.data.ParameterizedString;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.account.AccountException;
-import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.util.ssl.BlindSSLSocketFactory;
@@ -47,7 +46,8 @@
import javax.net.ssl.SSLSocketFactory;
@Singleton class Helper {
- private final GroupCache groupCache;
+ static final String LDAP_UUID = "ldap:";
+
private final Config config;
private final String server;
private final String username;
@@ -58,8 +58,7 @@
private final String readTimeOutMillis;
@Inject
- Helper(@GerritServerConfig final Config config, final GroupCache groupCache) {
- this.groupCache = groupCache;
+ Helper(@GerritServerConfig final Config config) {
this.config = config;
this.server = LdapRealm.required(config, "server");
this.username = LdapRealm.optional(config, "username");
@@ -195,12 +194,7 @@
final Set<AccountGroup.UUID> actual = new HashSet<AccountGroup.UUID>();
for (String dn : groupDNs) {
- for (AccountGroup group : groupCache
- .get(new AccountGroup.ExternalNameKey(dn))) {
- if (group.getType() == AccountGroup.Type.LDAP) {
- actual.add(group.getGroupUUID());
- }
- }
+ actual.add(new AccountGroup.UUID(LDAP_UUID + dn));
}
if (actual.isEmpty()) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapGroupBackend.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapGroupBackend.java
new file mode 100644
index 0000000..5c30e5c
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapGroupBackend.java
@@ -0,0 +1,227 @@
+// Copyright (C) 2012 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.auth.ldap;
+
+import static com.google.gerrit.server.account.GroupBackends.GROUP_REF_NAME_COMPARATOR;
+import static com.google.gerrit.server.auth.ldap.Helper.LDAP_UUID;
+import static com.google.gerrit.server.auth.ldap.LdapModule.GROUP_CACHE;
+import static com.google.gerrit.server.auth.ldap.LdapModule.GROUP_EXIST_CACHE;
+
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.Sets;
+import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.common.data.ParameterizedString;
+import com.google.gerrit.reviewdb.client.AccountExternalId;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.GroupBackend;
+import com.google.gerrit.server.account.GroupMembership;
+import com.google.gerrit.server.account.ListGroupMembership;
+import com.google.gerrit.server.auth.ldap.Helper.LdapSchema;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.name.Named;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+
+import javax.naming.InvalidNameException;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
+
+/**
+ * Implementation of GroupBackend for the LDAP group system.
+ */
+public class LdapGroupBackend implements GroupBackend {
+ private static final Logger log = LoggerFactory.getLogger(LdapGroupBackend.class);
+
+ private static final String LDAP_NAME = "ldap/";
+ private static final String GROUPNAME = "groupname";
+
+ private final Helper helper;
+ private final LoadingCache<String, Set<AccountGroup.UUID>> membershipCache;
+ private final LoadingCache<String, Boolean> existsCache;
+ private final Provider<CurrentUser> userProvider;
+
+ @Inject
+ LdapGroupBackend(
+ Helper helper,
+ @Named(GROUP_CACHE) LoadingCache<String, Set<AccountGroup.UUID>> membershipCache,
+ @Named(GROUP_EXIST_CACHE) LoadingCache<String, Boolean> existsCache,
+ Provider<CurrentUser> userProvider) {
+ this.helper = helper;
+ this.membershipCache = membershipCache;
+ this.existsCache = existsCache;
+ this.userProvider = userProvider;
+ }
+
+ private static boolean isLdapUUID(AccountGroup.UUID uuid) {
+ return uuid.get().startsWith(LDAP_UUID);
+ }
+
+ private static GroupReference groupReference(LdapQuery.Result res)
+ throws NamingException {
+ return new GroupReference(
+ new AccountGroup.UUID(LDAP_UUID + res.getDN()),
+ LDAP_NAME + cnFor(res.getDN()));
+ }
+
+ private static String cnFor(String dn) {
+ try {
+ LdapName name = new LdapName(dn);
+ if (!name.isEmpty()) {
+ String cn = name.get(name.size() - 1);
+ int index = cn.indexOf('=');
+ if (index >= 0) {
+ cn = cn.substring(index + 1);
+ }
+ return cn;
+ }
+ } catch (InvalidNameException e) {
+ log.warn("Cannot parse LDAP dn for cn", e);
+ }
+ return dn;
+ }
+
+ @Override
+ public boolean handles(AccountGroup.UUID uuid) {
+ return isLdapUUID(uuid);
+ }
+
+ @Override
+ public GroupDescription.Basic get(final AccountGroup.UUID uuid) {
+ if (!handles(uuid)) {
+ return null;
+ }
+
+ String groupDn = uuid.get().substring(LDAP_UUID.length());
+ CurrentUser user = userProvider.get();
+ if (!(user instanceof IdentifiedUser)
+ || !membershipsOf((IdentifiedUser) user).contains(uuid)) {
+ try {
+ if (!existsCache.get(groupDn)) {
+ return null;
+ }
+ } catch (ExecutionException e) {
+ log.warn(String.format("Cannot lookup group %s in LDAP", groupDn), e);
+ return null;
+ }
+ }
+
+ final String name = LDAP_NAME + cnFor(groupDn);
+ return new GroupDescription.Basic() {
+ @Override
+ public AccountGroup.UUID getGroupUUID() {
+ return uuid;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean isVisibleToAll() {
+ return false;
+ }
+ };
+ }
+
+ @Override
+ public Collection<GroupReference> suggest(String name) {
+ AccountGroup.UUID uuid = new AccountGroup.UUID(name);
+ if (isLdapUUID(uuid)) {
+ GroupDescription.Basic g = get(uuid);
+ if (g == null) {
+ return Collections.emptySet();
+ }
+ return Collections.singleton(GroupReference.forGroup(g));
+ } else if (name.startsWith(LDAP_NAME)) {
+ return suggestLdap(name.substring(LDAP_NAME.length()));
+ }
+ return Collections.emptySet();
+ }
+
+ @Override
+ public GroupMembership membershipsOf(IdentifiedUser user) {
+ String id = findId(user.state().getExternalIds());
+ if (id == null) {
+ return GroupMembership.EMPTY;
+ }
+
+ try {
+ return new ListGroupMembership(membershipCache.get(id));
+ } catch (ExecutionException e) {
+ log.warn(String.format("Cannot lookup membershipsOf %s in LDAP", id), e);
+ return GroupMembership.EMPTY;
+ }
+ }
+
+ private static String findId(final Collection<AccountExternalId> ids) {
+ for (final AccountExternalId i : ids) {
+ if (i.isScheme(AccountExternalId.SCHEME_GERRIT)) {
+ return i.getSchemeRest();
+ }
+ }
+ return null;
+ }
+
+
+ private Set<GroupReference> suggestLdap(String name) {
+ if (name.isEmpty()) {
+ return Collections.emptySet();
+ }
+
+ Set<GroupReference> out = Sets.newTreeSet(GROUP_REF_NAME_COMPARATOR);
+ try {
+ DirContext ctx = helper.open();
+ try {
+ // Do exact lookups until there are at least 3 characters.
+ name = Rdn.escapeValue(name) + ((name.length() >= 3) ? "*" : "");
+ LdapSchema schema = helper.getSchema(ctx);
+ ParameterizedString filter = ParameterizedString.asis(
+ schema.groupPattern.replace(GROUPNAME, name).toString());
+ Set<String> returnAttrs = Collections.<String>emptySet();
+ Map<String, String> params = Collections.emptyMap();
+ for (String groupBase : schema.groupBases) {
+ LdapQuery query = new LdapQuery(
+ groupBase, schema.groupScope, filter, returnAttrs);
+ for (LdapQuery.Result res : query.query(ctx, params)) {
+ out.add(groupReference(res));
+ }
+ }
+ } finally {
+ try {
+ ctx.close();
+ } catch (NamingException e) {
+ log.warn("Cannot close LDAP query handle", e);
+ }
+ }
+ } catch (NamingException e) {
+ log.warn("Cannot query LDAP for groups matching requested name", e);
+ }
+ return out;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapModule.java
index 6eb2f54..29533b9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapModule.java
@@ -16,10 +16,12 @@
import static java.util.concurrent.TimeUnit.HOURS;
+import com.google.common.base.Optional;
+import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.account.Realm;
-import com.google.gerrit.server.cache.Cache;
import com.google.gerrit.server.cache.CacheModule;
import com.google.inject.Scopes;
import com.google.inject.TypeLiteral;
@@ -29,20 +31,31 @@
public class LdapModule extends CacheModule {
static final String USERNAME_CACHE = "ldap_usernames";
static final String GROUP_CACHE = "ldap_groups";
+ static final String GROUP_EXIST_CACHE = "ldap_group_existence";
+
@Override
protected void configure() {
- final TypeLiteral<Cache<String, Set<AccountGroup.UUID>>> groups =
- new TypeLiteral<Cache<String, Set<AccountGroup.UUID>>>() {};
- core(groups, GROUP_CACHE).maxAge(1, HOURS) //
- .populateWith(LdapRealm.MemberLoader.class);
+ cache(GROUP_CACHE,
+ String.class,
+ new TypeLiteral<Set<AccountGroup.UUID>>() {})
+ .expireAfterWrite(1, HOURS)
+ .loader(LdapRealm.MemberLoader.class);
- final TypeLiteral<Cache<String, Account.Id>> usernames =
- new TypeLiteral<Cache<String, Account.Id>>() {};
- core(usernames, USERNAME_CACHE) //
- .populateWith(LdapRealm.UserLoader.class);
+ cache(USERNAME_CACHE,
+ String.class,
+ new TypeLiteral<Optional<Account.Id>>() {})
+ .loader(LdapRealm.UserLoader.class);
+
+ cache(GROUP_EXIST_CACHE,
+ String.class,
+ new TypeLiteral<Boolean>() {})
+ .expireAfterWrite(1, HOURS)
+ .loader(LdapRealm.ExistenceLoader.class);
bind(Realm.class).to(LdapRealm.class).in(Scopes.SINGLETON);
bind(Helper.class);
+
+ DynamicSet.bind(binder(), GroupBackend.class).to(LdapGroupBackend.class);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
index 910bf06..72eb7ec 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
@@ -16,7 +16,10 @@
import static com.google.gerrit.reviewdb.client.AccountExternalId.SCHEME_GERRIT;
-import com.google.common.collect.Iterables;
+import com.google.common.base.Optional;
+import com.google.common.base.Strings;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
import com.google.gerrit.common.data.ParameterizedString;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountExternalId;
@@ -24,20 +27,13 @@
import com.google.gerrit.reviewdb.client.AuthType;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.account.AccountException;
-import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.AuthRequest;
import com.google.gerrit.server.account.EmailExpander;
-import com.google.gerrit.server.account.GroupMembership;
-import com.google.gerrit.server.account.MaterializedGroupMembership;
import com.google.gerrit.server.account.Realm;
import com.google.gerrit.server.auth.AuthenticationUnavailableException;
-import com.google.gerrit.server.auth.ldap.Helper.LdapSchema;
-import com.google.gerrit.server.cache.Cache;
-import com.google.gerrit.server.cache.EntryCreator;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -48,15 +44,16 @@
import org.slf4j.LoggerFactory;
import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import javax.naming.CompositeName;
+import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
@@ -65,34 +62,30 @@
static final Logger log = LoggerFactory.getLogger(LdapRealm.class);
static final String LDAP = "com.sun.jndi.ldap.LdapCtxFactory";
static final String USERNAME = "username";
- private static final String GROUPNAME = "groupname";
private final Helper helper;
private final AuthConfig authConfig;
private final EmailExpander emailExpander;
- private final Cache<String, Account.Id> usernameCache;
+ private final LoadingCache<String, Optional<Account.Id>> usernameCache;
private final Set<Account.FieldName> readOnlyAccountFields;
private final Config config;
- private final Cache<String, Set<AccountGroup.UUID>> membershipCache;
- private final MaterializedGroupMembership.Factory groupMembershipFactory;
+ private final LoadingCache<String, Set<AccountGroup.UUID>> membershipCache;
@Inject
LdapRealm(
final Helper helper,
final AuthConfig authConfig,
final EmailExpander emailExpander,
- @Named(LdapModule.GROUP_CACHE) final Cache<String, Set<AccountGroup.UUID>> membershipCache,
- @Named(LdapModule.USERNAME_CACHE) final Cache<String, Account.Id> usernameCache,
- @GerritServerConfig final Config config,
- final MaterializedGroupMembership.Factory groupMembershipFactory) {
+ @Named(LdapModule.GROUP_CACHE) final LoadingCache<String, Set<AccountGroup.UUID>> membershipCache,
+ @Named(LdapModule.USERNAME_CACHE) final LoadingCache<String, Optional<Account.Id>> usernameCache,
+ @GerritServerConfig final Config config) {
this.helper = helper;
this.authConfig = authConfig;
this.emailExpander = emailExpander;
this.usernameCache = usernameCache;
this.membershipCache = membershipCache;
this.config = config;
- this.groupMembershipFactory = groupMembershipFactory;
this.readOnlyAccountFields = new HashSet<Account.FieldName>();
@@ -189,6 +182,7 @@
return r.isEmpty() ? null : r;
}
+ @Override
public AuthRequest authenticate(final AuthRequest who)
throws AccountException {
if (config.getBoolean("ldap", "localUsernameToLowerCase", false)) {
@@ -261,65 +255,24 @@
@Override
public void onCreateAccount(final AuthRequest who, final Account account) {
- usernameCache.put(who.getLocalUser(), account.getId());
+ usernameCache.put(who.getLocalUser(), Optional.of(account.getId()));
}
@Override
- public GroupMembership groups(final AccountState who) {
- return groupMembershipFactory.create(Iterables.concat(
- membershipCache.get(findId(who.getExternalIds())),
- who.getInternalGroups()));
- }
-
- private static String findId(final Collection<AccountExternalId> ids) {
- for (final AccountExternalId i : ids) {
- if (i.isScheme(AccountExternalId.SCHEME_GERRIT)) {
- return i.getSchemeRest();
- }
+ public Account.Id lookup(String accountName) {
+ if (Strings.isNullOrEmpty(accountName)) {
+ return null;
}
- return null;
- }
-
- @Override
- public Account.Id lookup(final String accountName) {
- return usernameCache.get(accountName);
- }
-
- @Override
- public Set<AccountGroup.ExternalNameKey> lookupGroups(String name) {
- final Set<AccountGroup.ExternalNameKey> out;
- final Map<String, String> params = Collections.<String, String> emptyMap();
-
- out = new HashSet<AccountGroup.ExternalNameKey>();
try {
- final DirContext ctx = helper.open();
- try {
- final LdapSchema schema = helper.getSchema(ctx);
- final ParameterizedString filter =
- ParameterizedString.asis(schema.groupPattern
- .replace(GROUPNAME, name).toString());
- for (String groupBase : schema.groupBases) {
- final LdapQuery query =
- new LdapQuery(groupBase, schema.groupScope, filter, Collections
- .<String> emptySet());
- for (LdapQuery.Result res : query.query(ctx, params)) {
- out.add(new AccountGroup.ExternalNameKey(res.getDN()));
- }
- }
- } finally {
- try {
- ctx.close();
- } catch (NamingException e) {
- log.warn("Cannot close LDAP query handle", e);
- }
- }
- } catch (NamingException e) {
- log.warn("Cannot query LDAP for groups matching requested name", e);
+ Optional<Account.Id> id = usernameCache.get(accountName);
+ return id != null ? id.orNull() : null;
+ } catch (ExecutionException e) {
+ log.warn(String.format("Cannot lookup account %s in LDAP", accountName), e);
+ return null;
}
- return out;
}
- static class UserLoader extends EntryCreator<String, Account.Id> {
+ static class UserLoader extends CacheLoader<String, Optional<Account.Id>> {
private final SchemaFactory<ReviewDb> schema;
@Inject
@@ -328,25 +281,23 @@
}
@Override
- public Account.Id createEntry(final String username) throws Exception {
+ public Optional<Account.Id> load(String username) throws Exception {
+ final ReviewDb db = schema.open();
try {
- final ReviewDb db = schema.open();
- try {
- final AccountExternalId extId =
- db.accountExternalIds().get(
- new AccountExternalId.Key(SCHEME_GERRIT, username));
- return extId != null ? extId.getAccountId() : null;
- } finally {
- db.close();
+ final AccountExternalId extId =
+ db.accountExternalIds().get(
+ new AccountExternalId.Key(SCHEME_GERRIT, username));
+ if (extId != null) {
+ return Optional.of(extId.getAccountId());
}
- } catch (OrmException e) {
- log.warn("Cannot query for username in database", e);
- return null;
+ return Optional.absent();
+ } finally {
+ db.close();
}
}
}
- static class MemberLoader extends EntryCreator<String, Set<AccountGroup.UUID>> {
+ static class MemberLoader extends CacheLoader<String, Set<AccountGroup.UUID>> {
private final Helper helper;
@Inject
@@ -355,8 +306,7 @@
}
@Override
- public Set<AccountGroup.UUID> createEntry(final String username)
- throws Exception {
+ public Set<AccountGroup.UUID> load(String username) throws Exception {
final DirContext ctx = helper.open();
try {
return helper.queryForGroups(ctx, username, null);
@@ -368,10 +318,34 @@
}
}
}
+ }
+
+ static class ExistenceLoader extends CacheLoader<String, Boolean> {
+ private final Helper helper;
+
+ @Inject
+ ExistenceLoader(final Helper helper) {
+ this.helper = helper;
+ }
@Override
- public Set<AccountGroup.UUID> missing(final String key) {
- return Collections.emptySet();
+ public Boolean load(final String groupDn) throws Exception {
+ final DirContext ctx = helper.open();
+ try {
+ Name compositeGroupName = new CompositeName().add(groupDn);
+ try {
+ ctx.getAttributes(compositeGroupName);
+ return true;
+ } catch (NamingException e) {
+ return false;
+ }
+ } finally {
+ try {
+ ctx.close();
+ } catch (NamingException e) {
+ log.warn("Cannot close LDAP query handle", e);
+ }
+ }
}
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/cache/Cache.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/Cache.java
deleted file mode 100644
index 7892ea1..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/cache/Cache.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.cache;
-
-/**
- * A fast in-memory and/or on-disk based cache.
- *
- * @type <K> type of key used to lookup entries in the cache.
- * @type <V> type of value stored within each cache entry.
- */
-public interface Cache<K, V> {
- /** Get the element from the cache, or null if not stored in the cache. */
- public V get(K key);
-
- /** Put one element into the cache, replacing any existing value. */
- public void put(K key, V value);
-
- /** Remove any existing value from the cache, no-op if not present. */
- public void remove(K key);
-
- /** Remove all cached items. */
- public void removeAll();
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/cache/CacheBinding.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/CacheBinding.java
new file mode 100644
index 0000000..625bd14
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/cache/CacheBinding.java
@@ -0,0 +1,46 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.cache;
+
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.Weigher;
+import com.google.inject.TypeLiteral;
+
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.Nullable;
+
+/** Configure a cache declared within a {@link CacheModule} instance. */
+public interface CacheBinding<K, V> {
+ /** Set the total size of the cache. */
+ CacheBinding<K, V> maximumWeight(long weight);
+
+ /** Set the time an element lives before being expired. */
+ CacheBinding<K, V> expireAfterWrite(long duration, TimeUnit durationUnits);
+
+ /** Populate the cache with items from the CacheLoader. */
+ CacheBinding<K, V> loader(Class<? extends CacheLoader<K, V>> clazz);
+
+ /** Algorithm to weigh an object with a method other than the unit weight 1. */
+ CacheBinding<K, V> weigher(Class<? extends Weigher<K, V>> clazz);
+
+ String name();
+ TypeLiteral<K> keyType();
+ TypeLiteral<V> valueType();
+ long maximumWeight();
+ @Nullable Long expireAfterWrite(TimeUnit unit);
+ @Nullable Weigher<K, V> weigher();
+ @Nullable CacheLoader<K, V> loader();
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/cache/CacheModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/CacheModule.java
index 7fb3b3b..c1e92da 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/cache/CacheModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/cache/CacheModule.java
@@ -14,33 +14,41 @@
package com.google.gerrit.server.cache;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.cache.Weigher;
+import com.google.gerrit.extensions.annotations.Exports;
import com.google.inject.AbstractModule;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.Scopes;
import com.google.inject.TypeLiteral;
-import com.google.inject.internal.UniqueAnnotations;
import com.google.inject.name.Names;
+import com.google.inject.util.Types;
import java.io.Serializable;
+import java.lang.reflect.Type;
/**
* Miniature DSL to support binding {@link Cache} instances in Guice.
*/
public abstract class CacheModule extends AbstractModule {
+ private static final TypeLiteral<Cache<?, ?>> ANY_CACHE =
+ new TypeLiteral<Cache<?, ?>>() {};
+
/**
- * Declare an unnamed in-memory cache.
+ * Declare a named in-memory cache.
*
* @param <K> type of key used to lookup entries.
* @param <V> type of value stored by the cache.
- * @param type type literal for the cache, this literal will be used to match
- * injection sites.
- * @return binding to describe the cache. Caller must set at least the name on
- * the returned binding.
+ * @return binding to describe the cache.
*/
- protected <K, V> UnnamedCacheBinding<K, V> core(
- final TypeLiteral<Cache<K, V>> type) {
- return core(Key.get(type));
+ protected <K, V> CacheBinding<K, V> cache(
+ String name,
+ Class<K> keyType,
+ Class<V> valType) {
+ return cache(name, TypeLiteral.get(keyType), TypeLiteral.get(valType));
}
/**
@@ -48,74 +56,127 @@
*
* @param <K> type of key used to lookup entries.
* @param <V> type of value stored by the cache.
- * @param type type literal for the cache, this literal will be used to match
- * injection sites. Injection sites are matched by this type literal
- * and with {@code @Named} annotations.
* @return binding to describe the cache.
*/
- protected <K, V> NamedCacheBinding<K, V> core(
- final TypeLiteral<Cache<K, V>> type, final String name) {
- return core(Key.get(type, Names.named(name))).name(name);
- }
-
- private <K, V> UnnamedCacheBinding<K, V> core(final Key<Cache<K, V>> key) {
- final boolean disk = false;
- final CacheProvider<K, V> b = new CacheProvider<K, V>(disk, this);
- bind(key).toProvider(b).in(Scopes.SINGLETON);
- return b;
+ protected <K, V> CacheBinding<K, V> cache(
+ String name,
+ Class<K> keyType,
+ TypeLiteral<V> valType) {
+ return cache(name, TypeLiteral.get(keyType), valType);
}
/**
- * Declare an unnamed in-memory/on-disk cache.
+ * Declare a named in-memory cache.
*
- * @param <K> type of key used to find entries, must be {@link Serializable}.
- * @param <V> type of value stored by the cache, must be {@link Serializable}.
- * @param type type literal for the cache, this literal will be used to match
- * injection sites. Injection sites are matched by this type literal
- * and with {@code @Named} annotations.
- * @return binding to describe the cache. Caller must set at least the name on
- * the returned binding.
+ * @param <K> type of key used to lookup entries.
+ * @param <V> type of value stored by the cache.
+ * @return binding to describe the cache.
*/
- protected <K extends Serializable, V extends Serializable> UnnamedCacheBinding<K, V> disk(
- final TypeLiteral<Cache<K, V>> type) {
- return disk(Key.get(type));
+ protected <K, V> CacheBinding<K, V> cache(
+ String name,
+ TypeLiteral<K> keyType,
+ TypeLiteral<V> valType) {
+ Type type = Types.newParameterizedType(
+ Cache.class,
+ keyType.getType(), valType.getType());
+
+ @SuppressWarnings("unchecked")
+ Key<Cache<K, V>> key = (Key<Cache<K, V>>) Key.get(type, Names.named(name));
+
+ CacheProvider<K, V> m =
+ new CacheProvider<K, V>(this, name, keyType, valType);
+ bind(key).toProvider(m).in(Scopes.SINGLETON);
+ bind(ANY_CACHE).annotatedWith(Exports.named(name)).to(key);
+ return m.maximumWeight(1024);
+ }
+
+ <K,V> Provider<CacheLoader<K,V>> bindCacheLoader(
+ CacheProvider<K, V> m,
+ Class<? extends CacheLoader<K,V>> impl) {
+ Type type = Types.newParameterizedType(
+ Cache.class,
+ m.keyType().getType(), m.valueType().getType());
+
+ Type loadingType = Types.newParameterizedType(
+ LoadingCache.class,
+ m.keyType().getType(), m.valueType().getType());
+
+ Type loaderType = Types.newParameterizedType(
+ CacheLoader.class,
+ m.keyType().getType(), m.valueType().getType());
+
+ @SuppressWarnings("unchecked")
+ Key<LoadingCache<K, V>> key =
+ (Key<LoadingCache<K, V>>) Key.get(type, Names.named(m.name));
+
+ @SuppressWarnings("unchecked")
+ Key<LoadingCache<K, V>> loadingKey =
+ (Key<LoadingCache<K, V>>) Key.get(loadingType, Names.named(m.name));
+
+ @SuppressWarnings("unchecked")
+ Key<CacheLoader<K, V>> loaderKey =
+ (Key<CacheLoader<K, V>>) Key.get(loaderType, Names.named(m.name));
+
+ bind(loaderKey).to(impl).in(Scopes.SINGLETON);
+ bind(loadingKey).to(key);
+ return getProvider(loaderKey);
+ }
+
+ <K,V> Provider<Weigher<K,V>> bindWeigher(
+ CacheProvider<K, V> m,
+ Class<? extends Weigher<K,V>> impl) {
+ Type weigherType = Types.newParameterizedType(
+ Weigher.class,
+ m.keyType().getType(), m.valueType().getType());
+
+ @SuppressWarnings("unchecked")
+ Key<Weigher<K, V>> key =
+ (Key<Weigher<K, V>>) Key.get(weigherType, Names.named(m.name));
+
+ bind(key).to(impl).in(Scopes.SINGLETON);
+ return getProvider(key);
}
/**
* Declare a named in-memory/on-disk cache.
*
- * @param <K> type of key used to find entries, must be {@link Serializable}.
- * @param <V> type of value stored by the cache, must be {@link Serializable}.
- * @param type type literal for the cache, this literal will be used to match
- * injection sites. Injection sites are matched by this type literal
- * and with {@code @Named} annotations.
+ * @param <K> type of key used to lookup entries.
+ * @param <V> type of value stored by the cache.
* @return binding to describe the cache.
*/
- protected <K extends Serializable, V extends Serializable> NamedCacheBinding<K, V> disk(
- final TypeLiteral<Cache<K, V>> type, final String name) {
- return disk(Key.get(type, Names.named(name))).name(name);
+ protected <K extends Serializable, V extends Serializable> CacheBinding<K, V> persist(
+ String name,
+ Class<K> keyType,
+ Class<V> valType) {
+ return persist(name, TypeLiteral.get(keyType), TypeLiteral.get(valType));
}
- private <K, V> UnnamedCacheBinding<K, V> disk(final Key<Cache<K, V>> key) {
- final boolean disk = true;
- final CacheProvider<K, V> b = new CacheProvider<K, V>(disk, this);
- bind(key).toProvider(b).in(Scopes.SINGLETON);
- return b;
+ /**
+ * Declare a named in-memory/on-disk cache.
+ *
+ * @param <K> type of key used to lookup entries.
+ * @param <V> type of value stored by the cache.
+ * @return binding to describe the cache.
+ */
+ protected <K extends Serializable, V extends Serializable> CacheBinding<K, V> persist(
+ String name,
+ Class<K> keyType,
+ TypeLiteral<V> valType) {
+ return persist(name, TypeLiteral.get(keyType), valType);
}
- <K, V> Provider<EntryCreator<K, V>> getEntryCreator(CacheProvider<K, V> cp,
- Class<? extends EntryCreator<K, V>> type) {
- Key<EntryCreator<K, V>> key = newKey();
- bind(key).to(type).in(Scopes.SINGLETON);
- return getProvider(key);
- }
-
- @SuppressWarnings("unchecked")
- private static <K, V> Key<EntryCreator<K, V>> newKey() {
- return (Key<EntryCreator<K, V>>) newKeyImpl();
- }
-
- private static Key<?> newKeyImpl() {
- return Key.get(EntryCreator.class, UniqueAnnotations.create());
+ /**
+ * Declare a named in-memory/on-disk cache.
+ *
+ * @param <K> type of key used to lookup entries.
+ * @param <V> type of value stored by the cache.
+ * @return binding to describe the cache.
+ */
+ protected <K extends Serializable, V extends Serializable> CacheBinding<K, V> persist(
+ String name,
+ TypeLiteral<K> keyType,
+ TypeLiteral<V> valType) {
+ return ((CacheProvider<K, V>) cache(name, keyType, valType))
+ .persist(true);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/cache/CacheProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/CacheProvider.java
index 1fa047b..1b8eea5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/cache/CacheProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/cache/CacheProvider.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2009 The Android Open Source Project
+// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -14,130 +14,156 @@
package com.google.gerrit.server.cache;
-import static com.google.gerrit.server.cache.EvictionPolicy.LFU;
-import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.SECONDS;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.Weigher;
+import com.google.gerrit.extensions.annotations.PluginName;
import com.google.inject.Inject;
import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
+import com.google.inject.TypeLiteral;
import java.util.concurrent.TimeUnit;
-public final class CacheProvider<K, V> implements Provider<Cache<K, V>>,
- NamedCacheBinding<K, V>, UnnamedCacheBinding<K, V> {
+import javax.annotation.Nullable;
+
+class CacheProvider<K, V>
+ implements Provider<Cache<K, V>>,
+ CacheBinding<K, V> {
private final CacheModule module;
- private final boolean disk;
- private int memoryLimit;
- private int diskLimit;
- private long maxAge;
- private EvictionPolicy evictionPolicy;
- private String cacheName;
- private ProxyCache<K, V> cache;
- private Provider<EntryCreator<K, V>> entryCreator;
+ final String name;
+ private final TypeLiteral<K> keyType;
+ private final TypeLiteral<V> valType;
+ private boolean persist;
+ private long maximumWeight;
+ private Long expireAfterWrite;
+ private Provider<CacheLoader<K, V>> loader;
+ private Provider<Weigher<K, V>> weigher;
- CacheProvider(final boolean disk, CacheModule module) {
- this.disk = disk;
+ private String plugin;
+ private MemoryCacheFactory memoryCacheFactory;
+ private PersistentCacheFactory persistentCacheFactory;
+ private boolean frozen;
+
+ CacheProvider(CacheModule module,
+ String name,
+ TypeLiteral<K> keyType,
+ TypeLiteral<V> valType) {
this.module = module;
+ this.name = name;
+ this.keyType = keyType;
+ this.valType = valType;
+ }
- memoryLimit(1024);
- maxAge(90, DAYS);
- evictionPolicy(LFU);
-
- if (disk) {
- diskLimit(16384);
- }
+ @Inject(optional = true)
+ void setPluginName(@PluginName String pluginName) {
+ this.plugin = pluginName;
}
@Inject
- void setCachePool(final CachePool pool) {
- this.cache = pool.register(this);
+ void setMemoryCacheFactory(MemoryCacheFactory factory) {
+ this.memoryCacheFactory = factory;
}
- public void bind(Cache<K, V> impl) {
- if (cache == null) {
- throw new ProvisionException("Cache was never registered");
- }
- cache.bind(impl);
+ @Inject(optional = true)
+ void setPersistentCacheFactory(@Nullable PersistentCacheFactory factory) {
+ this.persistentCacheFactory = factory;
}
- public EntryCreator<K, V> getEntryCreator() {
- return entryCreator != null ? entryCreator.get() : null;
- }
-
- public String getName() {
- if (cacheName == null) {
- throw new ProvisionException("Cache has no name");
- }
- return cacheName;
- }
-
- public boolean disk() {
- return disk;
- }
-
- public int memoryLimit() {
- return memoryLimit;
- }
-
- public int diskLimit() {
- return diskLimit;
- }
-
- public long maxAge() {
- return maxAge;
- }
-
- public EvictionPolicy evictionPolicy() {
- return evictionPolicy;
- }
-
- public NamedCacheBinding<K, V> name(final String name) {
- if (cacheName != null) {
- throw new IllegalStateException("Cache name already set");
- }
- cacheName = name;
- return this;
- }
-
- public NamedCacheBinding<K, V> memoryLimit(final int objects) {
- memoryLimit = objects;
- return this;
- }
-
- public NamedCacheBinding<K, V> diskLimit(final int objects) {
- if (!disk) {
- // TODO This should really be a compile time type error, but I'm
- // too lazy to create the mess of permutations required to setup
- // type safe returns for bindings in our little DSL.
- //
- throw new IllegalStateException("Cache is not disk based");
- }
- diskLimit = objects;
- return this;
- }
-
- public NamedCacheBinding<K, V> maxAge(final long duration, final TimeUnit unit) {
- maxAge = SECONDS.convert(duration, unit);
+ CacheBinding<K, V> persist(boolean p) {
+ Preconditions.checkState(!frozen, "binding frozen, cannot be modified");
+ persist = p;
return this;
}
@Override
- public NamedCacheBinding<K, V> evictionPolicy(final EvictionPolicy policy) {
- evictionPolicy = policy;
+ public CacheBinding<K, V> maximumWeight(long weight) {
+ Preconditions.checkState(!frozen, "binding frozen, cannot be modified");
+ maximumWeight = weight;
return this;
}
- public NamedCacheBinding<K, V> populateWith(
- Class<? extends EntryCreator<K, V>> creator) {
- entryCreator = module.getEntryCreator(this, creator);
+ @Override
+ public CacheBinding<K, V> expireAfterWrite(long duration, TimeUnit unit) {
+ Preconditions.checkState(!frozen, "binding frozen, cannot be modified");
+ expireAfterWrite = SECONDS.convert(duration, unit);
return this;
}
- public Cache<K, V> get() {
- if (cache == null) {
- throw new ProvisionException("Cache \"" + cacheName + "\" not available");
+ @Override
+ public CacheBinding<K, V> loader(Class<? extends CacheLoader<K, V>> impl) {
+ Preconditions.checkState(!frozen, "binding frozen, cannot be modified");
+ loader = module.bindCacheLoader(this, impl);
+ return this;
+ }
+
+ @Override
+ public CacheBinding<K, V> weigher(Class<? extends Weigher<K, V>> impl) {
+ Preconditions.checkState(!frozen, "binding frozen, cannot be modified");
+ weigher = module.bindWeigher(this, impl);
+ return this;
+ }
+
+ @Override
+ public String name() {
+ if (!Strings.isNullOrEmpty(plugin)) {
+ return plugin + "." + name;
}
- return cache;
+ return name;
+ }
+
+ @Override
+ public TypeLiteral<K> keyType() {
+ return keyType;
+ }
+
+ @Override
+ public TypeLiteral<V> valueType() {
+ return valType;
+ }
+
+ @Override
+ public long maximumWeight() {
+ return maximumWeight;
+ }
+
+ @Override
+ @Nullable
+ public Long expireAfterWrite(TimeUnit unit) {
+ return expireAfterWrite != null
+ ? unit.convert(expireAfterWrite, SECONDS)
+ : null;
+ }
+
+ @Override
+ @Nullable
+ public Weigher<K, V> weigher() {
+ return weigher != null ? weigher.get() : null;
+ }
+
+ @Override
+ @Nullable
+ public CacheLoader<K, V> loader() {
+ return loader != null ? loader.get() : null;
+ }
+
+ @Override
+ public Cache<K, V> get() {
+ frozen = true;
+
+ if (loader != null) {
+ CacheLoader<K, V> ldr = loader.get();
+ if (persist && persistentCacheFactory != null) {
+ return persistentCacheFactory.build(this, ldr);
+ }
+ return memoryCacheFactory.build(this, ldr);
+ } else if (persist && persistentCacheFactory != null) {
+ return persistentCacheFactory.build(this);
+ } else {
+ return memoryCacheFactory.build(this);
+ }
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/cache/ConcurrentHashMapCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/ConcurrentHashMapCache.java
deleted file mode 100644
index bafdc49..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/cache/ConcurrentHashMapCache.java
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (C) 2011 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.package com.google.gerrit.server.git;
-
-package com.google.gerrit.server.cache;
-
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * An infinitely sized cache backed by java.util.ConcurrentHashMap.
- * <p>
- * This cache type is only suitable for unit tests, as it has no upper limit on
- * number of items held in the cache. No upper limit can result in memory leaks
- * in production servers.
- */
-public class ConcurrentHashMapCache<K, V> implements Cache<K, V> {
- private final ConcurrentHashMap<K, V> map = new ConcurrentHashMap<K, V>();
-
- @Override
- public V get(K key) {
- return map.get(key);
- }
-
- @Override
- public void put(K key, V value) {
- map.put(key, value);
- }
-
- @Override
- public void remove(K key) {
- map.remove(key);
- }
-
- @Override
- public void removeAll() {
- map.clear();
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/cache/EntryCreator.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/EntryCreator.java
deleted file mode 100644
index af07e08..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/cache/EntryCreator.java
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.cache;
-
-/**
- * Creates a cache entry on demand when its not found.
- *
- * @param <K> type of the cache's key.
- * @param <V> type of the cache's value element.
- */
-public abstract class EntryCreator<K, V> {
- /**
- * Invoked on a cache miss, to compute the cache entry.
- *
- * @param key entry whose content needs to be obtained.
- * @return new cache content. The caller will automatically put this object
- * into the cache.
- * @throws Exception the cache content cannot be computed. No entry will be
- * stored in the cache, and {@link #missing(Object)} will be invoked
- * instead. Future requests for the same key will retry this method.
- */
- public abstract V createEntry(K key) throws Exception;
-
- /** Invoked when {@link #createEntry(Object)} fails, by default return null. */
- public V missing(K key) {
- return null;
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/cache/EvictionPolicy.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/EvictionPolicy.java
deleted file mode 100644
index cff4f11..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/cache/EvictionPolicy.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.cache;
-
-/** How entries should be evicted from the cache. */
-public enum EvictionPolicy {
- /** Least recently used is evicted first. */
- LRU,
-
- /** Least frequently used is evicted first. */
- LFU;
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/IncompleteUserInfoException.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/MemoryCacheFactory.java
similarity index 62%
copy from gerrit-server/src/main/java/com/google/gerrit/server/git/IncompleteUserInfoException.java
copy to gerrit-server/src/main/java/com/google/gerrit/server/cache/MemoryCacheFactory.java
index 204d777..6b8b489 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/IncompleteUserInfoException.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/cache/MemoryCacheFactory.java
@@ -12,12 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.git;
+package com.google.gerrit.server.cache;
-public class IncompleteUserInfoException extends Exception {
- private static final long serialVersionUID = 1L;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
- public IncompleteUserInfoException(final String userName, final String missingInfo) {
- super("For the user \"" + userName + "\" " + missingInfo + " is not set.");
- }
+public interface MemoryCacheFactory {
+ <K, V> Cache<K, V> build(CacheBinding<K, V> def);
+
+ <K, V> LoadingCache<K, V> build(
+ CacheBinding<K, V> def,
+ CacheLoader<K, V> loader);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/cache/NamedCacheBinding.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/NamedCacheBinding.java
deleted file mode 100644
index 3394c71..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/cache/NamedCacheBinding.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.cache;
-
-import java.util.concurrent.TimeUnit;
-
-/** Configure a cache declared within a {@link CacheModule} instance. */
-public interface NamedCacheBinding<K, V> {
- /** Set the number of objects to cache in memory. */
- public NamedCacheBinding<K, V> memoryLimit(int objects);
-
- /** Set the number of objects to cache in memory. */
- public NamedCacheBinding<K, V> diskLimit(int objects);
-
- /** Set the time an element lives before being expired. */
- public NamedCacheBinding<K, V> maxAge(long duration, TimeUnit durationUnits);
-
- /** Set the eviction policy for elements when the cache is full. */
- public NamedCacheBinding<K, V> evictionPolicy(EvictionPolicy policy);
-
- /** Populate the cache with items from the EntryCreator. */
- public NamedCacheBinding<K, V> populateWith(Class<? extends EntryCreator<K, V>> creator);
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/IncompleteUserInfoException.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/PersistentCacheFactory.java
similarity index 62%
copy from gerrit-server/src/main/java/com/google/gerrit/server/git/IncompleteUserInfoException.java
copy to gerrit-server/src/main/java/com/google/gerrit/server/cache/PersistentCacheFactory.java
index 204d777..983e956 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/IncompleteUserInfoException.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/cache/PersistentCacheFactory.java
@@ -12,12 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.git;
+package com.google.gerrit.server.cache;
-public class IncompleteUserInfoException extends Exception {
- private static final long serialVersionUID = 1L;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
- public IncompleteUserInfoException(final String userName, final String missingInfo) {
- super("For the user \"" + userName + "\" " + missingInfo + " is not set.");
- }
+public interface PersistentCacheFactory {
+ <K, V> Cache<K, V> build(CacheBinding<K, V> def);
+
+ <K, V> LoadingCache<K, V> build(
+ CacheBinding<K, V> def,
+ CacheLoader<K, V> loader);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/cache/ProxyCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/ProxyCache.java
deleted file mode 100644
index c1b0292..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/cache/ProxyCache.java
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (C) 2010 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.cache;
-
-/** Proxy around a cache which has not yet been created. */
-public final class ProxyCache<K, V> implements Cache<K, V> {
- private volatile Cache<K, V> self;
-
- public void bind(Cache<K, V> self) {
- this.self = self;
- }
-
- public V get(K key) {
- return self.get(key);
- }
-
- public void put(K key, V value) {
- self.put(key, value);
- }
-
- public void remove(K key) {
- self.remove(key);
- }
-
- public void removeAll() {
- self.removeAll();
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/cache/UnnamedCacheBinding.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/UnnamedCacheBinding.java
deleted file mode 100644
index 43039e1..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/cache/UnnamedCacheBinding.java
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.cache;
-
-
-/** Configure a cache declared within a {@link CacheModule} instance. */
-public interface UnnamedCacheBinding<K, V> {
- /** Set the name of the cache. */
- public NamedCacheBinding<K, V> name(String cacheName);
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/Submit.java b/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/Submit.java
index 6648c7b..9cff992 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/Submit.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/Submit.java
@@ -113,6 +113,10 @@
errMsg.append("change " + changeId + ": needs " + lbl.label);
break;
+ case MAY:
+ // The MAY label didn't cause the NOT_READY status
+ break;
+
case IMPOSSIBLE:
if (errMsg.length() > 0) errMsg.append("; ");
errMsg.append("change " + changeId + ": needs " + lbl.label
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 2875920..9e3aeca 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -16,9 +16,11 @@
import static com.google.inject.Scopes.SINGLETON;
+import com.google.common.cache.Cache;
import com.google.gerrit.common.data.ApprovalTypes;
import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
import com.google.gerrit.extensions.events.NewProjectCreatedListener;
+import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.reviewdb.client.AuthType;
import com.google.gerrit.rules.PrologModule;
@@ -36,11 +38,15 @@
import com.google.gerrit.server.account.CapabilityControl;
import com.google.gerrit.server.account.DefaultRealm;
import com.google.gerrit.server.account.EmailExpander;
+import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.account.GroupCacheImpl;
+import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.account.GroupIncludeCacheImpl;
import com.google.gerrit.server.account.GroupInfoCacheFactory;
-import com.google.gerrit.server.account.MaterializedGroupMembership;
+import com.google.gerrit.server.account.IncludingGroupMembership;
+import com.google.gerrit.server.account.InternalGroupBackend;
import com.google.gerrit.server.account.Realm;
+import com.google.gerrit.server.account.UniversalGroupBackend;
import com.google.gerrit.server.auth.ldap.LdapModule;
import com.google.gerrit.server.events.EventFactory;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
@@ -68,7 +74,7 @@
import com.google.gerrit.server.util.ThreadLocalRequestContext;
import com.google.gerrit.server.workflow.FunctionState;
import com.google.inject.Inject;
-import com.google.inject.servlet.RequestScoped;
+import com.google.inject.TypeLiteral;
import org.apache.velocity.runtime.RuntimeInstance;
import org.eclipse.jgit.lib.Config;
@@ -128,12 +134,18 @@
factory(InternalUser.Factory.class);
factory(ProjectNode.Factory.class);
factory(ProjectState.Factory.class);
- factory(MaterializedGroupMembership.Factory.class);
bind(PermissionCollection.Factory.class);
bind(AccountVisibility.class)
.toProvider(AccountVisibilityProvider.class)
.in(SINGLETON);
+ bind(GroupControl.Factory.class).in(SINGLETON);
+ factory(IncludingGroupMembership.Factory.class);
+ bind(InternalGroupBackend.class).in(SINGLETON);
+ bind(GroupBackend.class).to(UniversalGroupBackend.class).in(SINGLETON);
+ DynamicSet.setOf(binder(), GroupBackend.class);
+ DynamicSet.bind(binder(), GroupBackend.class).to(InternalGroupBackend.class);
+
bind(FileTypeRegistry.class).to(MimeUtilFileTypeRegistry.class);
bind(ToolsCatalog.class);
bind(EventFactory.class);
@@ -156,6 +168,7 @@
factory(FunctionState.Factory.class);
bind(GitReferenceUpdated.class);
+ DynamicMap.mapOf(binder(), new TypeLiteral<Cache<?, ?>>() {});
DynamicSet.setOf(binder(), GitReferenceUpdatedListener.class);
DynamicSet.setOf(binder(), NewProjectCreatedListener.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
index 5d6ecc3..226f926 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
@@ -17,13 +17,11 @@
import static com.google.inject.Scopes.SINGLETON;
import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.RequestCleanup;
import com.google.gerrit.server.account.AccountControl;
import com.google.gerrit.server.account.AccountResolver;
-import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.account.GroupDetailFactory;
import com.google.gerrit.server.account.GroupMembers;
import com.google.gerrit.server.account.PerformCreateGroup;
@@ -79,7 +77,6 @@
bind(PerRequestProjectControlCache.class).in(RequestScoped.class);
bind(ChangeControl.Factory.class).in(SINGLETON);
- bind(GroupControl.Factory.class).in(SINGLETON);
bind(ProjectControl.Factory.class).in(SINGLETON);
bind(AccountControl.Factory.class).in(SINGLETON);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GitReceivePackGroupsProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GitReceivePackGroupsProvider.java
index c89f025..8b517a3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GitReceivePackGroupsProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GitReceivePackGroupsProvider.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.config;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.server.account.GroupCache;
+import com.google.gerrit.server.account.GroupBackend;
import com.google.inject.Inject;
import org.eclipse.jgit.lib.Config;
@@ -24,9 +24,9 @@
public class GitReceivePackGroupsProvider extends GroupSetProvider {
@Inject
- public GitReceivePackGroupsProvider(GroupCache gc,
+ public GitReceivePackGroupsProvider(GroupBackend gb,
@GerritServerConfig Config config) {
- super(gc, config, "receive", null, "allowGroup");
+ super(gb, config, "receive", null, "allowGroup");
// If no group was set, default to "registered users"
//
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GitUploadPackGroupsProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GitUploadPackGroupsProvider.java
index b5de742..c519902 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GitUploadPackGroupsProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GitUploadPackGroupsProvider.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.config;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.server.account.GroupCache;
+import com.google.gerrit.server.account.GroupBackend;
import com.google.inject.Inject;
import org.eclipse.jgit.lib.Config;
@@ -25,9 +25,9 @@
public class GitUploadPackGroupsProvider extends GroupSetProvider {
@Inject
- public GitUploadPackGroupsProvider(GroupCache gc,
+ public GitUploadPackGroupsProvider(GroupBackend gb,
@GerritServerConfig Config config) {
- super(gc, config, "upload", null, "allowGroup");
+ super(gb, config, "upload", null, "allowGroup");
// If no group was set, default to "registered users" and "anonymous"
//
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GroupSetProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GroupSetProvider.java
index 3619cda..5fa243b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GroupSetProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GroupSetProvider.java
@@ -15,8 +15,10 @@
package com.google.gerrit.server.config;
import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.server.account.GroupCache;
+import com.google.gerrit.server.account.GroupBackend;
+import com.google.gerrit.server.account.GroupBackends;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -34,17 +36,17 @@
protected Set<AccountGroup.UUID> groupIds;
@Inject
- protected GroupSetProvider(GroupCache groupCache,
+ protected GroupSetProvider(GroupBackend groupBackend,
@GerritServerConfig Config config, String section,
String subsection, String name) {
String[] groupNames = config.getStringList(section, subsection, name);
ImmutableSet.Builder<AccountGroup.UUID> builder = ImmutableSet.builder();
for (String n : groupNames) {
- AccountGroup g = groupCache.get(new AccountGroup.NameKey(n));
- if (g != null) {
- builder.add(g.getGroupUUID());
- } else {
+ GroupReference g = GroupBackends.findBestSuggestion(groupBackend, n);
+ if (g == null) {
log.warn("Group \"{0}\" not in database, skipping.", n);
+ } else {
+ builder.add(g.getUUID());
}
}
groupIds = builder.build();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectOwnerGroupsProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectOwnerGroupsProvider.java
index 7172b6f..6622b0f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectOwnerGroupsProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectOwnerGroupsProvider.java
@@ -14,7 +14,7 @@
package com.google.gerrit.server.config;
-import com.google.gerrit.server.account.GroupCache;
+import com.google.gerrit.server.account.GroupBackend;
import com.google.inject.Inject;
import org.eclipse.jgit.lib.Config;
@@ -32,8 +32,8 @@
*/
public class ProjectOwnerGroupsProvider extends GroupSetProvider {
@Inject
- public ProjectOwnerGroupsProvider(GroupCache gc,
+ public ProjectOwnerGroupsProvider(GroupBackend gb,
@GerritServerConfig final Config config) {
- super(gc, config, "repository", "*", "ownerGroup");
+ super(gb, config, "repository", "*", "ownerGroup");
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
index c538aa6..9fa582a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
@@ -32,6 +32,7 @@
import com.google.gerrit.server.patch.PatchList;
import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.patch.PatchListEntry;
+import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
@@ -232,16 +233,19 @@
public void addPatchSetFileNames(PatchSetAttribute patchSetAttribute,
Change change, PatchSet patchSet) {
- PatchList patchList = patchListCache.get(change, patchSet);
- for (PatchListEntry patch : patchList.getPatches()) {
- if (patchSetAttribute.files == null) {
- patchSetAttribute.files = new ArrayList<PatchAttribute>();
- }
+ try {
+ PatchList patchList = patchListCache.get(change, patchSet);
+ for (PatchListEntry patch : patchList.getPatches()) {
+ if (patchSetAttribute.files == null) {
+ patchSetAttribute.files = new ArrayList<PatchAttribute>();
+ }
- PatchAttribute p = new PatchAttribute();
- p.file = patch.getNewName();
- p.type = patch.getChangeType();
- patchSetAttribute.files.add(p);
+ PatchAttribute p = new PatchAttribute();
+ p.file = patch.getNewName();
+ p.type = patch.getChangeType();
+ patchSetAttribute.files.add(p);
+ }
+ } catch (PatchListNotAvailableException e) {
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/BanCommit.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/BanCommit.java
index c9c9753..4bfee9c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/BanCommit.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/BanCommit.java
@@ -17,10 +17,8 @@
import static com.google.gerrit.server.git.GitRepositoryManager.REF_REJECT_COMMITS;
import com.google.gerrit.common.errors.PermissionDeniedException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.project.ProjectControl;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -44,7 +42,9 @@
import org.eclipse.jgit.revwalk.RevWalk;
import java.io.IOException;
+import java.util.Date;
import java.util.List;
+import java.util.TimeZone;
public class BanCommit {
@@ -55,25 +55,23 @@
BanCommit create();
}
- private final Provider<CurrentUser> currentUser;
+ private final Provider<IdentifiedUser> currentUser;
private final GitRepositoryManager repoManager;
- private final AccountCache accountCache;
private final PersonIdent gerritIdent;
@Inject
- BanCommit(final Provider<CurrentUser> currentUser,
- final GitRepositoryManager repoManager, final AccountCache accountCache,
+ BanCommit(final Provider<IdentifiedUser> currentUser,
+ final GitRepositoryManager repoManager,
@GerritPersonIdent final PersonIdent gerritIdent) {
this.currentUser = currentUser;
this.repoManager = repoManager;
- this.accountCache = accountCache;
this.gerritIdent = gerritIdent;
}
public BanCommitResult ban(final ProjectControl projectControl,
final List<ObjectId> commitsToBan, final String reason)
throws PermissionDeniedException, IOException,
- IncompleteUserInfoException, InterruptedException, MergeException {
+ InterruptedException, MergeException {
if (!projectControl.isOwner()) {
throw new PermissionDeniedException(
"No project owner: not permitted to ban commits");
@@ -148,16 +146,10 @@
return result;
}
- private PersonIdent createPersonIdent() throws IncompleteUserInfoException {
- final String userName = currentUser.get().getUserName();
- final Account account = accountCache.getByUsername(userName).getAccount();
- if (account.getFullName() == null) {
- throw new IncompleteUserInfoException(userName, "full name");
- }
- if (account.getPreferredEmail() == null) {
- throw new IncompleteUserInfoException(userName, "preferred email");
- }
- return new PersonIdent(account.getFullName(), account.getPreferredEmail());
+ private PersonIdent createPersonIdent() {
+ Date now = new Date();
+ TimeZone tz = gerritIdent.getTimeZone();
+ return currentUser.get().newCommitterIdent(now, tz);
}
private static ObjectId commit(final NoteMap noteMap,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
index 6ddcb39..4db9409 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
@@ -900,6 +900,7 @@
final ObjectId id = commit(m, mergeCommit);
final CodeReviewCommit newCommit = (CodeReviewCommit) rw.parseCommit(id);
+ final Change oldChange = n.change;
n.change =
db.changes().atomicUpdate(n.change.getId(),
@@ -929,6 +930,9 @@
}
});
+ this.submitted.remove(oldChange);
+ this.submitted.add(n.change);
+
if (approvalList != null) {
for (PatchSetApproval a : approvalList) {
db.patchSetApprovals().insert(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
index 75c9bd1..507f064 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
@@ -21,6 +21,7 @@
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.ContributorAgreement;
import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.common.data.GroupDescription;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRule;
@@ -31,8 +32,9 @@
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.Project.State;
import com.google.gerrit.reviewdb.client.Project.SubmitType;
-import com.google.gerrit.server.account.GroupCache;
+import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.config.ConfigUtil;
+import com.google.gerrit.common.data.RefConfigSection;
import com.google.gerrit.server.mail.Address;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -233,13 +235,13 @@
/**
* Check all GroupReferences use current group name, repairing stale ones.
*
- * @param groupCache cache to use when looking up group information by UUID.
+ * @param groupBackend cache to use when looking up group information by UUID.
* @return true if one or more group names was stale.
*/
- public boolean updateGroupNames(GroupCache groupCache) {
+ public boolean updateGroupNames(GroupBackend groupBackend) {
boolean dirty = false;
for (GroupReference ref : groupsByUUID.values()) {
- AccountGroup g = groupCache.get(ref.getUUID());
+ GroupDescription.Basic g = groupBackend.get(ref.getUUID());
if (g != null && !g.getName().equals(ref.getName())) {
dirty = true;
ref.setName(g.getName());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
index b72b162..e9a35b8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
@@ -15,6 +15,11 @@
package com.google.gerrit.server.git;
import static com.google.gerrit.server.git.MultiProgressMonitor.UNKNOWN;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_MISSING_OBJECT;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_NONFASTFORWARD;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.ListMultimap;
@@ -80,7 +85,6 @@
import org.eclipse.jgit.transport.AdvertiseRefsHook;
import org.eclipse.jgit.transport.AdvertiseRefsHookChain;
import org.eclipse.jgit.transport.ReceiveCommand;
-import org.eclipse.jgit.transport.ReceiveCommand.Result;
import org.eclipse.jgit.transport.ReceivePack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -456,8 +460,7 @@
commandProgress = progress.beginSubTask("refs", UNKNOWN);
parseCommands(commands);
- if (newChange != null
- && newChange.getResult() == ReceiveCommand.Result.NOT_ATTEMPTED) {
+ if (newChange != null && newChange.getResult() == NOT_ATTEMPTED) {
createNewChanges();
}
newProgress.end();
@@ -474,7 +477,7 @@
}
for (final ReceiveCommand c : commands) {
- if (c.getResult() == Result.OK) {
+ if (c.getResult() == OK) {
switch (c.getType()) {
case CREATE:
if (isHead(c)) {
@@ -573,7 +576,7 @@
private void parseCommands(final Collection<ReceiveCommand> commands) {
for (final ReceiveCommand cmd : commands) {
- if (cmd.getResult() != ReceiveCommand.Result.NOT_ATTEMPTED) {
+ if (cmd.getResult() != NOT_ATTEMPTED) {
// Already rejected by the core receive process.
//
continue;
@@ -621,7 +624,7 @@
continue;
}
- if (cmd.getResult() != ReceiveCommand.Result.NOT_ATTEMPTED) {
+ if (cmd.getResult() != NOT_ATTEMPTED) {
continue;
}
@@ -687,7 +690,9 @@
RefControl ctl = projectControl.controlForRef(cmd.getRefName());
if (ctl.canCreate(rp.getRevWalk(), obj)) {
validateNewCommits(ctl, cmd);
- cmd.execute(rp);
+ if (cmd.getResult() == NOT_ATTEMPTED) {
+ cmd.execute(rp);
+ }
} else {
errors.put(Error.CREATE, ctl.getRefName());
reject(cmd, "can not create new references");
@@ -702,7 +707,9 @@
}
validateNewCommits(ctl, cmd);
- cmd.execute(rp);
+ if (cmd.getResult() == NOT_ATTEMPTED) {
+ cmd.execute(rp);
+ }
} else {
if (GitRepositoryManager.REF_CONFIG.equals(ctl.getRefName())) {
errors.put(Error.CONFIG_UPDATE, GitRepositoryManager.REF_CONFIG);
@@ -735,7 +742,9 @@
private void parseDelete(final ReceiveCommand cmd) {
RefControl ctl = projectControl.controlForRef(cmd.getRefName());
if (ctl.canDelete()) {
- cmd.execute(rp);
+ if (cmd.getResult() == NOT_ATTEMPTED) {
+ cmd.execute(rp);
+ }
} else {
if (GitRepositoryManager.REF_CONFIG.equals(ctl.getRefName())) {
reject(cmd, "cannot delete project configuration");
@@ -762,15 +771,17 @@
RefControl ctl = projectControl.controlForRef(cmd.getRefName());
if (newObject != null) {
validateNewCommits(ctl, cmd);
- if (cmd.getResult() != ReceiveCommand.Result.NOT_ATTEMPTED) {
+ if (cmd.getResult() != NOT_ATTEMPTED) {
return;
}
}
if (ctl.canForceUpdate()) {
- cmd.execute(rp);
+ if (cmd.getResult() == NOT_ATTEMPTED) {
+ cmd.execute(rp);
+ }
} else {
- cmd.setResult(ReceiveCommand.Result.REJECTED_NONFASTFORWARD, " need '"
+ cmd.setResult(REJECTED_NONFASTFORWARD, " need '"
+ PermissionRule.FORCE_PUSH + "' privilege.");
}
}
@@ -877,7 +888,7 @@
walk.setRevFilter(oldRevFilter);
}
} catch (IOException e) {
- newChange.setResult(Result.REJECTED_MISSING_OBJECT);
+ newChange.setResult(REJECTED_MISSING_OBJECT);
log.error("Invalid pack upload; one or more objects weren't sent", e);
return;
}
@@ -1052,7 +1063,7 @@
// Should never happen, the core receive process would have
// identified the missing object earlier before we got control.
//
- newChange.setResult(Result.REJECTED_MISSING_OBJECT);
+ newChange.setResult(REJECTED_MISSING_OBJECT);
log.error("Invalid pack upload; one or more objects weren't sent", e);
return;
} catch (OrmException e) {
@@ -1080,7 +1091,7 @@
}
newProgress.update(1);
}
- newChange.setResult(ReceiveCommand.Result.OK);
+ newChange.setResult(OK);
}
private static boolean isValidChangeId(String idStr) {
@@ -1207,7 +1218,7 @@
+ request.ontoChange + ", commit " + request.newCommit.name(), err);
reject(request.cmd, "database error");
}
- if (request.cmd.getResult() == ReceiveCommand.Result.NOT_ATTEMPTED) {
+ if (request.cmd.getResult() == NOT_ATTEMPTED) {
log.error("Replacement patch for change " + request.ontoChange
+ ", commit " + request.newCommit.name() + " wasn't attempted."
+ " This is a bug in the receive process implementation.");
@@ -1460,7 +1471,7 @@
}
replication.fire(project.getNameKey(), ru.getName());
hooks.doPatchsetCreatedHook(result.change, ps, db);
- request.cmd.setResult(ReceiveCommand.Result.OK);
+ request.cmd.setResult(OK);
workQueue.getDefaultQueue()
.submit(requestScopePropagator.wrap(new Runnable() {
@@ -1599,7 +1610,7 @@
}
}
} catch (IOException err) {
- cmd.setResult(Result.REJECTED_MISSING_OBJECT);
+ cmd.setResult(REJECTED_MISSING_OBJECT);
log.error("Invalid pack upload; one or more objects weren't sent", err);
}
}
@@ -2043,7 +2054,7 @@
}
private void reject(final ReceiveCommand cmd, final String why) {
- cmd.setResult(ReceiveCommand.Result.REJECTED_OTHER_REASON, why);
+ cmd.setResult(REJECTED_OTHER_REASON, why);
commandProgress.update(1);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/TagCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/TagCache.java
index ac4882f..3c64229 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/TagCache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/TagCache.java
@@ -14,13 +14,12 @@
package com.google.gerrit.server.git;
+import com.google.common.cache.Cache;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.cache.Cache;
import com.google.gerrit.server.cache.CacheModule;
import com.google.inject.Inject;
import com.google.inject.Module;
import com.google.inject.Singleton;
-import com.google.inject.TypeLiteral;
import com.google.inject.name.Named;
import org.eclipse.jgit.lib.ObjectId;
@@ -38,19 +37,17 @@
return new CacheModule() {
@Override
protected void configure() {
- final TypeLiteral<Cache<EntryKey, EntryVal>> type =
- new TypeLiteral<Cache<EntryKey, EntryVal>>() {};
- disk(type, CACHE_NAME);
+ persist(CACHE_NAME, String.class, EntryVal.class);
bind(TagCache.class);
}
};
}
- private final Cache<EntryKey, EntryVal> cache;
+ private final Cache<String, EntryVal> cache;
private final Object createLock = new Object();
@Inject
- TagCache(@Named(CACHE_NAME) Cache<EntryKey, EntryVal> cache) {
+ TagCache(@Named(CACHE_NAME) Cache<String, EntryVal> cache) {
this.cache = cache;
}
@@ -74,67 +71,43 @@
// never fail with an exception. Some of these references can be null
// (e.g. not all projects are cached, or the cache is not current).
//
- EntryVal val = cache.get(new EntryKey(name));
+ EntryVal val = cache.getIfPresent(name.get());
if (val != null) {
TagSetHolder holder = val.holder;
if (holder != null) {
TagSet tags = holder.getTagSet();
if (tags != null) {
- tags.updateFastForward(refName, oldValue, newValue);
+ if (tags.updateFastForward(refName, oldValue, newValue)) {
+ cache.put(name.get(), val);
+ }
}
}
}
}
TagSetHolder get(Project.NameKey name) {
- EntryKey key = new EntryKey(name);
- EntryVal val = cache.get(key);
+ EntryVal val = cache.getIfPresent(name.get());
if (val == null) {
synchronized (createLock) {
- val = cache.get(key);
+ val = cache.getIfPresent(name.get());
if (val == null) {
val = new EntryVal();
val.holder = new TagSetHolder(name);
- cache.put(key, val);
+ cache.put(name.get(), val);
}
}
}
return val.holder;
}
- static class EntryKey implements Serializable {
- static final long serialVersionUID = 1L;
-
- private transient String name;
-
- EntryKey(Project.NameKey name) {
- this.name = name.get();
- }
-
- @Override
- public int hashCode() {
- return name.hashCode();
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof EntryKey) {
- return name.equals(((EntryKey) o).name);
- }
- return false;
- }
-
- private void readObject(ObjectInputStream in) throws IOException {
- name = in.readUTF();
- }
-
- private void writeObject(ObjectOutputStream out) throws IOException {
- out.writeUTF(name);
- }
+ void put(Project.NameKey name, TagSetHolder tags) {
+ EntryVal val = new EntryVal();
+ val.holder = tags;
+ cache.put(name.get(), val);
}
static class EntryVal implements Serializable {
- static final long serialVersionUID = EntryKey.serialVersionUID;
+ static final long serialVersionUID = 1L;
transient TagSetHolder holder;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/TagMatcher.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/TagMatcher.java
index 6cf873d..7d95db2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/TagMatcher.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/TagMatcher.java
@@ -30,15 +30,22 @@
final List<Ref> newRefs = new ArrayList<Ref>();
final List<LostRef> lostRefs = new ArrayList<LostRef>();
final TagSetHolder holder;
+ final TagCache cache;
final Repository db;
final Collection<Ref> include;
TagSet tags;
- boolean updated;
+ final boolean updated;
private boolean rebuiltForNewTags;
- TagMatcher(TagSetHolder holder, Repository db, Collection<Ref> include,
- TagSet tags, boolean updated) {
+ TagMatcher(
+ TagSetHolder holder,
+ TagCache cache,
+ Repository db,
+ Collection<Ref> include,
+ TagSet tags,
+ boolean updated) {
this.holder = holder;
+ this.cache = cache;
this.db = db;
this.include = include;
this.tags = tags;
@@ -63,7 +70,7 @@
}
rebuiltForNewTags = true;
- holder.rebuildForNewTags(this);
+ holder.rebuildForNewTags(cache, this);
return isReachable(tagRef);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/TagSet.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/TagSet.java
index 8830580..c57942c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/TagSet.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/TagSet.java
@@ -58,7 +58,7 @@
return tags.get(id);
}
- void updateFastForward(String refName, ObjectId oldValue,
+ boolean updateFastForward(String refName, ObjectId oldValue,
ObjectId newValue) {
CachedRef ref = refs.get(refName);
if (ref != null) {
@@ -68,9 +68,10 @@
//
ObjectId cur = ref.get();
if (cur.equals(oldValue)) {
- ref.compareAndSet(cur, newValue);
+ return ref.compareAndSet(cur, newValue);
}
}
+ return false;
}
void prepare(TagMatcher m) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/TagSetHolder.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/TagSetHolder.java
index 91c8a5c..d5120e0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/TagSetHolder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/TagSetHolder.java
@@ -42,51 +42,52 @@
this.tags = tags;
}
- TagMatcher matcher(Repository db, Collection<Ref> include) {
+ TagMatcher matcher(TagCache cache, Repository db, Collection<Ref> include) {
TagSet tags = this.tags;
if (tags == null) {
- tags = build(db);
+ tags = build(cache, db);
}
- TagMatcher m = new TagMatcher(this, db, include, tags, false);
+ TagMatcher m = new TagMatcher(this, cache, db, include, tags, false);
tags.prepare(m);
if (!m.newRefs.isEmpty() || !m.lostRefs.isEmpty()) {
- tags = rebuild(db, tags, m);
+ tags = rebuild(cache, db, tags, m);
- m = new TagMatcher(this, db, include, tags, true);
+ m = new TagMatcher(this, cache, db, include, tags, true);
tags.prepare(m);
}
return m;
}
- void rebuildForNewTags(TagMatcher m) {
- m.tags = rebuild(m.db, m.tags, null);
-
+ void rebuildForNewTags(TagCache cache, TagMatcher m) {
+ m.tags = rebuild(cache, m.db, m.tags, null);
m.mask.clear();
m.newRefs.clear();
m.lostRefs.clear();
m.tags.prepare(m);
}
- private TagSet build(Repository db) {
+ private TagSet build(TagCache cache, Repository db) {
synchronized (buildLock) {
TagSet tags = this.tags;
if (tags == null) {
tags = new TagSet(projectName);
tags.build(db, null, null);
this.tags = tags;
+ cache.put(projectName, this);
}
return tags;
}
}
- private TagSet rebuild(Repository db, TagSet old, TagMatcher m) {
+ private TagSet rebuild(TagCache cache, Repository db, TagSet old, TagMatcher m) {
synchronized (buildLock) {
TagSet cur = this.tags;
if (cur == old) {
cur = new TagSet(projectName);
cur.build(db, old, m);
this.tags = cur;
+ cache.put(projectName, this);
}
return cur;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/VisibleRefFilter.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/VisibleRefFilter.java
index fc47f10..bf0d75d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/VisibleRefFilter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/VisibleRefFilter.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.git;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.Project;
@@ -60,6 +62,13 @@
}
public Map<String, Ref> filter(Map<String, Ref> refs, boolean filterTagsSeperately) {
+ if (projectCtl.allRefsAreVisibleExcept(
+ ImmutableSet.of(GitRepositoryManager.REF_CONFIG))) {
+ Map<String, Ref> r = Maps.newHashMap(refs);
+ r.remove(GitRepositoryManager.REF_CONFIG);
+ return r;
+ }
+
final Set<Change.Id> visibleChanges = visibleChanges();
final Map<String, Ref> result = new HashMap<String, Ref>();
final List<Ref> deferredTags = new ArrayList<Ref>();
@@ -92,8 +101,10 @@
// to identify what tags we can actually reach, and what we cannot.
//
if (!deferredTags.isEmpty() && (!result.isEmpty() || filterTagsSeperately)) {
- TagMatcher tags = tagCache.get(projectName).
- matcher(db, filterTagsSeperately ? filter(db.getAllRefs()).values() : result.values());
+ TagMatcher tags = tagCache.get(projectName).matcher(
+ tagCache,
+ db,
+ filterTagsSeperately ? filter(db.getAllRefs()).values() : result.values());
for (Ref tag : deferredTags) {
if (tags.isReachable(tag)) {
result.put(tag.getName(), tag);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ioutil/ColumnFormatter.java b/gerrit-server/src/main/java/com/google/gerrit/server/ioutil/ColumnFormatter.java
new file mode 100644
index 0000000..a73f1cb
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ioutil/ColumnFormatter.java
@@ -0,0 +1,82 @@
+// Copyright (C) 2012 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.ioutil;
+
+import com.google.gerrit.server.StringUtil;
+
+import java.io.PrintWriter;
+
+/**
+ * Simple output formatter for column-oriented data, writing its output to
+ * a {@link java.io.PrintWriter} object. Handles escaping of the column
+ * data so that the resulting output is unambiguous and reasonably safe and
+ * machine parsable.
+ */
+public class ColumnFormatter {
+ private char columnSeparator;
+ private boolean firstColumn;
+ private final PrintWriter out;
+
+ /**
+ * @param out The writer to which output should be sent.
+ * @param columnSeparator A character that should serve as the separator
+ * token between columns of output. As only non-printable characters
+ * in the column text are ever escaped, the column separator must be
+ * a non-printable character if the output needs to be unambiguously
+ * parsed.
+ */
+ public ColumnFormatter(final PrintWriter out, final char columnSeparator) {
+ this.out = out;
+ this.columnSeparator = columnSeparator;
+ this.firstColumn = true;
+ }
+
+ /**
+ * Adds a text string as a new column in the current line of output,
+ * taking care of escaping as necessary.
+ *
+ * @param content the string to add.
+ */
+ public void addColumn(final String content) {
+ if (!firstColumn) {
+ out.print(columnSeparator);
+ }
+ out.print(StringUtil.escapeString(content));
+ firstColumn = false;
+ }
+
+ /**
+ * Finishes the output by flushing the current line and takes care of any
+ * other cleanup action.
+ */
+ public void finish() {
+ nextLine();
+ out.flush();
+ }
+
+ /**
+ * Flushes the current line of output and makes the formatter ready to
+ * start receiving new column data for a new line (or end-of-file).
+ * If the current line is empty nothing is done, i.e. consecutive calls
+ * to this method without intervening calls to {@link #addColumn} will
+ * be squashed.
+ */
+ public void nextLine() {
+ if (!firstColumn) {
+ out.print('\n');
+ firstColumn = true;
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java
index e1a1725..31c8bd5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java
@@ -16,6 +16,7 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
+import com.google.gerrit.common.data.GroupDescriptions;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
@@ -35,6 +36,7 @@
import com.google.gerrit.server.git.NotifyConfig;
import com.google.gerrit.server.patch.PatchList;
import com.google.gerrit.server.patch.PatchListEntry;
+import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.Predicate;
@@ -270,13 +272,12 @@
}
}
-
/** Get the patch list corresponding to this patch set. */
- protected PatchList getPatchList() {
+ protected PatchList getPatchList() throws PatchListNotAvailableException {
if (patchSet != null) {
return args.patchListCache.get(change, patchSet);
}
- return null;
+ throw new PatchListNotAvailableException("no patchSet specified");
}
/** Get the project entity the change is in; null if its been deleted. */
@@ -387,7 +388,8 @@
private void add(Watchers matching, NotifyConfig nc, Project.NameKey project)
throws OrmException, QueryParseException {
for (GroupReference ref : nc.getGroups()) {
- AccountGroup group = args.groupCache.get(ref.getUUID());
+ AccountGroup group =
+ GroupDescriptions.toAccountGroup(args.groupBackend.get(ref.getUUID()));
if (group == null) {
log.warn(String.format(
"Project %s has invalid group %s in notify section %s",
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java
index eaf9eee..e7cc1ff 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java
@@ -21,6 +21,7 @@
import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.patch.PatchFile;
import com.google.gerrit.server.patch.PatchList;
+import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -77,11 +78,22 @@
}
public String getInlineComments() {
+ return getInlineComments(1);
+ }
+
+ public String getInlineComments(int lines) {
StringBuilder cmts = new StringBuilder();
final Repository repo = getRepository();
try {
- final PatchList patchList = repo != null ? getPatchList() : null;
+ PatchList patchList = null;
+ if (repo != null) {
+ try {
+ patchList = getPatchList();
+ } catch (PatchListNotAvailableException e) {
+ patchList = null;
+ }
+ }
Patch.Key currentFileKey = null;
PatchFile currentFileData = null;
@@ -113,19 +125,29 @@
}
}
- cmts.append("Line " + lineNbr);
if (currentFileData != null) {
+ int maxLines;
try {
- final String lineStr = currentFileData.getLine(side, lineNbr);
- cmts.append(": ");
- cmts.append(lineStr);
- } catch (Throwable cce) {
- // Don't quote the line if we can't safely convert it.
+ maxLines = currentFileData.getLineCount(side);
+ } catch (Throwable e) {
+ maxLines = lineNbr;
+ }
+
+ final int startLine = Math.max(1, lineNbr - lines + 1);
+ final int stopLine = Math.min(maxLines, lineNbr + lines);
+
+ for (int line = startLine; line <= lineNbr; ++line) {
+ appendFileLine(cmts, currentFileData, side, line);
+ }
+
+ cmts.append(c.getMessage().trim());
+ cmts.append("\n");
+
+ for (int line = lineNbr + 1; line < stopLine; ++line) {
+ appendFileLine(cmts, currentFileData, side, line);
}
}
- cmts.append("\n");
- cmts.append(c.getMessage().trim());
cmts.append("\n\n");
}
} finally {
@@ -136,6 +158,18 @@
return cmts.toString();
}
+ private void appendFileLine(StringBuilder cmts, PatchFile fileData, short side, int line) {
+ cmts.append("Line " + line);
+ try {
+ final String lineStr = fileData.getLine(side, line);
+ cmts.append(": ");
+ cmts.append(lineStr);
+ } catch (Throwable e) {
+ // Don't quote the line if we can't safely convert it.
+ }
+ cmts.append("\n");
+ }
+
private Repository getRepository() {
try {
return args.server.openRepository(projectState.getProject().getNameKey());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailArguments.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailArguments.java
index fa49b06..e6c96a3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailArguments.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailArguments.java
@@ -20,7 +20,7 @@
import com.google.gerrit.server.IdentifiedUser.GenericFactory;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.CapabilityControl;
-import com.google.gerrit.server.account.GroupCache;
+import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -39,7 +39,7 @@
class EmailArguments {
final GitRepositoryManager server;
final ProjectCache projectCache;
- final GroupCache groupCache;
+ final GroupBackend groupBackend;
final AccountCache accountCache;
final PatchListCache patchListCache;
final FromAddressGenerator fromAddressGenerator;
@@ -58,7 +58,7 @@
@Inject
EmailArguments(GitRepositoryManager server, ProjectCache projectCache,
- GroupCache groupCache, AccountCache accountCache,
+ GroupBackend groupBackend, AccountCache accountCache,
PatchListCache patchListCache, FromAddressGenerator fromAddressGenerator,
EmailSender emailSender, PatchSetInfoFactory patchSetInfoFactory,
GenericFactory identifiedUserFactory,
@@ -71,7 +71,7 @@
RuntimeInstance velocityRuntime) {
this.server = server;
this.projectCache = projectCache;
- this.groupCache = groupCache;
+ this.groupBackend = groupBackend;
this.accountCache = accountCache;
this.patchListCache = patchListCache;
this.fromAddressGenerator = fromAddressGenerator;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/NewChangeSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/NewChangeSender.java
index 2e459d4..f1f83bb 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/NewChangeSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/NewChangeSender.java
@@ -16,10 +16,22 @@
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.server.patch.PatchList;
+import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.ssh.SshInfo;
import com.jcraft.jsch.HostKey;
+import org.eclipse.jgit.diff.DiffFormatter;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.util.RawParseUtils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
@@ -28,6 +40,9 @@
/** Sends an email alerting a user to a new change for them to review. */
public abstract class NewChangeSender extends ChangeEmail {
+ private static final Logger log =
+ LoggerFactory.getLogger(NewChangeSender.class);
+
private final SshInfo sshInfo;
private final Set<Account.Id> reviewers = new HashSet<Account.Id>();
private final Set<Account.Id> extraCC = new HashSet<Account.Id>();
@@ -85,4 +100,41 @@
}
return host;
}
+
+ /** Show patch set as unified difference. */
+ public String getUnifiedDiff() {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ Repository repo = getRepository();
+ if (repo != null) {
+ DiffFormatter df = new DiffFormatter(out);
+ try {
+ PatchList patchList = getPatchList();
+ if (patchList.getOldId() != null) {
+ df.setRepository(repo);
+ df.setDetectRenames(true);
+ df.format(patchList.getOldId(), patchList.getNewId());
+ }
+ } catch (PatchListNotAvailableException e) {
+ log.error("Cannot format patch", e);
+ } catch (IOException e) {
+ log.error("Cannot format patch", e);
+ } finally {
+ df.release();
+ repo.close();
+ }
+ }
+ return RawParseUtils.decode(out.toByteArray());
+ }
+
+ private Repository getRepository() {
+ try {
+ return args.server.openRepository(change.getProject());
+ } catch (RepositoryNotFoundException e) {
+ log.error("Cannot open repository", e);
+ return null;
+ } catch (IOException e) {
+ log.error("Cannot open repository", e);
+ return null;
+ }
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/IntraLineDiffKey.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/IntraLineDiffKey.java
index c5c5925..08af5e7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/IntraLineDiffKey.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/IntraLineDiffKey.java
@@ -70,11 +70,11 @@
return edits;
}
- ObjectId getBlobA() {
+ public ObjectId getBlobA() {
return aId;
}
- ObjectId getBlobB() {
+ public ObjectId getBlobB() {
return bId;
}
@@ -114,6 +114,9 @@
public String toString() {
StringBuilder n = new StringBuilder();
n.append("IntraLineDiffKey[");
+ if (projectKey != null) {
+ n.append(projectKey.get()).append(" ");
+ }
n.append(aId.name());
n.append("..");
n.append(bId.name());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/IntraLineLoader.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/IntraLineLoader.java
index 358d3ba..5b65920 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/IntraLineLoader.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/IntraLineLoader.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.patch;
-import com.google.gerrit.server.cache.EntryCreator;
+import com.google.common.cache.CacheLoader;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.inject.Inject;
@@ -35,9 +35,8 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
-class IntraLineLoader extends EntryCreator<IntraLineDiffKey, IntraLineDiff> {
- private static final Logger log = LoggerFactory
- .getLogger(IntraLineLoader.class);
+class IntraLineLoader extends CacheLoader<IntraLineDiffKey, IntraLineDiff> {
+ static final Logger log = LoggerFactory.getLogger(IntraLineLoader.class);
private static final Pattern BLANK_LINE_RE = Pattern
.compile("^[ \\t]*(|[{}]|/\\*\\*?|\\*)[ \\t]*$");
@@ -62,7 +61,7 @@
}
@Override
- public IntraLineDiff createEntry(IntraLineDiffKey key) throws Exception {
+ public IntraLineDiff load(IntraLineDiffKey key) throws Exception {
Worker w = workerPool.poll();
if (w == null) {
w = new Worker();
@@ -119,7 +118,7 @@
throws Exception {
if (!input.offer(new Input(key))) {
log.error("Cannot enqueue task to thread " + thread.getName());
- return null;
+ return Result.TIMEOUT;
}
Result r = result.poll(timeoutMillis, TimeUnit.MILLISECONDS);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/IntraLineWeigher.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/IntraLineWeigher.java
new file mode 100644
index 0000000..f6cff15
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/IntraLineWeigher.java
@@ -0,0 +1,28 @@
+// Copyright (C) 2012 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.patch;
+
+import com.google.common.cache.Weigher;
+
+/** Approximates memory usage for IntralineDiff in bytes of memory used. */
+public class IntraLineWeigher implements
+ Weigher<IntraLineDiffKey, IntraLineDiff> {
+ @Override
+ public int weigh(IntraLineDiffKey key, IntraLineDiff value) {
+ return 16 + 8*8 + 2*36 // Size of IntraLineDiffKey, 64 bit JVM
+ + 16 + 2*8 + 16+8+4+20 // Size of IntraLineDiff, 64 bit JVM
+ + (8 + 16 + 4*4) * value.getEdits().size();
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchFile.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchFile.java
index f120ebf..6fcf581 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchFile.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchFile.java
@@ -111,6 +111,35 @@
}
}
+ /**
+ * Return number of lines in file.
+ *
+ * @param file the file index to extract.
+ * @return number of lines in file.
+ * @throws CorruptEntityException the patch cannot be read.
+ * @throws IOException the patch or complete file content cannot be read.
+ * @throws NoSuchEntityException the file is not exist.
+ */
+ public int getLineCount(final int file)
+ throws CorruptEntityException, IOException, NoSuchEntityException {
+ switch (file) {
+ case 0:
+ if (a == null) {
+ a = load(aTree, entry.getOldName());
+ }
+ return a.size();
+
+ case 1:
+ if (b == null) {
+ b = load(bTree, entry.getNewName());
+ }
+ return b.size();
+
+ default:
+ throw new NoSuchEntityException();
+ }
+ }
+
private Text load(final ObjectId tree, final String path)
throws MissingObjectException, IncorrectObjectTypeException,
CorruptObjectException, IOException {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchList.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchList.java
index aab8e39..93d7bf7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchList.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchList.java
@@ -142,8 +142,12 @@
}
private int search(final String fileName) {
+ if (Patch.COMMIT_MSG.equals(fileName)) {
+ return 0;
+ }
+
int high = patches.length;
- int low = 0;
+ int low = 1;
while (low < high) {
final int mid = (low + high) >>> 1;
final int cmp = patches[mid].getNewName().compareTo(fileName);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCache.java
index 8a61d30..fe77f5d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCache.java
@@ -19,9 +19,10 @@
/** Provides a cached list of {@link PatchListEntry}. */
public interface PatchListCache {
- public PatchList get(PatchListKey key);
+ public PatchList get(PatchListKey key) throws PatchListNotAvailableException;
- public PatchList get(Change change, PatchSet patchSet);
+ public PatchList get(Change change, PatchSet patchSet)
+ throws PatchListNotAvailableException;
public IntraLineDiff getIntraLineDiff(IntraLineDiffKey key);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCacheImpl.java
index 26dbe2d..967e6a7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCacheImpl.java
@@ -15,24 +15,23 @@
package com.google.gerrit.server.patch;
-
+import com.google.common.cache.LoadingCache;
+import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace;
-import com.google.gerrit.server.cache.Cache;
import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.cache.EvictionPolicy;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.inject.Inject;
import com.google.inject.Module;
import com.google.inject.Singleton;
-import com.google.inject.TypeLiteral;
import com.google.inject.name.Named;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
+import java.util.concurrent.ExecutionException;
+
/** Provides a cached list of {@link PatchListEntry}. */
@Singleton
public class PatchListCacheImpl implements PatchListCache {
@@ -43,21 +42,15 @@
return new CacheModule() {
@Override
protected void configure() {
- final TypeLiteral<Cache<PatchListKey, PatchList>> fileType =
- new TypeLiteral<Cache<PatchListKey, PatchList>>() {};
- disk(fileType, FILE_NAME) //
- .memoryLimit(128) // very large items, cache only a few
- .evictionPolicy(EvictionPolicy.LRU) // prefer most recent
- .populateWith(PatchListLoader.class) //
- ;
+ persist(FILE_NAME, PatchListKey.class, PatchList.class)
+ .maximumWeight(10 << 20)
+ .loader(PatchListLoader.class)
+ .weigher(PatchListWeigher.class);
- final TypeLiteral<Cache<IntraLineDiffKey, IntraLineDiff>> intraType =
- new TypeLiteral<Cache<IntraLineDiffKey, IntraLineDiff>>() {};
- disk(intraType, INTRA_NAME) //
- .memoryLimit(128) // very large items, cache only a few
- .evictionPolicy(EvictionPolicy.LRU) // prefer most recent
- .populateWith(IntraLineLoader.class) //
- ;
+ persist(INTRA_NAME, IntraLineDiffKey.class, IntraLineDiff.class)
+ .maximumWeight(10 << 20)
+ .loader(IntraLineLoader.class)
+ .weigher(IntraLineWeigher.class);
bind(PatchListCacheImpl.class);
bind(PatchListCache.class).to(PatchListCacheImpl.class);
@@ -65,14 +58,14 @@
};
}
- private final Cache<PatchListKey, PatchList> fileCache;
- private final Cache<IntraLineDiffKey, IntraLineDiff> intraCache;
+ private final LoadingCache<PatchListKey, PatchList> fileCache;
+ private final LoadingCache<IntraLineDiffKey, IntraLineDiff> intraCache;
private final boolean computeIntraline;
@Inject
PatchListCacheImpl(
- @Named(FILE_NAME) final Cache<PatchListKey, PatchList> fileCache,
- @Named(INTRA_NAME) final Cache<IntraLineDiffKey, IntraLineDiff> intraCache,
+ @Named(FILE_NAME) LoadingCache<PatchListKey, PatchList> fileCache,
+ @Named(INTRA_NAME) LoadingCache<IntraLineDiffKey, IntraLineDiff> intraCache,
@GerritServerConfig Config cfg) {
this.fileCache = fileCache;
this.intraCache = intraCache;
@@ -82,11 +75,19 @@
cfg.getBoolean("cache", "diff", "intraline", true));
}
- public PatchList get(final PatchListKey key) {
- return fileCache.get(key);
+ @Override
+ public PatchList get(PatchListKey key) throws PatchListNotAvailableException {
+ try {
+ return fileCache.get(key);
+ } catch (ExecutionException e) {
+ PatchListLoader.log.warn("Error computing " + key, e);
+ throw new PatchListNotAvailableException(e.getCause());
+ }
}
- public PatchList get(final Change change, final PatchSet patchSet) {
+ @Override
+ public PatchList get(final Change change, final PatchSet patchSet)
+ throws PatchListNotAvailableException {
final Project.NameKey projectKey = change.getProject();
final ObjectId a = null;
final ObjectId b = ObjectId.fromString(patchSet.getRevision().get());
@@ -97,11 +98,12 @@
@Override
public IntraLineDiff getIntraLineDiff(IntraLineDiffKey key) {
if (computeIntraline) {
- IntraLineDiff d = intraCache.get(key);
- if (d == null) {
- d = new IntraLineDiff(IntraLineDiff.Status.ERROR);
+ try {
+ return intraCache.get(key);
+ } catch (ExecutionException e) {
+ IntraLineLoader.log.warn("Error computing " + key, e);
+ return new IntraLineDiff(IntraLineDiff.Status.ERROR);
}
- return d;
} else {
return new IntraLineDiff(IntraLineDiff.Status.DISABLED);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListEntry.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListEntry.java
index 33ed54e..ff9e6cf 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListEntry.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListEntry.java
@@ -122,6 +122,22 @@
this.deletions = deletions;
}
+ int weigh() {
+ int size = 16 + 6*8 + 2*4 + 20 + 16+8+4+20;
+ size += stringSize(oldName);
+ size += stringSize(newName);
+ size += header.length;
+ size += (8 + 16 + 4*4) * edits.size();
+ return size;
+ }
+
+ private static int stringSize(String str) {
+ if (str != null) {
+ return 16 + 3*4 + 16 + str.length() * 2;
+ }
+ return 0;
+ }
+
public ChangeType getChangeType() {
return changeType;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListLoader.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListLoader.java
index 5bba42b..d6e84bb 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListLoader.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListLoader.java
@@ -15,9 +15,9 @@
package com.google.gerrit.server.patch;
-import com.google.gerrit.reviewdb.client.Patch;
+import com.google.common.cache.CacheLoader;
import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace;
-import com.google.gerrit.server.cache.EntryCreator;
+import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.inject.Inject;
@@ -54,6 +54,8 @@
import org.eclipse.jgit.treewalk.filter.TreeFilter;
import org.eclipse.jgit.util.TemporaryBuffer;
import org.eclipse.jgit.util.io.DisabledOutputStream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
@@ -62,7 +64,9 @@
import java.util.List;
import java.util.Map;
-class PatchListLoader extends EntryCreator<PatchListKey, PatchList> {
+class PatchListLoader extends CacheLoader<PatchListKey, PatchList> {
+ static final Logger log = LoggerFactory.getLogger(PatchListLoader.class);
+
private final GitRepositoryManager repoManager;
@Inject
@@ -71,7 +75,7 @@
}
@Override
- public PatchList createEntry(final PatchListKey key) throws Exception {
+ public PatchList load(final PatchListKey key) throws Exception {
final Repository repo = repoManager.openRepository(key.projectKey);
try {
return readPatchList(key, repo);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/IncompleteUserInfoException.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListNotAvailableException.java
similarity index 71%
rename from gerrit-server/src/main/java/com/google/gerrit/server/git/IncompleteUserInfoException.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListNotAvailableException.java
index 204d777..2ccc9f1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/IncompleteUserInfoException.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListNotAvailableException.java
@@ -12,12 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.git;
+package com.google.gerrit.server.patch;
-public class IncompleteUserInfoException extends Exception {
+public class PatchListNotAvailableException extends Exception {
private static final long serialVersionUID = 1L;
- public IncompleteUserInfoException(final String userName, final String missingInfo) {
- super("For the user \"" + userName + "\" " + missingInfo + " is not set.");
+ public PatchListNotAvailableException(String message) {
+ super(message);
+ }
+
+ public PatchListNotAvailableException(Throwable cause) {
+ super(cause);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListWeigher.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListWeigher.java
new file mode 100644
index 0000000..d715246
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListWeigher.java
@@ -0,0 +1,30 @@
+// Copyright (C) 2012 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.patch;
+
+import com.google.common.cache.Weigher;
+
+/** Approximates memory usage for PatchList in bytes of memory used. */
+public class PatchListWeigher implements Weigher<PatchListKey, PatchList> {
+ @Override
+ public int weigh(PatchListKey key, PatchList value) {
+ int size = 16 + 4*8 + 2*36 // Size of PatchListKey, 64 bit JVM
+ + 16 + 3*8 + 3*4 + 20; // Size of PatchList, 64 bit JVM
+ for (PatchListEntry e : value.getPatches()) {
+ size += e.weigh();
+ }
+ return size;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java
new file mode 100644
index 0000000..dca47e0
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java
@@ -0,0 +1,107 @@
+// Copyright (C) 2012 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.plugins;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.gerrit.server.OutputFormat;
+import com.google.gson.reflect.TypeToken;
+import com.google.inject.Inject;
+
+import org.kohsuke.args4j.Option;
+
+import java.io.BufferedWriter;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+
+/** List projects visible to the calling user. */
+public class ListPlugins {
+ private final PluginLoader pluginLoader;
+
+ @Option(name = "--format", metaVar = "FMT", usage = "Output display format")
+ private OutputFormat format = OutputFormat.TEXT;
+
+ @Inject
+ protected ListPlugins(PluginLoader pluginLoader) {
+ this.pluginLoader = pluginLoader;
+ }
+
+ public OutputFormat getFormat() {
+ return format;
+ }
+
+ public ListPlugins setFormat(OutputFormat fmt) {
+ this.format = fmt;
+ return this;
+ }
+
+ public void display(OutputStream out) {
+ final PrintWriter stdout;
+ try {
+ stdout =
+ new PrintWriter(new BufferedWriter(new OutputStreamWriter(out,
+ "UTF-8")));
+ } catch (UnsupportedEncodingException e) {
+ // Our encoding is required by the specifications for the runtime.
+ throw new RuntimeException("JVM lacks UTF-8 encoding", e);
+ }
+
+ Map<String, PluginInfo> output = Maps.newTreeMap();
+
+ List<Plugin> plugins = Lists.newArrayList(pluginLoader.getPlugins());
+ Collections.sort(plugins, new Comparator<Plugin>() {
+ @Override
+ public int compare(Plugin a, Plugin b) {
+ return a.getName().compareTo(b.getName());
+ }
+ });
+
+ if (!format.isJson()) {
+ stdout.format("%-30s %-10s\n", "Name", "Version");
+ stdout
+ .print("----------------------------------------------------------------------\n");
+ }
+
+ for (Plugin p : plugins) {
+ PluginInfo info = new PluginInfo();
+ info.version = p.getVersion();
+
+ if (format.isJson()) {
+ output.put(p.getName(), info);
+ } else {
+ stdout.format("%-30s %-10s\n", p.getName(),
+ Strings.nullToEmpty(info.version));
+ }
+ }
+
+ if (format.isJson()) {
+ format.newGson().toJson(output,
+ new TypeToken<Map<String, PluginInfo>>() {}.getType(), stdout);
+ stdout.print('\n');
+ }
+ stdout.flush();
+ }
+
+ private static class PluginInfo {
+ String version;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginGuiceEnvironment.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginGuiceEnvironment.java
index 3aa259e..f16131c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginGuiceEnvironment.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginGuiceEnvironment.java
@@ -385,8 +385,15 @@
final Map<Key<?>, Binding<?>> bindings = Maps.newLinkedHashMap();
for (Map.Entry<Key<?>, Binding<?>> e : src.getBindings().entrySet()) {
- if (!dynamicTypes.contains(e.getKey().getTypeLiteral())
- && shouldCopy(e.getKey())) {
+ if (dynamicTypes.contains(e.getKey().getTypeLiteral())
+ && e.getKey().getAnnotation() != null) {
+ // A type used in DynamicSet or DynamicMap that has an annotation
+ // must be picked up by the set/map itself. A type used in either
+ // but without an annotation may be magic glue implementing F and
+ // using DynamicSet<F> or DynamicMap<F> internally. That should be
+ // exported to plugins.
+ continue;
+ } else if (shouldCopy(e.getKey())) {
bindings.put(e.getKey(), e.getValue());
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
index f232c5c..64f7d97 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
@@ -292,13 +292,17 @@
return false;
}
+ public List<SubmitRecord> getSubmitRecords(ReviewDb db, PatchSet patchSet) {
+ return canSubmit(db, patchSet, null, false, true);
+ }
+
public List<SubmitRecord> canSubmit(ReviewDb db, PatchSet patchSet) {
- return canSubmit(db, patchSet, null, false);
+ return canSubmit(db, patchSet, null, false, false);
}
public List<SubmitRecord> canSubmit(ReviewDb db, PatchSet patchSet,
- @Nullable ChangeData cd, boolean fastEvalLabels) {
- if (change.getStatus().isClosed()) {
+ @Nullable ChangeData cd, boolean fastEvalLabels, boolean allowClosed) {
+ if (!allowClosed && change.getStatus().isClosed()) {
SubmitRecord rec = new SubmitRecord();
rec.status = SubmitRecord.Status.CLOSED;
return Collections.singletonList(rec);
@@ -495,6 +499,9 @@
} else if ("need".equals(status.name())) {
lbl.status = SubmitRecord.Label.Status.NEED;
+ } else if ("may".equals(status.name())) {
+ lbl.status = SubmitRecord.Label.Status.MAY;
+
} else if ("impossible".equals(status.name())) {
lbl.status = SubmitRecord.Label.Status.IMPOSSIBLE;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java
index 577a92d..879f772 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProject.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.project;
import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.data.GroupDescription;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRule;
@@ -26,7 +27,7 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.GroupCache;
+import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.config.ProjectOwnerGroups;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -51,6 +52,8 @@
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.Set;
@@ -71,7 +74,7 @@
private final PersonIdent serverIdent;
private CreateProjectArgs createProjectArgs;
private ProjectCache projectCache;
- private GroupCache groupCache;
+ private GroupBackend groupBackend;
private MetaDataUpdate.User metaDataUpdateFactory;
@Inject
@@ -80,8 +83,8 @@
GitReferenceUpdated referenceUpdated,
DynamicSet<NewProjectCreatedListener> createdListener,
ReviewDb db,
- @GerritPersonIdent PersonIdent personIdent, final GroupCache groupCache,
- final MetaDataUpdate.User metaDataUpdateFactory,
+ @GerritPersonIdent PersonIdent personIdent, GroupBackend groupBackend,
+ MetaDataUpdate.User metaDataUpdateFactory,
@Assisted CreateProjectArgs createPArgs, ProjectCache pCache) {
this.projectOwnerGroups = pOwnerGroups;
this.currentUser = identifiedUser;
@@ -91,7 +94,7 @@
this.serverIdent = personIdent;
this.createProjectArgs = createPArgs;
this.projectCache = pCache;
- this.groupCache = groupCache;
+ this.groupBackend = groupBackend;
this.metaDataUpdateFactory = metaDataUpdateFactory;
}
@@ -101,7 +104,7 @@
try {
final String head =
createProjectArgs.permissionsOnly ? GitRepositoryManager.REF_CONFIG
- : createProjectArgs.branch;
+ : createProjectArgs.branch.get(0);
final Repository repo = repoManager.createRepository(nameKey);
try {
NewProjectCreatedListener.Event event = new NewProjectCreatedListener.Event() {
@@ -127,7 +130,7 @@
if (!createProjectArgs.permissionsOnly
&& createProjectArgs.createEmptyCommit) {
- createEmptyCommit(repo, nameKey, createProjectArgs.branch);
+ createEmptyCommits(repo, nameKey, createProjectArgs.branch);
}
} finally {
repo.close();
@@ -185,9 +188,9 @@
final AccessSection all =
config.getAccessSection(AccessSection.ALL, true);
for (AccountGroup.UUID ownerId : createProjectArgs.ownerIds) {
- AccountGroup accountGroup = groupCache.get(ownerId);
- if (accountGroup != null) {
- GroupReference group = config.resolve(accountGroup);
+ GroupDescription.Basic g = groupBackend.get(ownerId);
+ if (g != null) {
+ GroupReference group = config.resolve(GroupReference.forGroup(g));
all.getPermission(Permission.OWNER, true).add(
new PermissionRule(group));
}
@@ -235,20 +238,32 @@
new ArrayList<AccountGroup.UUID>(projectOwnerGroups);
}
- while (createProjectArgs.branch.startsWith("/")) {
- createProjectArgs.branch = createProjectArgs.branch.substring(1);
+ List<String> transformedBranches = new ArrayList<String>();
+ if (createProjectArgs.branch == null ||
+ createProjectArgs.branch.isEmpty()) {
+ createProjectArgs.branch = Collections.singletonList(Constants.MASTER);
}
- if (!createProjectArgs.branch.startsWith(Constants.R_HEADS)) {
- createProjectArgs.branch = Constants.R_HEADS + createProjectArgs.branch;
+ for (String branch : createProjectArgs.branch) {
+ while (branch.startsWith("/")) {
+ branch = branch.substring(1);
+ }
+ if (!branch.startsWith(Constants.R_HEADS)) {
+ branch = Constants.R_HEADS + branch;
+ }
+ if (!Repository.isValidRefName(branch)) {
+ throw new ProjectCreationFailedException(String.format(
+ "Branch \"%s\" is not a valid name.", branch));
+ }
+ if (!transformedBranches.contains(branch)) {
+ transformedBranches.add(branch);
+ }
}
- if (!Repository.isValidRefName(createProjectArgs.branch)) {
- throw new ProjectCreationFailedException(String.format(
- "Branch \"%s\" is not a valid name.", createProjectArgs.branch));
- }
+ createProjectArgs.branch = transformedBranches;
}
- private void createEmptyCommit(final Repository repo,
- final Project.NameKey project, final String ref) throws IOException {
+ private void createEmptyCommits(final Repository repo,
+ final Project.NameKey project, final List<String> refs)
+ throws IOException {
ObjectInserter oi = repo.newObjectInserter();
try {
CommitBuilder cb = new CommitBuilder();
@@ -260,15 +275,18 @@
ObjectId id = oi.insert(cb);
oi.flush();
- RefUpdate ru = repo.updateRef(Constants.HEAD);
- ru.setNewObjectId(id);
- final Result result = ru.update();
- switch (result) {
- case NEW:
- referenceUpdated.fire(project, ref);
- break;
- default: {
- throw new IOException(result.name());
+ for (String ref : refs) {
+ RefUpdate ru = repo.updateRef(ref);
+ ru.setNewObjectId(id);
+ final Result result = ru.update();
+ switch (result) {
+ case NEW:
+ referenceUpdated.fire(project, ref);
+ break;
+ default: {
+ throw new IOException(String.format(
+ "Failed to create ref \"%s\": %s", ref, result.name()));
+ }
}
}
} catch (IOException e) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProjectArgs.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProjectArgs.java
index 98adf85..2dee4f4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProjectArgs.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateProjectArgs.java
@@ -30,7 +30,7 @@
public boolean contributorAgreements;
public boolean signedOffBy;
public boolean permissionsOnly;
- public String branch;
+ public List<String> branch;
public boolean contentMerge;
public boolean changeIdRequired;
public boolean createEmptyCommit;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java
index 716a5a8..8b4e000 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java
@@ -19,6 +19,7 @@
import com.google.gerrit.reviewdb.client.Project.NameKey;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.OutputFormat;
+import com.google.gerrit.server.StringUtil;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.util.TreeFormatter;
import com.google.gson.reflect.TypeToken;
@@ -269,7 +270,7 @@
if (info.description != null) {
// We still want to list every project as one-liners, hence escaping \n.
- stdout.print(" - " + info.description.replace("\n", "\\n"));
+ stdout.print(" - " + StringUtil.escapeString(info.description));
}
stdout.print('\n');
}
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 3b1f55c..cb18398 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
@@ -14,10 +14,11 @@
package com.google.gerrit.server.project;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.Sets;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.cache.Cache;
import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.cache.EntryCreator;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.ProjectConfig;
@@ -27,20 +28,24 @@
import com.google.inject.TypeLiteral;
import com.google.inject.name.Named;
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Repository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.SortedSet;
-import java.util.TreeSet;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/** Cache of project information, including access rights. */
@Singleton
public class ProjectCacheImpl implements ProjectCache {
+ private static final Logger log = LoggerFactory
+ .getLogger(ProjectCacheImpl.class);
+
private static final String CACHE_NAME = "projects";
private static final String CACHE_LIST = "project_list";
@@ -48,13 +53,14 @@
return new CacheModule() {
@Override
protected void configure() {
- final TypeLiteral<Cache<Project.NameKey, ProjectState>> nameType =
- new TypeLiteral<Cache<Project.NameKey, ProjectState>>() {};
- core(nameType, CACHE_NAME).populateWith(Loader.class);
+ cache(CACHE_NAME, String.class, ProjectState.class)
+ .loader(Loader.class);
- final TypeLiteral<Cache<ListKey, SortedSet<Project.NameKey>>> listType =
- new TypeLiteral<Cache<ListKey, SortedSet<Project.NameKey>>>() {};
- core(listType, CACHE_LIST).populateWith(Lister.class);
+ cache(CACHE_LIST,
+ ListKey.class,
+ new TypeLiteral<SortedSet<Project.NameKey>>() {})
+ .maximumWeight(1)
+ .loader(Lister.class);
bind(ProjectCacheImpl.class);
bind(ProjectCache.class).to(ProjectCacheImpl.class);
@@ -63,16 +69,16 @@
}
private final AllProjectsName allProjectsName;
- private final Cache<Project.NameKey, ProjectState> byName;
- private final Cache<ListKey,SortedSet<Project.NameKey>> list;
+ private final LoadingCache<String, ProjectState> byName;
+ private final LoadingCache<ListKey, SortedSet<Project.NameKey>> list;
private final Lock listLock;
private final ProjectCacheClock clock;
@Inject
ProjectCacheImpl(
final AllProjectsName allProjectsName,
- @Named(CACHE_NAME) final Cache<Project.NameKey, ProjectState> byName,
- @Named(CACHE_LIST) final Cache<ListKey, SortedSet<Project.NameKey>> list,
+ @Named(CACHE_NAME) LoadingCache<String, ProjectState> byName,
+ @Named(CACHE_LIST) LoadingCache<ListKey, SortedSet<Project.NameKey>> list,
ProjectCacheClock clock) {
this.allProjectsName = allProjectsName;
this.byName = byName;
@@ -99,18 +105,26 @@
* @return the cached data; null if no such project exists.
*/
public ProjectState get(final Project.NameKey projectName) {
- ProjectState state = byName.get(projectName);
- if (state != null && state.needsRefresh(clock.read())) {
- byName.remove(projectName);
- state = byName.get(projectName);
+ if (projectName == null) {
+ return null;
}
- return state;
+ try {
+ ProjectState state = byName.get(projectName.get());
+ if (state != null && state.needsRefresh(clock.read())) {
+ byName.invalidate(projectName.get());
+ state = byName.get(projectName.get());
+ }
+ return state;
+ } catch (ExecutionException e) {
+ log.warn(String.format("Cannot read project %s", projectName.get()), e);
+ return null;
+ }
}
/** Invalidate the cached information about the given project. */
public void evict(final Project p) {
if (p != null) {
- byName.remove(p.getNameKey());
+ byName.invalidate(p.getNameKey().get());
}
}
@@ -118,10 +132,11 @@
public void remove(final Project p) {
listLock.lock();
try {
- SortedSet<Project.NameKey> n = list.get(ListKey.ALL);
- n = new TreeSet<Project.NameKey>(n);
+ SortedSet<Project.NameKey> n = Sets.newTreeSet(list.get(ListKey.ALL));
n.remove(p.getNameKey());
list.put(ListKey.ALL, Collections.unmodifiableSortedSet(n));
+ } catch (ExecutionException e) {
+ log.warn("Cannot list avaliable projects", e);
} finally {
listLock.unlock();
}
@@ -132,10 +147,11 @@
public void onCreateProject(Project.NameKey newProjectName) {
listLock.lock();
try {
- SortedSet<Project.NameKey> n = list.get(ListKey.ALL);
- n = new TreeSet<Project.NameKey>(n);
+ SortedSet<Project.NameKey> n = Sets.newTreeSet(list.get(ListKey.ALL));
n.add(newProjectName);
list.put(ListKey.ALL, Collections.unmodifiableSortedSet(n));
+ } catch (ExecutionException e) {
+ log.warn("Cannot list avaliable projects", e);
} finally {
listLock.unlock();
}
@@ -143,18 +159,28 @@
@Override
public Iterable<Project.NameKey> all() {
- return list.get(ListKey.ALL);
+ try {
+ return list.get(ListKey.ALL);
+ } catch (ExecutionException e) {
+ log.warn("Cannot list available projects", e);
+ return Collections.emptyList();
+ }
}
@Override
public Iterable<Project.NameKey> byName(final String pfx) {
+ final Iterable<Project.NameKey> src;
+ try {
+ src = list.get(ListKey.ALL).tailSet(new Project.NameKey(pfx));
+ } catch (ExecutionException e) {
+ return Collections.emptyList();
+ }
return new Iterable<Project.NameKey>() {
@Override
public Iterator<Project.NameKey> iterator() {
return new Iterator<Project.NameKey>() {
+ private Iterator<Project.NameKey> itr = src.iterator();
private Project.NameKey next;
- private Iterator<Project.NameKey> itr =
- list.get(ListKey.ALL).tailSet(new Project.NameKey(pfx)).iterator();
@Override
public boolean hasNext() {
@@ -196,7 +222,7 @@
};
}
- static class Loader extends EntryCreator<Project.NameKey, ProjectState> {
+ static class Loader extends CacheLoader<String, ProjectState> {
private final ProjectState.Factory projectStateFactory;
private final GitRepositoryManager mgr;
@@ -207,19 +233,15 @@
}
@Override
- public ProjectState createEntry(Project.NameKey key) throws Exception {
+ public ProjectState load(String projectName) throws Exception {
+ Project.NameKey key = new Project.NameKey(projectName);
+ Repository git = mgr.openRepository(key);
try {
- Repository git = mgr.openRepository(key);
- try {
- final ProjectConfig cfg = new ProjectConfig(key);
- cfg.load(git);
- return projectStateFactory.create(cfg);
- } finally {
- git.close();
- }
-
- } catch (RepositoryNotFoundException notFound) {
- return null;
+ ProjectConfig cfg = new ProjectConfig(key);
+ cfg.load(git);
+ return projectStateFactory.create(cfg);
+ } finally {
+ git.close();
}
}
}
@@ -231,7 +253,7 @@
}
}
- static class Lister extends EntryCreator<ListKey, SortedSet<Project.NameKey>> {
+ static class Lister extends CacheLoader<ListKey, SortedSet<Project.NameKey>> {
private final GitRepositoryManager mgr;
@Inject
@@ -240,7 +262,7 @@
}
@Override
- public SortedSet<Project.NameKey> createEntry(ListKey key) throws Exception {
+ public SortedSet<Project.NameKey> load(ListKey key) throws Exception {
return mgr.list();
}
}
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 67d91d5..513f1b1 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
@@ -38,6 +38,7 @@
import com.google.inject.assistedinject.Assisted;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -196,8 +197,12 @@
/** Can this user see all the refs in this projects? */
public boolean allRefsAreVisible() {
+ return allRefsAreVisibleExcept(Collections.<String> emptySet());
+ }
+
+ public boolean allRefsAreVisibleExcept(Set<String> except) {
return user instanceof InternalUser
- || canPerformOnAllRefs(Permission.READ);
+ || canPerformOnAllRefs(Permission.READ, except);
}
/** Is this user a project owner? Ownership does not imply {@link #isVisible()} */
@@ -347,7 +352,7 @@
return false;
}
- private boolean canPerformOnAllRefs(String permission) {
+ private boolean canPerformOnAllRefs(String permission, Set<String> except) {
boolean canPerform = false;
Set<String> patterns = allRefPatterns(permission);
if (patterns.contains(AccessSection.ALL)) {
@@ -358,6 +363,8 @@
for (final String pattern : patterns) {
if (controlForRef(pattern).canPerform(permission)) {
canPerform = true;
+ } else if (except.contains(pattern)) {
+ continue;
} else {
return false;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SectionSortCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SectionSortCache.java
index 40d4290..686ff59 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SectionSortCache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SectionSortCache.java
@@ -14,14 +14,13 @@
package com.google.gerrit.server.project;
+import com.google.common.cache.Cache;
import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.server.cache.Cache;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.util.MostSpecificComparator;
import com.google.inject.Inject;
import com.google.inject.Module;
import com.google.inject.Singleton;
-import com.google.inject.TypeLiteral;
import com.google.inject.name.Named;
import java.util.Arrays;
@@ -38,9 +37,7 @@
return new CacheModule() {
@Override
protected void configure() {
- final TypeLiteral<Cache<EntryKey, EntryVal>> type =
- new TypeLiteral<Cache<EntryKey, EntryVal>>() {};
- core(type, CACHE_NAME);
+ cache(CACHE_NAME, EntryKey.class, EntryVal.class);
bind(SectionSortCache.class);
}
};
@@ -60,7 +57,7 @@
}
EntryKey key = new EntryKey(ref, sections);
- EntryVal val = cache.get(key);
+ EntryVal val = cache.getIfPresent(key);
if (val != null) {
int[] srcIdx = val.order;
if (srcIdx != null) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
index db3470e..d6762db 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -31,6 +31,7 @@
import com.google.gerrit.server.patch.PatchList;
import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.patch.PatchListEntry;
+import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.ResultSet;
@@ -142,7 +143,14 @@
return null;
}
- PatchList p = cache.get(c, ps);
+ PatchList p;
+ try {
+ p = cache.get(c, ps);
+ } catch (PatchListNotAvailableException e) {
+ currentFiles = new String[0];
+ return currentFiles;
+ }
+
List<String> r = new ArrayList<String>(p.getPatches().size());
for (PatchListEntry e : p.getPatches()) {
if (Patch.COMMIT_MSG.equals(e.getNewName())) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index 8c1157e..ddc4c28 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -16,6 +16,7 @@
import com.google.common.collect.Lists;
import com.google.gerrit.common.data.ApprovalTypes;
+import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Change;
@@ -26,7 +27,8 @@
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountResolver;
import com.google.gerrit.server.account.CapabilityControl;
-import com.google.gerrit.server.account.GroupCache;
+import com.google.gerrit.server.account.GroupBackend;
+import com.google.gerrit.server.account.GroupBackends;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.patch.PatchListCache;
@@ -105,7 +107,7 @@
final ChangeControl.Factory changeControlFactory;
final ChangeControl.GenericFactory changeControlGenericFactory;
final AccountResolver accountResolver;
- final GroupCache groupCache;
+ final GroupBackend groupBackend;
final ApprovalTypes approvalTypes;
final AllProjectsName allProjectsName;
final PatchListCache patchListCache;
@@ -119,7 +121,8 @@
CapabilityControl.Factory capabilityControlFactory,
ChangeControl.Factory changeControlFactory,
ChangeControl.GenericFactory changeControlGenericFactory,
- AccountResolver accountResolver, GroupCache groupCache,
+ AccountResolver accountResolver,
+ GroupBackend groupBackend,
ApprovalTypes approvalTypes,
AllProjectsName allProjectsName,
PatchListCache patchListCache,
@@ -132,7 +135,7 @@
this.changeControlFactory = changeControlFactory;
this.changeControlGenericFactory = changeControlGenericFactory;
this.accountResolver = accountResolver;
- this.groupCache = groupCache;
+ this.groupBackend = groupBackend;
this.approvalTypes = approvalTypes;
this.allProjectsName = allProjectsName;
this.patchListCache = patchListCache;
@@ -367,18 +370,11 @@
// If its not an account, maybe its a group?
//
- AccountGroup g = args.groupCache.get(new AccountGroup.NameKey(who));
- if (g != null) {
- return visibleto(new SingleGroupUser(args.capabilityControlFactory,
- g.getGroupUUID()));
- }
-
- Collection<AccountGroup> matches =
- args.groupCache.get(new AccountGroup.ExternalNameKey(who));
- if (matches != null && !matches.isEmpty()) {
+ Collection<GroupReference> suggestions = args.groupBackend.suggest(who);
+ if (!suggestions.isEmpty()) {
HashSet<AccountGroup.UUID> ids = new HashSet<AccountGroup.UUID>();
- for (AccountGroup group : matches) {
- ids.add(group.getGroupUUID());
+ for (GroupReference ref : suggestions) {
+ ids.add(ref.getUUID());
}
return visibleto(new SingleGroupUser(args.capabilityControlFactory, ids));
}
@@ -410,11 +406,11 @@
@Operator
public Predicate<ChangeData> ownerin(String group)
throws QueryParseException {
- AccountGroup g = args.groupCache.get(new AccountGroup.NameKey(group));
+ GroupReference g = GroupBackends.findBestSuggestion(args.groupBackend, group);
if (g == null) {
throw error("Group " + group + " not found");
}
- return new OwnerinPredicate(args.dbProvider, args.userFactory, g.getGroupUUID());
+ return new OwnerinPredicate(args.dbProvider, args.userFactory, g.getUUID());
}
@Operator
@@ -431,11 +427,11 @@
@Operator
public Predicate<ChangeData> reviewerin(String group)
throws QueryParseException {
- AccountGroup g = args.groupCache.get(new AccountGroup.NameKey(group));
+ GroupReference g = GroupBackends.findBestSuggestion(args.groupBackend, group);
if (g == null) {
throw error("Group " + group + " not found");
}
- return new ReviewerinPredicate(args.dbProvider, args.userFactory, g.getGroupUUID());
+ return new ReviewerinPredicate(args.dbProvider, args.userFactory, g.getUUID());
}
@Operator
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ListChanges.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ListChanges.java
index 6f9094a..adf4f19 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ListChanges.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ListChanges.java
@@ -205,6 +205,9 @@
}
private AccountAttribute asAccountAttribute(Account.Id user) {
+ if (user == null) {
+ return null;
+ }
AccountAttribute a = accounts.get(user);
if (a == null) {
a = new AccountAttribute();
@@ -226,7 +229,7 @@
PatchSet ps = cd.currentPatchSet(db);
Map<String, LabelInfo> labels = Maps.newLinkedHashMap();
- for (SubmitRecord rec : ctl.canSubmit(db.get(), ps, cd, true)) {
+ for (SubmitRecord rec : ctl.canSubmit(db.get(), ps, cd, true, false)) {
if (rec.labels == null) {
continue;
}
@@ -243,6 +246,7 @@
n.rejected = asAccountAttribute(r.appliedBy);
break;
}
+ n.optional = n._status == SubmitRecord.Label.Status.MAY ? true : null;
labels.put(r.label, n);
}
}
@@ -314,5 +318,6 @@
AccountAttribute recommended;
AccountAttribute disliked;
Short value;
+ Boolean optional;
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaCreator.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaCreator.java
index 08d94af..ff6dc6c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaCreator.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaCreator.java
@@ -252,8 +252,9 @@
all.getPermission(Permission.FORGE_AUTHOR, true) //
.add(rule(config, registered));
- meta.getPermission(Permission.READ, true) //
- .add(rule(config, owners));
+ Permission metaReadPermission = meta.getPermission(Permission.READ, true);
+ metaReadPermission.setExclusiveGroup(true);
+ metaReadPermission.add(rule(config, owners));
md.setMessage("Initialized Gerrit Code Review " + Version.getVersion());
if (!config.commit(md)) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
index 9c89c73..0a34b44 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
@@ -32,7 +32,7 @@
/** A version of the database schema. */
public abstract class SchemaVersion {
/** The current schema version. */
- public static final Class<Schema_67> C = Schema_67.class;
+ public static final Class<Schema_69> C = Schema_69.class;
public static class Module extends AbstractModule {
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_68.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_68.java
new file mode 100644
index 0000000..4dc2b6e
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_68.java
@@ -0,0 +1,60 @@
+// Copyright (C) 2012 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.schema;
+
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gwtorm.jdbc.JdbcSchema;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import java.sql.SQLException;
+import java.sql.Statement;
+
+public class Schema_68 extends SchemaVersion {
+ @Inject
+ Schema_68(Provider<Schema_67> prior) {
+ super(prior);
+ }
+
+ @Override
+ protected void migrateData(final ReviewDb db, final UpdateUI ui)
+ throws SQLException {
+ final Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
+ try {
+ stmt.execute("CREATE INDEX submodule_subscription_access_bySubscription"
+ + " ON submodule_subscriptions (submodule_project_name, submodule_branch_name)");
+ } catch (SQLException e) {
+ // the index creation might have failed because the index exists already,
+ // in this case the exception can be safely ignored,
+ // but there are also other possible reasons for an exception here that
+ // should not be ignored,
+ // -> ask the user whether to ignore this exception or not
+ ui.message("warning: Cannot create index for submodule subscriptions");
+ ui.message(e.getMessage());
+
+ if (ui.isBatch()) {
+ ui.message("you may ignore this warning when running in interactive mode");
+ throw e;
+ } else {
+ final boolean answer = ui.yesno(false, "Ignore warning and proceed with schema upgrade");
+ if (!answer) {
+ throw e;
+ }
+ }
+ } finally {
+ stmt.close();
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_69.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_69.java
new file mode 100644
index 0000000..3d6b93a
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_69.java
@@ -0,0 +1,230 @@
+// Copyright (C) 2012 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.schema;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.GerritPersonIdent;
+import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.MetaDataUpdate;
+import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gwtorm.jdbc.JdbcSchema;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+
+import java.io.IOException;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.naming.NamingException;
+import javax.naming.ldap.LdapName;
+
+public class Schema_69 extends SchemaVersion {
+ private final GitRepositoryManager mgr;
+ private final PersonIdent serverUser;
+
+ @Inject
+ Schema_69(Provider<Schema_68> prior,
+ GitRepositoryManager mgr,
+ @GerritPersonIdent PersonIdent serverUser) {
+ super(prior);
+ this.mgr = mgr;
+ this.serverUser = serverUser;
+ }
+
+ @Override
+ protected void migrateData(ReviewDb db, UpdateUI ui)
+ throws OrmException, SQLException {
+
+ // Find all groups that have an LDAP type.
+ Map<AccountGroup.UUID, GroupReference> ldapUUIDMap = Maps.newHashMap();
+ Set<AccountGroup.UUID> toResolve = Sets.newHashSet();
+ List<AccountGroup.Id> toDelete = Lists.newArrayList();
+ List<AccountGroup.NameKey> namesToDelete = Lists.newArrayList();
+ Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
+ try {
+ ResultSet rs = stmt.executeQuery(
+ "SELECT group_id, group_uuid, external_name, name FROM account_groups"
+ + " WHERE group_type ='LDAP'");
+ try {
+ while (rs.next()) {
+ AccountGroup.Id groupId = new AccountGroup.Id(rs.getInt(1));
+ AccountGroup.UUID groupUUID = new AccountGroup.UUID(rs.getString(2));
+ AccountGroup.NameKey name = new AccountGroup.NameKey(rs.getString(4));
+ String dn = rs.getString(3);
+
+ if (isNullOrEmpty(dn)) {
+ // The LDAP group does not have a DN. Determine if the UUID is used.
+ toResolve.add(groupUUID);
+ } else {
+ toDelete.add(groupId);
+ namesToDelete.add(name);
+ GroupReference ref = groupReference(dn);
+ ldapUUIDMap.put(groupUUID, ref);
+ }
+ }
+ } catch (NamingException e) {
+ throw new RuntimeException(e);
+ } finally {
+ rs.close();
+ }
+ } finally {
+ stmt.close();
+ }
+ if (toDelete.isEmpty() && toResolve.isEmpty()) {
+ return; // No ldap groups. Nothing to do.
+ }
+
+ ui.message("Update LDAP groups to be GroupReferences.");
+
+ // Update the groupOwnerUUID for LDAP groups to point to the new UUID.
+ List<AccountGroup> toUpdate = Lists.newArrayList();
+ Set<AccountGroup.UUID> resolveToUpdate = Sets.newHashSet();
+ Map<AccountGroup.UUID, AccountGroup> resolveGroups = Maps.newHashMap();
+ for (AccountGroup g : db.accountGroups().all()) {
+ if (ldapUUIDMap.containsKey(g.getGroupUUID())) {
+ continue; // Ignore the LDAP groups with a valid DN.
+ } else if (toResolve.contains(g.getGroupUUID())) {
+ resolveGroups.put(g.getGroupUUID(), g); // Keep the ones to resolve.
+ continue;
+ }
+
+ GroupReference ref = ldapUUIDMap.get(g.getOwnerGroupUUID());
+ if (ref != null) {
+ // Update the owner group UUID to the new ldap UUID scheme.
+ g.setOwnerGroupUUID(ref.getUUID());
+ toUpdate.add(g);
+ } else if (toResolve.contains(g.getOwnerGroupUUID())) {
+ // The unresolved group is used as an owner.
+ // Add to the list of LDAP groups to be made INTERNAL.
+ resolveToUpdate.add(g.getOwnerGroupUUID());
+ }
+ }
+
+ toResolve.removeAll(resolveToUpdate);
+
+ // Update project.config group references to use the new LDAP GroupReference
+ for (Project.NameKey name : mgr.list()) {
+ Repository git;
+ try {
+ git = mgr.openRepository(name);
+ } catch (RepositoryNotFoundException e) {
+ throw new OrmException(e);
+ } catch (IOException e) {
+ throw new OrmException(e);
+ }
+
+ try {
+ MetaDataUpdate md =
+ new MetaDataUpdate(GitReferenceUpdated.DISABLED, name, git);
+ md.getCommitBuilder().setAuthor(serverUser);
+ md.getCommitBuilder().setCommitter(serverUser);
+
+ ProjectConfig config = ProjectConfig.read(md);
+
+ // Update the existing refences to the new reference.
+ boolean updated = false;
+ for (Map.Entry<AccountGroup.UUID, GroupReference> entry: ldapUUIDMap.entrySet()) {
+ GroupReference ref = config.getGroup(entry.getKey());
+ if (ref != null) {
+ updated = true;
+ ref.setName(entry.getValue().getName());
+ ref.setUUID(entry.getValue().getUUID());
+ config.resolve(ref);
+ }
+ }
+
+ // Determine if a toResolve group is used and should be made INTERNAL.
+ Iterator<AccountGroup.UUID> iter = toResolve.iterator();
+ while (iter.hasNext()) {
+ AccountGroup.UUID uuid = iter.next();
+ if (config.getGroup(uuid) != null) {
+ resolveToUpdate.add(uuid);
+ iter.remove();
+ }
+ }
+
+ if (!updated) {
+ continue;
+ }
+
+ md.setMessage("Switch LDAP group UUIDs to DNs\n");
+ if (!config.commit(md)) {
+ throw new OrmException("Cannot update " + name);
+ }
+ } catch (IOException e) {
+ throw new OrmException(e);
+ } catch (ConfigInvalidException e) {
+ throw new OrmException(e);
+ } finally {
+ git.close();
+ }
+ }
+
+ for (AccountGroup.UUID uuid : resolveToUpdate) {
+ AccountGroup group = resolveGroups.get(uuid);
+ group.setType(AccountGroup.Type.INTERNAL);
+ toUpdate.add(group);
+
+ ui.message(String.format(
+ "*** Group has no DN and is inuse. Updated to be INTERNAL: %s",
+ group.getName()));
+ }
+
+ for (AccountGroup.UUID uuid : toResolve) {
+ AccountGroup group = resolveGroups.get(uuid);
+ toDelete.add(group.getId());
+ namesToDelete.add(group.getNameKey());
+ }
+
+ // Update group owners
+ db.accountGroups().update(toUpdate);
+ // Delete existing LDAP groups
+ db.accountGroupNames().deleteKeys(namesToDelete);
+ db.accountGroups().deleteKeys(toDelete);
+ }
+
+ private static GroupReference groupReference(String dn)
+ throws NamingException {
+ LdapName name = new LdapName(dn);
+ Preconditions.checkState(!name.isEmpty(), "Invalid LDAP dn: %s", dn);
+ String cn = name.get(name.size() - 1);
+ int index = cn.indexOf('=');
+ if (index >= 0) {
+ cn = cn.substring(index + 1);
+ }
+ return new GroupReference(new AccountGroup.UUID("ldap:" + dn), "ldap/" + cn);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/UpdateUI.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/UpdateUI.java
index 64b3afa..eff5575 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/UpdateUI.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/UpdateUI.java
@@ -24,6 +24,8 @@
boolean yesno(boolean def, String msg);
+ boolean isBatch();
+
void pruneSchema(StatementExecutor e, List<String> pruneList)
throws OrmException;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/util/GuiceRequestScopePropagator.java b/gerrit-server/src/main/java/com/google/gerrit/server/util/GuiceRequestScopePropagator.java
index cc3c2f7..fa07176 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/util/GuiceRequestScopePropagator.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/util/GuiceRequestScopePropagator.java
@@ -15,7 +15,6 @@
package com.google.gerrit.server.util;
import com.google.common.collect.Maps;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.RemotePeer;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.inject.Inject;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/util/ThreadLocalRequestContext.java b/gerrit-server/src/main/java/com/google/gerrit/server/util/ThreadLocalRequestContext.java
index 88240ca..b411512 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/util/ThreadLocalRequestContext.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/util/ThreadLocalRequestContext.java
@@ -21,7 +21,6 @@
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.Module;
-import com.google.inject.Provider;
import com.google.inject.Provides;
import com.google.inject.ProvisionException;
import com.google.inject.name.Named;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/workflow/FunctionState.java b/gerrit-server/src/main/java/com/google/gerrit/server/workflow/FunctionState.java
index d08bd1f..74c97f3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/workflow/FunctionState.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/workflow/FunctionState.java
@@ -26,7 +26,6 @@
import com.google.gerrit.reviewdb.client.ApprovalCategory.Id;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.project.ChangeControl;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -57,7 +56,7 @@
@Inject
FunctionState(final ApprovalTypes approvalTypes,
- final IdentifiedUser.GenericFactory userFactory, final GroupCache egc,
+ final IdentifiedUser.GenericFactory userFactory,
@Assisted final ChangeControl c, @Assisted final PatchSet.Id psId,
@Assisted final Collection<PatchSetApproval> all) {
this.approvalTypes = approvalTypes;
diff --git a/gerrit-server/src/main/prolog/gerrit_common.pl b/gerrit-server/src/main/prolog/gerrit_common.pl
index 5acc831..a75acc0 100644
--- a/gerrit-server/src/main/prolog/gerrit_common.pl
+++ b/gerrit-server/src/main/prolog/gerrit_common.pl
@@ -149,6 +149,7 @@
is_all_ok([]).
is_all_ok([label(_, ok(__)) | Ls]) :- is_all_ok(Ls).
+is_all_ok([label(_, may(__)) | Ls]) :- is_all_ok(Ls).
is_all_ok(_) :- fail.
@@ -209,8 +210,8 @@
%%
legacy_submit_rule('MaxWithBlock', Label, Id, Min, Max, T) :- !, max_with_block(Label, Min, Max, T).
legacy_submit_rule('MaxNoBlock', Label, Id, Min, Max, T) :- !, max_no_block(Label, Max, T).
-legacy_submit_rule('NoBlock', Label, Id, Min, Max, T) :- !, T = ok(_).
-legacy_submit_rule('NoOp', Label, Id, Min, Max, T) :- !, T = ok(_).
+legacy_submit_rule('NoBlock', Label, Id, Min, Max, T) :- !, T = may(_).
+legacy_submit_rule('NoOp', Label, Id, Min, Max, T) :- !, T = may(_).
legacy_submit_rule(Fun, Label, Id, Min, Max, T) :- T = impossible(unsupported(Fun)).
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Comment.vm b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Comment.vm
index 547c1b4..9af98a6 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Comment.vm
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/Comment.vm
@@ -43,5 +43,11 @@
$email.coverLetter
#end
+##
+## It is possible to increase the span of the quoted lines by using the line
+## count parameter when calling $email.inlineComments as a function.
+##
+## Example: #if($email.inlineComments)$email.getInlineComments(5)#end
+##
#if($email.inlineComments)$email.inlineComments#end
#end
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/NewChange.vm b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/NewChange.vm
index 8e08dc4..3e3257f 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/NewChange.vm
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/NewChange.vm
@@ -52,3 +52,6 @@
#if($email.sshHost)
git pull ssh://$email.sshHost/$projectName $patchSet.refName
#end
+
+## It is possible to include the patch as a unified diff in the email:
+#$email.UnifiedDiff
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/StringUtilTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/StringUtilTest.java
new file mode 100644
index 0000000..24f3386
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/StringUtilTest.java
@@ -0,0 +1,54 @@
+// Copyright (C) 2012 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;
+
+import junit.framework.TestCase;
+
+public class StringUtilTest extends TestCase {
+ /**
+ * Test the boundary condition that the first character of a string
+ * should be escaped.
+ */
+ public void testEscapeFirstChar() {
+ assertEquals(StringUtil.escapeString("\tLeading tab"), "\\tLeading tab");
+ }
+
+ /**
+ * Test the boundary condition that the last character of a string
+ * should be escaped.
+ */
+ public void testEscapeLastChar() {
+ assertEquals(StringUtil.escapeString("Trailing tab\t"), "Trailing tab\\t");
+ }
+
+ /**
+ * Test that various forms of input strings are escaped (or left as-is)
+ * in the expected way.
+ */
+ public void testEscapeString() {
+ final String[] testPairs =
+ { "", "",
+ "plain string", "plain string",
+ "string with \"quotes\"", "string with \"quotes\"",
+ "string with 'quotes'", "string with 'quotes'",
+ "string with 'quotes'", "string with 'quotes'",
+ "C:\\Program Files\\MyProgram", "C:\\\\Program Files\\\\MyProgram",
+ "string\nwith\nnewlines", "string\\nwith\\nnewlines",
+ "string\twith\ttabs", "string\\twith\\ttabs" };
+ for (int i = 0; i < testPairs.length; i += 2) {
+ assertEquals(StringUtil.escapeString(testPairs[i]), testPairs[i + 1]);
+ }
+ }
+}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/ioutil/ColumnFormatterTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/ioutil/ColumnFormatterTest.java
new file mode 100644
index 0000000..2d432e6
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/ioutil/ColumnFormatterTest.java
@@ -0,0 +1,138 @@
+// Copyright (C) 2012 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.ioutil;
+
+import com.google.gerrit.server.ioutil.ColumnFormatter;
+
+import junit.framework.TestCase;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+public class ColumnFormatterTest extends TestCase {
+ /**
+ * Holds an in-memory {@link java.io.PrintWriter} object and allows
+ * comparisons of its contents to a supplied string via an assert statement.
+ */
+ class PrintWriterComparator {
+ private PrintWriter printWriter;
+ private StringWriter stringWriter;
+
+ public PrintWriterComparator() {
+ stringWriter = new StringWriter();
+ printWriter = new PrintWriter(stringWriter);
+ }
+
+ public void assertEquals(String str) {
+ printWriter.flush();
+ TestCase.assertEquals(stringWriter.toString(), str);
+ }
+
+ public PrintWriter getPrintWriter() {
+ return printWriter;
+ }
+ }
+
+ /**
+ * Test that only lines with at least one column of text emit output.
+ */
+ public void testEmptyLine() {
+ final PrintWriterComparator comparator = new PrintWriterComparator();
+ final ColumnFormatter formatter =
+ new ColumnFormatter(comparator.getPrintWriter(), '\t');
+ formatter.addColumn("foo");
+ formatter.addColumn("bar");
+ formatter.nextLine();
+ formatter.nextLine();
+ formatter.nextLine();
+ formatter.addColumn("foo");
+ formatter.addColumn("bar");
+ formatter.finish();
+ comparator.assertEquals("foo\tbar\nfoo\tbar\n");
+ }
+
+ /**
+ * Test that there is no output if no columns are ever added.
+ */
+ public void testEmptyOutput() {
+ final PrintWriterComparator comparator = new PrintWriterComparator();
+ final ColumnFormatter formatter =
+ new ColumnFormatter(comparator.getPrintWriter(), '\t');
+ formatter.nextLine();
+ formatter.nextLine();
+ formatter.finish();
+ comparator.assertEquals("");
+ }
+
+ /**
+ * Test that there is no output (nor any exceptions) if we finalize
+ * the output immediately after the creation of the {@link ColumnFormatter}.
+ */
+ public void testNoNextLine() {
+ final PrintWriterComparator comparator = new PrintWriterComparator();
+ final ColumnFormatter formatter =
+ new ColumnFormatter(comparator.getPrintWriter(), '\t');
+ formatter.finish();
+ comparator.assertEquals("");
+ }
+
+ /**
+ * Test that the text in added columns is escaped while the column separator
+ * (which of course shouldn't be escaped) is left alone.
+ */
+ public void testEscapingTakesPlace() {
+ final PrintWriterComparator comparator = new PrintWriterComparator();
+ final ColumnFormatter formatter =
+ new ColumnFormatter(comparator.getPrintWriter(), '\t');
+ formatter.addColumn("foo");
+ formatter.addColumn(
+ "\tan indented multi-line\ntext");
+ formatter.nextLine();
+ formatter.finish();
+ comparator.assertEquals("foo\t\\tan indented multi-line\\ntext\n");
+ }
+
+ /**
+ * Test that we get the correct output with multi-line input where the number
+ * of columns in each line varies.
+ */
+ public void testMultiLineDifferentColumnCount() {
+ final PrintWriterComparator comparator = new PrintWriterComparator();
+ final ColumnFormatter formatter =
+ new ColumnFormatter(comparator.getPrintWriter(), '\t');
+ formatter.addColumn("foo");
+ formatter.addColumn("bar");
+ formatter.addColumn("baz");
+ formatter.nextLine();
+ formatter.addColumn("foo");
+ formatter.addColumn("bar");
+ formatter.nextLine();
+ formatter.finish();
+ comparator.assertEquals("foo\tbar\tbaz\nfoo\tbar\n");
+ }
+
+ /**
+ * Test that we get the correct output with a single column of input.
+ */
+ public void testOneColumn() {
+ final PrintWriterComparator comparator = new PrintWriterComparator();
+ final ColumnFormatter formatter =
+ new ColumnFormatter(comparator.getPrintWriter(), '\t');
+ formatter.addColumn("foo");
+ formatter.nextLine();
+ formatter.finish();
+ comparator.assertEquals("foo\n");
+ }
+}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
index 469dafe..e4d9418 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
@@ -20,6 +20,8 @@
import static com.google.gerrit.common.data.Permission.READ;
import static com.google.gerrit.common.data.Permission.SUBMIT;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Lists;
import com.google.gerrit.common.data.Capable;
import com.google.gerrit.common.data.GroupReference;
@@ -36,7 +38,6 @@
import com.google.gerrit.server.account.CapabilityControl;
import com.google.gerrit.server.account.GroupMembership;
import com.google.gerrit.server.account.ListGroupMembership;
-import com.google.gerrit.server.cache.ConcurrentHashMapCache;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.FactoryModule;
import com.google.gerrit.server.config.GerritServerConfig;
@@ -321,10 +322,9 @@
local.createInMemory();
local.getProject().setParentName(parent.getProject().getName());
- sectionSorter =
- new PermissionCollection.Factory(
- new SectionSortCache(
- new ConcurrentHashMapCache<SectionSortCache.EntryKey, SectionSortCache.EntryVal>()));
+ Cache<SectionSortCache.EntryKey, SectionSortCache.EntryVal> c =
+ CacheBuilder.newBuilder().build();
+ sectionSorter = new PermissionCollection.Factory(new SectionSortCache(c));
}
private static void assertOwner(String ref, ProjectControl u) {
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java
index 34f7430..cc8d47d 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java
@@ -108,6 +108,11 @@
}
@Override
+ public boolean isBatch() {
+ return true;
+ }
+
+ @Override
public void pruneSchema(StatementExecutor e, List<String> pruneList)
throws OrmException {
for (String sql : pruneList) {
diff --git a/gerrit-sshd/pom.xml b/gerrit-sshd/pom.xml
index 1c197a0..31b2422 100644
--- a/gerrit-sshd/pom.xml
+++ b/gerrit-sshd/pom.xml
@@ -67,7 +67,7 @@
<dependency>
<groupId>com.google.gerrit</groupId>
- <artifactId>gerrit-ehcache</artifactId>
+ <artifactId>gerrit-cache-h2</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/AliasCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/AliasCommand.java
index d28d102..9582c93 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/AliasCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/AliasCommand.java
@@ -16,6 +16,7 @@
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Atomics;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.CapabilityControl;
import com.google.inject.Provider;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DispatchCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DispatchCommand.java
index f0810ba..301d68d 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DispatchCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DispatchCommand.java
@@ -16,9 +16,10 @@
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.Atomics;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.CapabilityControl;
-import com.google.gerrit.sshd.args4j.SubcommandHandler;
+import com.google.gerrit.server.args4j.SubcommandHandler;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshKeyCacheImpl.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshKeyCacheImpl.java
index 1f5ac28..0a1f708 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshKeyCacheImpl.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshKeyCacheImpl.java
@@ -16,13 +16,13 @@
import static com.google.gerrit.reviewdb.client.AccountExternalId.SCHEME_USERNAME;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
import com.google.gerrit.common.errors.InvalidSshKeyException;
import com.google.gerrit.reviewdb.client.AccountExternalId;
import com.google.gerrit.reviewdb.client.AccountSshKey;
import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.cache.Cache;
import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.cache.EntryCreator;
import com.google.gerrit.server.ssh.SshKeyCache;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.SchemaFactory;
@@ -42,6 +42,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.ExecutionException;
/** Provides the {@link SshKeyCacheEntry}. */
@Singleton
@@ -57,9 +58,10 @@
return new CacheModule() {
@Override
protected void configure() {
- final TypeLiteral<Cache<String, Iterable<SshKeyCacheEntry>>> type =
- new TypeLiteral<Cache<String, Iterable<SshKeyCacheEntry>>>() {};
- core(type, CACHE_NAME).populateWith(Loader.class);
+ cache(CACHE_NAME,
+ String.class,
+ new TypeLiteral<Iterable<SshKeyCacheEntry>>(){})
+ .loader(Loader.class);
bind(SshKeyCacheImpl.class);
bind(SshKeyCache.class).to(SshKeyCacheImpl.class);
}
@@ -71,20 +73,27 @@
.asList(new SshKeyCacheEntry[0]));
}
- private final Cache<String, Iterable<SshKeyCacheEntry>> cache;
+ private final LoadingCache<String, Iterable<SshKeyCacheEntry>> cache;
@Inject
SshKeyCacheImpl(
- @Named(CACHE_NAME) final Cache<String, Iterable<SshKeyCacheEntry>> cache) {
+ @Named(CACHE_NAME) LoadingCache<String, Iterable<SshKeyCacheEntry>> cache) {
this.cache = cache;
}
- public Iterable<SshKeyCacheEntry> get(String username) {
- return cache.get(username);
+ Iterable<SshKeyCacheEntry> get(String username) {
+ try {
+ return cache.get(username);
+ } catch (ExecutionException e) {
+ log.warn("Cannot load SSH keys for " + username, e);
+ return Collections.emptyList();
+ }
}
public void evict(String username) {
- cache.remove(username);
+ if (username != null) {
+ cache.invalidate(username);
+ }
}
@Override
@@ -107,7 +116,7 @@
}
}
- static class Loader extends EntryCreator<String, Iterable<SshKeyCacheEntry>> {
+ static class Loader extends CacheLoader<String, Iterable<SshKeyCacheEntry>> {
private final SchemaFactory<ReviewDb> schema;
@Inject
@@ -116,8 +125,7 @@
}
@Override
- public Iterable<SshKeyCacheEntry> createEntry(String username)
- throws Exception {
+ public Iterable<SshKeyCacheEntry> load(String username) throws Exception {
final ReviewDb db = schema.open();
try {
final AccountExternalId.Key key =
@@ -143,11 +151,6 @@
}
}
- @Override
- public Iterable<SshKeyCacheEntry> missing(String username) {
- return Collections.emptyList();
- }
-
private void add(ReviewDb db, List<SshKeyCacheEntry> kl, AccountSshKey k) {
try {
kl.add(new SshKeyCacheEntry(k.getKey(), SshUtil.parse(k)));
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshModule.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshModule.java
index bcb1d9f..f800783 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshModule.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshModule.java
@@ -23,8 +23,7 @@
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.CmdLineParserModule;
import com.google.gerrit.server.PeerDaemonUser;
import com.google.gerrit.server.RemotePeer;
import com.google.gerrit.server.account.AccountManager;
@@ -40,18 +39,8 @@
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.ssh.SshInfo;
import com.google.gerrit.server.util.RequestScopePropagator;
-import com.google.gerrit.sshd.args4j.AccountGroupIdHandler;
-import com.google.gerrit.sshd.args4j.AccountGroupUUIDHandler;
-import com.google.gerrit.sshd.args4j.AccountIdHandler;
-import com.google.gerrit.sshd.args4j.ChangeIdHandler;
-import com.google.gerrit.sshd.args4j.ObjectIdHandler;
-import com.google.gerrit.sshd.args4j.PatchSetIdHandler;
-import com.google.gerrit.sshd.args4j.ProjectControlHandler;
-import com.google.gerrit.sshd.args4j.SocketAddressHandler;
import com.google.gerrit.sshd.commands.DefaultCommandModule;
import com.google.gerrit.sshd.commands.QueryShell;
-import com.google.gerrit.util.cli.CmdLineParser;
-import com.google.gerrit.util.cli.OptionHandlerUtil;
import com.google.inject.Inject;
import com.google.inject.internal.UniqueAnnotations;
import com.google.inject.servlet.RequestScoped;
@@ -61,7 +50,6 @@
import org.apache.sshd.server.PublickeyAuthenticator;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
-import org.kohsuke.args4j.spi.OptionHandler;
import java.net.SocketAddress;
import java.util.Map;
@@ -85,7 +73,7 @@
bind(SshScope.class).in(SINGLETON);
configureRequestScope();
- configureCmdLineParser();
+ install(new CmdLineParserModule());
configureAliases();
install(SshKeyCacheImpl.module());
@@ -157,22 +145,4 @@
install(new GerritRequestModule());
}
-
- private void configureCmdLineParser() {
- factory(CmdLineParser.Factory.class);
-
- registerOptionHandler(Account.Id.class, AccountIdHandler.class);
- registerOptionHandler(AccountGroup.Id.class, AccountGroupIdHandler.class);
- registerOptionHandler(AccountGroup.UUID.class, AccountGroupUUIDHandler.class);
- registerOptionHandler(Change.Id.class, ChangeIdHandler.class);
- registerOptionHandler(ObjectId.class, ObjectIdHandler.class);
- registerOptionHandler(PatchSet.Id.class, PatchSetIdHandler.class);
- registerOptionHandler(ProjectControl.class, ProjectControlHandler.class);
- registerOptionHandler(SocketAddress.class, SocketAddressHandler.class);
- }
-
- private <T> void registerOptionHandler(Class<T> type,
- Class<? extends OptionHandler<T>> impl) {
- install(OptionHandlerUtil.moduleFor(type, impl));
- }
}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminQueryShell.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminQueryShell.java
index 5f1992c..08c650c 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminQueryShell.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminQueryShell.java
@@ -15,8 +15,8 @@
package com.google.gerrit.sshd.commands;
import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.sshd.AdminHighPriorityCommand;
-import com.google.gerrit.sshd.RequiresCapability;
import com.google.gerrit.sshd.SshCommand;
import com.google.inject.Inject;
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 047cdd4..cfd917c 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
@@ -15,6 +15,7 @@
package com.google.gerrit.sshd.commands;
import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.git.MetaDataUpdate;
@@ -22,7 +23,6 @@
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.sshd.RequiresCapability;
import com.google.gerrit.sshd.SshCommand;
import com.google.inject.Inject;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/BanCommitCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/BanCommitCommand.java
index f13e1a6..4350d1e 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/BanCommitCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/BanCommitCommand.java
@@ -17,7 +17,6 @@
import com.google.gerrit.common.errors.PermissionDeniedException;
import com.google.gerrit.server.git.BanCommit;
import com.google.gerrit.server.git.BanCommitResult;
-import com.google.gerrit.server.git.IncompleteUserInfoException;
import com.google.gerrit.server.git.MergeException;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.sshd.SshCommand;
@@ -77,8 +76,6 @@
throw die(e);
} catch (IOException e) {
throw die(e);
- } catch (IncompleteUserInfoException e) {
- throw die(e);
} catch (MergeException e) {
throw die(e);
} catch (InterruptedException e) {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CacheCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CacheCommand.java
index 1e7c5b3..500c84a 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CacheCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CacheCommand.java
@@ -14,37 +14,33 @@
package com.google.gerrit.sshd.commands;
-import com.google.gerrit.ehcache.EhcachePoolImpl;
+import com.google.common.cache.Cache;
+import com.google.common.collect.Sets;
+import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.sshd.SshCommand;
import com.google.inject.Inject;
-import net.sf.ehcache.CacheManager;
-import net.sf.ehcache.Ehcache;
-
-import java.util.Arrays;
import java.util.SortedSet;
-import java.util.TreeSet;
abstract class CacheCommand extends SshCommand {
@Inject
- protected EhcachePoolImpl cachePool;
+ protected DynamicMap<Cache<?, ?>> cacheMap;
protected SortedSet<String> cacheNames() {
- final SortedSet<String> names = new TreeSet<String>();
- for (final Ehcache c : getAllCaches()) {
- names.add(c.getName());
+ SortedSet<String> names = Sets.newTreeSet();
+ for (String plugin : cacheMap.plugins()) {
+ for (String name : cacheMap.byPlugin(plugin).keySet()) {
+ names.add(cacheNameOf(plugin, name));
+ }
}
return names;
}
- protected Ehcache[] getAllCaches() {
- final CacheManager cacheMgr = cachePool.getCacheManager();
- final String[] cacheNames = cacheMgr.getCacheNames();
- Arrays.sort(cacheNames);
- final Ehcache[] r = new Ehcache[cacheNames.length];
- for (int i = 0; i < cacheNames.length; i++) {
- r[i] = cacheMgr.getEhcache(cacheNames[i]);
+ protected String cacheNameOf(String plugin, String name) {
+ if ("gerrit".equals(plugin)) {
+ return name;
+ } else {
+ return plugin + "." + name;
}
- return r;
}
}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java
index 29f2294..ac7ee08 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java
@@ -16,6 +16,7 @@
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.errors.InvalidSshKeyException;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountExternalId;
import com.google.gerrit.reviewdb.client.AccountGroup;
@@ -27,7 +28,6 @@
import com.google.gerrit.server.account.AccountByEmailCache;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.ssh.SshKeyCache;
-import com.google.gerrit.sshd.RequiresCapability;
import com.google.gerrit.sshd.SshCommand;
import com.google.gwtorm.server.OrmDuplicateKeyException;
import com.google.gwtorm.server.OrmException;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
index 28b6f48..728c20c 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
@@ -17,10 +17,10 @@
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.errors.NameAlreadyUsedException;
import com.google.gerrit.common.errors.PermissionDeniedException;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.account.PerformCreateGroup;
-import com.google.gerrit.sshd.RequiresCapability;
import com.google.gerrit.sshd.SshCommand;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
index 1f5bc6f..5dff6b0 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
@@ -16,6 +16,7 @@
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.errors.ProjectCreationFailedException;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.Project.SubmitType;
@@ -23,7 +24,6 @@
import com.google.gerrit.server.project.CreateProjectArgs;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.SuggestParentCandidates;
-import com.google.gerrit.sshd.RequiresCapability;
import com.google.gerrit.sshd.SshCommand;
import com.google.inject.Inject;
@@ -79,7 +79,7 @@
@Option(name = "--branch", aliases = {"-b"}, metaVar = "BRANCH", usage = "initial branch name\n"
+ "(default: master)")
- private String branch = Constants.MASTER;
+ private List<String> branch;
@Option(name = "--empty-commit", usage = "to create initial empty commit")
private boolean createEmptyCommit;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/FlushCaches.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/FlushCaches.java
index 9ba20ed..fa63041 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/FlushCaches.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/FlushCaches.java
@@ -14,18 +14,19 @@
package com.google.gerrit.sshd.commands;
+import com.google.common.cache.Cache;
import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.sshd.BaseCommand;
-import com.google.gerrit.sshd.RequiresCapability;
import com.google.inject.Inject;
-
-import net.sf.ehcache.Ehcache;
+import com.google.inject.Provider;
import org.kohsuke.args4j.Option;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.SortedSet;
/** Causes the caches to purge all entries and reload. */
@@ -95,13 +96,16 @@
private void doBulkFlush() {
try {
- for (final Ehcache c : getAllCaches()) {
- final String name = c.getName();
- if (flush(name)) {
- try {
- c.removeAll();
- } catch (Throwable e) {
- stderr.println("error: cannot flush cache \"" + name + "\": " + e);
+ for (String plugin : cacheMap.plugins()) {
+ for (Map.Entry<String, Provider<Cache<?, ?>>> entry :
+ cacheMap.byPlugin(plugin).entrySet()) {
+ String n = cacheNameOf(plugin, entry.getKey());
+ if (flush(n)) {
+ try {
+ entry.getValue().get().invalidateAll();
+ } catch (Throwable err) {
+ stderr.println("error: cannot flush cache \"" + n + "\": " + err);
+ }
}
}
}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/KillCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/KillCommand.java
index 12ab225..83e88e5 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/KillCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/KillCommand.java
@@ -15,11 +15,11 @@
package com.google.gerrit.sshd.commands;
import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.server.git.WorkQueue.Task;
import com.google.gerrit.server.util.IdGenerator;
import com.google.gerrit.sshd.AdminHighPriorityCommand;
-import com.google.gerrit.sshd.RequiresCapability;
import com.google.gerrit.sshd.SshCommand;
import com.google.inject.Inject;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginInstallCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginInstallCommand.java
index 2328847..12722ec 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginInstallCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginInstallCommand.java
@@ -16,9 +16,9 @@
import com.google.common.base.Strings;
import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.server.plugins.PluginInstallException;
import com.google.gerrit.server.plugins.PluginLoader;
-import com.google.gerrit.sshd.RequiresCapability;
import com.google.gerrit.sshd.SshCommand;
import com.google.inject.Inject;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginLsCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginLsCommand.java
index 644cf13..6d7490f 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginLsCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginLsCommand.java
@@ -14,39 +14,29 @@
package com.google.gerrit.sshd.commands;
-import com.google.common.base.Strings;
-import com.google.common.collect.Lists;
import com.google.gerrit.common.data.GlobalCapability;
-import com.google.gerrit.server.plugins.Plugin;
-import com.google.gerrit.server.plugins.PluginLoader;
-import com.google.gerrit.sshd.RequiresCapability;
-import com.google.gerrit.sshd.SshCommand;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
+import com.google.gerrit.server.plugins.ListPlugins;
+import com.google.gerrit.sshd.BaseCommand;
import com.google.inject.Inject;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
+import org.apache.sshd.server.Environment;
+
+import java.io.IOException;
@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
-final class PluginLsCommand extends SshCommand {
+final class PluginLsCommand extends BaseCommand {
@Inject
- private PluginLoader loader;
+ private ListPlugins impl;
@Override
- protected void run() {
- List<Plugin> running = Lists.newArrayList(loader.getPlugins());
- Collections.sort(running, new Comparator<Plugin>() {
+ public void start(Environment env) throws IOException {
+ startThread(new CommandRunnable() {
@Override
- public int compare(Plugin a, Plugin b) {
- return a.getName().compareTo(b.getName());
+ public void run() throws Exception {
+ parseCommandLine(impl);
+ impl.display(out);
}
});
-
- stdout.format("%-30s %-10s\n", "Name", "Version");
- stdout.print("----------------------------------------------------------------------\n");
- for (Plugin p : running) {
- stdout.format("%-30s %-10s\n", p.getName(),
- Strings.nullToEmpty(p.getVersion()));
- }
}
}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginReloadCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginReloadCommand.java
index d60465c..d2429a9 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginReloadCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginReloadCommand.java
@@ -17,8 +17,8 @@
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.server.plugins.InvalidPluginException;
import com.google.gerrit.server.plugins.PluginInstallException;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.server.plugins.PluginLoader;
-import com.google.gerrit.sshd.RequiresCapability;
import com.google.gerrit.sshd.SshCommand;
import com.google.inject.Inject;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginRemoveCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginRemoveCommand.java
index 6444e71..8baab77 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginRemoveCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginRemoveCommand.java
@@ -16,8 +16,8 @@
import com.google.common.collect.Sets;
import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.server.plugins.PluginLoader;
-import com.google.gerrit.sshd.RequiresCapability;
import com.google.gerrit.sshd.SshCommand;
import com.google.inject.Inject;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowCaches.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowCaches.java
index 97a0d86..bdcb4fb 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowCaches.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowCaches.java
@@ -14,22 +14,24 @@
package com.google.gerrit.sshd.commands;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheStats;
+import com.google.common.collect.Maps;
import com.google.gerrit.common.Version;
import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.events.LifecycleListener;
+import com.google.gerrit.server.cache.h2.H2CacheImpl;
import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.server.git.WorkQueue.Task;
-import com.google.gerrit.sshd.RequiresCapability;
import com.google.gerrit.sshd.SshDaemon;
import com.google.inject.Inject;
-
-import net.sf.ehcache.Ehcache;
-import net.sf.ehcache.Statistics;
-import net.sf.ehcache.config.CacheConfiguration;
+import com.google.inject.Provider;
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.session.IoSession;
+import org.apache.sshd.server.Environment;
import org.eclipse.jgit.storage.file.WindowCacheStatAccessor;
import org.kohsuke.args4j.Option;
@@ -43,6 +45,8 @@
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
+import java.util.Map;
+import java.util.SortedMap;
/** Show the current cache states. */
@RequiresCapability(GlobalCapability.VIEW_CACHES)
@@ -76,8 +80,26 @@
@SitePath
private File sitePath;
+ @Option(name = "--width", aliases = {"-w"}, metaVar = "COLS", usage = "width of output table")
+ private int columns = 80;
+ private int nw;
+
+ @Override
+ public void start(Environment env) throws IOException {
+ String s = env.getEnv().get(Environment.ENV_COLUMNS);
+ if (s != null && !s.isEmpty()) {
+ try {
+ columns = Integer.parseInt(s);
+ } catch (NumberFormatException err) {
+ columns = 80;
+ }
+ }
+ super.start(env);
+ }
+
@Override
protected void run() {
+ nw = columns - 50;
Date now = new Date();
stdout.format(
"%-25s %-20s now %16s\n",
@@ -91,60 +113,46 @@
stdout.print('\n');
stdout.print(String.format(//
- "%1s %-18s %-4s|%-20s| %-5s |%-14s|\n" //
+ "%1s %-"+nw+"s|%-21s| %-5s |%-9s|\n" //
, "" //
, "Name" //
- , "Max" //
- , "Object Count" //
+ , "Entries" //
, "AvgGet" //
, "Hit Ratio" //
));
stdout.print(String.format(//
- "%1s %-18s %-4s|%6s %6s %6s| %-5s |%-4s %-4s %-4s|\n" //
+ "%1s %-"+nw+"s|%6s %6s %7s| %-5s |%-4s %-4s|\n" //
, "" //
, "" //
- , "Age" //
- , "Disk" //
, "Mem" //
- , "Cnt" //
- , "" //
, "Disk" //
+ , "Space" //
+ , "" //
, "Mem" //
- , "Agg" //
+ , "Disk" //
));
- stdout.print("------------------"
- + "-------+--------------------+----------+--------------+\n");
- for (final Ehcache cache : getAllCaches()) {
- final CacheConfiguration cfg = cache.getCacheConfiguration();
- final boolean useDisk = cfg.isDiskPersistent() || cfg.isOverflowToDisk();
- final Statistics stat = cache.getStatistics();
- final long total = stat.getCacheHits() + stat.getCacheMisses();
+ stdout.print("--");
+ for (int i = 0; i < nw; i++) {
+ stdout.print('-');
+ }
+ stdout.print("+---------------------+---------+---------+\n");
- if (useDisk) {
- stdout.print(String.format(//
- "D %-18s %-4s|%6s %6s %6s| %7s |%4s %4s %4s|\n" //
- , cache.getName() //
- , interval(cfg.getTimeToLiveSeconds()) //
- , count(stat.getDiskStoreObjectCount()) //
- , count(stat.getMemoryStoreObjectCount()) //
- , count(stat.getObjectCount()) //
- , duration(stat.getAverageGetTime()) //
- , percent(stat.getOnDiskHits(), total) //
- , percent(stat.getInMemoryHits(), total) //
- , percent(stat.getCacheHits(), total) //
- ));
- } else {
- stdout.print(String.format(//
- " %-18s %-4s|%6s %6s %6s| %7s |%4s %4s %4s|\n" //
- , cache.getName() //
- , interval(cfg.getTimeToLiveSeconds()) //
- , "", "" //
- , count(stat.getObjectCount()) //
- , duration(stat.getAverageGetTime()) //
- , "", "" //
- , percent(stat.getCacheHits(), total) //
- ));
- }
+ Map<String, H2CacheImpl<?, ?>> disks = Maps.newTreeMap();
+ printMemoryCaches(disks, sortedCoreCaches());
+ printMemoryCaches(disks, sortedPluginCaches());
+ for (Map.Entry<String, H2CacheImpl<?, ?>> entry : disks.entrySet()) {
+ H2CacheImpl<?, ?> cache = entry.getValue();
+ CacheStats stat = cache.stats();
+ H2CacheImpl.DiskStats disk = cache.diskStats();
+ stdout.print(String.format(
+ "D %-"+nw+"s|%6s %6s %7s| %7s |%4s %4s|\n",
+ entry.getKey(),
+ count(cache.size()),
+ count(disk.size()),
+ bytes(disk.space()),
+ duration(stat.averageLoadPenalty()),
+ percent(stat.hitCount(), stat.requestCount()),
+ percent(disk.hitCount(), disk.requestCount())));
}
stdout.print('\n');
@@ -165,6 +173,51 @@
stdout.flush();
}
+ private void printMemoryCaches(
+ Map<String, H2CacheImpl<?, ?>> disks,
+ Map<String, Cache<?,?>> caches) {
+ for (Map.Entry<String, Cache<?,?>> entry : caches.entrySet()) {
+ Cache<?,?> cache = entry.getValue();
+ if (cache instanceof H2CacheImpl) {
+ disks.put(entry.getKey(), (H2CacheImpl<?,?>)cache);
+ continue;
+ }
+ CacheStats stat = cache.stats();
+ stdout.print(String.format(
+ " %-"+nw+"s|%6s %6s %7s| %7s |%4s %4s|\n",
+ entry.getKey(),
+ count(cache.size()),
+ "",
+ "",
+ duration(stat.averageLoadPenalty()),
+ percent(stat.hitCount(), stat.requestCount()),
+ ""));
+ }
+ }
+
+ private Map<String, Cache<?, ?>> sortedCoreCaches() {
+ SortedMap<String, Cache<?, ?>> m = Maps.newTreeMap();
+ for (Map.Entry<String, Provider<Cache<?, ?>>> entry :
+ cacheMap.byPlugin("gerrit").entrySet()) {
+ m.put(cacheNameOf("gerrit", entry.getKey()), entry.getValue().get());
+ }
+ return m;
+ }
+
+ private Map<String, Cache<?, ?>> sortedPluginCaches() {
+ SortedMap<String, Cache<?, ?>> m = Maps.newTreeMap();
+ for (String plugin : cacheMap.plugins()) {
+ if ("gerrit".equals(plugin)) {
+ continue;
+ }
+ for (Map.Entry<String, Provider<Cache<?, ?>>> entry :
+ cacheMap.byPlugin(plugin).entrySet()) {
+ m.put(cacheNameOf(plugin, entry.getKey()), entry.getValue().get());
+ }
+ }
+ return m;
+ }
+
private void memSummary() {
final Runtime r = Runtime.getRuntime();
final long mMax = r.maxMemory();
@@ -300,45 +353,24 @@
return String.format("%6d", cnt);
}
- private String duration(double ms) {
- if (Math.abs(ms) <= 0.05) {
+ private String duration(double ns) {
+ if (ns < 0.5) {
return "";
}
- String suffix = "ms";
- if (ms >= 1000) {
- ms /= 1000;
+ String suffix = "ns";
+ if (ns >= 1000.0) {
+ ns /= 1000.0;
+ suffix = "us";
+ }
+ if (ns >= 1000.0) {
+ ns /= 1000.0;
+ suffix = "ms";
+ }
+ if (ns >= 1000.0) {
+ ns /= 1000.0;
suffix = "s ";
}
- return String.format("%4.1f%s", ms, suffix);
- }
-
- private String interval(double ttl) {
- if (ttl == 0) {
- return "inf";
- }
-
- String suffix = "s";
- if (ttl >= 60) {
- ttl /= 60;
- suffix = "m";
-
- if (ttl >= 60) {
- ttl /= 60;
- suffix = "h";
- }
-
- if (ttl >= 24) {
- ttl /= 24;
- suffix = "d";
-
- if (ttl >= 365) {
- ttl /= 365;
- suffix = "y";
- }
- }
- }
-
- return Integer.toString((int) ttl) + suffix;
+ return String.format("%4.1f%s", ns, suffix);
}
private String percent(final long value, final long total) {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowConnections.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowConnections.java
index 4085dcb..a1a5b8f 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowConnections.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowConnections.java
@@ -15,10 +15,10 @@
package com.google.gerrit.sshd.commands;
import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.util.IdGenerator;
-import com.google.gerrit.sshd.RequiresCapability;
import com.google.gerrit.sshd.SshCommand;
import com.google.gerrit.sshd.SshDaemon;
import com.google.gerrit.sshd.SshSession;
diff --git a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
index 58d6116..519dec8 100644
--- a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
+++ b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
@@ -18,12 +18,12 @@
import static com.google.inject.Stage.PRODUCTION;
import com.google.gerrit.common.ChangeHookRunner;
-import com.google.gerrit.ehcache.EhcachePoolImpl;
import com.google.gerrit.httpd.auth.openid.OpenIdModule;
import com.google.gerrit.httpd.plugins.HttpPluginModule;
import com.google.gerrit.lifecycle.LifecycleManager;
import com.google.gerrit.lifecycle.LifecycleModule;
import com.google.gerrit.reviewdb.client.AuthType;
+import com.google.gerrit.server.cache.h2.DefaultCacheFactory;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.AuthConfigModule;
import com.google.gerrit.server.config.CanonicalWebUrlModule;
@@ -200,7 +200,7 @@
modules.add(new ChangeHookRunner.Module());
modules.add(new ReceiveCommitsExecutorModule());
modules.add(cfgInjector.getInstance(GerritGlobalModule.class));
- modules.add(new EhcachePoolImpl.Module());
+ modules.add(new DefaultCacheFactory.Module());
modules.add(new SmtpEmailSender.Module());
modules.add(new SignedTokenEmailTokenVerifier.Module());
modules.add(new PluginModule());
diff --git a/gerrit-war/src/main/resources/log4j.properties b/gerrit-war/src/main/resources/log4j.properties
index 5993790..45f630e 100644
--- a/gerrit-war/src/main/resources/log4j.properties
+++ b/gerrit-war/src/main/resources/log4j.properties
@@ -48,10 +48,6 @@
log4j.logger.org.openid4java.server.RealmVerifier=ERROR
log4j.logger.org.openid4java.message.AuthSuccess=ERROR
-# Silence non-critical messages from ehcache
-#
-log4j.logger.net.sf.ehcache=WARN
-
# Silence non-critical messages from c3p0 (if used).
#
log4j.logger.com.mchange.v2.c3p0=WARN
diff --git a/pom.xml b/pom.xml
index ad32cbd..2f5a296 100644
--- a/pom.xml
+++ b/pom.xml
@@ -46,7 +46,7 @@
</issueManagement>
<properties>
- <jgitVersion>1.3.0.201202151440-r.140-g8c73245</jgitVersion>
+ <jgitVersion>1.3.0.201202151440-r.190-g65f6e06</jgitVersion>
<gwtormVersion>1.4</gwtormVersion>
<gwtjsonrpcVersion>1.3</gwtjsonrpcVersion>
<gwtexpuiVersion>1.2.5</gwtexpuiVersion>
@@ -74,7 +74,7 @@
<module>gerrit-antlr</module>
<module>gerrit-common</module>
- <module>gerrit-ehcache</module>
+ <module>gerrit-cache-h2</module>
<module>gerrit-httpd</module>
<module>gerrit-launcher</module>
<module>gerrit-main</module>
@@ -89,6 +89,7 @@
<module>gerrit-extension-api</module>
<module>gerrit-plugin-api</module>
+ <module>gerrit-plugin-archetype</module>
<module>gerrit-gwtui</module>
</modules>
@@ -461,6 +462,12 @@
</dependency>
<dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>12.0</version>
+ </dependency>
+
+ <dependency>
<groupId>gwtorm</groupId>
<artifactId>gwtorm</artifactId>
<version>${gwtormVersion}</version>
@@ -553,12 +560,6 @@
</dependency>
<dependency>
- <groupId>net.sf.ehcache</groupId>
- <artifactId>ehcache-core</artifactId>
- <version>1.7.2</version>
- </dependency>
-
- <dependency>
<groupId>args4j</groupId>
<artifactId>args4j</artifactId>
<version>2.0.16</version>
@@ -833,13 +834,13 @@
<repositories>
<repository>
- <id>jgit-repository</id>
- <url>http://download.eclipse.org/jgit/maven</url>
+ <id>gerrit-maven</id>
+ <url>https://gerrit-maven.commondatastorage.googleapis.com</url>
</repository>
<repository>
- <id>gerrit-maven-repository</id>
- <url>https://gerrit-maven-repository.googlecode.com/svn/</url>
+ <id>jgit-repository</id>
+ <url>http://download.eclipse.org/jgit/maven</url>
</repository>
<repository>