Merge changes If0d4ed46,I37e0362a
* changes:
gr-change-actions: Fix typo in comment
gr-change-actions: Remove redundant code
diff --git a/Documentation/cmd-index.txt b/Documentation/cmd-index.txt
index 1666444..496c205 100644
--- a/Documentation/cmd-index.txt
+++ b/Documentation/cmd-index.txt
@@ -212,7 +212,15 @@
=== Trace
For executing SSH commands tracing can be enabled by setting the
-`--trace` option.
+`--trace` and `--trace-id <trace-id>` options. It is recommended to use
+the ID of the issue that is being investigated as trace ID.
+
+----
+ $ ssh -p 29418 review.example.com gerrit create-project --trace --trace-id issue/123 foo/bar
+----
+
+It is also possible to omit the trace ID and get a unique trace ID
+generated.
----
$ ssh -p 29418 review.example.com gerrit create-project --trace foo/bar
@@ -220,8 +228,8 @@
Enabling tracing results in additional logs with debug information that
are written to the `error_log`. All logs that correspond to the traced
-request are associated with a unique trace ID. This trace ID is printed
-to the stderr command output:
+request are associated with the trace ID. The trace ID is printed to
+the stderr command output:
----
TRACE_ID: 1534174322774-7edf2a7b
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 7f32488..0108a04 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -3748,6 +3748,14 @@
+
Common unit suffixes of 'k', 'm', or 'g' are supported.
+[[receive.inheritProjectMaxObjectSizeLimit]]receive.inheritProjectMaxObjectSizeLimit::
++
+Controls whether the project-level link:config-project-config.html[`receive.maxObjectSizeLimit`]
+value is inherited from the parent project. When `true`, the value is
+inherited, otherwise it is not inherited.
++
+Default is false, the value is not inherited.
+
[[receive.maxTrustDepth]]receive.maxTrustDepth::
+
If signed push validation is link:#receive.enableSignedPush[enabled],
diff --git a/Documentation/config-labels.txt b/Documentation/config-labels.txt
index 1aa6cd7c..ff43520 100644
--- a/Documentation/config-labels.txt
+++ b/Documentation/config-labels.txt
@@ -374,6 +374,15 @@
link:access-control.html#reference[here], but must not contain `${username}` or
`${shardeduserid}`.
+[[label_ignoreSelfApproval]]
+=== `label.Label-Name.ignoreSelfApproval`
+
+If true, the label may be voted on by the uploader of the latest patch set,
+but their approval does not make a change submittable. Instead, a
+non-uploader who has the right to vote has to approve the change.
+
+Defaults to false.
+
[[label_example]]
=== Example
diff --git a/Documentation/config-project-config.txt b/Documentation/config-project-config.txt
index b652bda9..cc5386f 100644
--- a/Documentation/config-project-config.txt
+++ b/Documentation/config-project-config.txt
@@ -148,8 +148,10 @@
than the global limit (if configured). In other words, it is only honored when
it further reduces the global limit.
+
-The setting is not inherited from the parent project; it must be explicitly
-set per project.
+When link:config-gerrit.html#receive.inheritProjectMaxObjectSizeLimit[
+`receive.inheritProjectmaxObjectSizeLimit`] is enabled in the global config,
+the value is inherited from the parent project. Otherwise, it is not inherited
+and must be explicitly set per project.
+
Default is zero.
+
diff --git a/Documentation/dev-contributing.txt b/Documentation/dev-contributing.txt
index cc0fe60..bc9f782 100644
--- a/Documentation/dev-contributing.txt
+++ b/Documentation/dev-contributing.txt
@@ -368,6 +368,36 @@
that they are vetted long enough before they go into a release and we can be sure
that the update doesn't introduce a regression.
+[[deprecating-features]]
+=== Deprecating features
+
+Gerrit should be as stable as possible and we aim to add only features that last.
+However, sometimes we are required to deprecate and remove features to be able
+to move forward with the project and keep the code-base clean. The following process
+should serve as a guideline on how to deprecate functionality in Gerrit. Its purpose
+is that we have a structured process for deprecation that users, administrators and
+developers can agree and rely on.
+
+General process:
+* Make sure that the feature (e.g. a field on the API) is not needed anymore or blocks
+ further development or improvement. If in doubt, consult the mailing list.
+* If you can provide a schema migration that moves users to a comparable feature, do
+ so and stop here.
+* Mark the feature as deprecated in the documentation and release notes.
+* If possible, mark the feature deprecated in any user-visible interface. For example,
+ if you are deprecating a Git push option, add a message to the Git response if
+ the user provided the option informing them about deprecation.
+* Annotate the code with `@Deprecated` and `@RemoveAfter(x.xx)` if applicable.
+ Alternatively, use `// DEPRECATED, remove after x.xx` (where x.xx is the version
+ number that has to be branched off before removing the feature)
+* Gate the feature behind a config that is off by default (forcing admins to turn
+ the deprecated feature on explicitly).
+* After the next release was branched off, remove any code that backed the feature.
+
+You can optionally consult the mailing list to ask if there are users of the feature you
+wish to deprecate. If there are no major users, you can remove the feature without
+following this process and without the grace period of one release.
+
GERRIT
------
Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/dev-eclipse.txt b/Documentation/dev-eclipse.txt
index 6e39502..0f23db5 100644
--- a/Documentation/dev-eclipse.txt
+++ b/Documentation/dev-eclipse.txt
@@ -3,7 +3,7 @@
This document is about configuring Gerrit Code Review into an
Eclipse workspace for development and debugging with GWT.
-Java 6 or later SDK is also required to run GWT's compiler and
+Java 8 or later SDK is also required to run GWT's compiler and
runtime debugging environment.
@@ -49,6 +49,19 @@
link:dev-build-plugins.html#_bundle_custom_plugin_in_release_war[bundling in release.war]
and run `tools/eclipse/project.py`.
+[[Newer Java versions]]
+
+Java 9 and later are supported, but some adjustments must be done, because
+Java 8 is still the default:
+
+* Add JRE, e.g.: directory: /usr/lib64/jvm/java-9-openjdk, name: java-9-openjdk-9
+* Change execution environemnt for gerrit project to: JavaSE-9 (java-9-openjdk-9)
+* Check that compiler compliance level in gerrit project is set to: 9
+* Add this parameter to VM argument for gerrit_daemin launcher:
+----
+ --add-modules java.activation \
+ --add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED
+----
[[Formatting]]
== Code Formatter Settings
diff --git a/Documentation/dev-release.txt b/Documentation/dev-release.txt
index 2afac94..0abd8a1 100644
--- a/Documentation/dev-release.txt
+++ b/Documentation/dev-release.txt
@@ -365,6 +365,17 @@
must reference the new version. Upload a change to bazlets repository with
api version upgrade.
+[[clean-up-on-master]]
+=== Clean up on master
+
+Once you are done with the release, check if there are any code changes in the
+master branch that were gated on the next release. Mostly, these are
+feature-deprecations that we were holding off on to have a stable release where
+the feature is still contained, but marked as deprecated.
+
+See link:dev-contributing.html#deprecating-features[Deprecating features] for
+details.
+
GERRIT
------
Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/rest-api-access.txt b/Documentation/rest-api-access.txt
index 65a15ca..c2a7d21 100644
--- a/Documentation/rest-api-access.txt
+++ b/Documentation/rest-api-access.txt
@@ -263,6 +263,7 @@
],
"can_upload": true,
"can_add": true,
+ "can_add_tags": true,
"config_visible": true,
"groups": {
"53a4f647a89ea57992571187d8025f830625192a": {
@@ -313,6 +314,7 @@
],
"can_upload": true,
"can_add": true,
+ "can_add_tags": true,
"config_visible": true
}
}
@@ -399,6 +401,8 @@
Whether the calling user can upload to any ref.
|`can_add` |not set if `false`|
Whether the calling user can add any ref.
+|`can_add_tags` |not set if `false`|
+Whether the calling user can add any tag ref.
|`config_visible` |not set if `false`|
Whether the calling user can see the `refs/meta/config` branch of the
project.
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index edb642e..e28a9c4 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -418,7 +418,10 @@
.Response
----
HTTP/1.1 200 OK
+ Content-Disposition: attachment
+ Content-Type: application/json; charset=UTF-8
+ )]}'
ok
----
@@ -1095,7 +1098,10 @@
.Response
----
HTTP/1.1 200 OK
+ Content-Disposition: attachment
+ Content-Type: application/json; charset=UTF-8
+ )]}'
ok
----
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt
index 310ec7b..b517d3c 100644
--- a/Documentation/rest-api-projects.txt
+++ b/Documentation/rest-api-projects.txt
@@ -1141,6 +1141,7 @@
],
"can_upload": true,
"can_add": true,
+ "can_add_tags": true,
"config_visible": true,
"groups": {
"c2ce4749a32ceb82cd6adcce65b8216e12afb41c": {
@@ -1242,6 +1243,7 @@
],
"can_upload": true,
"can_add": true,
+ "can_add_tags": true,
"config_visible": true,
"groups": {
"global:Anonymous-Users": {
@@ -3442,10 +3444,10 @@
formatted string. +
Not set if there is no limit for the object size configured on project
level.
-|`inherited_value` |optional|
-The max object size limit that is inherited from the global config as a
-formatted string. +
-Not set if there is no global limit for the object size.
+|`summary` |optional|
+A string describing whether the value was inherited or overridden from
+the parent project or global config. +
+Not set if not inherited or overridden.
|===============================
[[project-access-input]]
diff --git a/Documentation/rest-api.txt b/Documentation/rest-api.txt
index a9c1ca6..8f6a47b 100644
--- a/Documentation/rest-api.txt
+++ b/Documentation/rest-api.txt
@@ -193,18 +193,36 @@
[[tracing]]
=== Request Tracing
-For each REST endpoint tracing can be enabled by setting the `trace`
-request parameter.
+For each REST endpoint tracing can be enabled by setting the
+`trace=<trace-id>` request parameter. It is recommended to use the ID
+of the issue that is being investigated as trace ID.
+
+.Example Request
+----
+ GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/suggest_reviewers?trace=issue/123&q=J
+----
+
+It is also possible to omit the trace ID and get a unique trace ID
+generated.
.Example Request
----
GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/suggest_reviewers?trace&q=J
----
+Alternatively request tracing can also be enabled by setting the
+`X-Gerrit-Trace` header:
+
+.Example Request
+----
+ GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/suggest_reviewers?q=J
+ X-Gerrit-Trace: issue/123
+----
+
Enabling tracing results in additional logs with debug information that
are written to the `error_log`. All logs that correspond to the traced
-request are associated with a unique trace ID. This trace ID is
-returned with the REST response in the `X-Gerrit-Trace` header.
+request are associated with the trace ID. The trace ID is returned with
+the REST response in the `X-Gerrit-Trace` header.
.Example Response
----
diff --git a/Documentation/user-request-tracing.txt b/Documentation/user-request-tracing.txt
index bb4c5e4..8430e97 100644
--- a/Documentation/user-request-tracing.txt
+++ b/Documentation/user-request-tracing.txt
@@ -11,10 +11,10 @@
request type:
* REST API: For REST calls tracing can be enabled by setting the
- `trace` request parameter, the trace ID is returned as
- `X-Gerrit-Trace` header. More information about this can be found in
- the link:rest-api.html#tracing[Request Tracing] section of the
- link:rest-api.html[REST API documentation].
+ `trace` request parameter or the `X-Gerrit-Trace` header, the trace
+ ID is returned as `X-Gerrit-Trace` header. More information about
+ this can be found in the link:rest-api.html#tracing[Request Tracing]
+ section of the link:rest-api.html[REST API documentation].
* SSH API: For SSH calls tracing can be enabled by setting the
`--trace` option. More information about this can be found in
the link:cmd-index.html#trace[Trace] section of the
@@ -26,6 +26,16 @@
link:user-upload.html[upload documentation]. Tracing for Git requests
other than Git push is not supported.
+When request tracing is enabled it is possible to provide an ID that
+should be used as trace ID. If a trace ID is not provided a trace ID is
+automatically generated. The trace ID must be provided to the support
+team so that they can find the trace.
+
+When doing traces it is recommended to specify the ID of the issue
+that is being investigated as trace ID so that the traces of the issue
+can be found more easily. When the issue ID is used as trace ID there
+is no need to find the generated trace ID and report it in the issue.
+
Since tracing consumes additional server resources tracing should only
be enabled for single requests if there is a concrete need for
debugging. In particular bots should never enable tracing for all their
@@ -44,3 +54,19 @@
By doing a grep with the trace ID over the error log the log entries
that correspond to the request can be found.
+
+== Which information is captured in a trace?
+
+* request details
+** REST API: request URL, request parameter names, calling user,
+ response code, response body on errors
+** SSH API: parameter names
+** Git API: push options, magic branch parameter names
+* cache misses, cache evictions
+* reads from NoteDb, writes to NoteDb
+* reads of meta data files, writes of meta data files
+* index queries (with parameters and matches)
+* reindex events
+* permission checks (e.g. which rule is responsible for a deny)
+* timer metrics
+* all other logs
diff --git a/Documentation/user-upload.txt b/Documentation/user-upload.txt
index 78bbe99..751e886 100644
--- a/Documentation/user-upload.txt
+++ b/Documentation/user-upload.txt
@@ -424,17 +424,26 @@
[[trace]]
==== Trace
-When pushing to Gerrit tracing can be enabled by setting the `trace`
-push option.
+When pushing to Gerrit tracing can be enabled by setting the
+`trace=<trace-id>` push option. It is recommended to use the ID of the
+issue that is being investigated as trace ID.
----
+ git push -o trace=issue/123 ssh://john.doe@git.example.com:29418/kernel/common HEAD:refs/for/master
+----
+
+It is also possible to omit the trace ID and get a unique trace ID
+generated.
+
+.Example Request
+----
git push -o trace ssh://john.doe@git.example.com:29418/kernel/common HEAD:refs/for/master
----
Enabling tracing results in additional logs with debug information that
are written to the `error_log`. All logs that correspond to the traced
-request are associated with a unique trace ID. This trace ID is
-returned in the command output:
+request are associated with the trace ID. This trace ID is returned in
+the command output:
----
remote: TRACE_ID: 1534174322774-7edf2a7b
diff --git a/WORKSPACE b/WORKSPACE
index 0a6caa2..dca68d3 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1038,8 +1038,8 @@
maven_jar(
name = "elasticsearch-rest-client",
- artifact = "org.elasticsearch.client:elasticsearch-rest-client:6.3.2",
- sha1 = "2077ea5f00fdd2d6af85223b730ba8047303297f",
+ artifact = "org.elasticsearch.client:elasticsearch-rest-client:6.4.0",
+ sha1 = "0eaa13decb9796eb671c5841d0770ae68b348da5",
)
JACKSON_VERSION = "2.8.9"
diff --git a/antlr3/BUILD b/antlr3/BUILD
index 6d7102a..fc96715 100644
--- a/antlr3/BUILD
+++ b/antlr3/BUILD
@@ -15,3 +15,17 @@
],
visibility = ["//visibility:public"],
)
+
+java_library(
+ name = "query_parser",
+ srcs = [":query"],
+ visibility = [
+ "//java/com/google/gerrit/index:__pkg__",
+ "//javatests/com/google/gerrit/index:__pkg__",
+ "//plugins:__pkg__",
+ ],
+ deps = [
+ "//java/com/google/gerrit/index:query_exception",
+ "//lib/antlr:java-runtime",
+ ],
+)
diff --git a/gerrit-acceptance-tests/tests.bzl b/gerrit-acceptance-tests/tests.bzl
deleted file mode 100644
index c1e34dd..0000000
--- a/gerrit-acceptance-tests/tests.bzl
+++ /dev/null
@@ -1,21 +0,0 @@
-load("//tools/bzl:junit.bzl", "junit_tests")
-
-def acceptance_tests(
- group,
- deps = [],
- labels = [],
- vm_args = ["-Xmx256m"],
- **kwargs):
- junit_tests(
- name = group,
- deps = deps + [
- "//gerrit-acceptance-tests:lib",
- ],
- tags = labels + [
- "acceptance",
- "slow",
- ],
- size = "large",
- jvm_flags = vm_args,
- **kwargs
- )
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ChangeInfo.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ChangeInfo.java
index 866d74f..0f786a6 100644
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ChangeInfo.java
+++ b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ChangeInfo.java
@@ -14,6 +14,8 @@
package com.google.gerrit.client.info;
+import static java.util.Comparator.comparing;
+
import com.google.gerrit.client.rpc.NativeMap;
import com.google.gerrit.client.rpc.NativeString;
import com.google.gerrit.client.rpc.Natives;
@@ -30,8 +32,6 @@
import com.google.gwtjsonrpc.client.impl.ser.JavaSqlTimestamp_JsonSerializer;
import java.sql.Timestamp;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -447,18 +447,8 @@
public static void sortRevisionInfoByNumber(JsArray<RevisionInfo> list) {
final int editParent = findEditParent(list);
- Collections.sort(
- Natives.asList(list),
- new Comparator<RevisionInfo>() {
- @Override
- public int compare(RevisionInfo a, RevisionInfo b) {
- return num(a) - num(b);
- }
-
- private int num(RevisionInfo r) {
- return !r.isEdit() ? 2 * (r._number() - 1) + 1 : 2 * editParent;
- }
- });
+ Natives.asList(list)
+ .sort(comparing(r -> !r.isEdit() ? 2 * (r._number() - 1) + 1 : 2 * editParent));
}
public static int findEditParent(JsArray<RevisionInfo> list) {
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/FileInfo.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/FileInfo.java
index 345a260..fc3dbf1 100644
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/FileInfo.java
+++ b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/FileInfo.java
@@ -19,7 +19,6 @@
import com.google.gerrit.reviewdb.client.Patch;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
-import java.util.Collections;
import java.util.Comparator;
public class FileInfo extends JavaScriptObject {
@@ -55,8 +54,7 @@
public final native void _row(int r) /*-{ this._row = r }-*/;
public static void sortFileInfoByPath(JsArray<FileInfo> list) {
- Collections.sort(
- Natives.asList(list), Comparator.comparing(FileInfo::path, FilenameComparator.INSTANCE));
+ Natives.asList(list).sort(Comparator.comparing(FileInfo::path, FilenameComparator.INSTANCE));
}
public static String getFileName(String path) {
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/rpc/NativeMap.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/rpc/NativeMap.java
index 4b17068..41306ff 100644
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/rpc/NativeMap.java
+++ b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/rpc/NativeMap.java
@@ -14,11 +14,12 @@
package com.google.gerrit.client.rpc;
+import static java.util.stream.Collectors.toCollection;
+
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.user.client.rpc.AsyncCallback;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -57,10 +58,7 @@
}
public final List<String> sortedKeys() {
- Set<String> keys = keySet();
- List<String> sorted = new ArrayList<>(keys);
- Collections.sort(sorted);
- return sorted;
+ return keySet().stream().sorted().collect(toCollection(ArrayList::new));
}
public final native JsArray<T> values() /*-{
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/access/ProjectAccessInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/access/ProjectAccessInfo.java
index b115c7d..88635df 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/access/ProjectAccessInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/access/ProjectAccessInfo.java
@@ -19,6 +19,8 @@
public class ProjectAccessInfo extends JavaScriptObject {
public final native boolean canAddRefs() /*-{ return this.can_add ? true : false; }-*/;
+ public final native boolean canAddTagRefs() /*-{ return this.can_add_tags ? true : false; }-*/;
+
public final native boolean isOwner() /*-{ return this.is_owner ? true : false; }-*/;
public final native boolean configVisible() /*-{ return this.config_visible ? true : false; }-*/;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGpgKeysScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGpgKeysScreen.java
index 0dc1dab..1a0090a 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGpgKeysScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGpgKeysScreen.java
@@ -14,6 +14,8 @@
package com.google.gerrit.client.account;
+import static java.util.Comparator.comparing;
+
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.info.GpgKeyInfo;
import com.google.gerrit.client.rpc.GerritCallback;
@@ -40,8 +42,6 @@
import com.google.gwtexpui.clippy.client.CopyableLabel;
import com.google.gwtexpui.globalkey.client.NpTextArea;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
public class MyGpgKeysScreen extends SettingsScreen {
@@ -118,14 +118,7 @@
List<GpgKeyInfo> list = Natives.asList(result.values());
// TODO(dborowitz): Sort on something more meaningful, like
// created date?
- Collections.sort(
- list,
- new Comparator<GpgKeyInfo>() {
- @Override
- public int compare(GpgKeyInfo a, GpgKeyInfo b) {
- return a.id().compareTo(b.id());
- }
- });
+ list.sort(comparing(GpgKeyInfo::id));
keys.clear();
keyText.setText("");
errorPanel.setVisible(false);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyIdentitiesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyIdentitiesScreen.java
index 5c6d40f..730d98e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyIdentitiesScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyIdentitiesScreen.java
@@ -14,6 +14,8 @@
package com.google.gerrit.client.account;
+import static java.util.Comparator.naturalOrder;
+
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.VoidResult;
import com.google.gerrit.client.rpc.GerritCallback;
@@ -29,7 +31,6 @@
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -169,7 +170,7 @@
void display(JsArray<ExternalIdInfo> results) {
List<ExternalIdInfo> idList = Natives.asList(results);
- Collections.sort(idList);
+ idList.sort(naturalOrder());
while (1 < table.getRowCount()) {
table.removeRow(table.getRowCount() - 1);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/UsernameField.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/UsernameField.java
index 80c6d1a..4fdd067 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/UsernameField.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/UsernameField.java
@@ -41,7 +41,7 @@
// corresponding regular expressions in the
// com.google.gerrit.server.account.externalids.ExternalId class.
private static final String USER_NAME_PATTERN_FIRST_REGEX = "[a-zA-Z0-9]";
- private static final String USER_NAME_PATTERN_REST_REGEX = "[a-zA-Z0-9._@-]";
+ private static final String USER_NAME_PATTERN_REST_REGEX = "[a-zA-Z0-9.!#$%&’*+=?^_`\\{|\\}~@-]";
private CopyableLabel userNameLbl;
private NpTextBox userNameTxt;
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 1b946cd..7bd8b82 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
@@ -14,6 +14,8 @@
package com.google.gerrit.client.admin;
+import static java.util.stream.Collectors.toCollection;
+
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.LabelType;
@@ -45,7 +47,6 @@
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.ValueListBox;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
public class AccessSectionEditor extends Composite
@@ -205,9 +206,8 @@
}
private void sortPermissions(AccessSection accessSection) {
- List<Permission> permissionList = new ArrayList<>(accessSection.getPermissions());
- Collections.sort(permissionList);
- accessSection.setPermissions(permissionList);
+ accessSection.setPermissions(
+ accessSection.getPermissions().stream().sorted().collect(toCollection(ArrayList::new)));
}
void setEditing(boolean editing) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupMembersScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupMembersScreen.java
index 2614224..6eaab5d 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupMembersScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupMembersScreen.java
@@ -14,6 +14,8 @@
package com.google.gerrit.client.admin;
+import static java.util.Comparator.comparing;
+
import com.google.gerrit.client.Dispatcher;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.VoidResult;
@@ -29,7 +31,6 @@
import com.google.gerrit.client.ui.FancyFlexTable;
import com.google.gerrit.client.ui.Hyperlink;
import com.google.gerrit.client.ui.SmallHeading;
-import com.google.gerrit.common.Nullable;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
@@ -295,26 +296,9 @@
void insert(AccountInfo info) {
Comparator<AccountInfo> c =
- new Comparator<AccountInfo>() {
- @Override
- public int compare(AccountInfo a, AccountInfo b) {
- int cmp = nullToEmpty(a.name()).compareTo(nullToEmpty(b.name()));
- if (cmp != 0) {
- return cmp;
- }
-
- cmp = nullToEmpty(a.email()).compareTo(nullToEmpty(b.email()));
- if (cmp != 0) {
- return cmp;
- }
-
- return a._accountId() - b._accountId();
- }
-
- public String nullToEmpty(String str) {
- return str == null ? "" : str;
- }
- };
+ comparing((AccountInfo a) -> nullToEmpty(a.name()))
+ .thenComparing(a -> nullToEmpty(a.email()))
+ .thenComparing(AccountInfo::_accountId);
int insertPos = getInsertRow(c, info);
if (insertPos >= 0) {
table.insertRow(insertPos);
@@ -405,20 +389,7 @@
void insert(GroupInfo info) {
Comparator<GroupInfo> c =
- new Comparator<GroupInfo>() {
- @Override
- public int compare(GroupInfo a, GroupInfo b) {
- int cmp = nullToEmpty(a.name()).compareTo(nullToEmpty(b.name()));
- if (cmp != 0) {
- return cmp;
- }
- return a.getGroupUUID().compareTo(b.getGroupUUID());
- }
-
- private String nullToEmpty(@Nullable String str) {
- return (str == null) ? "" : str;
- }
- };
+ comparing((GroupInfo g) -> nullToEmpty(g.name())).thenComparing(GroupInfo::getGroupUUID);
int insertPos = getInsertRow(c, info);
if (insertPos >= 0) {
table.insertRow(insertPos);
@@ -457,4 +428,9 @@
setRowItem(row, i);
}
}
+
+ // Like Guava's Strings#nullToEmpty, which can't be used in GWT UI code.
+ private static String nullToEmpty(String str) {
+ return str == null ? "" : str;
+ }
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.java
index fe27e9c..7b18a39 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.java
@@ -36,8 +36,6 @@
String effectiveMaxObjectSizeLimit(String effectiveMaxObjectSizeLimit);
- String globalMaxObjectSizeLimit(String globalMaxObjectSizeLimit);
-
String noMaxObjectSizeLimit();
String pluginProjectOptionsTitle(String pluginName);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.properties
index f746365..c9aa987 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.properties
@@ -6,7 +6,6 @@
deletedReference = Reference {0} was deleted
deletedSection = Section {0} was deleted
effectiveMaxObjectSizeLimit = effective: {0} bytes
-globalMaxObjectSizeLimit = The global max object size limit is set to {0}. The limit cannot be increased on project level.
noMaxObjectSizeLimit = No max object size limit is set.
pluginProjectOptionsTitle = {0} Plugin Options
pluginProjectOptionsTitle = {0} Plugin
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupTable.java
index 259847e..be0db41 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupTable.java
@@ -14,6 +14,8 @@
package com.google.gerrit.client.admin;
+import static java.util.Comparator.comparing;
+
import com.google.gerrit.client.Dispatcher;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.groups.GroupList;
@@ -32,8 +34,6 @@
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
import com.google.gwt.user.client.ui.HTMLTable.Cell;
import com.google.gwt.user.client.ui.Image;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
public class GroupTable extends NavigationTable<GroupInfo> {
@@ -105,14 +105,7 @@
table.removeRow(table.getRowCount() - 1);
}
- Collections.sort(
- list,
- new Comparator<GroupInfo>() {
- @Override
- public int compare(GroupInfo a, GroupInfo b) {
- return a.name().compareTo(b.name());
- }
- });
+ list.sort(comparing(GroupInfo::name));
for (GroupInfo group : list.subList(fromIndex, toIndex)) {
final int row = table.getRowCount();
table.insertRow(row);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java
index 05a29ac..d10a031 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoScreen.java
@@ -442,9 +442,8 @@
if (result.maxObjectSizeLimit().value() != null) {
effectiveMaxObjectSizeLimit.setText(
AdminMessages.I.effectiveMaxObjectSizeLimit(result.maxObjectSizeLimit().value()));
- if (result.maxObjectSizeLimit().inheritedValue() != null) {
- effectiveMaxObjectSizeLimit.setTitle(
- AdminMessages.I.globalMaxObjectSizeLimit(result.maxObjectSizeLimit().inheritedValue()));
+ if (result.maxObjectSizeLimit().summary() != null) {
+ effectiveMaxObjectSizeLimit.setTitle(result.maxObjectSizeLimit().summary());
}
} else {
effectiveMaxObjectSizeLimit.setText(AdminMessages.I.noMaxObjectSizeLimit());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectTagsScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectTagsScreen.java
index 18e4176..22c331d 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectTagsScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectTagsScreen.java
@@ -94,7 +94,7 @@
new GerritCallback<ProjectAccessInfo>() {
@Override
public void onSuccess(ProjectAccessInfo result) {
- addPanel.setVisible(result.canAddRefs());
+ addPanel.setVisible(result.canAddTagRefs());
}
});
query = new Query(match).start(start).run();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Labels.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Labels.java
index 1f4820f..801a927 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Labels.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Labels.java
@@ -14,6 +14,10 @@
package com.google.gerrit.client.change;
+import static java.util.stream.Collectors.collectingAndThen;
+import static java.util.stream.Collectors.toCollection;
+import static java.util.stream.Collectors.toList;
+
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.changes.ChangeApi;
import com.google.gerrit.client.changes.Util;
@@ -135,9 +139,12 @@
}
void set(ChangeInfo info) {
- List<String> names = new ArrayList<>(info.labels());
+ List<String> names =
+ info.labels()
+ .stream()
+ .sorted()
+ .collect(collectingAndThen(toList(), Collections::unmodifiableList));
Set<Integer> removable = info.removableReviewerIds();
- Collections.sort(names);
resize(names.size(), 2);
@@ -197,8 +204,7 @@
}
private static List<Integer> sort(Set<Integer> keySet, int a, int b) {
- List<Integer> r = new ArrayList<>(keySet);
- Collections.sort(r);
+ List<Integer> r = keySet.stream().sorted().collect(toCollection(ArrayList::new));
if (keySet.contains(a)) {
r.remove(Integer.valueOf(a));
r.add(0, a);
@@ -238,31 +244,32 @@
Set<Integer> removable,
String label,
Map<Integer, VotableInfo> votable) {
- List<AccountInfo> users = new ArrayList<>(in);
- Collections.sort(
- users,
- new Comparator<AccountInfo>() {
- @Override
- public int compare(AccountInfo a, AccountInfo b) {
- String as = name(a);
- String bs = name(b);
- if (as.isEmpty()) {
- return 1;
- } else if (bs.isEmpty()) {
- return -1;
- }
- return as.compareTo(bs);
- }
+ List<AccountInfo> users =
+ in.stream()
+ .sorted(
+ new Comparator<AccountInfo>() {
+ @Override
+ public int compare(AccountInfo a, AccountInfo b) {
+ String as = name(a);
+ String bs = name(b);
+ if (as.isEmpty()) {
+ return 1;
+ } else if (bs.isEmpty()) {
+ return -1;
+ }
+ return as.compareTo(bs);
+ }
- private String name(AccountInfo a) {
- if (a.name() != null) {
- return a.name();
- } else if (a.email() != null) {
- return a.email();
- }
- return "";
- }
- });
+ private String name(AccountInfo a) {
+ if (a.name() != null) {
+ return a.name();
+ } else if (a.email() != null) {
+ return a.email();
+ }
+ return "";
+ }
+ })
+ .collect(collectingAndThen(toList(), Collections::unmodifiableList));
SafeHtmlBuilder html = new SafeHtmlBuilder();
Iterator<? extends AccountInfo> itr = users.iterator();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java
index 0bbd614..8a1a2d5 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ReplyBox.java
@@ -16,6 +16,8 @@
import static com.google.gwt.event.dom.client.KeyCodes.KEY_ENTER;
import static com.google.gwt.event.dom.client.KeyCodes.KEY_MAC_ENTER;
+import static java.util.stream.Collectors.collectingAndThen;
+import static java.util.stream.Collectors.toList;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.changes.ChangeApi;
@@ -123,11 +125,15 @@
this.lc = new LocalComments(project, psId.getParentKey());
initWidget(uiBinder.createAndBindUi(this));
- List<String> names = new ArrayList<>(permitted.keySet());
+ List<String> names =
+ permitted
+ .keySet()
+ .stream()
+ .sorted()
+ .collect(collectingAndThen(toList(), Collections::unmodifiableList));
if (names.isEmpty()) {
UIObject.setVisible(labelsParent, false);
} else {
- Collections.sort(names);
renderLabels(names, all, permitted);
}
@@ -439,8 +445,11 @@
clp, project, psId, Util.C.commitMessage(), copyPath(Patch.MERGE_LIST, l)));
}
- List<String> paths = new ArrayList<>(m.keySet());
- Collections.sort(paths);
+ List<String> paths =
+ m.keySet()
+ .stream()
+ .sorted()
+ .collect(collectingAndThen(toList(), Collections::unmodifiableList));
for (String path : paths) {
if (!Patch.isMagic(path)) {
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 c6e4e2f..7ec1102 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
@@ -14,6 +14,8 @@
package com.google.gerrit.client.changes;
+import static java.util.Comparator.comparing;
+
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.NotFoundScreen;
import com.google.gerrit.client.info.ChangeInfo;
@@ -176,7 +178,7 @@
}
}
- Collections.sort(Natives.asList(out), outComparator());
+ Natives.asList(out).sort(outComparator());
table.updateColumnsForLabels(wip, out, in, done);
workInProgress.display(wip);
@@ -187,16 +189,7 @@
}
private Comparator<ChangeInfo> outComparator() {
- return new Comparator<ChangeInfo>() {
- @Override
- public int compare(ChangeInfo a, ChangeInfo b) {
- int cmp = a.created().compareTo(b.created());
- if (cmp != 0) {
- return cmp;
- }
- return a._number() - b._number();
- }
- };
+ return comparing(ChangeInfo::created).thenComparing(ChangeInfo::_number);
}
private boolean hasChanges(JsArray<ChangeList> result) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java
index caea87e..4fda78b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java
@@ -16,11 +16,13 @@
import static com.google.gerrit.client.FormatUtil.relativeFormat;
import static com.google.gerrit.client.FormatUtil.shortFormat;
+import static java.util.stream.Collectors.toList;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.info.AccountInfo;
import com.google.gerrit.client.info.ChangeInfo;
import com.google.gerrit.client.info.ChangeInfo.LabelInfo;
+import com.google.gerrit.client.rpc.Natives;
import com.google.gerrit.client.ui.AccountLinkPanel;
import com.google.gerrit.client.ui.BranchLink;
import com.google.gerrit.client.ui.ChangeLink;
@@ -45,6 +47,7 @@
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.Widget;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
@@ -185,17 +188,13 @@
}
public void updateColumnsForLabels(ChangeList... lists) {
- labelNames = new ArrayList<>();
- for (ChangeList list : lists) {
- for (int i = 0; i < list.length(); i++) {
- for (String name : list.get(i).labels()) {
- if (!labelNames.contains(name)) {
- labelNames.add(name);
- }
- }
- }
- }
- Collections.sort(labelNames);
+ labelNames =
+ Arrays.stream(lists)
+ .flatMap(l -> Natives.asList(l).stream())
+ .flatMap(c -> c.labels().stream())
+ .distinct()
+ .sorted()
+ .collect(toList());
int baseColumns = BASE_COLUMNS;
if (baseColumns + labelNames.size() < columns) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardsTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardsTable.java
index 0e4ef4e..dcb9c01 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardsTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/dashboards/DashboardsTable.java
@@ -14,6 +14,8 @@
package com.google.gerrit.client.dashboards;
+import static java.util.Comparator.comparing;
+
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.rpc.Natives;
import com.google.gerrit.client.ui.NavigationTable;
@@ -25,8 +27,6 @@
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
import com.google.gwt.user.client.ui.Image;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -75,14 +75,7 @@
table.removeRow(table.getRowCount() - 1);
}
- Collections.sort(
- list,
- new Comparator<DashboardInfo>() {
- @Override
- public int compare(DashboardInfo a, DashboardInfo b) {
- return a.id().compareTo(b.id());
- }
- });
+ list.sort(comparing(DashboardInfo::id));
String ref = null;
for (DashboardInfo d : list) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentsCollections.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentsCollections.java
index 533b745..2698584 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentsCollections.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CommentsCollections.java
@@ -14,6 +14,8 @@
package com.google.gerrit.client.diff;
+import static java.util.Comparator.comparing;
+
import com.google.gerrit.client.DiffObject;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.changes.CommentApi;
@@ -27,8 +29,6 @@
import com.google.gerrit.reviewdb.client.Project;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.user.client.rpc.AsyncCallback;
-import java.util.Collections;
-import java.util.Comparator;
/** Collection of published and draft comments loaded from the server. */
class CommentsCollections {
@@ -158,14 +158,7 @@
for (CommentInfo c : Natives.asList(in)) {
c.path(path);
}
- Collections.sort(
- Natives.asList(in),
- new Comparator<CommentInfo>() {
- @Override
- public int compare(CommentInfo a, CommentInfo b) {
- return a.updated().compareTo(b.updated());
- }
- });
+ Natives.asList(in).sort(comparing(CommentInfo::updated));
}
return in;
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedChunkManager.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedChunkManager.java
index 1a662e2..98ad023 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedChunkManager.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/UnifiedChunkManager.java
@@ -14,6 +14,8 @@
package com.google.gerrit.client.diff;
+import static java.util.Comparator.comparing;
+
import com.google.gerrit.client.diff.DiffInfo.Region;
import com.google.gerrit.client.diff.DiffInfo.Span;
import com.google.gerrit.client.rpc.Natives;
@@ -227,12 +229,7 @@
/** Diff chunks are ordered by their starting lines in CodeMirror */
private Comparator<UnifiedDiffChunkInfo> getDiffChunkComparatorCmLine() {
- return new Comparator<UnifiedDiffChunkInfo>() {
- @Override
- public int compare(UnifiedDiffChunkInfo o1, UnifiedDiffChunkInfo o2) {
- return o1.getCmLine() - o2.getCmLine();
- }
- };
+ return comparing(UnifiedDiffChunkInfo::getCmLine);
}
@Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java
index 4185ef3..684f8e6 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ConfigInfo.java
@@ -175,10 +175,10 @@
public static class MaxObjectSizeLimitInfo extends JavaScriptObject {
public final native String value() /*-{ return this.value; }-*/;
- public final native String inheritedValue() /*-{ return this.inherited_value; }-*/;
-
public final native String configuredValue() /*-{ return this.configured_value }-*/;
+ public final native String summary() /*-{ return this.summary; }-*/;
+
protected MaxObjectSizeLimitInfo() {}
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectsTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectsTable.java
index ac89180..3576b12 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectsTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectsTable.java
@@ -14,14 +14,14 @@
package com.google.gerrit.client.ui;
+import static java.util.Comparator.comparing;
+
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.projects.ProjectInfo;
import com.google.gerrit.client.projects.ProjectMap;
import com.google.gerrit.client.rpc.Natives;
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
import com.google.gwt.user.client.ui.Image;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
public class ProjectsTable extends NavigationTable<ProjectInfo> {
@@ -69,14 +69,7 @@
}
List<ProjectInfo> list = Natives.asList(projects.values());
- Collections.sort(
- list,
- new Comparator<ProjectInfo>() {
- @Override
- public int compare(ProjectInfo a, ProjectInfo b) {
- return a.name().compareTo(b.name());
- }
- });
+ list.sort(comparing(ProjectInfo::name));
for (ProjectInfo p : list.subList(fromIndex, toIndex)) {
insert(table.getRowCount(), p);
}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInfo.java b/gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInfo.java
index c6f113e..9df066d 100644
--- a/gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInfo.java
+++ b/gerrit-gwtui/src/main/java/net/codemirror/mode/ModeInfo.java
@@ -14,6 +14,8 @@
package net.codemirror.mode;
+import static java.util.Comparator.comparing;
+
import com.google.gerrit.client.rpc.NativeMap;
import com.google.gerrit.client.rpc.Natives;
import com.google.gwt.core.client.JavaScriptObject;
@@ -21,8 +23,6 @@
import com.google.gwt.core.client.JsArrayString;
import com.google.gwt.resources.client.DataResource;
import com.google.gwt.safehtml.shared.SafeUri;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
@@ -242,14 +242,7 @@
byMime.put(m.mode(), m);
}
}
- Collections.sort(
- Natives.asList(filtered),
- new Comparator<ModeInfo>() {
- @Override
- public int compare(ModeInfo a, ModeInfo b) {
- return a.name().toLowerCase().compareTo(b.name().toLowerCase());
- }
- });
+ Natives.asList(filtered).sort(comparing(m -> m.name().toLowerCase()));
setAll(filtered);
}
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 1d87880..69d603f 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -1487,12 +1487,7 @@
assertNotifyTo(expected.email, expected.fullName);
}
- protected void assertNotifyTo(
- com.google.gerrit.acceptance.testsuite.account.TestAccount expected) {
- assertNotifyTo(expected.preferredEmail().orElse(null), expected.fullname().orElse(null));
- }
-
- private void assertNotifyTo(String expectedEmail, String expectedFullname) {
+ protected void assertNotifyTo(String expectedEmail, String expectedFullname) {
Address expectedAddress = new Address(expectedFullname, expectedEmail);
assertThat(sender.getMessages()).hasSize(1);
Message m = sender.getMessages().get(0);
@@ -1506,11 +1501,6 @@
assertNotifyCc(expected.emailAddress);
}
- protected void assertNotifyCc(
- com.google.gerrit.acceptance.testsuite.account.TestAccount expected) {
- assertNotifyCc(expected.preferredEmail().orElse(null), expected.fullname().orElse(null));
- }
-
protected void assertNotifyCc(String expectedEmail, String expectedFullname) {
Address expectedAddress = new Address(expectedFullname, expectedEmail);
assertNotifyCc(expectedAddress);
@@ -1533,13 +1523,10 @@
assertThat(m.headers().get("Cc").isEmpty()).isTrue();
}
- protected void assertNotifyBcc(
- com.google.gerrit.acceptance.testsuite.account.TestAccount expected) {
+ protected void assertNotifyBcc(String expectedEmail, String expectedFullName) {
assertThat(sender.getMessages()).hasSize(1);
Message m = sender.getMessages().get(0);
- assertThat(m.rcpt())
- .containsExactly(
- new Address(expected.fullname().orElse(null), expected.preferredEmail().orElse(null)));
+ assertThat(m.rcpt()).containsExactly(new Address(expectedFullName, expectedEmail));
assertThat(m.headers().get("To").isEmpty()).isTrue();
assertThat(m.headers().get("Cc").isEmpty()).isTrue();
}
diff --git a/java/com/google/gerrit/acceptance/EventRecorder.java b/java/com/google/gerrit/acceptance/EventRecorder.java
index 218ee18..5654c35 100644
--- a/java/com/google/gerrit/acceptance/EventRecorder.java
+++ b/java/com/google/gerrit/acceptance/EventRecorder.java
@@ -63,6 +63,7 @@
eventListenerRegistration =
eventListeners.add(
+ "gerrit",
new UserScopedEventListener() {
@Override
public void onEvent(Event e) {
diff --git a/java/com/google/gerrit/acceptance/GerritServer.java b/java/com/google/gerrit/acceptance/GerritServer.java
index 6e5424c..582c7cb 100644
--- a/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/java/com/google/gerrit/acceptance/GerritServer.java
@@ -24,6 +24,8 @@
import com.google.common.collect.ImmutableList;
import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
import com.google.gerrit.acceptance.testsuite.account.AccountOperationsImpl;
+import com.google.gerrit.acceptance.testsuite.group.GroupOperations;
+import com.google.gerrit.acceptance.testsuite.group.GroupOperationsImpl;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.config.FactoryModule;
import com.google.gerrit.lucene.LuceneIndexModule;
@@ -441,6 +443,7 @@
bindConstant().annotatedWith(SshEnabled.class).to(daemon.getEnableSshd());
bind(AccountCreator.class);
bind(AccountOperations.class).to(AccountOperationsImpl.class);
+ bind(GroupOperations.class).to(GroupOperationsImpl.class);
factory(PushOneCommit.Factory.class);
install(InProcessProtocol.module());
install(new NoSshModule());
diff --git a/java/com/google/gerrit/acceptance/HttpResponse.java b/java/com/google/gerrit/acceptance/HttpResponse.java
index b62e932..3e98d71 100644
--- a/java/com/google/gerrit/acceptance/HttpResponse.java
+++ b/java/com/google/gerrit/acceptance/HttpResponse.java
@@ -14,11 +14,16 @@
package com.google.gerrit.acceptance;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.ByteBuffer;
+import java.util.Arrays;
import org.apache.http.Header;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
@@ -34,7 +39,7 @@
public Reader getReader() throws IllegalStateException, IOException {
if (reader == null && response.getEntity() != null) {
- reader = new InputStreamReader(response.getEntity().getContent());
+ reader = new InputStreamReader(response.getEntity().getContent(), UTF_8);
}
return reader;
}
@@ -59,6 +64,13 @@
return hdr != null ? hdr.getValue() : null;
}
+ public ImmutableList<String> getHeaders(String name) {
+ return Arrays.asList(response.getHeaders(name))
+ .stream()
+ .map(Header::getValue)
+ .collect(toImmutableList());
+ }
+
public boolean hasContent() {
Preconditions.checkNotNull(response, "Response is not initialized.");
return response.getEntity() != null;
diff --git a/java/com/google/gerrit/acceptance/SshSession.java b/java/com/google/gerrit/acceptance/SshSession.java
index 27dae3b..52d7f28 100644
--- a/java/com/google/gerrit/acceptance/SshSession.java
+++ b/java/com/google/gerrit/acceptance/SshSession.java
@@ -17,6 +17,7 @@
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.gerrit.acceptance.testsuite.account.TestSshKeys;
import com.jcraft.jsch.ChannelExec;
@@ -54,10 +55,10 @@
InputStream err = channel.getErrStream();
channel.connect();
- Scanner s = new Scanner(err).useDelimiter("\\A");
+ Scanner s = new Scanner(err, UTF_8.name()).useDelimiter("\\A");
error = s.hasNext() ? s.next() : null;
- s = new Scanner(in).useDelimiter("\\A");
+ s = new Scanner(in, UTF_8.name()).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
} finally {
channel.disconnect();
diff --git a/java/com/google/gerrit/acceptance/testsuite/account/AccountOperations.java b/java/com/google/gerrit/acceptance/testsuite/account/AccountOperations.java
index 58a00d0..61b7599 100644
--- a/java/com/google/gerrit/acceptance/testsuite/account/AccountOperations.java
+++ b/java/com/google/gerrit/acceptance/testsuite/account/AccountOperations.java
@@ -42,7 +42,7 @@
* <p>Example:
*
* <pre>
- * TestAccount createdAccount = accountOperations
+ * Account.Id createdAccountId = accountOperations
* .newAccount()
* .username("janedoe")
* .preferredEmail("janedoe@example.com")
diff --git a/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java
index 94b511b..ebbcfe4 100644
--- a/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java
+++ b/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java
@@ -59,12 +59,12 @@
return TestAccountCreation.builder(this::createAccount);
}
- private TestAccount createAccount(TestAccountCreation accountCreation) throws Exception {
+ private Account.Id createAccount(TestAccountCreation accountCreation) throws Exception {
AccountsUpdate.AccountUpdater accountUpdater =
(account, updateBuilder) ->
fillBuilder(updateBuilder, accountCreation, account.getAccount().getId());
AccountState createdAccount = createAccount(accountUpdater);
- return toTestAccount(createdAccount);
+ return createdAccount.getAccount().getId();
}
private AccountState createAccount(AccountsUpdate.AccountUpdater accountUpdater)
@@ -85,17 +85,6 @@
accountCreation.active().ifPresent(builder::setActive);
}
- private static TestAccount toTestAccount(AccountState accountState) {
- Account createdAccount = accountState.getAccount();
- return TestAccount.builder()
- .accountId(createdAccount.getId())
- .preferredEmail(Optional.ofNullable(createdAccount.getPreferredEmail()))
- .fullname(Optional.ofNullable(createdAccount.getFullName()))
- .username(accountState.getUserName())
- .active(accountState.getAccount().isActive())
- .build();
- }
-
private static InternalAccountUpdate.Builder setPreferredEmail(
InternalAccountUpdate.Builder builder, Account.Id accountId, String preferredEmail) {
return builder
@@ -133,6 +122,17 @@
return toTestAccount(account);
}
+ private TestAccount toTestAccount(AccountState accountState) {
+ Account account = accountState.getAccount();
+ return TestAccount.builder()
+ .accountId(account.getId())
+ .preferredEmail(Optional.ofNullable(account.getPreferredEmail()))
+ .fullname(Optional.ofNullable(account.getFullName()))
+ .username(accountState.getUserName())
+ .active(accountState.getAccount().isActive())
+ .build();
+ }
+
@Override
public TestAccountUpdate.Builder forUpdate() {
return TestAccountUpdate.builder(this::updateAccount);
diff --git a/java/com/google/gerrit/acceptance/testsuite/account/TestAccountCreation.java b/java/com/google/gerrit/acceptance/testsuite/account/TestAccountCreation.java
index a82d180..ab32409 100644
--- a/java/com/google/gerrit/acceptance/testsuite/account/TestAccountCreation.java
+++ b/java/com/google/gerrit/acceptance/testsuite/account/TestAccountCreation.java
@@ -16,6 +16,7 @@
import com.google.auto.value.AutoValue;
import com.google.gerrit.acceptance.testsuite.ThrowingFunction;
+import com.google.gerrit.reviewdb.client.Account;
import java.util.Optional;
@AutoValue
@@ -32,9 +33,9 @@
public abstract Optional<Boolean> active();
- abstract ThrowingFunction<TestAccountCreation, TestAccount> accountCreator();
+ abstract ThrowingFunction<TestAccountCreation, Account.Id> accountCreator();
- public static Builder builder(ThrowingFunction<TestAccountCreation, TestAccount> accountCreator) {
+ public static Builder builder(ThrowingFunction<TestAccountCreation, Account.Id> accountCreator) {
return new AutoValue_TestAccountCreation.Builder()
.accountCreator(accountCreator)
.httpPassword("http-pass");
@@ -83,11 +84,11 @@
}
abstract Builder accountCreator(
- ThrowingFunction<TestAccountCreation, TestAccount> accountCreator);
+ ThrowingFunction<TestAccountCreation, Account.Id> accountCreator);
abstract TestAccountCreation autoBuild();
- public TestAccount create() throws Exception {
+ public Account.Id create() throws Exception {
TestAccountCreation accountUpdate = autoBuild();
return accountUpdate.accountCreator().apply(accountUpdate);
}
diff --git a/java/com/google/gerrit/acceptance/testsuite/group/GroupOperations.java b/java/com/google/gerrit/acceptance/testsuite/group/GroupOperations.java
new file mode 100644
index 0000000..f75ca2e
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/testsuite/group/GroupOperations.java
@@ -0,0 +1,97 @@
+// Copyright (C) 2018 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.acceptance.testsuite.group;
+
+import com.google.gerrit.reviewdb.client.AccountGroup;
+
+/**
+ * An aggregation of operations on groups for test purposes.
+ *
+ * <p>To execute the operations, no Gerrit permissions are necessary.
+ *
+ * <p><strong>Note:</strong> This interface is not implemented using the REST or extension API.
+ * Hence, it cannot be used for testing those APIs.
+ */
+public interface GroupOperations {
+ /**
+ * Starts the fluent chain for querying or modifying a group. Please see the methods of {@link
+ * MoreGroupOperations} for details on possible operations.
+ *
+ * @return an aggregation of operations on a specific group
+ */
+ MoreGroupOperations group(AccountGroup.UUID groupUuid);
+
+ /**
+ * Starts the fluent chain to create a group. The returned builder can be used to specify the
+ * attributes of the new group. To create the group for real, {@link
+ * TestGroupCreation.Builder#create()} must be called.
+ *
+ * <p>Example:
+ *
+ * <pre>
+ * AccountGroup.UUID createdGroupUuid = groupOperations
+ * .newGroup()
+ * .name("verifiers")
+ * .description("All verifiers of this server")
+ * .create();
+ * </pre>
+ *
+ * <p><strong>Note:</strong> If another group with the provided name already exists, the creation
+ * of the group will fail.
+ *
+ * @return a builder to create the new group
+ */
+ TestGroupCreation.Builder newGroup();
+
+ /** An aggregation of methods on a specific group. */
+ interface MoreGroupOperations {
+
+ /**
+ * Checks whether the group exists.
+ *
+ * @return {@code true} if the group exists
+ */
+ boolean exists() throws Exception;
+
+ /**
+ * Retrieves the group.
+ *
+ * <p><strong>Note:</strong> This call will fail with an exception if the requested group
+ * doesn't exist. If you want to check for the existence of a group, use {@link #exists()}
+ * instead.
+ *
+ * @return the corresponding {@code TestGroup}
+ */
+ TestGroup get() throws Exception;
+
+ /**
+ * Starts the fluent chain to update a group. The returned builder can be used to specify how
+ * the attributes of the group should be modified. To update the group for real, {@link
+ * TestGroupUpdate.Builder#update()} must be called.
+ *
+ * <p>Example:
+ *
+ * <pre>
+ * groupOperations.forUpdate().description("Another description for this group").update();
+ * </pre>
+ *
+ * <p><strong>Note:</strong> The update will fail with an exception if the group to update
+ * doesn't exist. If you want to check for the existence of a group, use {@link #exists()}.
+ *
+ * @return a builder to update the group
+ */
+ TestGroupUpdate.Builder forUpdate();
+ }
+}
diff --git a/java/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImpl.java
new file mode 100644
index 0000000..f9769c5
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImpl.java
@@ -0,0 +1,159 @@
+// Copyright (C) 2018 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.acceptance.testsuite.group;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.server.GerritPersonIdent;
+import com.google.gerrit.server.Sequences;
+import com.google.gerrit.server.ServerInitiated;
+import com.google.gerrit.server.account.GroupUUID;
+import com.google.gerrit.server.group.InternalGroup;
+import com.google.gerrit.server.group.db.Groups;
+import com.google.gerrit.server.group.db.GroupsUpdate;
+import com.google.gerrit.server.group.db.InternalGroupCreation;
+import com.google.gerrit.server.group.db.InternalGroupUpdate;
+import com.google.gwtorm.server.OrmDuplicateKeyException;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import java.io.IOException;
+import java.util.Optional;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.PersonIdent;
+
+/**
+ * The implementation of {@code GroupOperations}.
+ *
+ * <p>There is only one implementation of {@code GroupOperations}. Nevertheless, we keep the
+ * separation between interface and implementation to enhance clarity.
+ */
+public class GroupOperationsImpl implements GroupOperations {
+ private final Groups groups;
+ private final GroupsUpdate groupsUpdate;
+ private final Sequences seq;
+ private final PersonIdent serverIdent;
+
+ @Inject
+ public GroupOperationsImpl(
+ Groups groups,
+ @ServerInitiated GroupsUpdate groupsUpdate,
+ Sequences seq,
+ @GerritPersonIdent PersonIdent serverIdent) {
+ this.groups = groups;
+ this.groupsUpdate = groupsUpdate;
+ this.seq = seq;
+ this.serverIdent = serverIdent;
+ }
+
+ @Override
+ public MoreGroupOperations group(AccountGroup.UUID groupUuid) {
+ return new MoreGroupOperationsImpl(groupUuid);
+ }
+
+ @Override
+ public TestGroupCreation.Builder newGroup() {
+ return TestGroupCreation.builder(this::createNewGroup);
+ }
+
+ private AccountGroup.UUID createNewGroup(TestGroupCreation groupCreation)
+ throws ConfigInvalidException, IOException, OrmException {
+ InternalGroupCreation internalGroupCreation = toInternalGroupCreation(groupCreation);
+ InternalGroupUpdate internalGroupUpdate = toInternalGroupUpdate(groupCreation);
+ InternalGroup internalGroup =
+ groupsUpdate.createGroup(internalGroupCreation, internalGroupUpdate);
+ return internalGroup.getGroupUUID();
+ }
+
+ private InternalGroupCreation toInternalGroupCreation(TestGroupCreation groupCreation)
+ throws OrmException {
+ AccountGroup.Id groupId = new AccountGroup.Id(seq.nextGroupId());
+ String groupName = groupCreation.name().orElse("group-with-id-" + groupId.get());
+ AccountGroup.UUID groupUuid = GroupUUID.make(groupName, serverIdent);
+ AccountGroup.NameKey nameKey = new AccountGroup.NameKey(groupName);
+ return InternalGroupCreation.builder()
+ .setId(groupId)
+ .setGroupUUID(groupUuid)
+ .setNameKey(nameKey)
+ .build();
+ }
+
+ private static InternalGroupUpdate toInternalGroupUpdate(TestGroupCreation groupCreation) {
+ InternalGroupUpdate.Builder builder = InternalGroupUpdate.builder();
+ groupCreation.description().ifPresent(builder::setDescription);
+ groupCreation.ownerGroupUuid().ifPresent(builder::setOwnerGroupUUID);
+ groupCreation.visibleToAll().ifPresent(builder::setVisibleToAll);
+ builder.setMemberModification(originalMembers -> groupCreation.members());
+ builder.setSubgroupModification(originalSubgroups -> groupCreation.subgroups());
+ return builder.build();
+ }
+
+ private class MoreGroupOperationsImpl implements MoreGroupOperations {
+ private final AccountGroup.UUID groupUuid;
+
+ MoreGroupOperationsImpl(AccountGroup.UUID groupUuid) {
+ this.groupUuid = groupUuid;
+ }
+
+ @Override
+ public boolean exists() throws Exception {
+ return groups.getGroup(groupUuid).isPresent();
+ }
+
+ @Override
+ public TestGroup get() throws Exception {
+ Optional<InternalGroup> group = groups.getGroup(groupUuid);
+ checkState(group.isPresent(), "Tried to get non-existing test group");
+ return toTestGroup(group.get());
+ }
+
+ private TestGroup toTestGroup(InternalGroup internalGroup) {
+ return TestGroup.builder()
+ .groupUuid(internalGroup.getGroupUUID())
+ .groupId(internalGroup.getId())
+ .nameKey(internalGroup.getNameKey())
+ .description(Optional.ofNullable(internalGroup.getDescription()))
+ .ownerGroupUuid(internalGroup.getOwnerGroupUUID())
+ .visibleToAll(internalGroup.isVisibleToAll())
+ .createdOn(internalGroup.getCreatedOn())
+ .members(internalGroup.getMembers())
+ .subgroups(internalGroup.getSubgroups())
+ .build();
+ }
+
+ @Override
+ public TestGroupUpdate.Builder forUpdate() {
+ return TestGroupUpdate.builder(this::updateGroup);
+ }
+
+ private void updateGroup(TestGroupUpdate groupUpdate)
+ throws OrmDuplicateKeyException, NoSuchGroupException, ConfigInvalidException, IOException {
+ InternalGroupUpdate internalGroupUpdate = toInternalGroupUpdate(groupUpdate);
+ groupsUpdate.updateGroup(groupUuid, internalGroupUpdate);
+ }
+
+ private InternalGroupUpdate toInternalGroupUpdate(TestGroupUpdate groupUpdate) {
+ InternalGroupUpdate.Builder builder = InternalGroupUpdate.builder();
+ groupUpdate.name().map(AccountGroup.NameKey::new).ifPresent(builder::setName);
+ groupUpdate.description().ifPresent(builder::setDescription);
+ groupUpdate.ownerGroupUuid().ifPresent(builder::setOwnerGroupUUID);
+ groupUpdate.visibleToAll().ifPresent(builder::setVisibleToAll);
+ builder.setMemberModification(groupUpdate.memberModification()::apply);
+ builder.setSubgroupModification(groupUpdate.subgroupModification()::apply);
+ return builder.build();
+ }
+ }
+}
diff --git a/java/com/google/gerrit/acceptance/testsuite/group/TestGroup.java b/java/com/google/gerrit/acceptance/testsuite/group/TestGroup.java
new file mode 100644
index 0000000..b450304
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/testsuite/group/TestGroup.java
@@ -0,0 +1,78 @@
+// Copyright (C) 2018 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.acceptance.testsuite.group;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import java.sql.Timestamp;
+import java.util.Optional;
+
+@AutoValue
+public abstract class TestGroup {
+
+ public abstract AccountGroup.UUID groupUuid();
+
+ public abstract AccountGroup.Id groupId();
+
+ public String name() {
+ return nameKey().get();
+ }
+
+ public abstract AccountGroup.NameKey nameKey();
+
+ public abstract Optional<String> description();
+
+ public abstract AccountGroup.UUID ownerGroupUuid();
+
+ public abstract boolean visibleToAll();
+
+ public abstract Timestamp createdOn();
+
+ public abstract ImmutableSet<Account.Id> members();
+
+ public abstract ImmutableSet<AccountGroup.UUID> subgroups();
+
+ static Builder builder() {
+ return new AutoValue_TestGroup.Builder();
+ }
+
+ @AutoValue.Builder
+ abstract static class Builder {
+
+ public abstract Builder groupUuid(AccountGroup.UUID groupUuid);
+
+ public abstract Builder groupId(AccountGroup.Id id);
+
+ public abstract Builder nameKey(AccountGroup.NameKey name);
+
+ public abstract Builder description(String description);
+
+ public abstract Builder description(Optional<String> description);
+
+ public abstract Builder ownerGroupUuid(AccountGroup.UUID ownerGroupUuid);
+
+ public abstract Builder visibleToAll(boolean visibleToAll);
+
+ public abstract Builder createdOn(Timestamp createdOn);
+
+ public abstract Builder members(ImmutableSet<Account.Id> members);
+
+ public abstract Builder subgroups(ImmutableSet<AccountGroup.UUID> subgroups);
+
+ abstract TestGroup build();
+ }
+}
diff --git a/java/com/google/gerrit/acceptance/testsuite/group/TestGroupCreation.java b/java/com/google/gerrit/acceptance/testsuite/group/TestGroupCreation.java
new file mode 100644
index 0000000..efed720
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/testsuite/group/TestGroupCreation.java
@@ -0,0 +1,112 @@
+// Copyright (C) 2018 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.acceptance.testsuite.group;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import com.google.gerrit.acceptance.testsuite.ThrowingFunction;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import java.util.Optional;
+import java.util.Set;
+
+@AutoValue
+public abstract class TestGroupCreation {
+
+ public abstract Optional<String> name();
+
+ public abstract Optional<String> description();
+
+ public abstract Optional<AccountGroup.UUID> ownerGroupUuid();
+
+ public abstract Optional<Boolean> visibleToAll();
+
+ public abstract ImmutableSet<Account.Id> members();
+
+ public abstract ImmutableSet<AccountGroup.UUID> subgroups();
+
+ abstract ThrowingFunction<TestGroupCreation, AccountGroup.UUID> groupCreator();
+
+ public static Builder builder(
+ ThrowingFunction<TestGroupCreation, AccountGroup.UUID> groupCreator) {
+ return new AutoValue_TestGroupCreation.Builder().groupCreator(groupCreator);
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+
+ public abstract Builder name(String name);
+
+ public abstract Builder description(String description);
+
+ public Builder clearDescription() {
+ return description("");
+ }
+
+ public abstract Builder ownerGroupUuid(AccountGroup.UUID ownerGroupUuid);
+
+ public abstract Builder visibleToAll(boolean visibleToAll);
+
+ public Builder clearMembers() {
+ return members(ImmutableSet.of());
+ }
+
+ public Builder members(Account.Id member1, Account.Id... otherMembers) {
+ return members(Sets.union(ImmutableSet.of(member1), ImmutableSet.copyOf(otherMembers)));
+ }
+
+ public abstract Builder members(Set<Account.Id> members);
+
+ abstract ImmutableSet.Builder<Account.Id> membersBuilder();
+
+ public Builder addMember(Account.Id member) {
+ membersBuilder().add(member);
+ return this;
+ }
+
+ public Builder clearSubgroups() {
+ return subgroups(ImmutableSet.of());
+ }
+
+ public Builder subgroups(AccountGroup.UUID subgroup1, AccountGroup.UUID... otherSubgroups) {
+ return subgroups(Sets.union(ImmutableSet.of(subgroup1), ImmutableSet.copyOf(otherSubgroups)));
+ }
+
+ public abstract Builder subgroups(Set<AccountGroup.UUID> subgroups);
+
+ abstract ImmutableSet.Builder<AccountGroup.UUID> subgroupsBuilder();
+
+ public Builder addSubgroup(AccountGroup.UUID subgroup) {
+ subgroupsBuilder().add(subgroup);
+ return this;
+ }
+
+ abstract Builder groupCreator(
+ ThrowingFunction<TestGroupCreation, AccountGroup.UUID> groupCreator);
+
+ abstract TestGroupCreation autoBuild();
+
+ /**
+ * Executes the group creation as specified.
+ *
+ * @return the UUID of the created group
+ */
+ public AccountGroup.UUID create() throws Exception {
+ TestGroupCreation groupCreation = autoBuild();
+ return groupCreation.groupCreator().apply(groupCreation);
+ }
+ }
+}
diff --git a/java/com/google/gerrit/acceptance/testsuite/group/TestGroupUpdate.java b/java/com/google/gerrit/acceptance/testsuite/group/TestGroupUpdate.java
new file mode 100644
index 0000000..095a270
--- /dev/null
+++ b/java/com/google/gerrit/acceptance/testsuite/group/TestGroupUpdate.java
@@ -0,0 +1,134 @@
+// Copyright (C) 2018 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.acceptance.testsuite.group;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import com.google.gerrit.acceptance.testsuite.ThrowingConsumer;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+
+@AutoValue
+public abstract class TestGroupUpdate {
+
+ public abstract Optional<String> name();
+
+ public abstract Optional<String> description();
+
+ public abstract Optional<AccountGroup.UUID> ownerGroupUuid();
+
+ public abstract Optional<Boolean> visibleToAll();
+
+ public abstract Function<ImmutableSet<Account.Id>, Set<Account.Id>> memberModification();
+
+ public abstract Function<ImmutableSet<AccountGroup.UUID>, Set<AccountGroup.UUID>>
+ subgroupModification();
+
+ abstract ThrowingConsumer<TestGroupUpdate> groupUpdater();
+
+ public static Builder builder(ThrowingConsumer<TestGroupUpdate> groupUpdater) {
+ return new AutoValue_TestGroupUpdate.Builder()
+ .groupUpdater(groupUpdater)
+ .memberModification(in -> in)
+ .subgroupModification(in -> in);
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+
+ public abstract Builder name(String name);
+
+ public abstract Builder description(String description);
+
+ public Builder clearDescription() {
+ return description("");
+ }
+
+ public abstract Builder ownerGroupUuid(AccountGroup.UUID ownerGroupUUID);
+
+ public abstract Builder visibleToAll(boolean visibleToAll);
+
+ abstract Builder memberModification(
+ Function<ImmutableSet<Account.Id>, Set<Account.Id>> memberModification);
+
+ abstract Function<ImmutableSet<Account.Id>, Set<Account.Id>> memberModification();
+
+ public Builder clearMembers() {
+ return memberModification(originalMembers -> ImmutableSet.of());
+ }
+
+ public Builder addMember(Account.Id member) {
+ Function<ImmutableSet<Account.Id>, Set<Account.Id>> previousModification =
+ memberModification();
+ memberModification(
+ originalMembers ->
+ Sets.union(previousModification.apply(originalMembers), ImmutableSet.of(member)));
+ return this;
+ }
+
+ public Builder removeMember(Account.Id member) {
+ Function<ImmutableSet<Account.Id>, Set<Account.Id>> previousModification =
+ memberModification();
+ memberModification(
+ originalMembers ->
+ Sets.difference(
+ previousModification.apply(originalMembers), ImmutableSet.of(member)));
+ return this;
+ }
+
+ abstract Builder subgroupModification(
+ Function<ImmutableSet<AccountGroup.UUID>, Set<AccountGroup.UUID>> subgroupModification);
+
+ abstract Function<ImmutableSet<AccountGroup.UUID>, Set<AccountGroup.UUID>>
+ subgroupModification();
+
+ public Builder clearSubgroups() {
+ return subgroupModification(originalMembers -> ImmutableSet.of());
+ }
+
+ public Builder addSubgroup(AccountGroup.UUID subgroup) {
+ Function<ImmutableSet<AccountGroup.UUID>, Set<AccountGroup.UUID>> previousModification =
+ subgroupModification();
+ subgroupModification(
+ originalSubgroups ->
+ Sets.union(previousModification.apply(originalSubgroups), ImmutableSet.of(subgroup)));
+ return this;
+ }
+
+ public Builder removeSubgroup(AccountGroup.UUID subgroup) {
+ Function<ImmutableSet<AccountGroup.UUID>, Set<AccountGroup.UUID>> previousModification =
+ subgroupModification();
+ subgroupModification(
+ originalSubgroups ->
+ Sets.difference(
+ previousModification.apply(originalSubgroups), ImmutableSet.of(subgroup)));
+ return this;
+ }
+
+ abstract Builder groupUpdater(ThrowingConsumer<TestGroupUpdate> groupUpdater);
+
+ abstract TestGroupUpdate autoBuild();
+
+ /** Executes the group update as specified. */
+ public void update() throws Exception {
+ TestGroupUpdate groupUpdater = autoBuild();
+ groupUpdater.groupUpdater().accept(groupUpdater);
+ }
+ }
+}
diff --git a/java/com/google/gerrit/common/data/LabelType.java b/java/com/google/gerrit/common/data/LabelType.java
index 29fc9c7..ff7d25b 100644
--- a/java/com/google/gerrit/common/data/LabelType.java
+++ b/java/com/google/gerrit/common/data/LabelType.java
@@ -17,13 +17,13 @@
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toList;
-import static java.util.stream.Collectors.toMap;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.reviewdb.client.LabelId;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -36,6 +36,7 @@
public static final boolean DEF_COPY_ALL_SCORES_ON_MERGE_FIRST_PARENT_UPDATE = false;
public static final boolean DEF_COPY_MAX_SCORE = false;
public static final boolean DEF_COPY_MIN_SCORE = false;
+ public static final boolean DEF_IGNORE_SELF_APPROVAL = false;
public static LabelType withDefaultValues(String name) {
checkName(name);
@@ -103,6 +104,7 @@
protected boolean copyAllScoresIfNoCodeChange;
protected boolean copyAllScoresIfNoChange;
protected boolean allowPostSubmit;
+ protected boolean ignoreSelfApproval;
protected short defaultValue;
protected List<LabelValue> values;
@@ -141,13 +143,12 @@
setCopyMaxScore(DEF_COPY_MAX_SCORE);
setCopyMinScore(DEF_COPY_MIN_SCORE);
setAllowPostSubmit(DEF_ALLOW_POST_SUBMIT);
+ setIgnoreSelfApproval(DEF_IGNORE_SELF_APPROVAL);
- byValue =
- values
- .stream()
- .collect(
- collectingAndThen(
- toMap(LabelValue::getValue, v -> v), Collections::unmodifiableMap));
+ byValue = new HashMap<>();
+ for (LabelValue v : values) {
+ byValue.put(v.getValue(), v);
+ }
}
public String getName() {
@@ -190,6 +191,14 @@
this.allowPostSubmit = allowPostSubmit;
}
+ public boolean ignoreSelfApproval() {
+ return ignoreSelfApproval;
+ }
+
+ public void setIgnoreSelfApproval(boolean ignoreSelfApproval) {
+ this.ignoreSelfApproval = ignoreSelfApproval;
+ }
+
public void setRefPatterns(List<String> refPatterns) {
if (refPatterns != null) {
this.refPatterns =
diff --git a/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java b/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
index 630594f..2c1c93a 100644
--- a/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
+++ b/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
@@ -71,6 +71,7 @@
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.nio.entity.NStringEntity;
+import org.elasticsearch.client.Request;
import org.elasticsearch.client.Response;
abstract class AbstractElasticIndex<K, V> implements Index<K, V> {
@@ -171,10 +172,10 @@
public void deleteAll() throws IOException {
// Delete the index, if it exists.
String endpoint = indexName + client.adapter().indicesExistParam();
- Response response = client.get().performRequest("HEAD", endpoint);
+ Response response = client.get().performRequest(new Request("HEAD", endpoint));
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.SC_OK) {
- response = client.get().performRequest("DELETE", indexName);
+ response = client.get().performRequest(new Request("DELETE", indexName));
statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
throw new IOException(
@@ -307,9 +308,13 @@
private Response performRequest(
String method, Object payload, String uri, Map<String, String> params) throws IOException {
+ Request request = new Request(method, uri);
String payloadStr = payload instanceof String ? (String) payload : payload.toString();
- HttpEntity entity = new NStringEntity(payloadStr, ContentType.APPLICATION_JSON);
- return client.get().performRequest(method, uri, params, entity);
+ request.setEntity(new NStringEntity(payloadStr, ContentType.APPLICATION_JSON));
+ for (Map.Entry<String, String> entry : params.entrySet()) {
+ request.addParameter(entry.getKey(), entry.getValue());
+ }
+ return client.get().performRequest(request);
}
protected class ElasticQuerySource implements DataSource<V> {
diff --git a/java/com/google/gerrit/elasticsearch/ElasticIndexVersionDiscovery.java b/java/com/google/gerrit/elasticsearch/ElasticIndexVersionDiscovery.java
index e36ab2d..a777f47 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticIndexVersionDiscovery.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticIndexVersionDiscovery.java
@@ -24,7 +24,7 @@
import java.util.List;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
-import org.apache.http.client.methods.HttpGet;
+import org.elasticsearch.client.Request;
import org.elasticsearch.client.Response;
@Singleton
@@ -40,10 +40,8 @@
List<String> discover(String prefix, String indexName) throws IOException {
String name = prefix + indexName + "_";
- Response response =
- client
- .get()
- .performRequest(HttpGet.METHOD_NAME, client.adapter().getVersionDiscoveryUrl(name));
+ Request request = new Request("GET", client.adapter().getVersionDiscoveryUrl(name));
+ Response response = client.get().performRequest(request);
StatusLine statusLine = response.getStatusLine();
if (statusLine.getStatusCode() != HttpStatus.SC_OK) {
diff --git a/java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java b/java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java
index 2beb528..8cb69e0 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticQueryAdapter.java
@@ -32,13 +32,14 @@
ElasticQueryAdapter(ElasticVersion version) {
this.ignoreUnmapped = version == ElasticVersion.V2_4;
- this.usePostV5Type = isV6(version);
- this.versionDiscoveryUrl = isV6(version) ? "%s*" : "%s*/_aliases";
+ this.usePostV5Type = version.isV6();
+ this.versionDiscoveryUrl = version.isV6() ? "%s*" : "%s*/_aliases";
switch (version) {
case V5_6:
case V6_2:
case V6_3:
+ case V6_4:
this.searchFilteringName = "_source";
this.indicesExistParam = "?allow_no_indices=false";
this.exactFieldType = "keyword";
@@ -58,10 +59,6 @@
}
}
- private boolean isV6(ElasticVersion version) {
- return version == ElasticVersion.V6_2 || version == ElasticVersion.V6_3;
- }
-
void setIgnoreUnmapped(JsonObject properties) {
if (ignoreUnmapped) {
properties.addProperty("ignore_unmapped", true);
diff --git a/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java b/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java
index 6ceb897..337f2ca 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java
@@ -29,6 +29,7 @@
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
+import org.elasticsearch.client.Request;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
@@ -105,7 +106,7 @@
private ElasticVersion getVersion() throws ElasticException {
try {
- Response response = client.performRequest("GET", "");
+ Response response = client.performRequest(new Request("GET", ""));
StatusLine statusLine = response.getStatusLine();
if (statusLine.getStatusCode() != HttpStatus.SC_OK) {
throw new FailedToGetVersion(statusLine);
diff --git a/java/com/google/gerrit/elasticsearch/ElasticVersion.java b/java/com/google/gerrit/elasticsearch/ElasticVersion.java
index 610a212..dfa5d21 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticVersion.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticVersion.java
@@ -21,7 +21,8 @@
V2_4("2.4.*"),
V5_6("5.6.*"),
V6_2("6.2.*"),
- V6_3("6.3.*");
+ V6_3("6.3.*"),
+ V6_4("6.4.*");
private final String version;
private final Pattern pattern;
@@ -31,29 +32,33 @@
this.pattern = Pattern.compile(version);
}
- public static class InvalidVersion extends ElasticException {
+ public static class UnsupportedVersion extends ElasticException {
private static final long serialVersionUID = 1L;
- InvalidVersion(String version) {
+ UnsupportedVersion(String version) {
super(
String.format(
- "Invalid version: [%s]. Supported versions: %s", version, supportedVersions()));
+ "Unsupported version: [%s]. Supported versions: %s", version, supportedVersions()));
}
}
- public static ElasticVersion forVersion(String version) throws InvalidVersion {
+ public static ElasticVersion forVersion(String version) throws UnsupportedVersion {
for (ElasticVersion value : ElasticVersion.values()) {
if (value.pattern.matcher(version).matches()) {
return value;
}
}
- throw new InvalidVersion(version);
+ throw new UnsupportedVersion(version);
}
public static String supportedVersions() {
return Joiner.on(", ").join(ElasticVersion.values());
}
+ public boolean isV6() {
+ return version.startsWith("6.");
+ }
+
@Override
public String toString() {
return version;
diff --git a/java/com/google/gerrit/extensions/annotations/RemoveAfter.java b/java/com/google/gerrit/extensions/annotations/RemoveAfter.java
new file mode 100644
index 0000000..aa31dd0
--- /dev/null
+++ b/java/com/google/gerrit/extensions/annotations/RemoveAfter.java
@@ -0,0 +1,37 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.extensions.annotations;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import com.google.inject.BindingAnnotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation for features that are deprecated, but still present to adhere to the one-release-grace
+ * period we promised to users.
+ */
+@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
+@Retention(SOURCE)
+@BindingAnnotation
+public @interface RemoveAfter {
+ /**
+ * Version after which the annotated functionality can be removed. Once the referenced version was
+ * branched off, the annotated code can be removed.
+ */
+ String value();
+}
diff --git a/java/com/google/gerrit/extensions/api/access/ProjectAccessInfo.java b/java/com/google/gerrit/extensions/api/access/ProjectAccessInfo.java
index 5d8e950..8273d84 100644
--- a/java/com/google/gerrit/extensions/api/access/ProjectAccessInfo.java
+++ b/java/com/google/gerrit/extensions/api/access/ProjectAccessInfo.java
@@ -29,6 +29,7 @@
public Set<String> ownerOf;
public Boolean canUpload;
public Boolean canAdd;
+ public Boolean canAddTags;
public Boolean configVisible;
public Map<String, GroupInfo> groups;
public List<WebLinkInfo> configWebLinks;
diff --git a/java/com/google/gerrit/extensions/api/changes/IncludedInInfo.java b/java/com/google/gerrit/extensions/api/changes/IncludedInInfo.java
index d876034..8fe47bd 100644
--- a/java/com/google/gerrit/extensions/api/changes/IncludedInInfo.java
+++ b/java/com/google/gerrit/extensions/api/changes/IncludedInInfo.java
@@ -13,6 +13,7 @@
// limitations under the License.
package com.google.gerrit.extensions.api.changes;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -23,9 +24,11 @@
public Map<String, Collection<String>> external;
public IncludedInInfo(
- List<String> branches, List<String> tags, Map<String, Collection<String>> external) {
- this.branches = branches;
- this.tags = tags;
+ Collection<String> branches,
+ Collection<String> tags,
+ Map<String, Collection<String>> external) {
+ this.branches = new ArrayList<>(branches);
+ this.tags = new ArrayList<>(tags);
this.external = external;
}
}
diff --git a/java/com/google/gerrit/extensions/api/projects/ConfigInfo.java b/java/com/google/gerrit/extensions/api/projects/ConfigInfo.java
index b5aff67..08ba486 100644
--- a/java/com/google/gerrit/extensions/api/projects/ConfigInfo.java
+++ b/java/com/google/gerrit/extensions/api/projects/ConfigInfo.java
@@ -59,14 +59,17 @@
}
public static class MaxObjectSizeLimitInfo {
- /* The effective value. Null if not set. */
+ /** The effective value in bytes. Null if not set. */
@Nullable public String value;
- /* The value configured on the project. Null if not set. */
+ /** The value configured explicitly on the project as a formatted string. Null if not set. */
@Nullable public String configuredValue;
- /* The value configured globally. Null if not set. */
- @Nullable public String inheritedValue;
+ /**
+ * Whether the value was inherited or overridden from the project's parent hierarchy or global
+ * config. Null if not inherited or overridden.
+ */
+ @Nullable public String summary;
}
public static class ConfigParameterInfo {
diff --git a/java/com/google/gerrit/extensions/registration/DynamicItem.java b/java/com/google/gerrit/extensions/registration/DynamicItem.java
index b5eafe3..4f36ab4 100644
--- a/java/com/google/gerrit/extensions/registration/DynamicItem.java
+++ b/java/com/google/gerrit/extensions/registration/DynamicItem.java
@@ -14,6 +14,7 @@
package com.google.gerrit.extensions.registration;
+import com.google.gerrit.common.Nullable;
import com.google.inject.Binder;
import com.google.inject.Key;
import com.google.inject.Provider;
@@ -77,7 +78,8 @@
* @param item item to store.
*/
public static <T> DynamicItem<T> itemOf(Class<T> member, T item) {
- return new DynamicItem<>(keyFor(TypeLiteral.get(member)), Providers.of(item), "gerrit");
+ return new DynamicItem<>(
+ keyFor(TypeLiteral.get(member)), Providers.of(item), PluginName.GERRIT);
}
@SuppressWarnings("unchecked")
@@ -126,12 +128,26 @@
* @return the configured item instance; null if no implementation has been bound to the item.
* This is common if no plugin registered an implementation for the type.
*/
+ @Nullable
public T get() {
NamedProvider<T> item = ref.get();
return item != null ? item.impl.get() : null;
}
/**
+ * Get the name of the plugin that has bound the configured item, or null.
+ *
+ * @return the name of the plugin that has bound the configured item; null if no implementation
+ * has been bound to the item. This is common if no plugin registered an implementation for
+ * the type.
+ */
+ @Nullable
+ public String getPluginName() {
+ NamedProvider<T> item = ref.get();
+ return item != null ? item.pluginName : null;
+ }
+
+ /**
* Set the element to provide.
*
* @param item the item to use. Must not be null.
@@ -154,7 +170,7 @@
NamedProvider<T> old = null;
while (!ref.compareAndSet(old, item)) {
old = ref.get();
- if (old != null && !"gerrit".equals(old.pluginName)) {
+ if (old != null && !PluginName.GERRIT.equals(old.pluginName)) {
throw new ProvisionException(
String.format(
"%s already provided by %s, ignoring plugin %s",
@@ -186,7 +202,9 @@
NamedProvider<T> old = null;
while (!ref.compareAndSet(old, item)) {
old = ref.get();
- if (old != null && !"gerrit".equals(old.pluginName) && !pluginName.equals(old.pluginName)) {
+ if (old != null
+ && !PluginName.GERRIT.equals(old.pluginName)
+ && !pluginName.equals(old.pluginName)) {
// We allow to replace:
// 1. Gerrit core items, e.g. websession cache
// can be replaced by plugin implementation
@@ -222,6 +240,7 @@
}
@Override
+ @Nullable
public ReloadableHandle replace(Key<T> newKey, Provider<T> newItem) {
NamedProvider<T> n = new NamedProvider<>(newItem, item.pluginName);
if (ref.compareAndSet(item, n)) {
diff --git a/java/com/google/gerrit/extensions/registration/DynamicItemProvider.java b/java/com/google/gerrit/extensions/registration/DynamicItemProvider.java
index 5b76741..d8dd1f9 100644
--- a/java/com/google/gerrit/extensions/registration/DynamicItemProvider.java
+++ b/java/com/google/gerrit/extensions/registration/DynamicItemProvider.java
@@ -36,7 +36,7 @@
@Override
public DynamicItem<T> get() {
- return new DynamicItem<>(key, find(injector, type), "gerrit");
+ return new DynamicItem<>(key, find(injector, type), PluginName.GERRIT);
}
private static <T> Provider<T> find(Injector src, TypeLiteral<T> type) {
diff --git a/java/com/google/gerrit/extensions/registration/DynamicMap.java b/java/com/google/gerrit/extensions/registration/DynamicMap.java
index 0bf6edd..96d19b2 100644
--- a/java/com/google/gerrit/extensions/registration/DynamicMap.java
+++ b/java/com/google/gerrit/extensions/registration/DynamicMap.java
@@ -41,12 +41,26 @@
* singleton and non-singleton members.
*/
public abstract class DynamicMap<T> implements Iterable<DynamicMap.Entry<T>> {
- public interface Entry<T> {
- String getPluginName();
+ public static class Entry<T> {
+ private final NamePair namePair;
+ private final Provider<T> provider;
- String getExportName();
+ private Entry(NamePair namePair, Provider<T> provider) {
+ this.namePair = namePair;
+ this.provider = provider;
+ }
- Provider<T> getProvider();
+ public String getPluginName() {
+ return namePair.pluginName;
+ }
+
+ public String getExportName() {
+ return namePair.exportName;
+ }
+
+ public Provider<T> getProvider() {
+ return provider;
+ }
}
/**
@@ -162,23 +176,8 @@
@Override
public Entry<T> next() {
- final Map.Entry<NamePair, Provider<T>> e = i.next();
- return new Entry<T>() {
- @Override
- public String getPluginName() {
- return e.getKey().pluginName;
- }
-
- @Override
- public String getExportName() {
- return e.getKey().exportName;
- }
-
- @Override
- public Provider<T> getProvider() {
- return e.getValue();
- }
- };
+ Map.Entry<NamePair, Provider<T>> e = i.next();
+ return new Entry<>(e.getKey(), e.getValue());
}
@Override
diff --git a/java/com/google/gerrit/extensions/registration/DynamicMapProvider.java b/java/com/google/gerrit/extensions/registration/DynamicMapProvider.java
index 420a356..9d96131 100644
--- a/java/com/google/gerrit/extensions/registration/DynamicMapProvider.java
+++ b/java/com/google/gerrit/extensions/registration/DynamicMapProvider.java
@@ -37,7 +37,7 @@
if (bindings != null) {
for (Binding<T> b : bindings) {
if (b.getKey().getAnnotation() != null) {
- m.put("gerrit", b.getKey(), b.getProvider());
+ m.put(PluginName.GERRIT, b.getKey(), b.getProvider());
}
}
}
diff --git a/java/com/google/gerrit/extensions/registration/DynamicSet.java b/java/com/google/gerrit/extensions/registration/DynamicSet.java
index 7ffb86d..6b3a49b 100644
--- a/java/com/google/gerrit/extensions/registration/DynamicSet.java
+++ b/java/com/google/gerrit/extensions/registration/DynamicSet.java
@@ -14,6 +14,12 @@
package com.google.gerrit.extensions.registration;
+import static com.google.common.collect.ImmutableSet.toImmutableSet;
+import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet;
+import static java.util.Comparator.naturalOrder;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSortedSet;
import com.google.inject.Binder;
import com.google.inject.Key;
import com.google.inject.Provider;
@@ -39,6 +45,24 @@
* singleton and non-singleton members.
*/
public class DynamicSet<T> implements Iterable<T> {
+ public static class Entry<T> {
+ private final String pluginName;
+ private final Provider<T> provider;
+
+ private Entry(String pluginName, Provider<T> provider) {
+ this.pluginName = pluginName;
+ this.provider = provider;
+ }
+
+ public String getPluginName() {
+ return pluginName;
+ }
+
+ public Provider<T> getProvider() {
+ return provider;
+ }
+ }
+
/**
* Declare a singleton {@code DynamicSet<T>} with a binder.
*
@@ -129,12 +153,12 @@
}
public static <T> DynamicSet<T> emptySet() {
- return new DynamicSet<>(Collections.<AtomicReference<Provider<T>>>emptySet());
+ return new DynamicSet<>(Collections.<AtomicReference<NamedProvider<T>>>emptySet());
}
- private final CopyOnWriteArrayList<AtomicReference<Provider<T>>> items;
+ private final CopyOnWriteArrayList<AtomicReference<NamedProvider<T>>> items;
- DynamicSet(Collection<AtomicReference<Provider<T>>> base) {
+ DynamicSet(Collection<AtomicReference<NamedProvider<T>>> base) {
items = new CopyOnWriteArrayList<>(base);
}
@@ -144,38 +168,59 @@
@Override
public Iterator<T> iterator() {
- final Iterator<AtomicReference<Provider<T>>> itr = items.iterator();
+ Iterator<Entry<T>> entryIterator = entries().iterator();
return new Iterator<T>() {
- private T next;
-
@Override
public boolean hasNext() {
- while (next == null && itr.hasNext()) {
- Provider<T> p = itr.next().get();
- if (p != null) {
- try {
- next = p.get();
- } catch (RuntimeException e) {
- // TODO Log failed member of DynamicSet.
- }
- }
- }
- return next != null;
+ return entryIterator.hasNext();
}
@Override
public T next() {
- if (hasNext()) {
- T result = next;
- next = null;
- return result;
- }
- throw new NoSuchElementException();
+ Entry<T> next = entryIterator.next();
+ return next != null ? next.getProvider().get() : null;
}
+ };
+ }
+ public Iterable<Entry<T>> entries() {
+ final Iterator<AtomicReference<NamedProvider<T>>> itr = items.iterator();
+ return new Iterable<Entry<T>>() {
@Override
- public void remove() {
- throw new UnsupportedOperationException();
+ public Iterator<Entry<T>> iterator() {
+ return new Iterator<Entry<T>>() {
+ private Entry<T> next;
+
+ @Override
+ public boolean hasNext() {
+ while (next == null && itr.hasNext()) {
+ NamedProvider<T> p = itr.next().get();
+ if (p != null) {
+ try {
+ next = new Entry<>(p.pluginName, p.impl);
+ } catch (RuntimeException e) {
+ // TODO Log failed member of DynamicSet.
+ }
+ }
+ }
+ return next != null;
+ }
+
+ @Override
+ public Entry<T> next() {
+ if (hasNext()) {
+ Entry<T> result = next;
+ next = null;
+ return result;
+ }
+ throw new NoSuchElementException();
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
}
};
}
@@ -198,13 +243,29 @@
}
/**
- * Add one new element to the set.
+ * Get the names of all running plugins supplying this type.
*
- * @param item the item to add to the collection. Must not be null.
- * @return handle to remove the item at a later point in time.
+ * @return sorted set of active plugins that supply at least one item.
*/
- public RegistrationHandle add(T item) {
- return add(Providers.of(item));
+ public ImmutableSortedSet<String> plugins() {
+ return items
+ .stream()
+ .map(i -> i.get().pluginName)
+ .collect(toImmutableSortedSet(naturalOrder()));
+ }
+
+ /**
+ * Get the items exported by a single plugin.
+ *
+ * @param pluginName name of the plugin.
+ * @return items exported by a plugin.
+ */
+ public ImmutableSet<Provider<T>> byPlugin(String pluginName) {
+ return items
+ .stream()
+ .filter(i -> i.get().pluginName.equals(pluginName))
+ .map(i -> i.get().impl)
+ .collect(toImmutableSet());
}
/**
@@ -213,13 +274,24 @@
* @param item the item to add to the collection. Must not be null.
* @return handle to remove the item at a later point in time.
*/
- public RegistrationHandle add(Provider<T> item) {
- final AtomicReference<Provider<T>> ref = new AtomicReference<>(item);
+ public RegistrationHandle add(String pluginName, T item) {
+ return add(pluginName, Providers.of(item));
+ }
+
+ /**
+ * Add one new element to the set.
+ *
+ * @param item the item to add to the collection. Must not be null.
+ * @return handle to remove the item at a later point in time.
+ */
+ public RegistrationHandle add(String pluginName, Provider<T> item) {
+ final AtomicReference<NamedProvider<T>> ref =
+ new AtomicReference<>(new NamedProvider<>(item, pluginName));
items.add(ref);
return new RegistrationHandle() {
@Override
public void remove() {
- if (ref.compareAndSet(item, null)) {
+ if (ref.compareAndSet(ref.get(), null)) {
items.remove(ref);
}
}
@@ -229,6 +301,7 @@
/**
* Add one new element that may be hot-replaceable in the future.
*
+ * @param pluginName unique name of the plugin providing the item.
* @param key unique description from the item's Guice binding. This can be later obtained from
* the registration handle to facilitate matching with the new equivalent instance during a
* hot reload.
@@ -236,18 +309,19 @@
* @return a handle that can remove this item later, or hot-swap the item without it ever leaving
* the collection.
*/
- public ReloadableRegistrationHandle<T> add(Key<T> key, Provider<T> item) {
- AtomicReference<Provider<T>> ref = new AtomicReference<>(item);
+ public ReloadableRegistrationHandle<T> add(String pluginName, Key<T> key, Provider<T> item) {
+ AtomicReference<NamedProvider<T>> ref =
+ new AtomicReference<>(new NamedProvider<>(item, pluginName));
items.add(ref);
- return new ReloadableHandle(ref, key, item);
+ return new ReloadableHandle(ref, key, ref.get());
}
private class ReloadableHandle implements ReloadableRegistrationHandle<T> {
- private final AtomicReference<Provider<T>> ref;
+ private final AtomicReference<NamedProvider<T>> ref;
private final Key<T> key;
- private final Provider<T> item;
+ private final NamedProvider<T> item;
- ReloadableHandle(AtomicReference<Provider<T>> ref, Key<T> key, Provider<T> item) {
+ ReloadableHandle(AtomicReference<NamedProvider<T>> ref, Key<T> key, NamedProvider<T> item) {
this.ref = ref;
this.key = key;
this.item = item;
@@ -267,8 +341,9 @@
@Override
public ReloadableHandle replace(Key<T> newKey, Provider<T> newItem) {
- if (ref.compareAndSet(item, newItem)) {
- return new ReloadableHandle(ref, newKey, newItem);
+ NamedProvider<T> n = new NamedProvider<>(newItem, item.pluginName);
+ if (ref.compareAndSet(item, n)) {
+ return new ReloadableHandle(ref, newKey, n);
}
return null;
}
diff --git a/java/com/google/gerrit/extensions/registration/DynamicSetProvider.java b/java/com/google/gerrit/extensions/registration/DynamicSetProvider.java
index 707c76a..6d36f54 100644
--- a/java/com/google/gerrit/extensions/registration/DynamicSetProvider.java
+++ b/java/com/google/gerrit/extensions/registration/DynamicSetProvider.java
@@ -38,16 +38,17 @@
return new DynamicSet<>(find(injector, type));
}
- private static <T> List<AtomicReference<Provider<T>>> find(Injector src, TypeLiteral<T> type) {
+ private static <T> List<AtomicReference<NamedProvider<T>>> find(
+ Injector src, TypeLiteral<T> type) {
List<Binding<T>> bindings = src.findBindingsByType(type);
int cnt = bindings != null ? bindings.size() : 0;
if (cnt == 0) {
return Collections.emptyList();
}
- List<AtomicReference<Provider<T>>> r = new ArrayList<>(cnt);
+ List<AtomicReference<NamedProvider<T>>> r = new ArrayList<>(cnt);
for (Binding<T> b : bindings) {
if (b.getKey().getAnnotation() != null) {
- r.add(new AtomicReference<>(b.getProvider()));
+ r.add(new AtomicReference<>(new NamedProvider<>(b.getProvider(), PluginName.GERRIT)));
}
}
return r;
diff --git a/java/com/google/gerrit/extensions/registration/PluginName.java b/java/com/google/gerrit/extensions/registration/PluginName.java
new file mode 100644
index 0000000..c110d45
--- /dev/null
+++ b/java/com/google/gerrit/extensions/registration/PluginName.java
@@ -0,0 +1,22 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.extensions.registration;
+
+public class PluginName {
+ /** Name that is used as plugin name if Gerrit core implements a plugin extension point. */
+ public static final String GERRIT = "gerrit";
+
+ private PluginName() {}
+}
diff --git a/java/com/google/gerrit/extensions/registration/PrivateInternals_DynamicTypes.java b/java/com/google/gerrit/extensions/registration/PrivateInternals_DynamicTypes.java
index 9342e0f..fd31fcd 100644
--- a/java/com/google/gerrit/extensions/registration/PrivateInternals_DynamicTypes.java
+++ b/java/com/google/gerrit/extensions/registration/PrivateInternals_DynamicTypes.java
@@ -107,7 +107,7 @@
}
public static List<RegistrationHandle> attachSets(
- Injector src, Map<TypeLiteral<?>, DynamicSet<?>> sets) {
+ Injector src, String pluginName, Map<TypeLiteral<?>, DynamicSet<?>> sets) {
if (src == null || sets == null || sets.isEmpty()) {
return Collections.emptyList();
}
@@ -123,7 +123,7 @@
for (Binding<Object> b : bindings(src, type)) {
if (b.getKey().getAnnotation() != null) {
- handles.add(set.add(b.getKey(), b.getProvider()));
+ handles.add(set.add(pluginName, b.getKey(), b.getProvider()));
}
}
}
@@ -174,8 +174,8 @@
handles = new ArrayList<>(4);
Injector parent = self.getParent();
while (parent != null) {
- handles.addAll(attachSets(self, dynamicSetsOf(parent)));
- handles.addAll(attachMaps(self, "gerrit", dynamicMapsOf(parent)));
+ handles.addAll(attachSets(self, PluginName.GERRIT, dynamicSetsOf(parent)));
+ handles.addAll(attachMaps(self, PluginName.GERRIT, dynamicMapsOf(parent)));
parent = parent.getParent();
}
if (handles.isEmpty()) {
diff --git a/java/com/google/gerrit/httpd/AllRequestFilter.java b/java/com/google/gerrit/httpd/AllRequestFilter.java
index b8b0bc8..9d171d5 100644
--- a/java/com/google/gerrit/httpd/AllRequestFilter.java
+++ b/java/com/google/gerrit/httpd/AllRequestFilter.java
@@ -76,7 +76,7 @@
// synchronized.
if (!initializedFilters.contains(filter)) {
filter.init(filterConfig);
- initializedFilters.add(filter);
+ initializedFilters.add("gerrit", filter);
}
} else {
ret = false;
@@ -89,7 +89,7 @@
initializedFilters = new DynamicSet<>();
for (AllRequestFilter filter : filtersToCleanUp) {
if (filters.contains(filter)) {
- initializedFilters.add(filter);
+ initializedFilters.add("gerrit", filter);
} else {
filter.destroy();
}
diff --git a/java/com/google/gerrit/httpd/BUILD b/java/com/google/gerrit/httpd/BUILD
index 3e71098..fae7c6a 100644
--- a/java/com/google/gerrit/httpd/BUILD
+++ b/java/com/google/gerrit/httpd/BUILD
@@ -16,6 +16,7 @@
"//java/com/google/gerrit/server/audit",
"//java/com/google/gerrit/server/git/receive",
"//java/com/google/gerrit/server/ioutil",
+ "//java/com/google/gerrit/server/logging",
"//java/com/google/gerrit/server/restapi",
"//java/com/google/gerrit/util/cli",
"//java/com/google/gerrit/util/http",
diff --git a/java/com/google/gerrit/httpd/GerritAuthModule.java b/java/com/google/gerrit/httpd/GerritAuthModule.java
new file mode 100644
index 0000000..c0ef207
--- /dev/null
+++ b/java/com/google/gerrit/httpd/GerritAuthModule.java
@@ -0,0 +1,55 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.httpd;
+
+import static com.google.gerrit.extensions.api.lfs.LfsDefinitions.LFS_URL_WO_AUTH_REGEX;
+
+import com.google.gerrit.extensions.client.GitBasicAuthPolicy;
+import com.google.gerrit.server.config.AuthConfig;
+import com.google.inject.Inject;
+import com.google.inject.servlet.ServletModule;
+import javax.servlet.Filter;
+
+/** Configures filter for authenticating REST requests. */
+public class GerritAuthModule extends ServletModule {
+ private static final String NOT_AUTHORIZED_LFS_URL_REGEX = "^(?:(?!/a/))" + LFS_URL_WO_AUTH_REGEX;
+ private final AuthConfig authConfig;
+
+ @Inject
+ GerritAuthModule(AuthConfig authConfig) {
+ this.authConfig = authConfig;
+ }
+
+ @Override
+ protected void configureServlets() {
+ Class<? extends Filter> authFilter = retreiveAuthFilterFromConfig(authConfig);
+
+ filterRegex(NOT_AUTHORIZED_LFS_URL_REGEX).through(authFilter);
+ filter("/a/*").through(authFilter);
+ }
+
+ static Class<? extends Filter> retreiveAuthFilterFromConfig(AuthConfig authConfig) {
+ Class<? extends Filter> authFilter;
+ if (authConfig.isTrustContainerAuth()) {
+ authFilter = ContainerAuthFilter.class;
+ } else {
+ authFilter =
+ authConfig.getGitBasicAuthPolicy() == GitBasicAuthPolicy.OAUTH
+ ? ProjectOAuthFilter.class
+ : ProjectBasicAuthFilter.class;
+ }
+ return authFilter;
+ }
+}
diff --git a/java/com/google/gerrit/httpd/GitOverHttpModule.java b/java/com/google/gerrit/httpd/GitOverHttpModule.java
index 3f3737d..8400d60 100644
--- a/java/com/google/gerrit/httpd/GitOverHttpModule.java
+++ b/java/com/google/gerrit/httpd/GitOverHttpModule.java
@@ -14,20 +14,16 @@
package com.google.gerrit.httpd;
-import static com.google.gerrit.extensions.api.lfs.LfsDefinitions.LFS_URL_WO_AUTH_REGEX;
+import static com.google.gerrit.httpd.GitOverHttpServlet.URL_REGEX;
-import com.google.gerrit.extensions.client.GitBasicAuthPolicy;
import com.google.gerrit.reviewdb.client.CoreDownloadSchemes;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.DownloadConfig;
import com.google.inject.Inject;
import com.google.inject.servlet.ServletModule;
-import javax.servlet.Filter;
/** Configures Git access over HTTP with authentication. */
public class GitOverHttpModule extends ServletModule {
- private static final String NOT_AUTHORIZED_LFS_URL_REGEX = "^(?:(?!/a/))" + LFS_URL_WO_AUTH_REGEX;
-
private final AuthConfig authConfig;
private final DownloadConfig downloadConfig;
@@ -39,28 +35,10 @@
@Override
protected void configureServlets() {
- Class<? extends Filter> authFilter;
- if (authConfig.isTrustContainerAuth()) {
- authFilter = ContainerAuthFilter.class;
- } else {
- authFilter =
- authConfig.getGitBasicAuthPolicy() == GitBasicAuthPolicy.OAUTH
- ? ProjectOAuthFilter.class
- : ProjectBasicAuthFilter.class;
+ if (downloadConfig.getDownloadSchemes().contains(CoreDownloadSchemes.ANON_HTTP)
+ || downloadConfig.getDownloadSchemes().contains(CoreDownloadSchemes.HTTP)) {
+ filterRegex(URL_REGEX).through(GerritAuthModule.retreiveAuthFilterFromConfig(authConfig));
+ serveRegex(URL_REGEX).with(GitOverHttpServlet.class);
}
-
- if (isHttpEnabled()) {
- String git = GitOverHttpServlet.URL_REGEX;
- filterRegex(git).through(authFilter);
- serveRegex(git).with(GitOverHttpServlet.class);
- }
-
- filterRegex(NOT_AUTHORIZED_LFS_URL_REGEX).through(authFilter);
- filter("/a/*").through(authFilter);
- }
-
- private boolean isHttpEnabled() {
- return downloadConfig.getDownloadSchemes().contains(CoreDownloadSchemes.ANON_HTTP)
- || downloadConfig.getDownloadSchemes().contains(CoreDownloadSchemes.HTTP);
}
}
diff --git a/java/com/google/gerrit/httpd/init/WebAppInitializer.java b/java/com/google/gerrit/httpd/init/WebAppInitializer.java
index 8e380f5..cb476af 100644
--- a/java/com/google/gerrit/httpd/init/WebAppInitializer.java
+++ b/java/com/google/gerrit/httpd/init/WebAppInitializer.java
@@ -23,6 +23,7 @@
import com.google.gerrit.extensions.client.AuthType;
import com.google.gerrit.gpg.GpgModule;
import com.google.gerrit.httpd.AllRequestFilter;
+import com.google.gerrit.httpd.GerritAuthModule;
import com.google.gerrit.httpd.GetUserFilter;
import com.google.gerrit.httpd.GitOverHttpModule;
import com.google.gerrit.httpd.H2CacheBasedWebSession;
@@ -422,9 +423,10 @@
private Injector createWebInjector() {
final List<Module> modules = new ArrayList<>();
modules.add(RequestContextFilter.module());
- modules.add(AllRequestFilter.module());
modules.add(RequestMetricsFilter.module());
+ modules.add(sysInjector.getInstance(GerritAuthModule.class));
modules.add(sysInjector.getInstance(GitOverHttpModule.class));
+ modules.add(AllRequestFilter.module());
modules.add(sysInjector.getInstance(WebModule.class));
modules.add(sysInjector.getInstance(RequireSslFilter.Module.class));
if (sshInjector != null) {
diff --git a/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java b/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java
index 9a24e47..74cadd3 100644
--- a/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java
+++ b/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java
@@ -456,8 +456,8 @@
}
}
- Collections.sort(cmds, PluginEntry.COMPARATOR_BY_NAME);
- Collections.sort(docs, PluginEntry.COMPARATOR_BY_NAME);
+ cmds.sort(PluginEntry.COMPARATOR_BY_NAME);
+ docs.sort(PluginEntry.COMPARATOR_BY_NAME);
StringBuilder md = new StringBuilder();
md.append(String.format("# Plugin %s #\n", pluginName));
diff --git a/java/com/google/gerrit/httpd/raw/BazelBuild.java b/java/com/google/gerrit/httpd/raw/BazelBuild.java
index 940a51b..2b390a9 100644
--- a/java/com/google/gerrit/httpd/raw/BazelBuild.java
+++ b/java/com/google/gerrit/httpd/raw/BazelBuild.java
@@ -23,14 +23,15 @@
import com.google.common.html.HtmlEscapers;
import com.google.common.io.ByteStreams;
import com.google.gerrit.common.TimeUtil;
+import com.google.gerrit.launcher.GerritLauncher;
import com.google.gerrit.util.http.CacheHeaders;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.PrintWriter;
-import java.nio.file.Files;
-import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Properties;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jgit.util.RawParseUtils;
@@ -122,20 +123,19 @@
}
}
- private Properties loadBuildProperties(Path propPath) throws IOException {
- Properties properties = new Properties();
- try (InputStream in = Files.newInputStream(propPath)) {
- properties.load(in);
- } catch (NoSuchFileException e) {
- // Ignore; will be run from PATH, with a descriptive error if it fails.
- }
- return properties;
- }
-
private ProcessBuilder newBuildProcess(Label label) throws IOException {
- Properties properties = loadBuildProperties(sourceRoot.resolve(".bazel_path"));
+ Properties properties = GerritLauncher.loadBuildProperties(sourceRoot.resolve(".bazel_path"));
String bazel = firstNonNull(properties.getProperty("bazel"), "bazel");
- ProcessBuilder proc = new ProcessBuilder(bazel, "build", label.fullName());
+ List<String> cmd = new ArrayList<>();
+ cmd.add(bazel);
+ cmd.add("build");
+ if (GerritLauncher.isJdk9OrLater()) {
+ String v = GerritLauncher.getJdkVersionPostJdk8();
+ cmd.add("--host_java_toolchain=@bazel_tools//tools/jdk:toolchain_java" + v);
+ cmd.add("--java_toolchain=@bazel_tools//tools/jdk:toolchain_java" + v);
+ }
+ cmd.add(label.fullName());
+ ProcessBuilder proc = new ProcessBuilder(cmd);
if (properties.containsKey("PATH")) {
proc.environment().put("PATH", properties.getProperty("PATH"));
}
diff --git a/java/com/google/gerrit/httpd/restapi/RestApiMetrics.java b/java/com/google/gerrit/httpd/restapi/RestApiMetrics.java
index 4af03a3..562687b 100644
--- a/java/com/google/gerrit/httpd/restapi/RestApiMetrics.java
+++ b/java/com/google/gerrit/httpd/restapi/RestApiMetrics.java
@@ -15,6 +15,7 @@
package com.google.gerrit.httpd.restapi;
import com.google.common.base.Strings;
+import com.google.gerrit.extensions.registration.PluginName;
import com.google.gerrit.httpd.restapi.RestApiServlet.ViewData;
import com.google.gerrit.metrics.Counter1;
import com.google.gerrit.metrics.Counter2;
@@ -79,7 +80,8 @@
break;
}
}
- if (!Strings.isNullOrEmpty(viewData.pluginName) && !"gerrit".equals(viewData.pluginName)) {
+ if (!Strings.isNullOrEmpty(viewData.pluginName)
+ && !PluginName.GERRIT.equals(viewData.pluginName)) {
impl = viewData.pluginName + '-' + impl;
}
return impl;
diff --git a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
index cfe712b..e0559f1 100644
--- a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
+++ b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
@@ -70,6 +70,7 @@
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.registration.PluginName;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.BinaryResult;
@@ -324,7 +325,7 @@
viewData = new ViewData(null, rc.list());
} else if (isPost(req)) {
RestView<RestResource> restCollectionView =
- rc.views().get("gerrit", "POST_ON_COLLECTION./");
+ rc.views().get(PluginName.GERRIT, "POST_ON_COLLECTION./");
if (restCollectionView != null) {
viewData = new ViewData(null, restCollectionView);
} else {
@@ -347,7 +348,7 @@
}
if (isPost(req) || isPut(req)) {
- RestView<RestResource> createView = rc.views().get("gerrit", "CREATE./");
+ RestView<RestResource> createView = rc.views().get(PluginName.GERRIT, "CREATE./");
if (createView != null) {
viewData = new ViewData(null, createView);
status = SC_CREATED;
@@ -356,7 +357,8 @@
throw e;
}
} else if (isDelete(req)) {
- RestView<RestResource> deleteView = rc.views().get("gerrit", "DELETE_MISSING./");
+ RestView<RestResource> deleteView =
+ rc.views().get(PluginName.GERRIT, "DELETE_MISSING./");
if (deleteView != null) {
viewData = new ViewData(null, deleteView);
status = SC_NO_CONTENT;
@@ -414,7 +416,7 @@
}
if (isPost(req) || isPut(req)) {
- RestView<RestResource> createView = c.views().get("gerrit", "CREATE./");
+ RestView<RestResource> createView = c.views().get(PluginName.GERRIT, "CREATE./");
if (createView != null) {
viewData = new ViewData(null, createView);
status = SC_CREATED;
@@ -423,7 +425,8 @@
throw e;
}
} else if (isDelete(req)) {
- RestView<RestResource> deleteView = c.views().get("gerrit", "DELETE_MISSING./");
+ RestView<RestResource> deleteView =
+ c.views().get(PluginName.GERRIT, "DELETE_MISSING./");
if (deleteView != null) {
viewData = new ViewData(null, deleteView);
status = SC_NO_CONTENT;
@@ -1245,14 +1248,14 @@
}
String name = method + "." + p.get(0);
- RestView<RestResource> core = views.get("gerrit", name);
+ RestView<RestResource> core = views.get(PluginName.GERRIT, name);
if (core != null) {
- return new ViewData("gerrit", core);
+ return new ViewData(PluginName.GERRIT, core);
}
- core = views.get("gerrit", "GET." + p.get(0));
+ core = views.get(PluginName.GERRIT, "GET." + p.get(0));
if (core != null) {
- return new ViewData("gerrit", core);
+ return new ViewData(PluginName.GERRIT, core);
}
Map<String, RestView<RestResource>> r = new TreeMap<>();
@@ -1320,13 +1323,42 @@
}
private TraceContext enableTracing(HttpServletRequest req, HttpServletResponse res) {
- String v = req.getParameter(ParameterParser.TRACE_PARAMETER);
- if (v != null && (v.isEmpty() || Boolean.parseBoolean(v))) {
- RequestId traceId = new RequestId();
- res.setHeader(X_GERRIT_TRACE, traceId.toString());
- return TraceContext.open().forceLogging().addTag(RequestId.Type.TRACE_ID, traceId);
+ // There are 2 ways to enable tracing for REST calls:
+ // 1. by using the 'trace' or 'trace=<trace-id>' request parameter
+ // 2. by setting the 'X-Gerrit-Trace:' or 'X-Gerrit-Trace:<trace-id>' header
+ String traceValueFromHeader = req.getHeader(X_GERRIT_TRACE);
+ String traceValueFromRequestParam = req.getParameter(ParameterParser.TRACE_PARAMETER);
+ boolean doTrace = traceValueFromHeader != null || traceValueFromRequestParam != null;
+
+ // Check whether no trace ID, one trace ID or 2 different trace IDs have been specified.
+ String traceId1;
+ String traceId2;
+ if (!Strings.isNullOrEmpty(traceValueFromHeader)) {
+ traceId1 = traceValueFromHeader;
+ if (!Strings.isNullOrEmpty(traceValueFromRequestParam)
+ && !traceValueFromHeader.equals(traceValueFromRequestParam)) {
+ traceId2 = traceValueFromRequestParam;
+ } else {
+ traceId2 = null;
+ }
+ } else {
+ traceId1 = Strings.emptyToNull(traceValueFromRequestParam);
+ traceId2 = null;
}
- return TraceContext.DISABLED;
+
+ // Use the first trace ID to start tracing. If this trace ID is null, a trace ID will be
+ // generated.
+ TraceContext traceContext =
+ TraceContext.newTrace(
+ doTrace,
+ traceId1,
+ (tagName, traceId) -> res.setHeader(X_GERRIT_TRACE, traceId.toString()));
+ // If a second trace ID was specified, add a tag for it as well.
+ if (traceId2 != null) {
+ traceContext.addTag(RequestId.Type.TRACE_ID, traceId2);
+ res.addHeader(X_GERRIT_TRACE, traceId2);
+ }
+ return traceContext;
}
private boolean isDelete(HttpServletRequest req) {
@@ -1360,10 +1392,15 @@
private void checkRequiresCapability(ViewData d)
throws AuthException, PermissionBackendException {
- globals
- .permissionBackend
- .currentUser()
- .checkAny(GlobalPermission.fromAnnotation(d.pluginName, d.view.getClass()));
+ try {
+ globals.permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
+ } catch (AuthException e) {
+ // Skiping
+ globals
+ .permissionBackend
+ .currentUser()
+ .checkAny(GlobalPermission.fromAnnotation(d.pluginName, d.view.getClass()));
+ }
}
private static long handleException(
diff --git a/java/com/google/gerrit/index/BUILD b/java/com/google/gerrit/index/BUILD
index 6604ca1..d5517e1 100644
--- a/java/com/google/gerrit/index/BUILD
+++ b/java/com/google/gerrit/index/BUILD
@@ -12,19 +12,6 @@
)
java_library(
- name = "query_parser",
- srcs = ["//antlr3:query"],
- visibility = [
- "//javatests/com/google/gerrit/index:__pkg__",
- "//plugins:__pkg__",
- ],
- deps = [
- ":query_exception",
- "//lib/antlr:java-runtime",
- ],
-)
-
-java_library(
name = "index",
srcs = glob(
["**/*.java"],
@@ -33,7 +20,7 @@
visibility = ["//visibility:public"],
deps = [
":query_exception",
- ":query_parser",
+ "//antlr3:query_parser",
"//java/com/google/gerrit/common:annotations",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/metrics",
diff --git a/java/com/google/gerrit/index/SiteIndexer.java b/java/com/google/gerrit/index/SiteIndexer.java
index 24b7a69..9c56396 100644
--- a/java/com/google/gerrit/index/SiteIndexer.java
+++ b/java/com/google/gerrit/index/SiteIndexer.java
@@ -15,12 +15,14 @@
package com.google.gerrit.index;
import static com.google.common.base.Preconditions.checkNotNull;
+import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Stopwatch;
import com.google.common.flogger.FluentLogger;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import java.io.OutputStream;
+import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.RejectedExecutionException;
@@ -64,7 +66,7 @@
protected int totalWork = -1;
protected OutputStream progressOut = NullOutputStream.INSTANCE;
- protected PrintWriter verboseWriter = new PrintWriter(NullOutputStream.INSTANCE);
+ protected PrintWriter verboseWriter = newPrintWriter(NullOutputStream.INSTANCE);
public void setTotalWork(int num) {
totalWork = num;
@@ -75,7 +77,7 @@
}
public void setVerboseOut(OutputStream out) {
- verboseWriter = new PrintWriter(checkNotNull(out));
+ verboseWriter = newPrintWriter(checkNotNull(out));
}
public abstract Result indexAll(I index);
@@ -86,6 +88,10 @@
new ErrorListener(future, desc, progress, ok), MoreExecutors.directExecutor());
}
+ protected PrintWriter newPrintWriter(OutputStream out) {
+ return new PrintWriter(new OutputStreamWriter(out, UTF_8));
+ }
+
private static class ErrorListener implements Runnable {
private final ListenableFuture<?> future;
private final String desc;
diff --git a/java/com/google/gerrit/index/query/AndSource.java b/java/com/google/gerrit/index/query/AndSource.java
index e2605f4..d1e1c30 100644
--- a/java/com/google/gerrit/index/query/AndSource.java
+++ b/java/com/google/gerrit/index/query/AndSource.java
@@ -15,6 +15,7 @@
package com.google.gerrit.index.query;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.ImmutableList.toImmutableList;
import com.google.common.base.Throwables;
import com.google.common.collect.FluentIterable;
@@ -26,7 +27,6 @@
import com.google.gwtorm.server.ResultSet;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@@ -175,10 +175,8 @@
return cardinality;
}
- private List<Predicate<T>> sort(Collection<? extends Predicate<T>> that) {
- List<Predicate<T>> r = new ArrayList<>(that);
- Collections.sort(r, this);
- return r;
+ private ImmutableList<Predicate<T>> sort(Collection<? extends Predicate<T>> that) {
+ return that.stream().sorted(this).collect(toImmutableList());
}
@Override
diff --git a/java/com/google/gerrit/launcher/GerritLauncher.java b/java/com/google/gerrit/launcher/GerritLauncher.java
index 13dad0e..0d26fe7 100644
--- a/java/com/google/gerrit/launcher/GerritLauncher.java
+++ b/java/com/google/gerrit/launcher/GerritLauncher.java
@@ -34,6 +34,7 @@
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.CodeSource;
@@ -44,6 +45,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Properties;
import java.util.Scanner;
import java.util.SortedMap;
import java.util.TreeMap;
@@ -644,6 +646,25 @@
return resolveInSourceRoot("eclipse-out");
}
+ public static boolean isJdk9OrLater() {
+ return Double.parseDouble(System.getProperty("java.class.version")) >= 53.0;
+ }
+
+ public static String getJdkVersionPostJdk8() {
+ // 9.0.4 => 9
+ return System.getProperty("java.version").substring(0, 1);
+ }
+
+ public static Properties loadBuildProperties(Path propPath) throws IOException {
+ Properties properties = new Properties();
+ try (InputStream in = Files.newInputStream(propPath)) {
+ properties.load(in);
+ } catch (NoSuchFileException e) {
+ // Ignore; will be run from PATH, with a descriptive error if it fails.
+ }
+ return properties;
+ }
+
static final String SOURCE_ROOT_RESOURCE = "/com/google/gerrit/launcher/workspace-root.txt";
/**
@@ -708,14 +729,36 @@
return ret;
}
- private static ClassLoader useDevClasspath() throws MalformedURLException, FileNotFoundException {
+ private static ClassLoader useDevClasspath() throws IOException {
Path out = getDeveloperEclipseOut();
List<URL> dirs = new ArrayList<>();
dirs.add(out.resolve("classes").toUri().toURL());
ClassLoader cl = GerritLauncher.class.getClassLoader();
- for (URL u : ((URLClassLoader) cl).getURLs()) {
- if (includeJar(u)) {
- dirs.add(u);
+
+ if (isJdk9OrLater()) {
+ Path rootPath = resolveInSourceRoot(".").normalize();
+
+ Properties properties = loadBuildProperties(rootPath.resolve(".bazel_path"));
+ Path outputBase = Paths.get(properties.getProperty("output_base"));
+
+ Path runtimeClasspath =
+ rootPath.resolve("bazel-bin/tools/eclipse/main_classpath_collect.runtime_classpath");
+ for (String f : Files.readAllLines(runtimeClasspath, UTF_8)) {
+ URL url;
+ if (f.startsWith("external")) {
+ url = outputBase.resolve(f).toUri().toURL();
+ } else {
+ url = rootPath.resolve(f).toUri().toURL();
+ }
+ if (includeJar(url)) {
+ dirs.add(url);
+ }
+ }
+ } else {
+ for (URL u : ((URLClassLoader) cl).getURLs()) {
+ if (includeJar(u)) {
+ dirs.add(u);
+ }
}
}
return URLClassLoader.newInstance(
@@ -724,7 +767,9 @@
private static boolean includeJar(URL u) {
String path = u.getPath();
- return path.endsWith(".jar") && !path.endsWith("-src.jar");
+ return path.endsWith(".jar")
+ && !path.endsWith("-src.jar")
+ && !path.contains("/com/google/gerrit");
}
private GerritLauncher() {}
diff --git a/java/com/google/gerrit/lucene/AbstractLuceneIndex.java b/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
index 12f88d5..dc293cd 100644
--- a/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
+++ b/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
@@ -39,7 +39,8 @@
import com.google.gerrit.index.query.FieldBundle;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.index.IndexUtils;
-import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
+import com.google.gerrit.server.logging.LoggingContextAwareExecutorService;
+import com.google.gerrit.server.logging.LoggingContextAwareScheduledExecutorService;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.ResultSet;
import java.io.IOException;
@@ -55,6 +56,7 @@
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -100,7 +102,7 @@
private final ReferenceManager<IndexSearcher> searcherManager;
private final ControlledRealTimeReopenThread<IndexSearcher> reopenThread;
private final Set<NrtFuture> notDoneNrtFutures;
- private ScheduledThreadPoolExecutor autoCommitExecutor;
+ private ScheduledExecutorService autoCommitExecutor;
AbstractLuceneIndex(
Schema<V> schema,
@@ -129,13 +131,13 @@
delegateWriter = autoCommitWriter;
autoCommitExecutor =
- new ScheduledThreadPoolExecutor(
- 1,
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat(index + " Commit-%d")
- .setDaemon(true)
- .build());
+ new LoggingContextAwareScheduledExecutorService(
+ new ScheduledThreadPoolExecutor(
+ 1,
+ new ThreadFactoryBuilder()
+ .setNameFormat(index + " Commit-%d")
+ .setDaemon(true)
+ .build()));
@SuppressWarnings("unused") // Error handling within Runnable.
Future<?> possiblyIgnoredError =
autoCommitExecutor.scheduleAtFixedRate(
@@ -170,13 +172,13 @@
writerThread =
MoreExecutors.listeningDecorator(
- Executors.newFixedThreadPool(
- 1,
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat(index + " Write-%d")
- .setDaemon(true)
- .build()));
+ new LoggingContextAwareExecutorService(
+ Executors.newFixedThreadPool(
+ 1,
+ new ThreadFactoryBuilder()
+ .setNameFormat(index + " Write-%d")
+ .setDaemon(true)
+ .build())));
reopenThread =
new ControlledRealTimeReopenThread<>(
diff --git a/java/com/google/gerrit/lucene/BUILD b/java/com/google/gerrit/lucene/BUILD
index 6cb7751..9c6ba74 100644
--- a/java/com/google/gerrit/lucene/BUILD
+++ b/java/com/google/gerrit/lucene/BUILD
@@ -33,6 +33,7 @@
"//java/com/google/gerrit/lifecycle",
"//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
+ "//java/com/google/gerrit/server/logging",
"//lib:guava",
"//lib:gwtorm",
"//lib/flogger:api",
diff --git a/java/com/google/gerrit/metrics/DisabledMetricMaker.java b/java/com/google/gerrit/metrics/DisabledMetricMaker.java
index ea408e2..d3c030d 100644
--- a/java/com/google/gerrit/metrics/DisabledMetricMaker.java
+++ b/java/com/google/gerrit/metrics/DisabledMetricMaker.java
@@ -68,7 +68,7 @@
@Override
public Timer0 newTimer(String name, Description desc) {
- return new Timer0() {
+ return new Timer0(name) {
@Override
public void record(long value, TimeUnit unit) {}
@@ -79,7 +79,7 @@
@Override
public <F1> Timer1<F1> newTimer(String name, Description desc, Field<F1> field1) {
- return new Timer1<F1>() {
+ return new Timer1<F1>(name) {
@Override
public void record(F1 field1, long value, TimeUnit unit) {}
@@ -91,7 +91,7 @@
@Override
public <F1, F2> Timer2<F1, F2> newTimer(
String name, Description desc, Field<F1> field1, Field<F2> field2) {
- return new Timer2<F1, F2>() {
+ return new Timer2<F1, F2>(name) {
@Override
public void record(F1 field1, F2 field2, long value, TimeUnit unit) {}
@@ -103,7 +103,7 @@
@Override
public <F1, F2, F3> Timer3<F1, F2, F3> newTimer(
String name, Description desc, Field<F1> field1, Field<F2> field2, Field<F3> field3) {
- return new Timer3<F1, F2, F3>() {
+ return new Timer3<F1, F2, F3>(name) {
@Override
public void record(F1 field1, F2 field2, F3 field3, long value, TimeUnit unit) {}
diff --git a/java/com/google/gerrit/metrics/Timer0.java b/java/com/google/gerrit/metrics/Timer0.java
index 55d1ddf..225b76f 100644
--- a/java/com/google/gerrit/metrics/Timer0.java
+++ b/java/com/google/gerrit/metrics/Timer0.java
@@ -16,6 +16,7 @@
import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.registration.RegistrationHandle;
import java.util.concurrent.TimeUnit;
@@ -31,6 +32,8 @@
*/
public abstract class Timer0 implements RegistrationHandle {
public static class Context extends TimerContext {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
private final Timer0 timer;
Context(Timer0 timer) {
@@ -39,10 +42,17 @@
@Override
public void record(long elapsed) {
+ logger.atFinest().log("%s took %dms", timer.name, TimeUnit.NANOSECONDS.toMillis(elapsed));
timer.record(elapsed, NANOSECONDS);
}
}
+ protected final String name;
+
+ public Timer0(String name) {
+ this.name = name;
+ }
+
/**
* Begin a timer for the current block, value will be recorded when closed.
*
diff --git a/java/com/google/gerrit/metrics/Timer1.java b/java/com/google/gerrit/metrics/Timer1.java
index f623841..0db0353 100644
--- a/java/com/google/gerrit/metrics/Timer1.java
+++ b/java/com/google/gerrit/metrics/Timer1.java
@@ -16,6 +16,7 @@
import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.registration.RegistrationHandle;
import java.util.concurrent.TimeUnit;
@@ -33,6 +34,8 @@
*/
public abstract class Timer1<F1> implements RegistrationHandle {
public static class Context extends TimerContext {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
private final Timer1<Object> timer;
private final Object field1;
@@ -44,10 +47,18 @@
@Override
public void record(long elapsed) {
+ logger.atFinest().log(
+ "%s (%s) took %dms", timer.name, field1, TimeUnit.NANOSECONDS.toMillis(elapsed));
timer.record(field1, elapsed, NANOSECONDS);
}
}
+ protected final String name;
+
+ public Timer1(String name) {
+ this.name = name;
+ }
+
/**
* Begin a timer for the current block, value will be recorded when closed.
*
diff --git a/java/com/google/gerrit/metrics/Timer2.java b/java/com/google/gerrit/metrics/Timer2.java
index b03ff83..cfdfb7a 100644
--- a/java/com/google/gerrit/metrics/Timer2.java
+++ b/java/com/google/gerrit/metrics/Timer2.java
@@ -16,6 +16,7 @@
import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.registration.RegistrationHandle;
import java.util.concurrent.TimeUnit;
@@ -34,6 +35,8 @@
*/
public abstract class Timer2<F1, F2> implements RegistrationHandle {
public static class Context extends TimerContext {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
private final Timer2<Object, Object> timer;
private final Object field1;
private final Object field2;
@@ -47,10 +50,19 @@
@Override
public void record(long elapsed) {
+ logger.atFinest().log(
+ "%s (%s, %s) took %dms",
+ timer.name, field1, field2, TimeUnit.NANOSECONDS.toMillis(elapsed));
timer.record(field1, field2, elapsed, NANOSECONDS);
}
}
+ protected final String name;
+
+ public Timer2(String name) {
+ this.name = name;
+ }
+
/**
* Begin a timer for the current block, value will be recorded when closed.
*
diff --git a/java/com/google/gerrit/metrics/Timer3.java b/java/com/google/gerrit/metrics/Timer3.java
index 91af42c..1711445 100644
--- a/java/com/google/gerrit/metrics/Timer3.java
+++ b/java/com/google/gerrit/metrics/Timer3.java
@@ -16,6 +16,7 @@
import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.registration.RegistrationHandle;
import java.util.concurrent.TimeUnit;
@@ -35,6 +36,8 @@
*/
public abstract class Timer3<F1, F2, F3> implements RegistrationHandle {
public static class Context extends TimerContext {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
private final Timer3<Object, Object, Object> timer;
private final Object field1;
private final Object field2;
@@ -50,10 +53,19 @@
@Override
public void record(long elapsed) {
+ logger.atFinest().log(
+ "%s (%s, %s, %s) took %dms",
+ timer.name, field1, field2, field3, TimeUnit.NANOSECONDS.toMillis(elapsed));
timer.record(field1, field2, field3, elapsed, NANOSECONDS);
}
}
+ protected final String name;
+
+ public Timer3(String name) {
+ this.name = name;
+ }
+
/**
* Begin a timer for the current block, value will be recorded when closed.
*
diff --git a/java/com/google/gerrit/metrics/dropwizard/BucketedTimer.java b/java/com/google/gerrit/metrics/dropwizard/BucketedTimer.java
index 3b19a62..a7ffe07 100644
--- a/java/com/google/gerrit/metrics/dropwizard/BucketedTimer.java
+++ b/java/com/google/gerrit/metrics/dropwizard/BucketedTimer.java
@@ -26,7 +26,7 @@
/** Abstract timer broken down into buckets by {@link Field} values. */
abstract class BucketedTimer implements BucketedMetric {
private final DropWizardMetricMaker metrics;
- private final String name;
+ protected final String name;
private final Description.FieldOrdering ordering;
protected final Field<?>[] fields;
protected final TimerImpl total;
diff --git a/java/com/google/gerrit/metrics/dropwizard/DropWizardMetricMaker.java b/java/com/google/gerrit/metrics/dropwizard/DropWizardMetricMaker.java
index fc53ee7..ead718f 100644
--- a/java/com/google/gerrit/metrics/dropwizard/DropWizardMetricMaker.java
+++ b/java/com/google/gerrit/metrics/dropwizard/DropWizardMetricMaker.java
@@ -393,11 +393,10 @@
}
class TimerImpl extends Timer0 {
- private final String name;
final com.codahale.metrics.Timer metric;
private TimerImpl(String name, com.codahale.metrics.Timer metric) {
- this.name = name;
+ super(name);
this.metric = metric;
}
diff --git a/java/com/google/gerrit/metrics/dropwizard/TimerImpl1.java b/java/com/google/gerrit/metrics/dropwizard/TimerImpl1.java
index fe6f70e..fc4ba3f 100644
--- a/java/com/google/gerrit/metrics/dropwizard/TimerImpl1.java
+++ b/java/com/google/gerrit/metrics/dropwizard/TimerImpl1.java
@@ -27,7 +27,7 @@
}
Timer1<F1> timer() {
- return new Timer1<F1>() {
+ return new Timer1<F1>(name) {
@Override
public void record(F1 field1, long value, TimeUnit unit) {
total.record(value, unit);
diff --git a/java/com/google/gerrit/metrics/dropwizard/TimerImplN.java b/java/com/google/gerrit/metrics/dropwizard/TimerImplN.java
index 43cc290..d04a65e 100644
--- a/java/com/google/gerrit/metrics/dropwizard/TimerImplN.java
+++ b/java/com/google/gerrit/metrics/dropwizard/TimerImplN.java
@@ -30,7 +30,7 @@
}
<F1, F2> Timer2<F1, F2> timer2() {
- return new Timer2<F1, F2>() {
+ return new Timer2<F1, F2>(name) {
@Override
public void record(F1 field1, F2 field2, long value, TimeUnit unit) {
total.record(value, unit);
@@ -45,7 +45,7 @@
}
<F1, F2, F3> Timer3<F1, F2, F3> timer3() {
- return new Timer3<F1, F2, F3>() {
+ return new Timer3<F1, F2, F3>(name) {
@Override
public void record(F1 field1, F2 field2, F3 field3, long value, TimeUnit unit) {
total.record(value, unit);
diff --git a/java/com/google/gerrit/pgm/Daemon.java b/java/com/google/gerrit/pgm/Daemon.java
index 517787c..1a9af55 100644
--- a/java/com/google/gerrit/pgm/Daemon.java
+++ b/java/com/google/gerrit/pgm/Daemon.java
@@ -26,6 +26,7 @@
import com.google.gerrit.extensions.client.AuthType;
import com.google.gerrit.gpg.GpgModule;
import com.google.gerrit.httpd.AllRequestFilter;
+import com.google.gerrit.httpd.GerritAuthModule;
import com.google.gerrit.httpd.GetUserFilter;
import com.google.gerrit.httpd.GitOverHttpModule;
import com.google.gerrit.httpd.H2CacheBasedWebSession;
@@ -575,10 +576,11 @@
modules.add(new ProjectQoSFilter.Module());
}
modules.add(RequestContextFilter.module());
- modules.add(AllRequestFilter.module());
modules.add(RequestMetricsFilter.module());
modules.add(H2CacheBasedWebSession.module());
+ modules.add(sysInjector.getInstance(GerritAuthModule.class));
modules.add(sysInjector.getInstance(GitOverHttpModule.class));
+ modules.add(AllRequestFilter.module());
modules.add(sysInjector.getInstance(WebModule.class));
modules.add(sysInjector.getInstance(RequireSslFilter.Module.class));
modules.add(new HttpPluginModule());
diff --git a/java/com/google/gerrit/pgm/MigrateToNoteDb.java b/java/com/google/gerrit/pgm/MigrateToNoteDb.java
index 07da3f7..61d7ed9 100644
--- a/java/com/google/gerrit/pgm/MigrateToNoteDb.java
+++ b/java/com/google/gerrit/pgm/MigrateToNoteDb.java
@@ -16,6 +16,7 @@
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.gerrit.server.schema.DataSourceProvider.Context.MULTI_USER;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
@@ -39,6 +40,7 @@
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
+import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -141,7 +143,7 @@
migrator.migrate();
}
}
- try (PrintWriter w = new PrintWriter(System.out, true)) {
+ try (PrintWriter w = new PrintWriter(new OutputStreamWriter(System.out, UTF_8), true)) {
gcAllUsers.run(w);
}
} finally {
diff --git a/java/com/google/gerrit/pgm/init/InitLogging.java b/java/com/google/gerrit/pgm/init/InitLogging.java
new file mode 100644
index 0000000..52d0d2f
--- /dev/null
+++ b/java/com/google/gerrit/pgm/init/InitLogging.java
@@ -0,0 +1,67 @@
+// Copyright (C) 2018 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.pgm.init;
+
+import com.google.gerrit.pgm.init.api.InitStep;
+import com.google.gerrit.pgm.init.api.Section;
+import com.google.inject.Inject;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class InitLogging implements InitStep {
+ private static final String CONTAINER = "container";
+ private static final String JAVA_OPTIONS = "javaOptions";
+ private static final String FLOGGER_BACKEND_PROPERTY = "flogger.backend_factory";
+ private static final String FLOGGER_LOGGING_CONTEXT = "flogger.logging_context";
+
+ private final Section container;
+
+ @Inject
+ public InitLogging(Section.Factory sections) {
+ this.container = sections.get(CONTAINER, null);
+ }
+
+ @Override
+ public void run() throws Exception {
+ List<String> javaOptions = new ArrayList<>(Arrays.asList(container.getList(JAVA_OPTIONS)));
+ if (!isSet(javaOptions, FLOGGER_BACKEND_PROPERTY)) {
+ javaOptions.add(
+ getJavaOption(
+ FLOGGER_BACKEND_PROPERTY,
+ "com.google.common.flogger.backend.log4j.Log4jBackendFactory#getInstance"));
+ }
+ if (!isSet(javaOptions, FLOGGER_LOGGING_CONTEXT)) {
+ javaOptions.add(
+ getJavaOption(
+ FLOGGER_LOGGING_CONTEXT,
+ "com.google.gerrit.server.logging.LoggingContext#getInstance"));
+ }
+ container.setList(JAVA_OPTIONS, javaOptions);
+ }
+
+ private static boolean isSet(List<String> javaOptions, String javaOptionName) {
+ return javaOptions
+ .stream()
+ .anyMatch(
+ o ->
+ o.startsWith("-D" + javaOptionName + "=")
+ || o.startsWith("\"-D" + javaOptionName + "="));
+ }
+
+ private static String getJavaOption(String javaOptionName, String value) {
+ return String.format("-D%s=%s", javaOptionName, value);
+ }
+}
diff --git a/java/com/google/gerrit/pgm/init/InitModule.java b/java/com/google/gerrit/pgm/init/InitModule.java
index 65cf355..f677ceb 100644
--- a/java/com/google/gerrit/pgm/init/InitModule.java
+++ b/java/com/google/gerrit/pgm/init/InitModule.java
@@ -49,6 +49,7 @@
if (initDb) {
step().to(InitDatabase.class);
}
+ step().to(InitLogging.class);
step().to(InitIndex.class);
step().to(InitAuth.class);
step().to(InitAdminUser.class);
diff --git a/java/com/google/gerrit/pgm/init/InitPlugins.java b/java/com/google/gerrit/pgm/init/InitPlugins.java
index 385d20c..e43114c 100644
--- a/java/com/google/gerrit/pgm/init/InitPlugins.java
+++ b/java/com/google/gerrit/pgm/init/InitPlugins.java
@@ -14,7 +14,8 @@
package com.google.gerrit.pgm.init;
-import com.google.common.collect.FluentIterable;
+import static java.util.Comparator.comparing;
+
import com.google.gerrit.common.PluginData;
import com.google.gerrit.pgm.init.api.ConsoleUI;
import com.google.gerrit.pgm.init.api.InitFlags;
@@ -25,12 +26,10 @@
import com.google.inject.Injector;
import com.google.inject.Singleton;
import java.io.IOException;
-import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Comparator;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
@@ -58,25 +57,16 @@
throws IOException {
final List<PluginData> result = new ArrayList<>();
pluginsDistribution.foreach(
- new PluginsDistribution.Processor() {
- @Override
- public void process(String pluginName, InputStream in) throws IOException {
- Path tmpPlugin = JarPluginProvider.storeInTemp(pluginName, in, site);
- String pluginVersion = getVersion(tmpPlugin);
- if (deleteTempPluginFile) {
- Files.delete(tmpPlugin);
- }
- result.add(new PluginData(pluginName, pluginVersion, tmpPlugin));
+ (pluginName, in) -> {
+ Path tmpPlugin = JarPluginProvider.storeInTemp(pluginName, in, site);
+ String pluginVersion = getVersion(tmpPlugin);
+ if (deleteTempPluginFile) {
+ Files.delete(tmpPlugin);
}
+ result.add(new PluginData(pluginName, pluginVersion, tmpPlugin));
});
- return FluentIterable.from(result)
- .toSortedList(
- new Comparator<PluginData>() {
- @Override
- public int compare(PluginData a, PluginData b) {
- return a.name.compareTo(b.name);
- }
- });
+ result.sort(comparing(p -> p.name));
+ return result;
}
private final ConsoleUI ui;
diff --git a/java/com/google/gerrit/pgm/init/InitSshd.java b/java/com/google/gerrit/pgm/init/InitSshd.java
index 0cc30f8..68bdefc 100644
--- a/java/com/google/gerrit/pgm/init/InitSshd.java
+++ b/java/com/google/gerrit/pgm/init/InitSshd.java
@@ -103,7 +103,7 @@
"-q" /* quiet */,
"-t",
"rsa",
- "-P",
+ "-N",
emptyPassphraseArg,
"-C",
comment,
diff --git a/java/com/google/gerrit/pgm/init/api/Section.java b/java/com/google/gerrit/pgm/init/api/Section.java
index 009e989..baf37b6 100644
--- a/java/com/google/gerrit/pgm/init/api/Section.java
+++ b/java/com/google/gerrit/pgm/init/api/Section.java
@@ -23,6 +23,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
+import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -59,6 +60,10 @@
return flags.cfg.getString(section, subsection, name);
}
+ public String[] getList(String name) {
+ return flags.cfg.getStringList(section, subsection, name);
+ }
+
public void set(String name, String value) {
final ArrayList<String> all = new ArrayList<>();
all.addAll(Arrays.asList(flags.cfg.getStringList(section, subsection, name)));
@@ -79,6 +84,10 @@
}
}
+ public void setList(String name, List<String> values) {
+ flags.cfg.setStringList(section, subsection, name, values);
+ }
+
public <T extends Enum<?>> void set(String name, T value) {
if (value != null) {
set(name, value.name());
diff --git a/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
index 8d77ed8..683a205 100644
--- a/java/com/google/gerrit/pgm/util/BatchProgramModule.java
+++ b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
@@ -75,6 +75,7 @@
import com.google.gerrit.server.query.change.ChangeQueryProcessor;
import com.google.gerrit.server.restapi.group.GroupModule;
import com.google.gerrit.server.rules.DefaultSubmitRule;
+import com.google.gerrit.server.rules.IgnoreSelfApprovalRule;
import com.google.gerrit.server.rules.PrologModule;
import com.google.gerrit.server.rules.SubmitRule;
import com.google.gerrit.server.update.BatchUpdate;
@@ -188,6 +189,7 @@
factory(SubmitRuleEvaluator.Factory.class);
install(new PrologModule());
install(new DefaultSubmitRule.Module());
+ install(new IgnoreSelfApprovalRule.Module());
bind(ChangeJson.Factory.class).toProvider(Providers.<ChangeJson.Factory>of(null));
bind(EventUtil.class).toProvider(Providers.<EventUtil>of(null));
diff --git a/java/com/google/gerrit/server/BUILD b/java/com/google/gerrit/server/BUILD
index 96fcd39..e5bc480 100644
--- a/java/com/google/gerrit/server/BUILD
+++ b/java/com/google/gerrit/server/BUILD
@@ -42,6 +42,7 @@
"//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server/cache/serialize",
"//java/com/google/gerrit/server/ioutil",
+ "//java/com/google/gerrit/server/logging",
"//java/com/google/gerrit/server/util/git",
"//java/com/google/gerrit/util/cli",
"//java/com/google/gerrit/util/ssl",
diff --git a/java/com/google/gerrit/server/ChangeUtil.java b/java/com/google/gerrit/server/ChangeUtil.java
index d90f5d0..571f322 100644
--- a/java/com/google/gerrit/server/ChangeUtil.java
+++ b/java/com/google/gerrit/server/ChangeUtil.java
@@ -37,17 +37,9 @@
private static final Random UUID_RANDOM = new SecureRandom();
private static final BaseEncoding UUID_ENCODING = BaseEncoding.base16().lowerCase();
- private static final int SUBJECT_MAX_LENGTH = 80;
- private static final String SUBJECT_CROP_APPENDIX = "...";
- private static final int SUBJECT_CROP_RANGE = 10;
-
public static final Ordering<PatchSet> PS_ID_ORDER =
Ordering.from(comparingInt(PatchSet::getPatchSetId));
- public static String formatChangeUrl(String canonicalWebUrl, Change change) {
- return canonicalWebUrl + "c/" + change.getProject().get() + "/+/" + change.getChangeId();
- }
-
/** @return a new unique identifier for change message entities. */
public static String messageUuid() {
byte[] buf = new byte[8];
@@ -123,21 +115,6 @@
id);
}
- public static String cropSubject(String subject) {
- if (subject.length() > SUBJECT_MAX_LENGTH) {
- int maxLength = SUBJECT_MAX_LENGTH - SUBJECT_CROP_APPENDIX.length();
- for (int cropPosition = maxLength;
- cropPosition > maxLength - SUBJECT_CROP_RANGE;
- cropPosition--) {
- if (Character.isWhitespace(subject.charAt(cropPosition - 1))) {
- return subject.substring(0, cropPosition) + SUBJECT_CROP_APPENDIX;
- }
- }
- return subject.substring(0, maxLength) + SUBJECT_CROP_APPENDIX;
- }
- return subject;
- }
-
public static String status(Change c) {
return c != null ? c.getStatus().name().toLowerCase() : "deleted";
}
diff --git a/java/com/google/gerrit/server/CommentsUtil.java b/java/com/google/gerrit/server/CommentsUtil.java
index 18d9b3d..99dfbbb 100644
--- a/java/com/google/gerrit/server/CommentsUtil.java
+++ b/java/com/google/gerrit/server/CommentsUtil.java
@@ -502,7 +502,7 @@
}
private static <T extends Comment> List<T> sort(List<T> comments) {
- Collections.sort(comments, COMMENT_ORDER);
+ comments.sort(COMMENT_ORDER);
return comments;
}
diff --git a/java/com/google/gerrit/server/IdentifiedUser.java b/java/com/google/gerrit/server/IdentifiedUser.java
index 16546f9..d9a4cae 100644
--- a/java/com/google/gerrit/server/IdentifiedUser.java
+++ b/java/com/google/gerrit/server/IdentifiedUser.java
@@ -15,10 +15,12 @@
package com.google.gerrit.server;
import static com.google.common.base.MoreObjects.firstNonNull;
+import static com.google.common.flogger.LazyArgs.lazy;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
+import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountCache;
@@ -54,6 +56,8 @@
/** An authenticated user. */
public class IdentifiedUser extends CurrentUser {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
/** Create an IdentifiedUser, ignoring any per-request state. */
@Singleton
public static class GenericFactory {
@@ -375,8 +379,13 @@
if (effectiveGroups == null) {
if (authConfig.isIdentityTrustable(state().getExternalIds())) {
effectiveGroups = groupBackend.membershipsOf(this);
+ logger.atFinest().log(
+ "Known groups of %s: %s", getLoggableName(), lazy(effectiveGroups::getKnownGroups));
} else {
effectiveGroups = registeredGroups;
+ logger.atFinest().log(
+ "%s has a non-trusted identity, falling back to %s as known groups",
+ getLoggableName(), lazy(registeredGroups::getKnownGroups));
}
}
return effectiveGroups;
diff --git a/java/com/google/gerrit/server/account/AccountCacheImpl.java b/java/com/google/gerrit/server/account/AccountCacheImpl.java
index c74f9d4..2cde94c 100644
--- a/java/com/google/gerrit/server/account/AccountCacheImpl.java
+++ b/java/com/google/gerrit/server/account/AccountCacheImpl.java
@@ -154,12 +154,14 @@
@Override
public void evict(@Nullable Account.Id accountId) {
if (accountId != null) {
+ logger.atFine().log("Evict account %d", accountId.get());
byId.invalidate(accountId);
}
}
@Override
public void evictAll() {
+ logger.atFine().log("Evict all accounts");
byId.invalidateAll();
}
diff --git a/java/com/google/gerrit/server/account/GroupBackends.java b/java/com/google/gerrit/server/account/GroupBackends.java
index 803d491..1b15512 100644
--- a/java/com/google/gerrit/server/account/GroupBackends.java
+++ b/java/com/google/gerrit/server/account/GroupBackends.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.account;
+import static java.util.Comparator.comparing;
+
import com.google.common.collect.Iterables;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.GroupReference;
@@ -25,12 +27,7 @@
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());
- }
- };
+ comparing(GroupReference::getName);
/**
* Runs {@link GroupBackend#suggest(String, ProjectState)} and filters the result to return the
diff --git a/java/com/google/gerrit/server/account/GroupCacheImpl.java b/java/com/google/gerrit/server/account/GroupCacheImpl.java
index 06b51a7..1f8cb88 100644
--- a/java/com/google/gerrit/server/account/GroupCacheImpl.java
+++ b/java/com/google/gerrit/server/account/GroupCacheImpl.java
@@ -116,6 +116,7 @@
@Override
public void evict(AccountGroup.Id groupId) {
if (groupId != null) {
+ logger.atFine().log("Evict group %s by ID", groupId.get());
byId.invalidate(groupId);
}
}
@@ -123,6 +124,7 @@
@Override
public void evict(AccountGroup.NameKey groupName) {
if (groupName != null) {
+ logger.atFine().log("Evict group '%s' by name", groupName.get());
byName.invalidate(groupName.get());
}
}
@@ -130,6 +132,7 @@
@Override
public void evict(AccountGroup.UUID groupUuid) {
if (groupUuid != null) {
+ logger.atFine().log("Evict group %s by UUID", groupUuid.get());
byUUID.invalidate(groupUuid.get());
}
}
diff --git a/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java b/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java
index 5906a06..5fb4ca1 100644
--- a/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java
+++ b/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java
@@ -112,6 +112,7 @@
@Override
public void evictGroupsWithMember(Account.Id memberId) {
if (memberId != null) {
+ logger.atFine().log("Evict groups with member %d", memberId.get());
groupsWithMember.invalidate(memberId);
}
}
@@ -119,9 +120,11 @@
@Override
public void evictParentGroupsOf(AccountGroup.UUID groupId) {
if (groupId != null) {
+ logger.atFine().log("Evict parent groups of %s", groupId.get());
parentGroups.invalidate(groupId);
if (!AccountGroup.isInternalGroup(groupId)) {
+ logger.atFine().log("Evict external group %s", groupId.get());
external.invalidate(EXTERNAL_NAME);
}
}
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalId.java b/java/com/google/gerrit/server/account/externalids/ExternalId.java
index db8ea41..96ea0cc 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalId.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalId.java
@@ -47,14 +47,12 @@
// corresponding regular expressions in the
// com.google.gerrit.client.account.UsernameField class.
private static final String USER_NAME_PATTERN_FIRST_REGEX = "[a-zA-Z0-9]";
- private static final String USER_NAME_PATTERN_REST_REGEX = "[a-zA-Z0-9._@-]";
+ private static final String USER_NAME_PATTERN_REST_REGEX = "[a-zA-Z0-9.!#$%&’*+=?^_`\\{|\\}~@-]";
private static final String USER_NAME_PATTERN_LAST_REGEX = "[a-zA-Z0-9]";
/** Regular expression that a username must match. */
private static final String USER_NAME_PATTERN_REGEX =
- "^"
- + //
- "("
+ "^("
+ //
USER_NAME_PATTERN_FIRST_REGEX
+ //
@@ -67,9 +65,7 @@
+ //
USER_NAME_PATTERN_FIRST_REGEX
+ //
- ")"
- + //
- "$";
+ ")$";
private static final Pattern USER_NAME_PATTERN = Pattern.compile(USER_NAME_PATTERN_REGEX);
diff --git a/java/com/google/gerrit/server/cache/CacheMetrics.java b/java/com/google/gerrit/server/cache/CacheMetrics.java
index 11f2034..3435652 100644
--- a/java/com/google/gerrit/server/cache/CacheMetrics.java
+++ b/java/com/google/gerrit/server/cache/CacheMetrics.java
@@ -18,6 +18,7 @@
import com.google.common.cache.CacheStats;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.registration.PluginName;
import com.google.gerrit.metrics.CallbackMetric;
import com.google.gerrit.metrics.CallbackMetric1;
import com.google.gerrit.metrics.Description;
@@ -95,7 +96,7 @@
}
private static String metricNameOf(DynamicMap.Entry<Cache<?, ?>> e) {
- if ("gerrit".equals(e.getPluginName())) {
+ if (PluginName.GERRIT.equals(e.getPluginName())) {
return e.getExportName();
}
return String.format("plugin/%s/%s", e.getPluginName(), e.getExportName());
diff --git a/java/com/google/gerrit/server/cache/ForwardingRemovalListener.java b/java/com/google/gerrit/server/cache/ForwardingRemovalListener.java
index be06601..a7fdbbd 100644
--- a/java/com/google/gerrit/server/cache/ForwardingRemovalListener.java
+++ b/java/com/google/gerrit/server/cache/ForwardingRemovalListener.java
@@ -18,6 +18,7 @@
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;
import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.registration.PluginName;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -36,7 +37,7 @@
private final DynamicSet<CacheRemovalListener> listeners;
private final String cacheName;
- private String pluginName = "gerrit";
+ private String pluginName = PluginName.GERRIT;
@Inject
ForwardingRemovalListener(
diff --git a/java/com/google/gerrit/server/cache/h2/BUILD b/java/com/google/gerrit/server/cache/h2/BUILD
index 2ce756b..f6418e3 100644
--- a/java/com/google/gerrit/server/cache/h2/BUILD
+++ b/java/com/google/gerrit/server/cache/h2/BUILD
@@ -9,6 +9,7 @@
"//java/com/google/gerrit/lifecycle",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/cache/serialize",
+ "//java/com/google/gerrit/server/logging",
"//lib:guava",
"//lib:h2",
"//lib/flogger:api",
diff --git a/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java b/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java
index a7824ea..af1228d 100644
--- a/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java
+++ b/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java
@@ -28,7 +28,8 @@
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.gerrit.server.logging.LoggingContextAwareThreadFactory;
+import com.google.gerrit.server.logging.LoggingContextAwareExecutorService;
+import com.google.gerrit.server.logging.LoggingContextAwareScheduledExecutorService;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -75,20 +76,17 @@
if (cacheDir != null) {
executor =
- Executors.newFixedThreadPool(
- 1,
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat("DiskCache-Store-%d")
- .build());
+ new LoggingContextAwareExecutorService(
+ Executors.newFixedThreadPool(
+ 1, new ThreadFactoryBuilder().setNameFormat("DiskCache-Store-%d").build()));
cleanup =
- Executors.newScheduledThreadPool(
- 1,
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat("DiskCache-Prune-%d")
- .setDaemon(true)
- .build());
+ new LoggingContextAwareScheduledExecutorService(
+ Executors.newScheduledThreadPool(
+ 1,
+ new ThreadFactoryBuilder()
+ .setNameFormat("DiskCache-Prune-%d")
+ .setDaemon(true)
+ .build()));
} else {
executor = null;
cleanup = null;
diff --git a/java/com/google/gerrit/server/change/ChangeInserter.java b/java/com/google/gerrit/server/change/ChangeInserter.java
index e8c55e8..38c97f7 100644
--- a/java/com/google/gerrit/server/change/ChangeInserter.java
+++ b/java/com/google/gerrit/server/change/ChangeInserter.java
@@ -552,8 +552,6 @@
return;
}
- PermissionBackend.ForRef perm =
- permissionBackend.user(ctx.getUser()).project(ctx.getProject()).ref(refName);
try {
try (CommitReceivedEvent event =
new CommitReceivedEvent(
@@ -565,7 +563,7 @@
ctx.getIdentifiedUser())) {
commitValidatorsFactory
.forGerritCommits(
- perm,
+ permissionBackend.user(ctx.getUser()).project(ctx.getProject()),
new Branch.NameKey(ctx.getProject(), refName),
ctx.getIdentifiedUser(),
new NoSshInfo(),
diff --git a/java/com/google/gerrit/server/change/ChangeJson.java b/java/com/google/gerrit/server/change/ChangeJson.java
index e02f666..173d1da 100644
--- a/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/java/com/google/gerrit/server/change/ChangeJson.java
@@ -1405,8 +1405,7 @@
out.commitWithFooters =
mergeUtilFactory
.create(projectCache.get(project))
- .createCommitMessageOnSubmit(
- commit, mergeTip, cd.notes(), userProvider.get(), in.getId());
+ .createCommitMessageOnSubmit(commit, mergeTip, cd.notes(), in.getId());
}
}
diff --git a/java/com/google/gerrit/server/change/IncludedIn.java b/java/com/google/gerrit/server/change/IncludedIn.java
index 8f8925a..d5d54ec 100644
--- a/java/com/google/gerrit/server/change/IncludedIn.java
+++ b/java/com/google/gerrit/server/change/IncludedIn.java
@@ -63,13 +63,13 @@
ListMultimap<String, String> external = MultimapBuilder.hashKeys().arrayListValues().build();
for (ExternalIncludedIn ext : externalIncludedIn) {
ListMultimap<String, String> extIncludedIns =
- ext.getIncludedIn(project.get(), rev.name(), d.getTags(), d.getBranches());
+ ext.getIncludedIn(project.get(), rev.name(), d.tags(), d.branches());
if (extIncludedIns != null) {
external.putAll(extIncludedIns);
}
}
return new IncludedInInfo(
- d.getBranches(), d.getTags(), (!external.isEmpty() ? external.asMap() : null));
+ d.branches(), d.tags(), (!external.isEmpty() ? external.asMap() : null));
}
}
}
diff --git a/java/com/google/gerrit/server/change/IncludedInResolver.java b/java/com/google/gerrit/server/change/IncludedInResolver.java
index d1bc0a2..62e9454 100644
--- a/java/com/google/gerrit/server/change/IncludedInResolver.java
+++ b/java/com/google/gerrit/server/change/IncludedInResolver.java
@@ -14,6 +14,13 @@
package com.google.gerrit.server.change;
+import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet;
+import static java.util.Comparator.comparing;
+import static java.util.Comparator.naturalOrder;
+import static java.util.stream.Collectors.toList;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
@@ -22,7 +29,6 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -93,11 +99,9 @@
parseCommits(allTagsAndBranches);
Set<String> allMatchingTagsAndBranches = includedIn(tipsByCommitTime, 0);
- Result detail = new Result();
- detail.setBranches(getMatchingRefNames(allMatchingTagsAndBranches, branches));
- detail.setTags(getMatchingRefNames(allMatchingTagsAndBranches, tags));
-
- return detail;
+ return new AutoValue_IncludedInResolver_Result(
+ getMatchingRefNames(allMatchingTagsAndBranches, branches),
+ getMatchingRefNames(allMatchingTagsAndBranches, tags));
}
private boolean includedInOne(Collection<Ref> refs) throws IOException {
@@ -151,15 +155,7 @@
*/
private void partition(List<RevCommit> before, List<RevCommit> after) {
int insertionPoint =
- Collections.binarySearch(
- tipsByCommitTime,
- target,
- new Comparator<RevCommit>() {
- @Override
- public int compare(RevCommit c1, RevCommit c2) {
- return c1.getCommitTime() - c2.getCommitTime();
- }
- });
+ Collections.binarySearch(tipsByCommitTime, target, comparing(RevCommit::getCommitTime));
if (insertionPoint < 0) {
insertionPoint = -(insertionPoint + 1);
}
@@ -175,15 +171,14 @@
* Returns the short names of refs which are as well in the matchingRefs list as well as in the
* allRef list.
*/
- private static List<String> getMatchingRefNames(
+ private static ImmutableSortedSet<String> getMatchingRefNames(
Set<String> matchingRefs, Collection<Ref> allRefs) {
- List<String> refNames = Lists.newArrayListWithCapacity(matchingRefs.size());
- for (Ref r : allRefs) {
- if (matchingRefs.contains(r.getName())) {
- refNames.add(Repository.shortenRefName(r.getName()));
- }
- }
- return refNames;
+ return allRefs
+ .stream()
+ .map(Ref::getName)
+ .filter(matchingRefs::contains)
+ .map(Repository::shortenRefName)
+ .collect(toImmutableSortedSet(naturalOrder()));
}
/** Parse commit of ref and store the relation between ref and commit. */
@@ -211,43 +206,14 @@
}
commitToRef.put(commit, ref.getName());
}
- tipsByCommitTime = Lists.newArrayList(commitToRef.keySet());
- sortOlderFirst(tipsByCommitTime);
+ tipsByCommitTime =
+ commitToRef.keySet().stream().sorted(comparing(RevCommit::getCommitTime)).collect(toList());
}
- private void sortOlderFirst(List<RevCommit> tips) {
- Collections.sort(
- tips,
- new Comparator<RevCommit>() {
- @Override
- public int compare(RevCommit c1, RevCommit c2) {
- return c1.getCommitTime() - c2.getCommitTime();
- }
- });
- }
+ @AutoValue
+ public abstract static class Result {
+ public abstract ImmutableSortedSet<String> branches();
- public static class Result {
- private List<String> branches;
- private List<String> tags;
-
- public Result() {}
-
- public void setBranches(List<String> b) {
- Collections.sort(b);
- branches = b;
- }
-
- public List<String> getBranches() {
- return branches;
- }
-
- public void setTags(List<String> t) {
- Collections.sort(t);
- tags = t;
- }
-
- public List<String> getTags() {
- return tags;
- }
+ public abstract ImmutableSortedSet<String> tags();
}
}
diff --git a/java/com/google/gerrit/server/change/PatchSetInserter.java b/java/com/google/gerrit/server/change/PatchSetInserter.java
index d71a93d..8bd6c17 100644
--- a/java/com/google/gerrit/server/change/PatchSetInserter.java
+++ b/java/com/google/gerrit/server/change/PatchSetInserter.java
@@ -323,9 +323,6 @@
return;
}
- PermissionBackend.ForRef perm =
- permissionBackend.user(ctx.getUser()).ref(origNotes.getChange().getDest());
-
String refName = getPatchSetId().toRefName();
try (CommitReceivedEvent event =
new CommitReceivedEvent(
@@ -340,7 +337,7 @@
ctx.getIdentifiedUser())) {
commitValidatorsFactory
.forGerritCommits(
- perm,
+ permissionBackend.user(ctx.getUser()).project(ctx.getProject()),
origNotes.getChange().getDest(),
ctx.getIdentifiedUser(),
new NoSshInfo(),
diff --git a/java/com/google/gerrit/server/change/RebaseChangeOp.java b/java/com/google/gerrit/server/change/RebaseChangeOp.java
index 909ea3a..1f216f0 100644
--- a/java/com/google/gerrit/server/change/RebaseChangeOp.java
+++ b/java/com/google/gerrit/server/change/RebaseChangeOp.java
@@ -165,8 +165,7 @@
rw.parseBody(baseCommit);
newCommitMessage =
newMergeUtil()
- .createCommitMessageOnSubmit(
- original, baseCommit, notes, changeOwner, originalPatchSet.getId());
+ .createCommitMessageOnSubmit(original, baseCommit, notes, originalPatchSet.getId());
} else {
newCommitMessage = original.getFullMessage();
}
diff --git a/java/com/google/gerrit/server/change/WalkSorter.java b/java/com/google/gerrit/server/change/WalkSorter.java
index cff1ac7..916a62b 100644
--- a/java/com/google/gerrit/server/change/WalkSorter.java
+++ b/java/com/google/gerrit/server/change/WalkSorter.java
@@ -34,7 +34,6 @@
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
@@ -110,7 +109,7 @@
for (Map.Entry<Project.NameKey, Collection<ChangeData>> e : byProject.asMap().entrySet()) {
sortedByProject.add(sortProject(e.getKey(), e.getValue()));
}
- Collections.sort(sortedByProject, PROJECT_LIST_SORTER);
+ sortedByProject.sort(PROJECT_LIST_SORTER);
return Iterables.concat(sortedByProject);
}
diff --git a/java/com/google/gerrit/server/config/CacheResource.java b/java/com/google/gerrit/server/config/CacheResource.java
index 16c7508..ffa7b5a 100644
--- a/java/com/google/gerrit/server/config/CacheResource.java
+++ b/java/com/google/gerrit/server/config/CacheResource.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.config;
import com.google.common.cache.Cache;
+import com.google.gerrit.extensions.registration.PluginName;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
@@ -52,7 +53,7 @@
}
public static String cacheNameOf(String plugin, String name) {
- if ("gerrit".equals(plugin)) {
+ if (PluginName.GERRIT.equals(plugin)) {
return name;
}
return plugin + "-" + name;
diff --git a/java/com/google/gerrit/server/config/GerritGlobalModule.java b/java/com/google/gerrit/server/config/GerritGlobalModule.java
index b6a257b..0761d2e 100644
--- a/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -170,6 +170,7 @@
import com.google.gerrit.server.restapi.change.SuggestReviewers;
import com.google.gerrit.server.restapi.group.GroupModule;
import com.google.gerrit.server.rules.DefaultSubmitRule;
+import com.google.gerrit.server.rules.IgnoreSelfApprovalRule;
import com.google.gerrit.server.rules.PrologModule;
import com.google.gerrit.server.rules.RulesCache;
import com.google.gerrit.server.rules.SubmitRule;
@@ -244,6 +245,7 @@
install(new NoteDbModule(cfg));
install(new PrologModule());
install(new DefaultSubmitRule.Module());
+ install(new IgnoreSelfApprovalRule.Module());
install(new ReceiveCommitsModule());
install(new SshAddressesModule());
install(ThreadLocalRequestContext.module());
diff --git a/java/com/google/gerrit/server/config/SysExecutorModule.java b/java/com/google/gerrit/server/config/SysExecutorModule.java
index 2e97a58..f552434 100644
--- a/java/com/google/gerrit/server/config/SysExecutorModule.java
+++ b/java/com/google/gerrit/server/config/SysExecutorModule.java
@@ -19,7 +19,7 @@
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.gerrit.server.FanOutExecutor;
import com.google.gerrit.server.git.WorkQueue;
-import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
+import com.google.gerrit.server.logging.LoggingContextAwareExecutorService;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;
@@ -83,18 +83,18 @@
return MoreExecutors.newDirectExecutorService();
}
return MoreExecutors.listeningDecorator(
- MoreExecutors.getExitingExecutorService(
- new ThreadPoolExecutor(
- 1,
- poolSize,
- 10,
- TimeUnit.MINUTES,
- new ArrayBlockingQueue<Runnable>(poolSize),
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat("ChangeUpdate-%d")
- .setDaemon(true)
- .build(),
- new ThreadPoolExecutor.CallerRunsPolicy())));
+ new LoggingContextAwareExecutorService(
+ MoreExecutors.getExitingExecutorService(
+ new ThreadPoolExecutor(
+ 1,
+ poolSize,
+ 10,
+ TimeUnit.MINUTES,
+ new ArrayBlockingQueue<Runnable>(poolSize),
+ new ThreadFactoryBuilder()
+ .setNameFormat("ChangeUpdate-%d")
+ .setDaemon(true)
+ .build(),
+ new ThreadPoolExecutor.CallerRunsPolicy()))));
}
}
diff --git a/java/com/google/gerrit/server/events/EventFactory.java b/java/com/google/gerrit/server/events/EventFactory.java
index 2fbc1c7..fbce4b2 100644
--- a/java/com/google/gerrit/server/events/EventFactory.java
+++ b/java/com/google/gerrit/server/events/EventFactory.java
@@ -73,7 +73,6 @@
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -329,10 +328,9 @@
}
}
// Sort by original parent order.
- Collections.sort(
- ca.dependsOn,
+ ca.dependsOn.sort(
comparing(
- (DependencyAttribute d) -> {
+ d -> {
for (int i = 0; i < parentNames.size(); i++) {
if (parentNames.get(i).equals(d.revision)) {
return i;
diff --git a/java/com/google/gerrit/server/extensions/webui/UiActions.java b/java/com/google/gerrit/server/extensions/webui/UiActions.java
index f8cb4ce..af28bed3 100644
--- a/java/com/google/gerrit/server/extensions/webui/UiActions.java
+++ b/java/com/google/gerrit/server/extensions/webui/UiActions.java
@@ -26,6 +26,7 @@
import com.google.gerrit.extensions.api.access.GlobalOrPluginPermission;
import com.google.gerrit.extensions.conditions.BooleanCondition;
import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.registration.PluginName;
import com.google.gerrit.extensions.restapi.RestCollection;
import com.google.gerrit.extensions.restapi.RestResource;
import com.google.gerrit.extensions.restapi.RestView;
@@ -169,7 +170,7 @@
PrivateInternals_UiActionDescription.setMethod(dsc, e.getExportName().substring(0, d));
PrivateInternals_UiActionDescription.setId(
- dsc, "gerrit".equals(e.getPluginName()) ? name : e.getPluginName() + '~' + name);
+ dsc, PluginName.GERRIT.equals(e.getPluginName()) ? name : e.getPluginName() + '~' + name);
return dsc;
}
}
diff --git a/java/com/google/gerrit/server/git/DefaultChangeReportFormatter.java b/java/com/google/gerrit/server/git/DefaultChangeReportFormatter.java
index 1c87a63..513d909 100644
--- a/java/com/google/gerrit/server/git/DefaultChangeReportFormatter.java
+++ b/java/com/google/gerrit/server/git/DefaultChangeReportFormatter.java
@@ -14,12 +14,16 @@
package com.google.gerrit.server.git;
-import com.google.gerrit.server.ChangeUtil;
+import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.inject.Inject;
/** Print a change description for use in git command-line progress. */
public class DefaultChangeReportFormatter implements ChangeReportFormatter {
+ private static final int SUBJECT_MAX_LENGTH = 80;
+ private static final String SUBJECT_CROP_APPENDIX = "...";
+ private static final int SUBJECT_CROP_RANGE = 10;
+
private final String canonicalWebUrl;
@Inject
@@ -37,19 +41,37 @@
return formatChangeUrl(canonicalWebUrl, input);
}
- @Override
- public String changeClosed(ChangeReportFormatter.Input input) {
- return String.format(
- "change %s closed", ChangeUtil.formatChangeUrl(canonicalWebUrl, input.change()));
+ public static String formatChangeUrl(String canonicalWebUrl, Change change) {
+ return canonicalWebUrl + "c/" + change.getProject().get() + "/+/" + change.getChangeId();
}
- private String formatChangeUrl(String url, Input input) {
+ @Override
+ public String changeClosed(ChangeReportFormatter.Input input) {
+ return String.format("change %s closed", formatChangeUrl(canonicalWebUrl, input.change()));
+ }
+
+ protected String cropSubject(String subject) {
+ if (subject.length() > SUBJECT_MAX_LENGTH) {
+ int maxLength = SUBJECT_MAX_LENGTH - SUBJECT_CROP_APPENDIX.length();
+ for (int cropPosition = maxLength;
+ cropPosition > maxLength - SUBJECT_CROP_RANGE;
+ cropPosition--) {
+ if (Character.isWhitespace(subject.charAt(cropPosition - 1))) {
+ return subject.substring(0, cropPosition) + SUBJECT_CROP_APPENDIX;
+ }
+ }
+ return subject.substring(0, maxLength) + SUBJECT_CROP_APPENDIX;
+ }
+ return subject;
+ }
+
+ protected String formatChangeUrl(String url, Input input) {
StringBuilder m =
new StringBuilder()
.append(" ")
- .append(ChangeUtil.formatChangeUrl(url, input.change()))
+ .append(formatChangeUrl(url, input.change()))
.append(" ")
- .append(ChangeUtil.cropSubject(input.subject()));
+ .append(cropSubject(input.subject()));
if (input.isEdit()) {
m.append(" [EDIT]");
}
diff --git a/java/com/google/gerrit/server/git/MergeUtil.java b/java/com/google/gerrit/server/git/MergeUtil.java
index 0231378..c035269 100644
--- a/java/com/google/gerrit/server/git/MergeUtil.java
+++ b/java/com/google/gerrit/server/git/MergeUtil.java
@@ -41,7 +41,6 @@
import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.GerritServerConfig;
@@ -220,7 +219,7 @@
} catch (IOException e) {
throw new IntegrationException("Branch head sorting failed", e);
}
- Collections.sort(result, CodeReviewCommit.ORDER);
+ result.sort(CodeReviewCommit.ORDER);
return result;
}
@@ -315,12 +314,10 @@
*
* @param n
* @param notes
- * @param user
* @param psId
* @return new message
*/
- private String createDetailedCommitMessage(
- RevCommit n, ChangeNotes notes, CurrentUser user, PatchSet.Id psId) {
+ private String createDetailedCommitMessage(RevCommit n, ChangeNotes notes, PatchSet.Id psId) {
Change c = notes.getChange();
final List<FooterLine> footers = n.getFooterLines();
final StringBuilder msgbuf = new StringBuilder();
@@ -424,12 +421,7 @@
}
public String createCommitMessageOnSubmit(CodeReviewCommit n, RevCommit mergeTip) {
- return createCommitMessageOnSubmit(
- n,
- mergeTip,
- n.notes(),
- identifiedUserFactory.create(n.notes().getChange().getOwner()),
- n.getPatchsetId());
+ return createCommitMessageOnSubmit(n, mergeTip, n.notes(), n.getPatchsetId());
}
/**
@@ -442,14 +434,13 @@
* @param n
* @param mergeTip
* @param notes
- * @param user
* @param id
* @return new message
*/
public String createCommitMessageOnSubmit(
- RevCommit n, RevCommit mergeTip, ChangeNotes notes, CurrentUser user, Id id) {
+ RevCommit n, RevCommit mergeTip, ChangeNotes notes, Id id) {
return commitMessageGenerator.generate(
- n, mergeTip, notes.getChange().getDest(), createDetailedCommitMessage(n, notes, user, id));
+ n, mergeTip, notes.getChange().getDest(), createDetailedCommitMessage(n, notes, id));
}
private static boolean isCodeReview(LabelId id) {
diff --git a/java/com/google/gerrit/server/git/TransferConfig.java b/java/com/google/gerrit/server/git/TransferConfig.java
index f85f24b..8c93833 100644
--- a/java/com/google/gerrit/server/git/TransferConfig.java
+++ b/java/com/google/gerrit/server/git/TransferConfig.java
@@ -28,6 +28,7 @@
private final PackConfig packConfig;
private final long maxObjectSizeLimit;
private final String maxObjectSizeLimitFormatted;
+ private final boolean inheritProjectMaxObjectSizeLimit;
@Inject
TransferConfig(@GerritServerConfig Config cfg) {
@@ -42,6 +43,8 @@
TimeUnit.SECONDS);
maxObjectSizeLimit = cfg.getLong("receive", "maxObjectSizeLimit", 0);
maxObjectSizeLimitFormatted = cfg.getString("receive", null, "maxObjectSizeLimit");
+ inheritProjectMaxObjectSizeLimit =
+ cfg.getBoolean("receive", "inheritProjectMaxObjectSizeLimit", false);
packConfig = new PackConfig();
packConfig.setDeltaCompress(false);
@@ -65,4 +68,8 @@
public String getFormattedMaxObjectSizeLimit() {
return maxObjectSizeLimitFormatted;
}
+
+ public boolean getInheritProjectMaxObjectSizeLimit() {
+ return inheritProjectMaxObjectSizeLimit;
+ }
}
diff --git a/java/com/google/gerrit/server/git/WorkQueue.java b/java/com/google/gerrit/server/git/WorkQueue.java
index a2c12df..a7336f0 100644
--- a/java/com/google/gerrit/server/git/WorkQueue.java
+++ b/java/com/google/gerrit/server/git/WorkQueue.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.git;
+import static java.util.stream.Collectors.toList;
+
import com.google.common.base.CaseFormat;
import com.google.common.base.Supplier;
import com.google.common.flogger.FluentLogger;
@@ -24,7 +26,8 @@
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.ScheduleConfig.Schedule;
-import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
+import com.google.gerrit.server.logging.LoggingContext;
+import com.google.gerrit.server.logging.LoggingContextAwareRunnable;
import com.google.gerrit.server.util.IdGenerator;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -43,6 +46,7 @@
import java.util.concurrent.Future;
import java.util.concurrent.RunnableScheduledFuture;
import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
@@ -166,12 +170,11 @@
if (threadPriority != Thread.NORM_PRIORITY) {
ThreadFactory parent = executor.getThreadFactory();
executor.setThreadFactory(
- new LoggingContextAwareThreadFactory(
- task -> {
- Thread t = parent.newThread(task);
- t.setPriority(threadPriority);
- return t;
- }));
+ task -> {
+ Thread t = parent.newThread(task);
+ t.setPriority(threadPriority);
+ return t;
+ });
}
return executor;
@@ -253,19 +256,18 @@
Executor(int corePoolSize, final String queueName) {
super(
corePoolSize,
- new LoggingContextAwareThreadFactory(
- new ThreadFactory() {
- private final ThreadFactory parent = Executors.defaultThreadFactory();
- private final AtomicInteger tid = new AtomicInteger(1);
+ new ThreadFactory() {
+ private final ThreadFactory parent = Executors.defaultThreadFactory();
+ private final AtomicInteger tid = new AtomicInteger(1);
- @Override
- public Thread newThread(Runnable task) {
- final Thread t = parent.newThread(task);
- t.setName(queueName + "-" + tid.getAndIncrement());
- t.setUncaughtExceptionHandler(LOG_UNCAUGHT_EXCEPTION);
- return t;
- }
- }));
+ @Override
+ public Thread newThread(Runnable task) {
+ final Thread t = parent.newThread(task);
+ t.setName(queueName + "-" + tid.getAndIncrement());
+ t.setUncaughtExceptionHandler(LOG_UNCAUGHT_EXCEPTION);
+ return t;
+ }
+ });
all =
new ConcurrentHashMap<>( //
@@ -277,6 +279,75 @@
}
@Override
+ public void execute(Runnable command) {
+ super.execute(LoggingContext.copy(command));
+ }
+
+ @Override
+ public <T> Future<T> submit(Callable<T> task) {
+ return super.submit(LoggingContext.copy(task));
+ }
+
+ @Override
+ public <T> Future<T> submit(Runnable task, T result) {
+ return super.submit(LoggingContext.copy(task), result);
+ }
+
+ @Override
+ public Future<?> submit(Runnable task) {
+ return super.submit(LoggingContext.copy(task));
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException {
+ return super.invokeAll(tasks.stream().map(LoggingContext::copy).collect(toList()));
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(
+ Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+ throws InterruptedException {
+ return super.invokeAll(
+ tasks.stream().map(LoggingContext::copy).collect(toList()), timeout, unit);
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException, ExecutionException {
+ return super.invokeAny(tasks.stream().map(LoggingContext::copy).collect(toList()));
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ return super.invokeAny(
+ tasks.stream().map(LoggingContext::copy).collect(toList()), timeout, unit);
+ }
+
+ @Override
+ public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
+ return super.schedule(LoggingContext.copy(command), delay, unit);
+ }
+
+ @Override
+ public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
+ return super.schedule(LoggingContext.copy(callable), delay, unit);
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleAtFixedRate(
+ Runnable command, long initialDelay, long period, TimeUnit unit) {
+ return super.scheduleAtFixedRate(LoggingContext.copy(command), initialDelay, period, unit);
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleWithFixedDelay(
+ Runnable command, long initialDelay, long delay, TimeUnit unit) {
+ return super.scheduleWithFixedDelay(LoggingContext.copy(command), initialDelay, delay, unit);
+ }
+
+ @Override
protected void terminated() {
super.terminated();
queues.remove(this);
@@ -370,6 +441,10 @@
Task<V> task;
+ if (runnable instanceof LoggingContextAwareRunnable) {
+ runnable = ((LoggingContextAwareRunnable) runnable).unwrap();
+ }
+
if (runnable instanceof ProjectRunnable) {
task = new ProjectTask<>((ProjectRunnable) runnable, r, this, id);
} else {
diff --git a/java/com/google/gerrit/server/git/meta/TabFile.java b/java/com/google/gerrit/server/git/meta/TabFile.java
index ef25cd8..4c0378a 100644
--- a/java/com/google/gerrit/server/git/meta/TabFile.java
+++ b/java/com/google/gerrit/server/git/meta/TabFile.java
@@ -14,13 +14,15 @@
package com.google.gerrit.server.git.meta;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+
+import com.google.common.collect.ImmutableList;
import com.google.gerrit.server.git.ValidationError;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -122,10 +124,8 @@
return buf.toString();
}
- protected static <T extends Comparable<? super T>> List<T> sort(Collection<T> m) {
- ArrayList<T> r = new ArrayList<>(m);
- Collections.sort(r);
- return r;
+ protected static <T extends Comparable<? super T>> ImmutableList<T> sort(Collection<T> m) {
+ return m.stream().sorted().collect(toImmutableList());
}
protected static String pad(int len, String src) {
diff --git a/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java b/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java
index 7fe0c04..eb62d54 100644
--- a/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java
+++ b/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java
@@ -94,6 +94,7 @@
// Don't expose the binding for ReceiveCommits.Factory. All callers should
// be using AsyncReceiveCommits.Factory instead.
install(new FactoryModuleBuilder().build(ReceiveCommits.Factory.class));
+ install(new FactoryModuleBuilder().build(BranchCommitValidator.Factory.class));
}
@Provides
@@ -224,7 +225,7 @@
receivePack.setAllowNonFastForwards(true);
receivePack.setRefLogIdent(user.newRefLogIdent());
receivePack.setTimeout(transferConfig.getTimeout());
- receivePack.setMaxObjectSizeLimit(projectState.getEffectiveMaxObjectSizeLimit());
+ receivePack.setMaxObjectSizeLimit(projectState.getEffectiveMaxObjectSizeLimit().value);
receivePack.setCheckReceivedObjects(projectState.getConfig().getCheckReceivedObjects());
receivePack.setRefFilter(new ReceiveRefFilter());
receivePack.setAllowPushOptions(true);
diff --git a/java/com/google/gerrit/server/git/receive/BUILD b/java/com/google/gerrit/server/git/receive/BUILD
index fddb9d6..f1c604b 100644
--- a/java/com/google/gerrit/server/git/receive/BUILD
+++ b/java/com/google/gerrit/server/git/receive/BUILD
@@ -8,6 +8,7 @@
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
+ "//java/com/google/gerrit/server/logging",
"//java/com/google/gerrit/util/cli",
"//lib:args4j",
"//lib:guava",
diff --git a/java/com/google/gerrit/server/git/receive/BranchCommitValidator.java b/java/com/google/gerrit/server/git/receive/BranchCommitValidator.java
new file mode 100644
index 0000000..24b6ab1
--- /dev/null
+++ b/java/com/google/gerrit/server/git/receive/BranchCommitValidator.java
@@ -0,0 +1,132 @@
+// Copyright (C) 2018 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.receive;
+
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
+
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.reviewdb.client.Branch;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.client.RevId;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.events.CommitReceivedEvent;
+import com.google.gerrit.server.git.validators.CommitValidationException;
+import com.google.gerrit.server.git.validators.CommitValidationMessage;
+import com.google.gerrit.server.git.validators.CommitValidators;
+import com.google.gerrit.server.git.validators.ValidationMessage;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.ssh.SshInfo;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+import java.io.IOException;
+import java.util.List;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.notes.NoteMap;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.transport.ReceiveCommand;
+
+/** Validates single commits for a branch. */
+public class BranchCommitValidator {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ private final CommitValidators.Factory commitValidatorsFactory;
+ private final IdentifiedUser user;
+ private final PermissionBackend.ForProject permissions;
+ private final Project project;
+ private final Branch.NameKey branch;
+ private final SshInfo sshInfo;
+
+ interface Factory {
+ BranchCommitValidator create(
+ ProjectState projectState, Branch.NameKey branch, IdentifiedUser user);
+ }
+
+ @Inject
+ BranchCommitValidator(
+ CommitValidators.Factory commitValidatorsFactory,
+ PermissionBackend permissionBackend,
+ SshInfo sshInfo,
+ @Assisted ProjectState projectState,
+ @Assisted Branch.NameKey branch,
+ @Assisted IdentifiedUser user) {
+ this.sshInfo = sshInfo;
+ this.user = user;
+ this.branch = branch;
+ this.commitValidatorsFactory = commitValidatorsFactory;
+ project = projectState.getProject();
+ permissions = permissionBackend.user(user).project(project.getNameKey());
+ }
+
+ /**
+ * Validates a single commit. If the commit does not validate, the command is rejected.
+ *
+ * @param objectReader the object reader to use.
+ * @param cmd the ReceiveCommand executing the push.
+ * @param commit the commit being validated.
+ * @param isMerged whether this is a merge commit created by magicBranch --merge option
+ * @param change the change for which this is a new patchset.
+ */
+ public boolean validCommit(
+ ObjectReader objectReader,
+ ReceiveCommand cmd,
+ RevCommit commit,
+ boolean isMerged,
+ List<ValidationMessage> messages,
+ NoteMap rejectCommits,
+ @Nullable Change change)
+ throws IOException {
+ try (CommitReceivedEvent receiveEvent =
+ new CommitReceivedEvent(cmd, project, branch.get(), objectReader, commit, user)) {
+ CommitValidators validators;
+ if (isMerged) {
+ validators =
+ commitValidatorsFactory.forMergedCommits(permissions, branch, user.asIdentifiedUser());
+ } else {
+ validators =
+ commitValidatorsFactory.forReceiveCommits(
+ permissions,
+ branch,
+ user.asIdentifiedUser(),
+ sshInfo,
+ rejectCommits,
+ receiveEvent.revWalk,
+ change);
+ }
+
+ for (CommitValidationMessage m : validators.validate(receiveEvent)) {
+ messages.add(
+ new CommitValidationMessage(messageForCommit(commit, m.getMessage()), m.isError()));
+ }
+ } catch (CommitValidationException e) {
+ logger.atFine().log("Commit validation failed on %s", commit.name());
+ for (CommitValidationMessage m : e.getMessages()) {
+ // The non-error messages may contain background explanation for the
+ // fatal error, so have to preserve all messages.
+ messages.add(
+ new CommitValidationMessage(messageForCommit(commit, m.getMessage()), m.isError()));
+ }
+ cmd.setResult(REJECTED_OTHER_REASON, messageForCommit(commit, e.getMessage()));
+ return false;
+ }
+ return true;
+ }
+
+ private String messageForCommit(RevCommit c, String msg) {
+ return String.format("commit %s: %s", c.abbreviate(RevId.ABBREV_LEN).name(), msg);
+ }
+}
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
index 4632083..4b475f9 100644
--- a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
+++ b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
@@ -39,7 +39,6 @@
import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_MISSING_OBJECT;
import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
-import com.google.auto.value.AutoValue;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
@@ -94,10 +93,7 @@
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.Sequences;
-import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.AccountResolver;
-import com.google.gerrit.server.account.AccountState;
-import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.change.ChangeInserter;
import com.google.gerrit.server.change.SetHashtagsOp;
import com.google.gerrit.server.config.AllProjectsName;
@@ -106,7 +102,6 @@
import com.google.gerrit.server.config.ProjectConfigEntry;
import com.google.gerrit.server.edit.ChangeEdit;
import com.google.gerrit.server.edit.ChangeEditUtil;
-import com.google.gerrit.server.events.CommitReceivedEvent;
import com.google.gerrit.server.git.BanCommit;
import com.google.gerrit.server.git.ChangeReportFormatter;
import com.google.gerrit.server.git.GroupCollector;
@@ -116,9 +111,7 @@
import com.google.gerrit.server.git.ReceivePackInitializer;
import com.google.gerrit.server.git.TagCache;
import com.google.gerrit.server.git.ValidationError;
-import com.google.gerrit.server.git.validators.CommitValidationException;
import com.google.gerrit.server.git.validators.CommitValidationMessage;
-import com.google.gerrit.server.git.validators.CommitValidators;
import com.google.gerrit.server.git.validators.RefOperationValidationException;
import com.google.gerrit.server.git.validators.RefOperationValidators;
import com.google.gerrit.server.git.validators.ValidationMessage;
@@ -145,7 +138,6 @@
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gerrit.server.ssh.SshInfo;
import com.google.gerrit.server.submit.MergeOp;
import com.google.gerrit.server.submit.MergeOpRepoManager;
import com.google.gerrit.server.submit.SubmoduleException;
@@ -218,38 +210,16 @@
*
* <p>Conceptually, most use of Gerrit is a push of some commits to refs/for/BRANCH. However, the
* receive-pack protocol that this is based on allows multiple ref updates to be processed at once.
+ * So we have to be prepared to also handle normal pushes (refs/heads/BRANCH), and legacy pushes
+ * (refs/changes/CHANGE). It is hard to split this class up further, because normal pushes can also
+ * result in updates to reviews, through the autoclose mechanism.
*/
class ReceiveCommits {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- private enum ReceiveError {
- CONFIG_UPDATE(
- "You are not allowed to perform this operation.\n"
- + "Configuration changes can only be pushed by project owners\n"
- + "who also have 'Push' rights on "
- + RefNames.REFS_CONFIG),
- UPDATE(
- "You are not allowed to perform this operation.\n"
- + "To push into this reference you need 'Push' rights."),
- DELETE(
- "You need 'Delete Reference' rights or 'Push' rights with the \n"
- + "'Force Push' flag set to delete references."),
- DELETE_CHANGES("Cannot delete from '" + REFS_CHANGES + "'"),
- CODE_REVIEW(
- "You need 'Push' rights to upload code review requests.\n"
- + "Verify that you are pushing to the right branch.");
-
- private final String value;
-
- ReceiveError(String value) {
- this.value = value;
- }
-
- String get() {
- return value;
- }
- }
-
+ private static final String CODE_REVIEW_ERROR =
+ "You need 'Push' rights to upload code review requests.\n"
+ + "Verify that you are pushing to the right branch.";
private static final String CANNOT_DELETE_CHANGES = "Cannot delete from '" + REFS_CHANGES + "'";
private static final String CANNOT_DELETE_CONFIG =
"Cannot delete project configuration from '" + RefNames.REFS_CONFIG + "'";
@@ -319,7 +289,6 @@
// Injected fields.
private final AccountResolver accountResolver;
- private final Provider<AccountsUpdate> accountsUpdateProvider;
private final AllProjectsName allProjectsName;
private final BatchUpdate.Factory batchUpdateFactory;
private final ChangeEditUtil editUtil;
@@ -328,7 +297,7 @@
private final ChangeNotes.Factory notesFactory;
private final ChangeReportFormatter changeFormatter;
private final CmdLineParser.Factory optionParserFactory;
- private final CommitValidators.Factory commitValidatorsFactory;
+ private final BranchCommitValidator.Factory commitValidatorFactory;
private final CreateGroupPermissionSyncer createGroupPermissionSyncer;
private final CreateRefControl createRefControl;
private final DynamicMap<ProjectConfigEntry> pluginConfigEntries;
@@ -350,7 +319,6 @@
private final ReviewDb db;
private final Sequences seq;
private final SetHashtagsOp.Factory hashtagsFactory;
- private final SshInfo sshInfo;
private final SubmoduleOp.Factory subOpFactory;
private final TagCache tagCache;
@@ -379,15 +347,6 @@
private final ListMultimap<String, String> pushOptions;
private final Map<Change.Id, ReplaceRequest> replaceByChange;
- @AutoValue
- protected abstract static class ValidCommitKey {
- abstract ObjectId getObjectId();
-
- abstract Branch.NameKey getBranch();
- }
-
- private final Set<ValidCommitKey> validCommits;
-
// Collections lazily populated during processing.
private ListMultimap<Change.Id, Ref> refsByChange;
private ListMultimap<ObjectId, Ref> refsById;
@@ -395,17 +354,15 @@
// Other settings populated during processing.
private MagicBranchInput magicBranch;
private boolean newChangeForAllNotInTarget;
- private String setFullNameTo;
private boolean setChangeAsPrivate;
private Optional<NoteDbPushOption> noteDbPushOption;
- private Optional<Boolean> tracePushOption;
+ private Optional<String> tracePushOption;
private MessageSender messageSender;
@Inject
ReceiveCommits(
AccountResolver accountResolver,
- @ServerInitiated Provider<AccountsUpdate> accountsUpdateProvider,
AllProjectsName allProjectsName,
BatchUpdate.Factory batchUpdateFactory,
@GerritServerConfig Config cfg,
@@ -415,7 +372,7 @@
ChangeNotes.Factory notesFactory,
DynamicItem<ChangeReportFormatter> changeFormatterProvider,
CmdLineParser.Factory optionParserFactory,
- CommitValidators.Factory commitValidatorsFactory,
+ BranchCommitValidator.Factory commitValidatorFactory,
CreateGroupPermissionSyncer createGroupPermissionSyncer,
CreateRefControl createRefControl,
DynamicMap<ProjectConfigEntry> pluginConfigEntries,
@@ -437,7 +394,6 @@
ReviewDb db,
Sequences seq,
SetHashtagsOp.Factory hashtagsFactory,
- SshInfo sshInfo,
SubmoduleOp.Factory subOpFactory,
TagCache tagCache,
@Assisted ProjectState projectState,
@@ -449,12 +405,11 @@
throws IOException {
// Injected fields.
this.accountResolver = accountResolver;
- this.accountsUpdateProvider = accountsUpdateProvider;
this.allProjectsName = allProjectsName;
this.batchUpdateFactory = batchUpdateFactory;
this.changeFormatter = changeFormatterProvider.get();
this.changeInserterFactory = changeInserterFactory;
- this.commitValidatorsFactory = commitValidatorsFactory;
+ this.commitValidatorFactory = commitValidatorFactory;
this.createRefControl = createRefControl;
this.createGroupPermissionSyncer = createGroupPermissionSyncer;
this.db = db;
@@ -480,7 +435,6 @@
this.retryHelper = retryHelper;
this.requestScopePropagator = requestScopePropagator;
this.seq = seq;
- this.sshInfo = sshInfo;
this.subOpFactory = subOpFactory;
this.tagCache = tagCache;
@@ -505,7 +459,6 @@
pushOptions = LinkedListMultimap.create();
replaceByChange = new LinkedHashMap<>();
updateGroups = new ArrayList<>();
- validCommits = new HashSet<>();
this.allowProjectOwnersToChangeParent =
cfg.getBoolean("receive", "allowProjectOwnersToChangeParent", false);
@@ -567,10 +520,6 @@
commandProgress.end();
progress.end();
-
- // Update account info with details discovered during commit walking. The account update happens
- // in a separate batch update, and failure doesn't cause the push itself to fail.
- updateAccountInfo();
}
// Process as many commands as possible, but may leave some commands in state NOT_ATTEMPTED.
@@ -578,52 +527,54 @@
Collection<ReceiveCommand> commands, MultiProgressMonitor progress) {
parsePushOptions();
try (TraceContext traceContext =
- TraceContext.open()
- .addTag(RequestId.Type.RECEIVE_ID, RequestId.forProject(project.getNameKey()))) {
- if (tracePushOption.orElse(false)) {
- RequestId traceId = new RequestId();
- traceContext.forceLogging().addTag(RequestId.Type.TRACE_ID, traceId);
- addMessage(RequestId.Type.TRACE_ID.name() + ": " + traceId);
+ TraceContext.newTrace(
+ tracePushOption.isPresent(),
+ tracePushOption.orElse(null),
+ (tagName, traceId) -> addMessage(tagName + ": " + traceId))) {
+ traceContext.addTag(RequestId.Type.RECEIVE_ID, new RequestId(project.getNameKey().get()));
+
+ // Log the push options here, rather than in parsePushOptions(), so that they are included
+ // into the trace if tracing is enabled.
+ logger.atFine().log("push options: %s", receivePack.getPushOptions());
+
+ if (!projectState.getProject().getState().permitsWrite()) {
+ for (ReceiveCommand cmd : commands) {
+ reject(cmd, "prohibited by Gerrit: project state does not permit write");
+ }
+ return;
+ }
+
+ logger.atFine().log("Parsing %d commands", commands.size());
+
+ List<ReceiveCommand> magicCommands = new ArrayList<>();
+ List<ReceiveCommand> directPatchSetPushCommands = new ArrayList<>();
+ List<ReceiveCommand> regularCommands = new ArrayList<>();
+
+ for (ReceiveCommand cmd : commands) {
+ if (MagicBranch.isMagicBranch(cmd.getRefName())) {
+ magicCommands.add(cmd);
+ } else if (isDirectChangesPush(cmd.getRefName())) {
+ directPatchSetPushCommands.add(cmd);
+ } else {
+ regularCommands.add(cmd);
+ }
+ }
+
+ int commandTypes =
+ (magicCommands.isEmpty() ? 0 : 1)
+ + (directPatchSetPushCommands.isEmpty() ? 0 : 1)
+ + (regularCommands.isEmpty() ? 0 : 1);
+
+ if (commandTypes > 1) {
+ for (ReceiveCommand cmd : commands) {
+ if (cmd.getResult() == NOT_ATTEMPTED) {
+ cmd.setResult(REJECTED_OTHER_REASON, "cannot combine normal pushes and magic pushes");
+ }
+ }
+ return;
}
try {
- if (!projectState.getProject().getState().permitsWrite()) {
- for (ReceiveCommand cmd : commands) {
- reject(cmd, "prohibited by Gerrit: project state does not permit write");
- }
- return;
- }
-
- logger.atFine().log("Parsing %d commands", commands.size());
-
- List<ReceiveCommand> magicCommands = new ArrayList<>();
- List<ReceiveCommand> directPatchSetPushCommands = new ArrayList<>();
- List<ReceiveCommand> regularCommands = new ArrayList<>();
-
- for (ReceiveCommand cmd : commands) {
- if (MagicBranch.isMagicBranch(cmd.getRefName())) {
- magicCommands.add(cmd);
- } else if (isDirectChangesPush(cmd.getRefName())) {
- directPatchSetPushCommands.add(cmd);
- } else {
- regularCommands.add(cmd);
- }
- }
-
- int commandTypes =
- (magicCommands.isEmpty() ? 0 : 1)
- + (directPatchSetPushCommands.isEmpty() ? 0 : 1)
- + (regularCommands.isEmpty() ? 0 : 1);
-
- if (commandTypes > 1) {
- for (ReceiveCommand cmd : commands) {
- if (cmd.getResult() == NOT_ATTEMPTED) {
- cmd.setResult(REJECTED_OTHER_REASON, "cannot combine normal pushes and magic pushes");
- }
- }
- return;
- }
-
if (!regularCommands.isEmpty()) {
handleRegularCommands(regularCommands, progress);
return;
@@ -960,7 +911,7 @@
List<String> traceValues = pushOptions.get("trace");
if (!traceValues.isEmpty()) {
String value = traceValues.get(traceValues.size() - 1);
- tracePushOption = Optional.of(value.isEmpty() || Boolean.parseBoolean(value));
+ tracePushOption = Optional.of(value);
} else {
tracePushOption = Optional.empty();
}
@@ -1083,131 +1034,139 @@
}
if (isConfig(cmd)) {
- logger.atFine().log("Processing %s command", cmd.getRefName());
- try {
- permissions.check(ProjectPermission.WRITE_CONFIG);
- } catch (AuthException e) {
- reject(
- cmd,
- String.format(
- "must be either project owner or have %s permission",
- ProjectPermission.WRITE_CONFIG.describeForException()));
- return;
- }
+ validateConfigPush(cmd);
+ }
+ }
- switch (cmd.getType()) {
- case CREATE:
- case UPDATE:
- case UPDATE_NONFASTFORWARD:
- try {
- ProjectConfig cfg = new ProjectConfig(project.getNameKey());
- cfg.load(project.getNameKey(), receivePack.getRevWalk(), cmd.getNewId());
- if (!cfg.getValidationErrors().isEmpty()) {
- addError("Invalid project configuration:");
- for (ValidationError err : cfg.getValidationErrors()) {
- addError(" " + err.getMessage());
- }
- reject(cmd, "invalid project configuration");
- logger.atSevere().log(
- "User %s tried to push invalid project configuration %s for %s",
- user.getLoggableName(), cmd.getNewId().name(), project.getName());
- return;
+ /** Validates a push to refs/meta/config, and reject the command if it fails. */
+ private void validateConfigPush(ReceiveCommand cmd) throws PermissionBackendException {
+ logger.atFine().log("Processing %s command", cmd.getRefName());
+ try {
+ permissions.check(ProjectPermission.WRITE_CONFIG);
+ } catch (AuthException e) {
+ reject(
+ cmd,
+ String.format(
+ "must be either project owner or have %s permission",
+ ProjectPermission.WRITE_CONFIG.describeForException()));
+ return;
+ }
+
+ switch (cmd.getType()) {
+ case CREATE:
+ case UPDATE:
+ case UPDATE_NONFASTFORWARD:
+ try {
+ ProjectConfig cfg = new ProjectConfig(project.getNameKey());
+ cfg.load(project.getNameKey(), receivePack.getRevWalk(), cmd.getNewId());
+ if (!cfg.getValidationErrors().isEmpty()) {
+ addError("Invalid project configuration:");
+ for (ValidationError err : cfg.getValidationErrors()) {
+ addError(" " + err.getMessage());
}
- Project.NameKey newParent = cfg.getProject().getParent(allProjectsName);
- Project.NameKey oldParent = project.getParent(allProjectsName);
- if (oldParent == null) {
- // update of the 'All-Projects' project
- if (newParent != null) {
- reject(cmd, "invalid project configuration: root project cannot have parent");
- return;
- }
- } else {
- if (!oldParent.equals(newParent)) {
- if (allowProjectOwnersToChangeParent) {
- try {
- permissionBackend
- .user(user)
- .project(project.getNameKey())
- .check(ProjectPermission.WRITE_CONFIG);
- } catch (AuthException e) {
- reject(
- cmd, "invalid project configuration: only project owners can set parent");
- return;
- }
- } else {
- try {
- permissionBackend.user(user).check(GlobalPermission.ADMINISTRATE_SERVER);
- } catch (AuthException e) {
- reject(cmd, "invalid project configuration: only Gerrit admin can set parent");
- return;
- }
- }
- }
-
- if (projectCache.get(newParent) == null) {
- reject(cmd, "invalid project configuration: parent does not exist");
- return;
- }
- }
-
- for (Entry<ProjectConfigEntry> e : pluginConfigEntries) {
- PluginConfig pluginCfg = cfg.getPluginConfig(e.getPluginName());
- ProjectConfigEntry configEntry = e.getProvider().get();
- String value = pluginCfg.getString(e.getExportName());
- String oldValue =
- projectState
- .getConfig()
- .getPluginConfig(e.getPluginName())
- .getString(e.getExportName());
- if (configEntry.getType() == ProjectConfigEntryType.ARRAY) {
- oldValue =
- Arrays.stream(
- projectState
- .getConfig()
- .getPluginConfig(e.getPluginName())
- .getStringList(e.getExportName()))
- .collect(joining("\n"));
- }
-
- if ((value == null ? oldValue != null : !value.equals(oldValue))
- && !configEntry.isEditable(projectState)) {
- reject(
- cmd,
- String.format(
- "invalid project configuration: Not allowed to set parameter"
- + " '%s' of plugin '%s' on project '%s'.",
- e.getExportName(), e.getPluginName(), project.getName()));
- continue;
- }
-
- if (ProjectConfigEntryType.LIST.equals(configEntry.getType())
- && value != null
- && !configEntry.getPermittedValues().contains(value)) {
- reject(
- cmd,
- String.format(
- "invalid project configuration: The value '%s' is "
- + "not permitted for parameter '%s' of plugin '%s'.",
- value, e.getExportName(), e.getPluginName()));
- }
- }
- } catch (Exception e) {
reject(cmd, "invalid project configuration");
- logger.atSevere().withCause(e).log(
+ logger.atSevere().log(
"User %s tried to push invalid project configuration %s for %s",
user.getLoggableName(), cmd.getNewId().name(), project.getName());
return;
}
- break;
+ Project.NameKey newParent = cfg.getProject().getParent(allProjectsName);
+ Project.NameKey oldParent = project.getParent(allProjectsName);
+ if (oldParent == null) {
+ // update of the 'All-Projects' project
+ if (newParent != null) {
+ reject(cmd, "invalid project configuration: root project cannot have parent");
+ return;
+ }
+ } else {
+ if (!oldParent.equals(newParent)) {
+ if (allowProjectOwnersToChangeParent) {
+ try {
+ permissionBackend
+ .user(user)
+ .project(project.getNameKey())
+ .check(ProjectPermission.WRITE_CONFIG);
+ } catch (AuthException e) {
+ reject(cmd, "invalid project configuration: only project owners can set parent");
+ return;
+ }
+ } else {
+ try {
+ permissionBackend.user(user).check(GlobalPermission.ADMINISTRATE_SERVER);
+ } catch (AuthException e) {
+ reject(cmd, "invalid project configuration: only Gerrit admin can set parent");
+ return;
+ }
+ }
+ }
- case DELETE:
- break;
+ if (projectCache.get(newParent) == null) {
+ reject(cmd, "invalid project configuration: parent does not exist");
+ return;
+ }
+ }
+ validatePluginConfig(cmd, cfg);
+ } catch (Exception e) {
+ reject(cmd, "invalid project configuration");
+ logger.atSevere().withCause(e).log(
+ "User %s tried to push invalid project configuration %s for %s",
+ user.getLoggableName(), cmd.getNewId().name(), project.getName());
+ return;
+ }
+ break;
- default:
- reject(
- cmd,
- "prohibited by Gerrit: don't know how to handle config update of type "
- + cmd.getType());
+ case DELETE:
+ break;
+
+ default:
+ reject(
+ cmd,
+ "prohibited by Gerrit: don't know how to handle config update of type "
+ + cmd.getType());
+ }
+ }
+
+ /**
+ * validates a push to refs/meta/config for plugin configuration, and rejects the push if it
+ * fails.
+ */
+ private void validatePluginConfig(ReceiveCommand cmd, ProjectConfig cfg) {
+ for (Entry<ProjectConfigEntry> e : pluginConfigEntries) {
+ PluginConfig pluginCfg = cfg.getPluginConfig(e.getPluginName());
+ ProjectConfigEntry configEntry = e.getProvider().get();
+ String value = pluginCfg.getString(e.getExportName());
+ String oldValue =
+ projectState.getConfig().getPluginConfig(e.getPluginName()).getString(e.getExportName());
+ if (configEntry.getType() == ProjectConfigEntryType.ARRAY) {
+ oldValue =
+ Arrays.stream(
+ projectState
+ .getConfig()
+ .getPluginConfig(e.getPluginName())
+ .getStringList(e.getExportName()))
+ .collect(joining("\n"));
+ }
+
+ if ((value == null ? oldValue != null : !value.equals(oldValue))
+ && !configEntry.isEditable(projectState)) {
+ reject(
+ cmd,
+ String.format(
+ "invalid project configuration: Not allowed to set parameter"
+ + " '%s' of plugin '%s' on project '%s'.",
+ e.getExportName(), e.getPluginName(), project.getName()));
+ continue;
+ }
+
+ if (ProjectConfigEntryType.LIST.equals(configEntry.getType())
+ && value != null
+ && !configEntry.getPermittedValues().contains(value)) {
+ reject(
+ cmd,
+ String.format(
+ "invalid project configuration: The value '%s' is "
+ + "not permitted for parameter '%s' of plugin '%s'.",
+ value, e.getExportName(), e.getPluginName()));
}
}
}
@@ -1381,7 +1340,7 @@
Set<String> hashtags = new HashSet<>();
@Option(name = "--trace", metaVar = "NAME", usage = "enable tracing")
- boolean trace;
+ String trace;
@Option(name = "--base", metaVar = "BASE", usage = "merge base of changes")
List<ObjectId> base;
@@ -1696,7 +1655,7 @@
// TODO(davido): Remove legacy support for drafts magic branch option
// after repo-tool supports private and work-in-progress changes.
if (magicBranch.draft && !receiveConfig.allowDrafts) {
- errors.put(ReceiveError.CODE_REVIEW.get(), ref);
+ errors.put(CODE_REVIEW_ERROR, ref);
reject(cmd, "draft workflow is disabled");
return;
}
@@ -1808,7 +1767,7 @@
return;
}
- if (validateConnected(magicBranch.dest, tip)) {
+ if (validateConnected(magicBranch.cmd, magicBranch.dest, tip)) {
this.magicBranch = magicBranch;
}
}
@@ -1817,7 +1776,7 @@
// branch. If they aren't, we want to abort. We do this check by
// looking to see if we can compute a merge base between the new
// commits and the target branch head.
- private boolean validateConnected(Branch.NameKey dest, RevCommit tip) {
+ private boolean validateConnected(ReceiveCommand cmd, Branch.NameKey dest, RevCommit tip) {
RevWalk walk = receivePack.getRevWalk();
try {
Ref targetRef = receivePack.getAdvertisedRefs().get(dest.get());
@@ -1840,7 +1799,7 @@
walk.markStart(tip);
walk.markStart(h);
if (walk.next() == null) {
- reject(magicBranch.cmd, "no common ancestry");
+ reject(cmd, "no common ancestry");
return false;
}
} finally {
@@ -1848,7 +1807,7 @@
walk.setRevFilter(oldRevFilter);
}
} catch (IOException e) {
- magicBranch.cmd.setResult(REJECTED_MISSING_OBJECT);
+ cmd.setResult(REJECTED_MISSING_OBJECT);
logger.atSevere().withCause(e).log("Invalid pack upload; one or more objects weren't sent");
return false;
}
@@ -1908,13 +1867,16 @@
return;
}
+ BranchCommitValidator validator =
+ commitValidatorFactory.create(projectState, changeEnt.getDest(), user);
try {
- if (validCommit(
+ if (validator.validCommit(
receivePack.getRevWalk().getObjectReader(),
- changeEnt.getDest(),
cmd,
newCommit,
false,
+ messages,
+ rejectCommits,
changeEnt)) {
logger.atFine().log("Replacing change %s", changeEnt.getId());
requestReplace(cmd, true, changeEnt, newCommit);
@@ -1972,6 +1934,9 @@
GroupCollector groupCollector =
GroupCollector.create(changeRefsById(), db, psUtil, notesFactory, project.getNameKey());
+ BranchCommitValidator validator =
+ commitValidatorFactory.create(projectState, magicBranch.dest, user);
+
try {
RevCommit start = setUpWalkForSelectingChanges();
if (start == null) {
@@ -2071,12 +2036,13 @@
logger.atFine().log("Creating new change for %s even though it is already tracked", name);
}
- if (!validCommit(
+ if (!validator.validCommit(
receivePack.getRevWalk().getObjectReader(),
- magicBranch.dest,
magicBranch.cmd,
c,
magicBranch.merged,
+ messages,
+ rejectCommits,
null)) {
// Not a change the user can propose? Abort as early as possible.
logger.atFine().log("Aborting early due to invalid commit");
@@ -3036,7 +3002,7 @@
return;
}
- boolean missingFullName = Strings.isNullOrEmpty(user.getAccount().getFullName());
+ BranchCommitValidator validator = commitValidatorFactory.create(projectState, branch, user);
RevWalk walk = receivePack.getRevWalk();
walk.reset();
walk.sort(RevSort.NONE);
@@ -3063,15 +3029,10 @@
continue;
}
- if (!validCommit(walk.getObjectReader(), branch, cmd, c, false, null)) {
+ if (!validator.validCommit(
+ walk.getObjectReader(), cmd, c, false, messages, rejectCommits, null)) {
break;
}
-
- if (missingFullName && user.hasEmailAddress(c.getCommitterIdent().getEmailAddress())) {
- logger.atFine().log("Will update full name of caller");
- setFullNameTo = c.getCommitterIdent().getName();
- missingFullName = false;
- }
}
logger.atFine().log("Validated %d new commits", n);
} catch (IOException err) {
@@ -3080,75 +3041,9 @@
}
}
- private String messageForCommit(RevCommit c, String msg) {
- return String.format("commit %s: %s", c.abbreviate(RevId.ABBREV_LEN).name(), msg);
- }
-
- /**
- * Validates a single commit. If the commit does not validate, the command is rejected.
- *
- * @param objectReader the object reader to use.
- * @param branch the branch to which this commit is pushed
- * @param cmd the ReceiveCommand executing the push.
- * @param commit the commit being validated.
- * @param isMerged whether this is a merge commit created by magicBranch --merge option
- * @param change the change for which this is a new patchset.
- */
- private boolean validCommit(
- ObjectReader objectReader,
- Branch.NameKey branch,
- ReceiveCommand cmd,
- RevCommit commit,
- boolean isMerged,
- @Nullable Change change)
- throws IOException {
- PermissionBackend.ForRef perm = permissions.ref(branch.get());
-
- ValidCommitKey key = new AutoValue_ReceiveCommits_ValidCommitKey(commit.copy(), branch);
- if (validCommits.contains(key)) {
- return true;
- }
-
- try (CommitReceivedEvent receiveEvent =
- new CommitReceivedEvent(cmd, project, branch.get(), objectReader, commit, user)) {
- CommitValidators validators =
- isMerged
- ? commitValidatorsFactory.forMergedCommits(
- project.getNameKey(), perm, user.asIdentifiedUser())
- : commitValidatorsFactory.forReceiveCommits(
- perm,
- branch,
- user.asIdentifiedUser(),
- sshInfo,
- repo,
- receiveEvent.revWalk,
- change);
-
- for (CommitValidationMessage m : validators.validate(receiveEvent)) {
- messages.add(
- new CommitValidationMessage(messageForCommit(commit, m.getMessage()), m.isError()));
- }
- } catch (CommitValidationException e) {
- logger.atFine().log("Commit validation failed on %s", commit.name());
- for (CommitValidationMessage m : e.getMessages()) {
- // TODO(hanwen): drop the non-error messages?
- messages.add(
- new CommitValidationMessage(messageForCommit(commit, m.getMessage()), m.isError()));
- }
- reject(cmd, messageForCommit(commit, e.getMessage()));
- return false;
- }
- validCommits.add(key);
- return true;
- }
-
private void autoCloseChanges(ReceiveCommand cmd, Task progress) {
logger.atFine().log("Starting auto-closing of changes");
String refName = cmd.getRefName();
- checkState(
- !MagicBranch.isMagicBranch(refName),
- "shouldn't be auto-closing changes on magic branch %s",
- refName);
// TODO(dborowitz): Combine this BatchUpdate with the main one in
// handleRegularCommands
@@ -3266,31 +3161,6 @@
}
}
- private void updateAccountInfo() {
- if (setFullNameTo == null) {
- return;
- }
- logger.atFine().log("Updating full name of caller");
- try {
- Optional<AccountState> accountState =
- accountsUpdateProvider
- .get()
- .update(
- "Set Full Name on Receive Commits",
- user.getAccountId(),
- (a, u) -> {
- if (Strings.isNullOrEmpty(a.getAccount().getFullName())) {
- u.setFullName(setFullNameTo);
- }
- });
- accountState
- .map(AccountState::getAccount)
- .ifPresent(a -> user.getAccount().setFullName(a.getFullName()));
- } catch (OrmException | IOException | ConfigInvalidException e) {
- logger.atWarning().withCause(e).log("Failed to update full name of caller");
- }
- }
-
private Map<Change.Key, ChangeNotes> openChangesByKeyByBranch(Branch.NameKey branch)
throws OrmException {
Map<Change.Key, ChangeNotes> r = new HashMap<>();
diff --git a/java/com/google/gerrit/server/git/validators/CommitValidators.java b/java/com/google/gerrit/server/git/validators/CommitValidators.java
index 3e382fb..7930fe8 100644
--- a/java/com/google/gerrit/server/git/validators/CommitValidators.java
+++ b/java/com/google/gerrit/server/git/validators/CommitValidators.java
@@ -35,7 +35,6 @@
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Branch.NameKey;
import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
@@ -45,11 +44,9 @@
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.events.CommitReceivedEvent;
-import com.google.gerrit.server.git.BanCommit;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.ValidationError;
import com.google.gerrit.server.permissions.PermissionBackend;
-import com.google.gerrit.server.permissions.PermissionBackend.ForRef;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.RefPermission;
import com.google.gerrit.server.project.ProjectCache;
@@ -78,6 +75,9 @@
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.util.SystemReader;
+/**
+ * Represents a list of CommitValidationListeners to run for a push to one branch of one project.
+ */
public class CommitValidators {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -123,15 +123,15 @@
}
public CommitValidators forReceiveCommits(
- PermissionBackend.ForRef perm,
+ PermissionBackend.ForProject forProject,
Branch.NameKey branch,
IdentifiedUser user,
SshInfo sshInfo,
- Repository repo,
+ NoteMap rejectCommits,
RevWalk rw,
@Nullable Change change)
throws IOException {
- NoteMap rejectCommits = BanCommit.loadRejectCommitsMap(repo, rw);
+ PermissionBackend.ForRef perm = forProject.ref(branch.get());
ProjectState projectState = projectCache.checkedGet(branch.getParentKey());
return new CommitValidators(
ImmutableList.of(
@@ -157,13 +157,14 @@
}
public CommitValidators forGerritCommits(
- ForRef perm,
+ PermissionBackend.ForProject forProject,
NameKey branch,
IdentifiedUser user,
SshInfo sshInfo,
RevWalk rw,
@Nullable Change change)
throws IOException {
+ PermissionBackend.ForRef perm = forProject.ref(branch.get());
ProjectState projectState = projectCache.checkedGet(branch.getParentKey());
return new CommitValidators(
ImmutableList.of(
@@ -187,7 +188,7 @@
}
public CommitValidators forMergedCommits(
- Project.NameKey project, PermissionBackend.ForRef perm, IdentifiedUser user)
+ PermissionBackend.ForProject forProject, Branch.NameKey branch, IdentifiedUser user)
throws IOException {
// Generally only include validators that are based on permissions of the
// user creating a change for a merged commit; generally exclude
@@ -202,10 +203,11 @@
// discuss what to do about it.
// - Plugin validators may do things like require certain commit message
// formats, so we play it safe and exclude them.
+ PermissionBackend.ForRef perm = forProject.ref(branch.get());
return new CommitValidators(
ImmutableList.of(
new UploadMergesPermissionValidator(perm),
- new ProjectStateValidationListener(projectCache.checkedGet(project)),
+ new ProjectStateValidationListener(projectCache.checkedGet(branch.getParentKey())),
new AuthorUploaderValidator(user, perm, canonicalWebUrl),
new CommitterUploaderValidator(user, perm, canonicalWebUrl)));
}
diff --git a/java/com/google/gerrit/server/index/account/AccountIndexerImpl.java b/java/com/google/gerrit/server/index/account/AccountIndexerImpl.java
index b8ececb..f8c17d1 100644
--- a/java/com/google/gerrit/server/index/account/AccountIndexerImpl.java
+++ b/java/com/google/gerrit/server/index/account/AccountIndexerImpl.java
@@ -23,6 +23,8 @@
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.logging.TraceContext;
+import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import java.io.IOException;
@@ -77,17 +79,25 @@
Optional<AccountState> accountState = byIdCache.get(id);
if (accountState.isPresent()) {
- logger.atInfo().log("Replace account %d in index", id.get());
+ logger.atFine().log("Replace account %d in index", id.get());
} else {
- logger.atInfo().log("Delete account %d from index", id.get());
+ logger.atFine().log("Delete account %d from index", id.get());
}
for (Index<Account.Id, AccountState> i : getWriteIndexes()) {
// Evict the cache to get an up-to-date value for sure.
if (accountState.isPresent()) {
- i.replace(accountState.get());
+ try (TraceTimer traceTimer =
+ TraceContext.newTimer(
+ "Replacing account %d in index version %d", id.get(), i.getSchema().getVersion())) {
+ i.replace(accountState.get());
+ }
} else {
- i.delete(id);
+ try (TraceTimer traceTimer =
+ TraceContext.newTimer(
+ "Deleteing account %d in index version %d", id.get(), i.getSchema().getVersion())) {
+ i.delete(id);
+ }
}
}
fireAccountIndexedEvent(id.get());
diff --git a/java/com/google/gerrit/server/index/account/AllAccountsIndexer.java b/java/com/google/gerrit/server/index/account/AllAccountsIndexer.java
index 0015268..acb7236 100644
--- a/java/com/google/gerrit/server/index/account/AllAccountsIndexer.java
+++ b/java/com/google/gerrit/server/index/account/AllAccountsIndexer.java
@@ -30,7 +30,6 @@
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
-import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@@ -60,7 +59,7 @@
@Override
public SiteIndexer.Result indexAll(AccountIndex index) {
- ProgressMonitor progress = new TextProgressMonitor(new PrintWriter(progressOut));
+ ProgressMonitor progress = new TextProgressMonitor(newPrintWriter(progressOut));
progress.start(2);
Stopwatch sw = Stopwatch.createStarted();
List<Account.Id> ids;
diff --git a/java/com/google/gerrit/server/index/change/ChangeIndexer.java b/java/com/google/gerrit/server/index/change/ChangeIndexer.java
index 8573862..d0a9749 100644
--- a/java/com/google/gerrit/server/index/change/ChangeIndexer.java
+++ b/java/com/google/gerrit/server/index/change/ChangeIndexer.java
@@ -33,6 +33,8 @@
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.index.IndexExecutor;
import com.google.gerrit.server.index.IndexUtils;
+import com.google.gerrit.server.logging.TraceContext;
+import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.project.NoSuchChangeException;
@@ -212,9 +214,14 @@
}
private void indexImpl(ChangeData cd) throws IOException {
- logger.atInfo().log("Replace change %d in index.", cd.getId().get());
+ logger.atFine().log("Replace change %d in index.", cd.getId().get());
for (Index<?, ChangeData> i : getWriteIndexes()) {
- i.replace(cd);
+ try (TraceTimer traceTimer =
+ TraceContext.newTimer(
+ "Replacing change %d in index version %d",
+ cd.getId().get(), i.getSchema().getVersion())) {
+ i.replace(cd);
+ }
}
fireChangeIndexedEvent(cd.project().get(), cd.getId().get());
}
@@ -412,12 +419,16 @@
@Override
public Void call() throws IOException {
- logger.atInfo().log("Delete change %d from index.", id.get());
+ logger.atFine().log("Delete change %d from index.", id.get());
// Don't bother setting a RequestContext to provide the DB.
// Implementations should not need to access the DB in order to delete a
// change ID.
for (ChangeIndex i : getWriteIndexes()) {
- i.delete(id);
+ try (TraceTimer traceTimer =
+ TraceContext.newTimer(
+ "Deleteing change %d in index version %d", id.get(), i.getSchema().getVersion())) {
+ i.delete(id);
+ }
}
fireChangeDeletedFromIndexEvent(id.get());
return null;
diff --git a/java/com/google/gerrit/server/index/group/AllGroupsIndexer.java b/java/com/google/gerrit/server/index/group/AllGroupsIndexer.java
index 2823c2e..3474934 100644
--- a/java/com/google/gerrit/server/index/group/AllGroupsIndexer.java
+++ b/java/com/google/gerrit/server/index/group/AllGroupsIndexer.java
@@ -33,7 +33,6 @@
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
-import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@@ -64,7 +63,7 @@
@Override
public SiteIndexer.Result indexAll(GroupIndex index) {
- ProgressMonitor progress = new TextProgressMonitor(new PrintWriter(progressOut));
+ ProgressMonitor progress = new TextProgressMonitor(newPrintWriter(progressOut));
progress.start(2);
Stopwatch sw = Stopwatch.createStarted();
List<AccountGroup.UUID> uuids;
diff --git a/java/com/google/gerrit/server/index/group/GroupIndexerImpl.java b/java/com/google/gerrit/server/index/group/GroupIndexerImpl.java
index 3f4c7be..d6ba253 100644
--- a/java/com/google/gerrit/server/index/group/GroupIndexerImpl.java
+++ b/java/com/google/gerrit/server/index/group/GroupIndexerImpl.java
@@ -23,6 +23,8 @@
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.group.InternalGroup;
+import com.google.gerrit.server.logging.TraceContext;
+import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import java.io.IOException;
@@ -78,16 +80,24 @@
Optional<InternalGroup> internalGroup = groupCache.get(uuid);
if (internalGroup.isPresent()) {
- logger.atInfo().log("Replace group %s in index", uuid.get());
+ logger.atFine().log("Replace group %s in index", uuid.get());
} else {
- logger.atInfo().log("Delete group %s from index", uuid.get());
+ logger.atFine().log("Delete group %s from index", uuid.get());
}
for (Index<AccountGroup.UUID, InternalGroup> i : getWriteIndexes()) {
if (internalGroup.isPresent()) {
- i.replace(internalGroup.get());
+ try (TraceTimer traceTimer =
+ TraceContext.newTimer(
+ "Replacing group %s in index version %d", uuid.get(), i.getSchema().getVersion())) {
+ i.replace(internalGroup.get());
+ }
} else {
- i.delete(uuid);
+ try (TraceTimer traceTimer =
+ TraceContext.newTimer(
+ "Deleting group %s in index version %d", uuid.get(), i.getSchema().getVersion())) {
+ i.delete(uuid);
+ }
}
}
fireGroupIndexedEvent(uuid.get());
diff --git a/java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java b/java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java
index f5bf8b5..c2a28af 100644
--- a/java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java
+++ b/java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java
@@ -24,6 +24,8 @@
import com.google.gerrit.index.project.ProjectIndexCollection;
import com.google.gerrit.index.project.ProjectIndexer;
import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.logging.TraceContext;
+import com.google.gerrit.server.logging.TraceContext.TraceTimer;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
import com.google.inject.assistedinject.Assisted;
@@ -72,16 +74,26 @@
public void index(Project.NameKey nameKey) throws IOException {
ProjectState projectState = projectCache.get(nameKey);
if (projectState != null) {
- logger.atInfo().log("Replace project %s in index", nameKey.get());
+ logger.atFine().log("Replace project %s in index", nameKey.get());
ProjectData projectData = projectState.toProjectData();
for (ProjectIndex i : getWriteIndexes()) {
- i.replace(projectData);
+ try (TraceTimer traceTimer =
+ TraceContext.newTimer(
+ "Replacing project %s in index version %d",
+ nameKey.get(), i.getSchema().getVersion())) {
+ i.replace(projectData);
+ }
}
fireProjectIndexedEvent(nameKey.get());
} else {
- logger.atInfo().log("Delete project %s from index", nameKey.get());
+ logger.atFine().log("Delete project %s from index", nameKey.get());
for (ProjectIndex i : getWriteIndexes()) {
- i.delete(nameKey);
+ try (TraceTimer traceTimer =
+ TraceContext.newTimer(
+ "Deleting project %s in index version %d",
+ nameKey.get(), i.getSchema().getVersion())) {
+ i.delete(nameKey);
+ }
}
}
}
diff --git a/java/com/google/gerrit/server/logging/BUILD b/java/com/google/gerrit/server/logging/BUILD
new file mode 100644
index 0000000..d3211f0
--- /dev/null
+++ b/java/com/google/gerrit/server/logging/BUILD
@@ -0,0 +1,13 @@
+java_library(
+ name = "logging",
+ srcs = glob(
+ ["**/*.java"],
+ ),
+ visibility = ["//visibility:public"],
+ deps = [
+ "//java/com/google/gerrit/common:annotations",
+ "//java/com/google/gerrit/common:server",
+ "//lib:guava",
+ "//lib/flogger:api",
+ ],
+)
diff --git a/java/com/google/gerrit/server/logging/LoggingContext.java b/java/com/google/gerrit/server/logging/LoggingContext.java
index 2ce4c93..1e81c29 100644
--- a/java/com/google/gerrit/server/logging/LoggingContext.java
+++ b/java/com/google/gerrit/server/logging/LoggingContext.java
@@ -16,6 +16,7 @@
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.flogger.backend.Tags;
+import java.util.concurrent.Callable;
import java.util.logging.Level;
/**
@@ -42,6 +43,20 @@
return INSTANCE;
}
+ public static Runnable copy(Runnable runnable) {
+ if (runnable instanceof LoggingContextAwareRunnable) {
+ return runnable;
+ }
+ return new LoggingContextAwareRunnable(runnable);
+ }
+
+ public static <T> Callable<T> copy(Callable<T> callable) {
+ if (callable instanceof LoggingContextAwareCallable) {
+ return callable;
+ }
+ return new LoggingContextAwareCallable<>(callable);
+ }
+
@Override
public boolean shouldForceLogging(String loggerName, Level level, boolean isEnabled) {
return isLoggingForced();
diff --git a/java/com/google/gerrit/server/logging/LoggingContextAwareCallable.java b/java/com/google/gerrit/server/logging/LoggingContextAwareCallable.java
new file mode 100644
index 0000000..6aff5c4
--- /dev/null
+++ b/java/com/google/gerrit/server/logging/LoggingContextAwareCallable.java
@@ -0,0 +1,66 @@
+// Copyright (C) 2018 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.logging;
+
+import com.google.common.collect.ImmutableSetMultimap;
+import java.util.concurrent.Callable;
+
+/**
+ * Wrapper for a {@link Callable} that copies the {@link LoggingContext} from the current thread to
+ * the thread that executes the callable.
+ *
+ * <p>The state of the logging context that is copied to the thread that executes the callable is
+ * fixed at the creation time of this wrapper. If the callable is submitted to an executor and is
+ * executed later this means that changes that are done to the logging context in between creating
+ * and executing the callable do not apply.
+ *
+ * <p>See {@link LoggingContextAwareRunnable} for an example.
+ *
+ * @see LoggingContextAwareRunnable
+ */
+class LoggingContextAwareCallable<T> implements Callable<T> {
+ private final Callable<T> callable;
+ private final Thread callingThread;
+ private final ImmutableSetMultimap<String, String> tags;
+ private final boolean forceLogging;
+
+ LoggingContextAwareCallable(Callable<T> callable) {
+ this.callable = callable;
+ this.callingThread = Thread.currentThread();
+ this.tags = LoggingContext.getInstance().getTagsAsMap();
+ this.forceLogging = LoggingContext.getInstance().isLoggingForced();
+ }
+
+ @Override
+ public T call() throws Exception {
+ if (callingThread.equals(Thread.currentThread())) {
+ // propagation of logging context is not needed
+ return callable.call();
+ }
+
+ // propagate logging context
+ LoggingContext loggingCtx = LoggingContext.getInstance();
+ ImmutableSetMultimap<String, String> oldTags = loggingCtx.getTagsAsMap();
+ boolean oldForceLogging = loggingCtx.isLoggingForced();
+ loggingCtx.setTags(tags);
+ loggingCtx.forceLogging(forceLogging);
+ try {
+ return callable.call();
+ } finally {
+ loggingCtx.setTags(oldTags);
+ loggingCtx.forceLogging(oldForceLogging);
+ }
+ }
+}
diff --git a/java/com/google/gerrit/server/logging/LoggingContextAwareExecutorService.java b/java/com/google/gerrit/server/logging/LoggingContextAwareExecutorService.java
new file mode 100644
index 0000000..17e152e
--- /dev/null
+++ b/java/com/google/gerrit/server/logging/LoggingContextAwareExecutorService.java
@@ -0,0 +1,110 @@
+// Copyright (C) 2018 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.logging;
+
+import static java.util.stream.Collectors.toList;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * An {@link ExecutorService} that copies the {@link LoggingContext} on executing a {@link Runnable}
+ * to the executing thread.
+ */
+public class LoggingContextAwareExecutorService implements ExecutorService {
+ private final ExecutorService executorService;
+
+ public LoggingContextAwareExecutorService(ExecutorService executorService) {
+ this.executorService = executorService;
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ executorService.execute(LoggingContext.copy(command));
+ }
+
+ @Override
+ public void shutdown() {
+ executorService.shutdown();
+ }
+
+ @Override
+ public List<Runnable> shutdownNow() {
+ return executorService.shutdownNow();
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return executorService.isShutdown();
+ }
+
+ @Override
+ public boolean isTerminated() {
+ return executorService.isTerminated();
+ }
+
+ @Override
+ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+ return executorService.awaitTermination(timeout, unit);
+ }
+
+ @Override
+ public <T> Future<T> submit(Callable<T> task) {
+ return executorService.submit(LoggingContext.copy(task));
+ }
+
+ @Override
+ public <T> Future<T> submit(Runnable task, T result) {
+ return executorService.submit(LoggingContext.copy(task), result);
+ }
+
+ @Override
+ public Future<?> submit(Runnable task) {
+ return executorService.submit(LoggingContext.copy(task));
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException {
+ return executorService.invokeAll(tasks.stream().map(LoggingContext::copy).collect(toList()));
+ }
+
+ @Override
+ public <T> List<Future<T>> invokeAll(
+ Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+ throws InterruptedException {
+ return executorService.invokeAll(
+ tasks.stream().map(LoggingContext::copy).collect(toList()), timeout, unit);
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+ throws InterruptedException, ExecutionException {
+ return executorService.invokeAny(tasks.stream().map(LoggingContext::copy).collect(toList()));
+ }
+
+ @Override
+ public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ return executorService.invokeAny(
+ tasks.stream().map(LoggingContext::copy).collect(toList()), timeout, unit);
+ }
+}
diff --git a/java/com/google/gerrit/server/logging/LoggingContextAwareRunnable.java b/java/com/google/gerrit/server/logging/LoggingContextAwareRunnable.java
new file mode 100644
index 0000000..0bd7d00
--- /dev/null
+++ b/java/com/google/gerrit/server/logging/LoggingContextAwareRunnable.java
@@ -0,0 +1,89 @@
+// Copyright (C) 2018 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.logging;
+
+import com.google.common.collect.ImmutableSetMultimap;
+
+/**
+ * Wrapper for a {@link Runnable} that copies the {@link LoggingContext} from the current thread to
+ * the thread that executes the runnable.
+ *
+ * <p>The state of the logging context that is copied to the thread that executes the runnable is
+ * fixed at the creation time of this wrapper. If the runnable is submitted to an executor and is
+ * executed later this means that changes that are done to the logging context in between creating
+ * and executing the runnable do not apply.
+ *
+ * <p>Example:
+ *
+ * <pre>
+ * try (TraceContext traceContext = TraceContext.newTrace(true, ...)) {
+ * executor
+ * .submit(new LoggingContextAwareRunnable(
+ * () -> {
+ * // Tracing is enabled since the runnable is created within the TraceContext.
+ * // Tracing is even enabled if the executor runs the runnable only after the
+ * // TraceContext was closed.
+ *
+ * // The tag "foo=bar" is not set, since it was added to the logging context only
+ * // after this runnable was created.
+ *
+ * // do stuff
+ * }))
+ * .get();
+ * traceContext.addTag("foo", "bar");
+ * }
+ * </pre>
+ *
+ * @see LoggingContextAwareCallable
+ */
+public class LoggingContextAwareRunnable implements Runnable {
+ private final Runnable runnable;
+ private final Thread callingThread;
+ private final ImmutableSetMultimap<String, String> tags;
+ private final boolean forceLogging;
+
+ LoggingContextAwareRunnable(Runnable runnable) {
+ this.runnable = runnable;
+ this.callingThread = Thread.currentThread();
+ this.tags = LoggingContext.getInstance().getTagsAsMap();
+ this.forceLogging = LoggingContext.getInstance().isLoggingForced();
+ }
+
+ public Runnable unwrap() {
+ return runnable;
+ }
+
+ @Override
+ public void run() {
+ if (callingThread.equals(Thread.currentThread())) {
+ // propagation of logging context is not needed
+ runnable.run();
+ return;
+ }
+
+ // propagate logging context
+ LoggingContext loggingCtx = LoggingContext.getInstance();
+ ImmutableSetMultimap<String, String> oldTags = loggingCtx.getTagsAsMap();
+ boolean oldForceLogging = loggingCtx.isLoggingForced();
+ loggingCtx.setTags(tags);
+ loggingCtx.forceLogging(forceLogging);
+ try {
+ runnable.run();
+ } finally {
+ loggingCtx.setTags(oldTags);
+ loggingCtx.forceLogging(oldForceLogging);
+ }
+ }
+}
diff --git a/java/com/google/gerrit/server/logging/LoggingContextAwareScheduledExecutorService.java b/java/com/google/gerrit/server/logging/LoggingContextAwareScheduledExecutorService.java
new file mode 100644
index 0000000..e17a91e
--- /dev/null
+++ b/java/com/google/gerrit/server/logging/LoggingContextAwareScheduledExecutorService.java
@@ -0,0 +1,59 @@
+// Copyright (C) 2018 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.logging;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link ScheduledExecutorService} that copies the {@link LoggingContext} on executing a {@link
+ * Runnable} to the executing thread.
+ */
+public class LoggingContextAwareScheduledExecutorService extends LoggingContextAwareExecutorService
+ implements ScheduledExecutorService {
+ private final ScheduledExecutorService scheduledExecutorService;
+
+ public LoggingContextAwareScheduledExecutorService(
+ ScheduledExecutorService scheduledExecutorService) {
+ super(scheduledExecutorService);
+ this.scheduledExecutorService = scheduledExecutorService;
+ }
+
+ @Override
+ public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
+ return scheduledExecutorService.schedule(LoggingContext.copy(command), delay, unit);
+ }
+
+ @Override
+ public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
+ return scheduledExecutorService.schedule(LoggingContext.copy(callable), delay, unit);
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleAtFixedRate(
+ Runnable command, long initialDelay, long period, TimeUnit unit) {
+ return scheduledExecutorService.scheduleAtFixedRate(
+ LoggingContext.copy(command), initialDelay, period, unit);
+ }
+
+ @Override
+ public ScheduledFuture<?> scheduleWithFixedDelay(
+ Runnable command, long initialDelay, long delay, TimeUnit unit) {
+ return scheduledExecutorService.scheduleWithFixedDelay(
+ LoggingContext.copy(command), initialDelay, delay, unit);
+ }
+}
diff --git a/java/com/google/gerrit/server/logging/LoggingContextAwareThreadFactory.java b/java/com/google/gerrit/server/logging/LoggingContextAwareThreadFactory.java
deleted file mode 100644
index 05ff0d3..0000000
--- a/java/com/google/gerrit/server/logging/LoggingContextAwareThreadFactory.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (C) 2018 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.logging;
-
-import com.google.common.collect.ImmutableSetMultimap;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
-
-/**
- * ThreadFactory that copies the logging context of the current thread to any new thread that is
- * created by this ThreadFactory.
- */
-public class LoggingContextAwareThreadFactory implements ThreadFactory {
- private final ThreadFactory parentThreadFactory;
-
- public LoggingContextAwareThreadFactory() {
- this.parentThreadFactory = Executors.defaultThreadFactory();
- }
-
- public LoggingContextAwareThreadFactory(ThreadFactory parentThreadFactory) {
- this.parentThreadFactory = parentThreadFactory;
- }
-
- @Override
- public Thread newThread(Runnable r) {
- Thread callingThread = Thread.currentThread();
- ImmutableSetMultimap<String, String> tags = LoggingContext.getInstance().getTagsAsMap();
- boolean forceLogging = LoggingContext.getInstance().isLoggingForced();
- return parentThreadFactory.newThread(
- () -> {
- if (callingThread.equals(Thread.currentThread())) {
- // propagation of logging context is not needed
- r.run();
- return;
- }
-
- // propagate logging context
- LoggingContext loggingCtx = LoggingContext.getInstance();
- loggingCtx.setTags(tags);
- loggingCtx.forceLogging(forceLogging);
- try {
- r.run();
- } finally {
- loggingCtx.clearTags();
- loggingCtx.forceLogging(false);
- }
- });
- }
-}
diff --git a/java/com/google/gerrit/server/logging/RequestId.java b/java/com/google/gerrit/server/logging/RequestId.java
index 81619fb..b0a8ad9 100644
--- a/java/com/google/gerrit/server/logging/RequestId.java
+++ b/java/com/google/gerrit/server/logging/RequestId.java
@@ -19,8 +19,6 @@
import com.google.common.hash.Hashing;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.TimeUtil;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
import java.net.InetAddress;
import java.net.UnknownHostException;
@@ -52,21 +50,13 @@
return LoggingContext.getInstance().getTagsAsMap().keySet().stream().anyMatch(Type::isId);
}
- public static RequestId forChange(Change c) {
- return new RequestId(c.getId().toString());
- }
-
- public static RequestId forProject(Project.NameKey p) {
- return new RequestId(p.toString());
- }
-
private final String str;
public RequestId() {
this(null);
}
- private RequestId(@Nullable String resourceId) {
+ public RequestId(@Nullable String resourceId) {
Hasher h = Hashing.murmur3_128().newHasher();
h.putLong(Thread.currentThread().getId()).putUnencodedChars(MACHINE_ID);
str =
diff --git a/java/com/google/gerrit/server/logging/TraceContext.java b/java/com/google/gerrit/server/logging/TraceContext.java
index 55a055a..977baa5 100644
--- a/java/com/google/gerrit/server/logging/TraceContext.java
+++ b/java/com/google/gerrit/server/logging/TraceContext.java
@@ -16,16 +16,212 @@
import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.base.Stopwatch;
+import com.google.common.base.Strings;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.common.Nullable;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+/**
+ * TraceContext that allows to set logging tags and enforce logging.
+ *
+ * <p>The logging tags are attached to all log entries that are triggered while the trace context is
+ * open. If force logging is enabled all logs that are triggered while the trace context is open are
+ * written to the log file regardless of the configured log level.
+ *
+ * <pre>
+ * try (TraceContext traceContext = TraceContext.open()
+ * .addTag("tag-name", "tag-value")
+ * .forceLogging()) {
+ * // This gets logged as: A log [CONTEXT forced=true tag-name="tag-value" ]
+ * // Since force logging is enabled this gets logged independently of the configured log
+ * // level.
+ * logger.atFinest().log("A log");
+ *
+ * // do stuff
+ * }
+ * </pre>
+ *
+ * <p>The logging tags and the force logging flag are stored in the {@link LoggingContext}. {@link
+ * LoggingContextAwareExecutorService}, {@link LoggingContextAwareScheduledExecutorService} and the
+ * executor in {@link com.google.gerrit.server.git.WorkQueue} ensure that the logging context is
+ * automatically copied to background threads.
+ *
+ * <p>On close of the trace context newly set tags are unset. Force logging is disabled on close if
+ * it got enabled while the trace context was open.
+ *
+ * <p>Trace contexts can be nested:
+ *
+ * <pre>
+ * // Initially there are no tags
+ * logger.atSevere().log("log without tag");
+ *
+ * // a tag can be set by opening a trace context
+ * try (TraceContext ctx = TraceContext.open().addTag("tag1", "value1")) {
+ * logger.atSevere().log("log with tag1=value1");
+ *
+ * // while a trace context is open further tags can be added.
+ * ctx.addTag("tag2", "value2")
+ * logger.atSevere().log("log with tag1=value1 and tag2=value2");
+ *
+ * // also by opening another trace context a another tag can be added
+ * try (TraceContext ctx2 = TraceContext.open().addTag("tag3", "value3")) {
+ * logger.atSevere().log("log with tag1=value1, tag2=value2 and tag3=value3");
+ *
+ * // it's possible to have the same tag name with multiple values
+ * ctx2.addTag("tag3", "value3a")
+ * logger.atSevere().log("log with tag1=value1, tag2=value2, tag3=value3 and tag3=value3a");
+ *
+ * // adding a tag with the same name and value as an existing tag has no effect
+ * try (TraceContext ctx3 = TraceContext.open().addTag("tag3", "value3a")) {
+ * logger.atSevere().log("log with tag1=value1, tag2=value2, tag3=value3 and tag3=value3a");
+ * }
+ *
+ * // closing ctx3 didn't remove tag3=value3a since it was already set before opening ctx3
+ * logger.atSevere().log("log with tag1=value1, tag2=value2, tag3=value3 and tag3=value3a");
+ * }
+ *
+ * // closing ctx2 removed tag3=value3 and tag3-value3a
+ * logger.atSevere().log("with tag1=value1 and tag2=value2");
+ * }
+ *
+ * // closing ctx1 removed tag1=value1 and tag2=value2
+ * logger.atSevere().log("log without tag");
+ * </pre>
+ */
public class TraceContext implements AutoCloseable {
- public static final TraceContext DISABLED = new TraceContext();
-
public static TraceContext open() {
return new TraceContext();
}
+ /**
+ * Opens a new trace context for request tracing.
+ *
+ * <ul>
+ * <li>sets a tag with a trace ID
+ * <li>enables force logging
+ * </ul>
+ *
+ * <p>if no trace ID is provided a new trace ID is only generated if request tracing was not
+ * started yet. If request tracing was already started the given {@code traceIdConsumer} is
+ * invoked with the existing trace ID and no new logging tag is set.
+ *
+ * <p>No-op if {@code trace} is {@code false}.
+ *
+ * @param trace whether tracing should be started
+ * @param traceId trace ID that should be used for tracing, if {@code null} a trace ID is
+ * generated
+ * @param traceIdConsumer consumer for the trace ID, should be used to return the generated trace
+ * ID to the client, not invoked if {@code trace} is {@code false}
+ * @return the trace context
+ */
+ public static TraceContext newTrace(
+ boolean trace, @Nullable String traceId, TraceIdConsumer traceIdConsumer) {
+ if (!trace) {
+ // Create an empty trace context.
+ return open();
+ }
+
+ if (!Strings.isNullOrEmpty(traceId)) {
+ traceIdConsumer.accept(RequestId.Type.TRACE_ID.name(), traceId);
+ return open().addTag(RequestId.Type.TRACE_ID, traceId).forceLogging();
+ }
+
+ Optional<String> existingTraceId =
+ LoggingContext.getInstance()
+ .getTagsAsMap()
+ .get(RequestId.Type.TRACE_ID.name())
+ .stream()
+ .findAny();
+ if (existingTraceId.isPresent()) {
+ // request tracing was already started, no need to generate a new trace ID
+ traceIdConsumer.accept(RequestId.Type.TRACE_ID.name(), existingTraceId.get());
+ return open();
+ }
+
+ RequestId newTraceId = new RequestId();
+ traceIdConsumer.accept(RequestId.Type.TRACE_ID.name(), newTraceId.toString());
+ return open().addTag(RequestId.Type.TRACE_ID, newTraceId).forceLogging();
+ }
+
+ @FunctionalInterface
+ public interface TraceIdConsumer {
+ void accept(String tagName, String traceId);
+ }
+
+ /**
+ * Opens a new timer that logs the time for an operation if request tracing is enabled.
+ *
+ * <p>If request tracing is not enabled this is a no-op.
+ *
+ * @param message the message
+ * @return the trace timer
+ */
+ public static TraceTimer newTimer(String message) {
+ return new TraceTimer(message);
+ }
+
+ /**
+ * Opens a new timer that logs the time for an operation if request tracing is enabled.
+ *
+ * <p>If request tracing is not enabled this is a no-op.
+ *
+ * @param format the message format string
+ * @param arg argument for the message
+ * @return the trace timer
+ */
+ public static TraceTimer newTimer(String format, Object arg) {
+ return new TraceTimer(format, arg);
+ }
+
+ /**
+ * Opens a new timer that logs the time for an operation if request tracing is enabled.
+ *
+ * <p>If request tracing is not enabled this is a no-op.
+ *
+ * @param format the message format string
+ * @param arg1 first argument for the message
+ * @param arg2 second argument for the message
+ * @return the trace timer
+ */
+ public static TraceTimer newTimer(String format, Object arg1, Object arg2) {
+ return new TraceTimer(format, arg1, arg2);
+ }
+
+ public static class TraceTimer implements AutoCloseable {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ private final Consumer<Long> logFn;
+ private final Stopwatch stopwatch;
+
+ private TraceTimer(String message) {
+ this(elapsedMs -> logger.atFine().log(message + " (%d ms)", elapsedMs));
+ }
+
+ private TraceTimer(String format, @Nullable Object arg) {
+ this(elapsedMs -> logger.atFine().log(format + " (%d ms)", arg, elapsedMs));
+ }
+
+ private TraceTimer(String format, @Nullable Object arg1, @Nullable Object arg2) {
+ this(elapsedMs -> logger.atFine().log(format + " (%d ms)", arg1, arg2, elapsedMs));
+ }
+
+ private TraceTimer(Consumer<Long> logFn) {
+ this.logFn = logFn;
+ this.stopwatch = Stopwatch.createStarted();
+ }
+
+ @Override
+ public void close() {
+ stopwatch.stop();
+ logFn.accept(stopwatch.elapsed(TimeUnit.MILLISECONDS));
+ }
+ }
+
// Table<TAG_NAME, TAG_VALUE, REMOVE_ON_CLOSE>
private final Table<String, String, Boolean> tags = HashBasedTable.create();
diff --git a/java/com/google/gerrit/server/mail/ListMailFilter.java b/java/com/google/gerrit/server/mail/ListMailFilter.java
index eee8c60..1549f8d 100644
--- a/java/com/google/gerrit/server/mail/ListMailFilter.java
+++ b/java/com/google/gerrit/server/mail/ListMailFilter.java
@@ -53,7 +53,8 @@
}
boolean match = mailPattern.matcher(message.from().getEmail()).find();
- if (mode == ListFilterMode.WHITELIST && !match || mode == ListFilterMode.BLACKLIST && match) {
+ if ((mode == ListFilterMode.WHITELIST && !match)
+ || (mode == ListFilterMode.BLACKLIST && match)) {
logger.atInfo().log("Mail message from %s rejected by list filter", message.from());
return false;
}
diff --git a/java/com/google/gerrit/server/mail/send/CommentSender.java b/java/com/google/gerrit/server/mail/send/CommentSender.java
index 54176e2..0baaa11c 100644
--- a/java/com/google/gerrit/server/mail/send/CommentSender.java
+++ b/java/com/google/gerrit/server/mail/send/CommentSender.java
@@ -239,7 +239,7 @@
}
}
- Collections.sort(groups, Comparator.comparing(g -> g.filename, FilenameComparator.INSTANCE));
+ groups.sort(Comparator.comparing(g -> g.filename, FilenameComparator.INSTANCE));
return groups;
}
diff --git a/java/com/google/gerrit/server/mime/MimeUtilFileTypeRegistry.java b/java/com/google/gerrit/server/mime/MimeUtilFileTypeRegistry.java
index eecf935..0e9a2b7 100644
--- a/java/com/google/gerrit/server/mime/MimeUtilFileTypeRegistry.java
+++ b/java/com/google/gerrit/server/mime/MimeUtilFileTypeRegistry.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.mime;
+import static java.util.Comparator.comparing;
+
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.inject.Inject;
@@ -22,12 +24,9 @@
import eu.medsea.mimeutil.MimeType;
import eu.medsea.mimeutil.MimeUtil2;
import java.io.InputStream;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.Comparator;
import java.util.HashSet;
-import java.util.List;
import java.util.Set;
import org.eclipse.jgit.lib.Config;
@@ -115,16 +114,7 @@
return MimeUtil2.UNKNOWN_MIME_TYPE;
}
- final List<MimeType> types = new ArrayList<>(mimeTypes);
- Collections.sort(
- types,
- new Comparator<MimeType>() {
- @Override
- public int compare(MimeType a, MimeType b) {
- return getCorrectedMimeSpecificity(b) - getCorrectedMimeSpecificity(a);
- }
- });
- return types.get(0);
+ return Collections.max(mimeTypes, comparing(this::getCorrectedMimeSpecificity));
}
@Override
diff --git a/java/com/google/gerrit/server/notedb/ChangeBundle.java b/java/com/google/gerrit/server/notedb/ChangeBundle.java
index 1d3c752..0ebee1a 100644
--- a/java/com/google/gerrit/server/notedb/ChangeBundle.java
+++ b/java/com/google/gerrit/server/notedb/ChangeBundle.java
@@ -17,11 +17,16 @@
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.collect.ImmutableSortedMap.toImmutableSortedMap;
import static com.google.gerrit.common.TimeUtil.truncateToSecond;
import static com.google.gerrit.reviewdb.server.ReviewDbUtil.checkColumns;
import static com.google.gerrit.reviewdb.server.ReviewDbUtil.intKeyOrdering;
import static com.google.gerrit.server.notedb.ChangeBundle.Source.NOTE_DB;
import static com.google.gerrit.server.notedb.ChangeBundle.Source.REVIEW_DB;
+import static java.util.Comparator.comparing;
+import static java.util.Comparator.naturalOrder;
+import static java.util.Comparator.nullsFirst;
import static java.util.stream.Collectors.toList;
import com.google.auto.value.AutoValue;
@@ -31,7 +36,6 @@
import com.google.common.base.Predicates;
import com.google.common.base.Strings;
import com.google.common.collect.Collections2;
-import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -43,13 +47,14 @@
import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;
+import com.google.common.collect.Streams;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.PatchLineComment;
import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.reviewdb.client.PatchSet.Id;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.CommentsUtil;
@@ -70,7 +75,6 @@
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
-import java.util.TreeMap;
/**
* A bundle of all entities rooted at a single {@link Change} entity.
@@ -105,110 +109,65 @@
Source.NOTE_DB);
}
- private static Map<ChangeMessage.Key, ChangeMessage> changeMessageMap(
- Iterable<ChangeMessage> in) {
- Map<ChangeMessage.Key, ChangeMessage> out =
- new TreeMap<>(
- new Comparator<ChangeMessage.Key>() {
- @Override
- public int compare(ChangeMessage.Key a, ChangeMessage.Key b) {
- return ComparisonChain.start()
- .compare(a.getParentKey().get(), b.getParentKey().get())
- .compare(a.get(), b.get())
- .result();
- }
- });
- for (ChangeMessage cm : in) {
- out.put(cm.getKey(), cm);
- }
- return out;
+ private static ImmutableSortedMap<ChangeMessage.Key, ChangeMessage> changeMessageMap(
+ Collection<ChangeMessage> in) {
+ return in.stream()
+ .collect(
+ toImmutableSortedMap(
+ comparing((ChangeMessage.Key k) -> k.getParentKey().get())
+ .thenComparing(k -> k.get()),
+ cm -> cm.getKey(),
+ cm -> cm));
}
// Unlike the *Map comparators, which are intended to make key lists diffable,
// this comparator sorts first on timestamp, then on every other field.
- private static final Ordering<ChangeMessage> CHANGE_MESSAGE_ORDER =
- new Ordering<ChangeMessage>() {
- final Ordering<Comparable<?>> nullsFirst = Ordering.natural().nullsFirst();
-
- @Override
- public int compare(ChangeMessage a, ChangeMessage b) {
- return ComparisonChain.start()
- .compare(a.getWrittenOn(), b.getWrittenOn())
- .compare(a.getKey().getParentKey().get(), b.getKey().getParentKey().get())
- .compare(psId(a), psId(b), nullsFirst)
- .compare(a.getAuthor(), b.getAuthor(), intKeyOrdering())
- .compare(a.getMessage(), b.getMessage(), nullsFirst)
- .result();
- }
-
- private Integer psId(ChangeMessage m) {
- return m.getPatchSetId() != null ? m.getPatchSetId().get() : null;
- }
- };
+ private static final Comparator<ChangeMessage> CHANGE_MESSAGE_COMPARATOR =
+ comparing(ChangeMessage::getWrittenOn)
+ .thenComparing(m -> m.getKey().getParentKey().get())
+ .thenComparing(
+ m -> m.getPatchSetId() != null ? m.getPatchSetId().get() : null,
+ nullsFirst(naturalOrder()))
+ .thenComparing(ChangeMessage::getAuthor, intKeyOrdering())
+ .thenComparing(ChangeMessage::getMessage, nullsFirst(naturalOrder()));
private static ImmutableList<ChangeMessage> changeMessageList(Iterable<ChangeMessage> in) {
- return CHANGE_MESSAGE_ORDER.immutableSortedCopy(in);
+ return Streams.stream(in).sorted(CHANGE_MESSAGE_COMPARATOR).collect(toImmutableList());
}
- private static TreeMap<PatchSet.Id, PatchSet> patchSetMap(Iterable<PatchSet> in) {
- TreeMap<PatchSet.Id, PatchSet> out =
- new TreeMap<>(
- new Comparator<PatchSet.Id>() {
- @Override
- public int compare(PatchSet.Id a, PatchSet.Id b) {
- return patchSetIdChain(a, b).result();
- }
- });
- for (PatchSet ps : in) {
- out.put(ps.getId(), ps);
- }
- return out;
+ private static ImmutableSortedMap<Id, PatchSet> patchSetMap(Iterable<PatchSet> in) {
+ return Streams.stream(in)
+ .collect(toImmutableSortedMap(patchSetIdComparator(), PatchSet::getId, ps -> ps));
}
- private static Map<PatchSetApproval.Key, PatchSetApproval> patchSetApprovalMap(
+ private static ImmutableSortedMap<PatchSetApproval.Key, PatchSetApproval> patchSetApprovalMap(
Iterable<PatchSetApproval> in) {
- Map<PatchSetApproval.Key, PatchSetApproval> out =
- new TreeMap<>(
- new Comparator<PatchSetApproval.Key>() {
- @Override
- public int compare(PatchSetApproval.Key a, PatchSetApproval.Key b) {
- return patchSetIdChain(a.getParentKey(), b.getParentKey())
- .compare(a.getAccountId().get(), b.getAccountId().get())
- .compare(a.getLabelId(), b.getLabelId())
- .result();
- }
- });
- for (PatchSetApproval psa : in) {
- out.put(psa.getKey(), psa);
- }
- return out;
+ return Streams.stream(in)
+ .collect(
+ toImmutableSortedMap(
+ comparing(PatchSetApproval.Key::getParentKey, patchSetIdComparator())
+ .thenComparing(PatchSetApproval.Key::getAccountId, intKeyOrdering())
+ .thenComparing(PatchSetApproval.Key::getLabelId),
+ PatchSetApproval::getKey,
+ a -> a));
}
- private static Map<PatchLineComment.Key, PatchLineComment> patchLineCommentMap(
+ private static ImmutableSortedMap<PatchLineComment.Key, PatchLineComment> patchLineCommentMap(
Iterable<PatchLineComment> in) {
- Map<PatchLineComment.Key, PatchLineComment> out =
- new TreeMap<>(
- new Comparator<PatchLineComment.Key>() {
- @Override
- public int compare(PatchLineComment.Key a, PatchLineComment.Key b) {
- Patch.Key pka = a.getParentKey();
- Patch.Key pkb = b.getParentKey();
- return patchSetIdChain(pka.getParentKey(), pkb.getParentKey())
- .compare(pka.get(), pkb.get())
- .compare(a.get(), b.get())
- .result();
- }
- });
- for (PatchLineComment plc : in) {
- out.put(plc.getKey(), plc);
- }
- return out;
+ return Streams.stream(in)
+ .collect(
+ toImmutableSortedMap(
+ comparing(
+ (PatchLineComment.Key k) -> k.getParentKey().getParentKey(),
+ patchSetIdComparator())
+ .thenComparing(PatchLineComment.Key::getParentKey)
+ .thenComparing(PatchLineComment.Key::get),
+ PatchLineComment::getKey,
+ c -> c));
}
- private static ComparisonChain patchSetIdChain(PatchSet.Id a, PatchSet.Id b) {
- return ComparisonChain.start()
- .compare(a.getParentKey().get(), b.getParentKey().get())
- .compare(a.get(), b.get());
+ private static Comparator<PatchSet.Id> patchSetIdComparator() {
+ return comparing((PatchSet.Id id) -> id.getParentKey().get()).thenComparing(id -> id.get());
}
static {
@@ -436,7 +395,7 @@
excludeOrigSubj = true;
String aTopic = trimOrNull(a.getTopic());
excludeTopic =
- Objects.equals(aTopic, b.getTopic()) || "".equals(aTopic) && b.getTopic() == null;
+ Objects.equals(aTopic, b.getTopic()) || ("".equals(aTopic) && b.getTopic() == null);
aUpdated = bundleA.getLatestTimestamp();
} else if (bundleA.source == NOTE_DB && bundleB.source == REVIEW_DB) {
boolean createdOnMatchesFirstPs =
@@ -454,7 +413,7 @@
excludeOrigSubj = true;
String bTopic = trimOrNull(b.getTopic());
excludeTopic =
- Objects.equals(bTopic, a.getTopic()) || a.getTopic() == null && "".equals(bTopic);
+ Objects.equals(bTopic, a.getTopic()) || (a.getTopic() == null && "".equals(bTopic));
bUpdated = bundleB.getLatestTimestamp();
}
@@ -598,9 +557,10 @@
}
if (!bs.isEmpty()) {
sb.append("Only in B:");
- for (ChangeMessage cm : CHANGE_MESSAGE_ORDER.sortedCopy(bs.values())) {
- sb.append("\n ").append(cm);
- }
+ bs.values()
+ .stream()
+ .sorted(CHANGE_MESSAGE_COMPARATOR)
+ .forEach(cm -> sb.append("\n ").append(cm));
}
diffs.add(sb.toString());
}
@@ -758,7 +718,8 @@
excludePostSubmit = a.getValue() == 0 && b.isPostSubmit();
} else if (bundleA.source == NOTE_DB && bundleB.source == REVIEW_DB) {
excludeGranted =
- tb.before(psb.getCreatedOn()) && ta.equals(psa.getCreatedOn()) || tb.compareTo(ta) < 0;
+ (tb.before(psb.getCreatedOn()) && ta.equals(psa.getCreatedOn()))
+ || (tb.compareTo(ta) < 0);
excludePostSubmit = b.getValue() == 0 && a.isPostSubmit();
}
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
index 4eeab81..cbb7020 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
@@ -79,7 +79,6 @@
import java.sql.Timestamp;
import java.text.ParseException;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -297,9 +296,7 @@
}
result.put(a.getPatchSetId(), a);
}
- for (Collection<PatchSetApproval> v : result.asMap().values()) {
- Collections.sort((List<PatchSetApproval>) v, ChangeNotes.PSA_BY_TIME);
- }
+ result.keySet().forEach(k -> result.get(k).sort(ChangeNotes.PSA_BY_TIME));
return result;
}
diff --git a/java/com/google/gerrit/server/notedb/LegacyChangeNoteWrite.java b/java/com/google/gerrit/server/notedb/LegacyChangeNoteWrite.java
index 7931d88..c9711b5 100644
--- a/java/com/google/gerrit/server/notedb/LegacyChangeNoteWrite.java
+++ b/java/com/google/gerrit/server/notedb/LegacyChangeNoteWrite.java
@@ -15,10 +15,12 @@
package com.google.gerrit.server.notedb;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.gerrit.server.CommentsUtil.COMMENT_ORDER;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ListMultimap;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Comment;
@@ -30,8 +32,6 @@
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.eclipse.jgit.lib.PersonIdent;
@@ -87,8 +87,7 @@
return;
}
- List<Integer> psIds = new ArrayList<>(comments.keySet());
- Collections.sort(psIds);
+ ImmutableList<Integer> psIds = comments.keySet().stream().sorted().collect(toImmutableList());
OutputStreamWriter streamWriter = new OutputStreamWriter(out, UTF_8);
try (PrintWriter writer = new PrintWriter(streamWriter)) {
diff --git a/java/com/google/gerrit/server/patch/DiffExecutorModule.java b/java/com/google/gerrit/server/patch/DiffExecutorModule.java
index f3776e0..eb6a280 100644
--- a/java/com/google/gerrit/server/patch/DiffExecutorModule.java
+++ b/java/com/google/gerrit/server/patch/DiffExecutorModule.java
@@ -15,7 +15,7 @@
package com.google.gerrit.server.patch;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
-import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
+import com.google.gerrit.server.logging.LoggingContextAwareExecutorService;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;
@@ -32,11 +32,8 @@
@Singleton
@DiffExecutor
public ExecutorService createDiffExecutor() {
- return Executors.newCachedThreadPool(
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat("Diff-%d")
- .setDaemon(true)
- .build());
+ return new LoggingContextAwareExecutorService(
+ Executors.newCachedThreadPool(
+ new ThreadFactoryBuilder().setNameFormat("Diff-%d").setDaemon(true).build()));
}
}
diff --git a/java/com/google/gerrit/server/patch/DiffSummaryLoader.java b/java/com/google/gerrit/server/patch/DiffSummaryLoader.java
index 8bca19f..9153638 100644
--- a/java/com/google/gerrit/server/patch/DiffSummaryLoader.java
+++ b/java/com/google/gerrit/server/patch/DiffSummaryLoader.java
@@ -19,7 +19,6 @@
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
@@ -66,8 +65,9 @@
break;
}
}
- Collections.sort(r);
return new DiffSummary(
- r.toArray(new String[r.size()]), patchList.getInsertions(), patchList.getDeletions());
+ r.stream().sorted().toArray(String[]::new),
+ patchList.getInsertions(),
+ patchList.getDeletions());
}
}
diff --git a/java/com/google/gerrit/server/patch/PatchList.java b/java/com/google/gerrit/server/patch/PatchList.java
index cf5df4a..dd717ba 100644
--- a/java/com/google/gerrit/server/patch/PatchList.java
+++ b/java/com/google/gerrit/server/patch/PatchList.java
@@ -47,12 +47,7 @@
private static final long serialVersionUID = PatchListKey.serialVersionUID;
private static final Comparator<PatchListEntry> PATCH_CMP =
- new Comparator<PatchListEntry>() {
- @Override
- public int compare(PatchListEntry a, PatchListEntry b) {
- return comparePaths(a.getNewName(), b.getNewName());
- }
- };
+ Comparator.comparing(PatchListEntry::getNewName, PatchList::comparePaths);
@VisibleForTesting
static int comparePaths(String a, String b) {
diff --git a/java/com/google/gerrit/server/patch/PatchScriptBuilder.java b/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
index a3d9048..61f0180 100644
--- a/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
+++ b/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.patch;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Comparator.comparing;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.data.CommentDetail;
@@ -34,7 +35,6 @@
import eu.medsea.mimeutil.MimeUtil2;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
@@ -55,13 +55,7 @@
static final int MAX_CONTEXT = 5000000;
static final int BIG_FILE = 9000;
- private static final Comparator<Edit> EDIT_SORT =
- new Comparator<Edit>() {
- @Override
- public int compare(Edit o1, Edit o2) {
- return o1.getBeginA() - o2.getBeginA();
- }
- };
+ private static final Comparator<Edit> EDIT_SORT = comparing(Edit::getBeginA);
private Repository db;
private Project.NameKey projectKey;
@@ -369,7 +363,7 @@
// them correctly later.
//
edits.addAll(empty);
- Collections.sort(edits, EDIT_SORT);
+ edits.sort(EDIT_SORT);
}
private void safeAdd(List<Edit> empty, Edit toAdd) {
diff --git a/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java b/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java
index 136b4ae..51a0f95 100644
--- a/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java
+++ b/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java
@@ -19,6 +19,7 @@
import static java.util.stream.Collectors.toSet;
import com.google.common.collect.Sets;
+import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.common.data.PermissionRule.Action;
import com.google.gerrit.extensions.api.access.GlobalOrPluginPermission;
@@ -46,6 +47,8 @@
@Singleton
public class DefaultPermissionBackend extends PermissionBackend {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
private static final CurrentUser.PropertyKey<Boolean> IS_ADMIN = CurrentUser.PropertyKey.create();
private final Provider<CurrentUser> currentUser;
@@ -186,6 +189,13 @@
private boolean isAdmin() {
if (admin == null) {
admin = computeAdmin();
+ if (admin) {
+ logger.atFinest().log(
+ "user %s is an administrator of the server", user.getLoggableName());
+ } else {
+ logger.atFinest().log(
+ "user %s is not an administrator of the server", user.getLoggableName());
+ }
}
return admin;
}
@@ -210,11 +220,32 @@
private boolean canEmailReviewers() {
List<PermissionRule> email = capabilities().emailReviewers;
- return allow(email) || notDenied(email);
+ if (allow(email)) {
+ logger.atFinest().log(
+ "user %s can email reviewers (allowed by %s)", user.getLoggableName(), email);
+ return true;
+ }
+
+ if (notDenied(email)) {
+ logger.atFinest().log(
+ "user %s can email reviewers (not denied by %s)", user.getLoggableName(), email);
+ return true;
+ }
+
+ logger.atFinest().log("user %s cannot email reviewers", user.getLoggableName());
+ return false;
}
private boolean has(String permissionName) {
- return allow(capabilities().getPermission(checkNotNull(permissionName)));
+ boolean has = allow(capabilities().getPermission(checkNotNull(permissionName)));
+ if (has) {
+ logger.atFinest().log(
+ "user %s has global capability %s", user.getLoggableName(), permissionName);
+ } else {
+ logger.atFinest().log(
+ "user %s doesn't have global capability %s", user.getLoggableName(), permissionName);
+ }
+ return has;
}
private boolean allow(Collection<PermissionRule> rules) {
diff --git a/java/com/google/gerrit/server/permissions/GlobalPermission.java b/java/com/google/gerrit/server/permissions/GlobalPermission.java
index 71718fb..01ef725 100644
--- a/java/com/google/gerrit/server/permissions/GlobalPermission.java
+++ b/java/com/google/gerrit/server/permissions/GlobalPermission.java
@@ -24,6 +24,7 @@
import com.google.gerrit.extensions.api.access.GerritPermission;
import com.google.gerrit.extensions.api.access.GlobalOrPluginPermission;
import com.google.gerrit.extensions.api.access.PluginPermission;
+import com.google.gerrit.extensions.registration.PluginName;
import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.LinkedHashSet;
@@ -116,7 +117,7 @@
Class<?> annotationClass)
throws PermissionBackendException {
if (pluginName != null
- && !"gerrit".equals(pluginName)
+ && !PluginName.GERRIT.equals(pluginName)
&& (scope == CapabilityScope.PLUGIN || scope == CapabilityScope.CONTEXT)) {
return new PluginPermission(pluginName, capability, fallBackToAdmin);
}
diff --git a/java/com/google/gerrit/server/permissions/ProjectControl.java b/java/com/google/gerrit/server/permissions/ProjectControl.java
index 67662c7..4a4ea37 100644
--- a/java/com/google/gerrit/server/permissions/ProjectControl.java
+++ b/java/com/google/gerrit/server/permissions/ProjectControl.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.permissions;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.gerrit.reviewdb.client.RefNames.REFS_TAGS;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.Permission;
@@ -212,6 +213,10 @@
return (canPerformOnAnyRef(Permission.CREATE) || isAdmin());
}
+ private boolean canAddTagRefs() {
+ return (canPerformOnTagRef(Permission.CREATE) || isAdmin());
+ }
+
private boolean canCreateChanges() {
for (SectionMatcher matcher : access()) {
AccessSection section = matcher.getSection();
@@ -233,6 +238,26 @@
return declaredOwner;
}
+ private boolean canPerformOnTagRef(String permissionName) {
+ for (SectionMatcher matcher : access()) {
+ AccessSection section = matcher.getSection();
+
+ if (section.getName().startsWith(REFS_TAGS)) {
+ Permission permission = section.getPermission(permissionName);
+ if (permission == null) {
+ continue;
+ }
+
+ Boolean can = canPerform(permissionName, section, permission);
+ if (can != null) {
+ return can;
+ }
+ }
+ }
+
+ return false;
+ }
+
private boolean canPerformOnAnyRef(String permissionName) {
for (SectionMatcher matcher : access()) {
AccessSection section = matcher.getSection();
@@ -241,25 +266,33 @@
continue;
}
- for (PermissionRule rule : permission.getRules()) {
- if (rule.isBlock() || rule.isDeny() || !match(rule)) {
- continue;
- }
-
- // Being in a group that was granted this permission is only an
- // approximation. There might be overrides and doNotInherit
- // that would render this to be false.
- //
- if (controlForRef(section.getName()).canPerform(permissionName)) {
- return true;
- }
- break;
+ Boolean can = canPerform(permissionName, section, permission);
+ if (can != null) {
+ return can;
}
}
return false;
}
+ private Boolean canPerform(String permissionName, AccessSection section, Permission permission) {
+ for (PermissionRule rule : permission.getRules()) {
+ if (rule.isBlock() || rule.isDeny() || !match(rule)) {
+ continue;
+ }
+
+ // Being in a group that was granted this permission is only an
+ // approximation. There might be overrides and doNotInherit
+ // that would render this to be false.
+ //
+ if (controlForRef(section.getName()).canPerform(permissionName)) {
+ return true;
+ }
+ break;
+ }
+ return null;
+ }
+
private boolean canPerformOnAllRefs(String permission, Set<String> ignore) {
boolean canPerform = false;
Set<String> patterns = allRefPatterns(permission);
@@ -403,6 +436,8 @@
case CREATE_REF:
return canAddRefs();
+ case CREATE_TAG_REF:
+ return canAddTagRefs();
case CREATE_CHANGE:
return canCreateChanges();
diff --git a/java/com/google/gerrit/server/permissions/ProjectPermission.java b/java/com/google/gerrit/server/permissions/ProjectPermission.java
index 3fee6cf..7c58ccb 100644
--- a/java/com/google/gerrit/server/permissions/ProjectPermission.java
+++ b/java/com/google/gerrit/server/permissions/ProjectPermission.java
@@ -51,6 +51,21 @@
CREATE_REF,
/**
+ * Can create at least one tag reference in the project.
+ *
+ * <p>This project level permission only validates the user may create some tag reference within
+ * the project. The exact reference name must be checked at creation:
+ *
+ * <pre>permissionBackend
+ * .user(user)
+ * .project(proj)
+ * .ref(ref)
+ * .check(RefPermission.CREATE);
+ * </pre>
+ */
+ CREATE_TAG_REF,
+
+ /**
* Can create at least one change in the project.
*
* <p>This project level permission only validates the user may create a change for some branch
diff --git a/java/com/google/gerrit/server/permissions/SectionSortCache.java b/java/com/google/gerrit/server/permissions/SectionSortCache.java
index 48c8bff..e5392b0 100644
--- a/java/com/google/gerrit/server/permissions/SectionSortCache.java
+++ b/java/com/google/gerrit/server/permissions/SectionSortCache.java
@@ -26,7 +26,6 @@
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
@@ -88,7 +87,7 @@
poison |= srcMap.put(sections.get(i), i) != null;
}
- Collections.sort(sections, new MostSpecificComparator(ref));
+ sections.sort(new MostSpecificComparator(ref));
int[] srcIdx;
if (isIdentityTransform(sections, srcMap)) {
diff --git a/java/com/google/gerrit/server/plugins/JarPluginProvider.java b/java/com/google/gerrit/server/plugins/JarPluginProvider.java
index 229f394..5b80059 100644
--- a/java/com/google/gerrit/server/plugins/JarPluginProvider.java
+++ b/java/com/google/gerrit/server/plugins/JarPluginProvider.java
@@ -16,6 +16,7 @@
import com.google.common.base.MoreObjects;
import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.extensions.registration.PluginName;
import com.google.gerrit.server.config.PluginConfig;
import com.google.gerrit.server.config.PluginConfigFactory;
import com.google.gerrit.server.config.SitePaths;
@@ -90,7 +91,7 @@
@Override
public String getProviderPluginName() {
- return "gerrit";
+ return PluginName.GERRIT;
}
private static String getExtension(Path path) {
diff --git a/java/com/google/gerrit/server/plugins/PluginEntry.java b/java/com/google/gerrit/server/plugins/PluginEntry.java
index f7b1e82..3a6c7b2 100644
--- a/java/com/google/gerrit/server/plugins/PluginEntry.java
+++ b/java/com/google/gerrit/server/plugins/PluginEntry.java
@@ -14,6 +14,8 @@
package com.google.gerrit.server.plugins;
+import static java.util.Comparator.comparing;
+
import java.util.Collections;
import java.util.Comparator;
import java.util.Map;
@@ -28,13 +30,7 @@
public class PluginEntry {
public static final String ATTR_CHARACTER_ENCODING = "Character-Encoding";
public static final String ATTR_CONTENT_TYPE = "Content-Type";
- public static final Comparator<PluginEntry> COMPARATOR_BY_NAME =
- new Comparator<PluginEntry>() {
- @Override
- public int compare(PluginEntry a, PluginEntry b) {
- return a.getName().compareTo(b.getName());
- }
- };
+ public static final Comparator<PluginEntry> COMPARATOR_BY_NAME = comparing(PluginEntry::getName);
private static final Map<Object, String> EMPTY_ATTRS = Collections.emptyMap();
private static final Optional<Long> NO_SIZE = Optional.empty();
diff --git a/java/com/google/gerrit/server/plugins/PluginGuiceEnvironment.java b/java/com/google/gerrit/server/plugins/PluginGuiceEnvironment.java
index 300bec7..8bc04a3 100644
--- a/java/com/google/gerrit/server/plugins/PluginGuiceEnvironment.java
+++ b/java/com/google/gerrit/server/plugins/PluginGuiceEnvironment.java
@@ -281,7 +281,8 @@
private void attachSet(
Map<TypeLiteral<?>, DynamicSet<?>> sets, @Nullable Injector src, Plugin plugin) {
- for (RegistrationHandle h : PrivateInternals_DynamicTypes.attachSets(src, sets)) {
+ for (RegistrationHandle h :
+ PrivateInternals_DynamicTypes.attachSets(src, plugin.getName(), sets)) {
plugin.add(h);
}
}
@@ -434,7 +435,7 @@
oi.remove();
replace(newPlugin, h2, b);
} else {
- newPlugin.add(set.add(b.getKey(), b.getProvider()));
+ newPlugin.add(set.add(newPlugin.getName(), b.getKey(), b.getProvider()));
}
}
}
diff --git a/java/com/google/gerrit/server/plugins/UniversalServerPluginProvider.java b/java/com/google/gerrit/server/plugins/UniversalServerPluginProvider.java
index 0bef1e5..4d89482 100644
--- a/java/com/google/gerrit/server/plugins/UniversalServerPluginProvider.java
+++ b/java/com/google/gerrit/server/plugins/UniversalServerPluginProvider.java
@@ -16,6 +16,7 @@
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.registration.PluginName;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.nio.file.Path;
@@ -60,7 +61,7 @@
@Override
public String getProviderPluginName() {
- return "gerrit";
+ return PluginName.GERRIT;
}
private ServerPluginProvider providerOf(Path srcPath) {
diff --git a/java/com/google/gerrit/server/project/ProjectCacheClock.java b/java/com/google/gerrit/server/project/ProjectCacheClock.java
index 188ee08..eb451fd 100644
--- a/java/com/google/gerrit/server/project/ProjectCacheClock.java
+++ b/java/com/google/gerrit/server/project/ProjectCacheClock.java
@@ -18,7 +18,7 @@
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.server.config.ConfigUtil;
import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
+import com.google.gerrit.server.logging.LoggingContextAwareScheduledExecutorService;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.concurrent.Executors;
@@ -54,14 +54,14 @@
// Start with generation 1 (to avoid magic 0 below).
generation.set(1);
executor =
- Executors.newScheduledThreadPool(
- 1,
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat("ProjectCacheClock-%d")
- .setDaemon(true)
- .setPriority(Thread.MIN_PRIORITY)
- .build());
+ new LoggingContextAwareScheduledExecutorService(
+ Executors.newScheduledThreadPool(
+ 1,
+ new ThreadFactoryBuilder()
+ .setNameFormat("ProjectCacheClock-%d")
+ .setDaemon(true)
+ .setPriority(Thread.MIN_PRIORITY)
+ .build()));
@SuppressWarnings("unused") // Runnable already handles errors
Future<?> possiblyIgnoredError =
executor.scheduleAtFixedRate(
diff --git a/java/com/google/gerrit/server/project/ProjectCacheImpl.java b/java/com/google/gerrit/server/project/ProjectCacheImpl.java
index 4942318..dd6ce56 100644
--- a/java/com/google/gerrit/server/project/ProjectCacheImpl.java
+++ b/java/com/google/gerrit/server/project/ProjectCacheImpl.java
@@ -178,6 +178,7 @@
@Override
public void evict(Project.NameKey p) throws IOException {
if (p != null) {
+ logger.atFine().log("Evict project '%s'", p.get());
byName.invalidate(p.get());
}
indexer.get().index(p);
diff --git a/java/com/google/gerrit/server/project/ProjectCacheWarmer.java b/java/com/google/gerrit/server/project/ProjectCacheWarmer.java
index adfaf62..10cf2de 100644
--- a/java/com/google/gerrit/server/project/ProjectCacheWarmer.java
+++ b/java/com/google/gerrit/server/project/ProjectCacheWarmer.java
@@ -19,11 +19,11 @@
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
+import com.google.gerrit.server.logging.LoggingContextAwareExecutorService;
import com.google.inject.Inject;
import com.google.inject.Singleton;
+import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.lib.Config;
@@ -44,13 +44,11 @@
public void start() {
int cpus = Runtime.getRuntime().availableProcessors();
if (config.getBoolean("cache", "projects", "loadOnStartup", false)) {
- ThreadPoolExecutor pool =
- new ScheduledThreadPoolExecutor(
- config.getInt("cache", "projects", "loadThreads", cpus),
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat("ProjectCacheLoader-%d")
- .build());
+ ExecutorService pool =
+ new LoggingContextAwareExecutorService(
+ new ScheduledThreadPoolExecutor(
+ config.getInt("cache", "projects", "loadThreads", cpus),
+ new ThreadFactoryBuilder().setNameFormat("ProjectCacheLoader-%d").build()));
Thread scheduler =
new Thread(
() -> {
diff --git a/java/com/google/gerrit/server/project/ProjectConfig.java b/java/com/google/gerrit/server/project/ProjectConfig.java
index 59fc323..bccc415 100644
--- a/java/com/google/gerrit/server/project/ProjectConfig.java
+++ b/java/com/google/gerrit/server/project/ProjectConfig.java
@@ -15,8 +15,10 @@
package com.google.gerrit.server.project;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.gerrit.common.data.Permission.isPermission;
import static com.google.gerrit.reviewdb.client.Project.DEFAULT_SUBMIT_TYPE;
+import static java.util.stream.Collectors.toList;
import com.google.common.base.CharMatcher;
import com.google.common.base.Joiner;
@@ -70,6 +72,7 @@
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;
@@ -89,6 +92,7 @@
public static final String KEY_DEFAULT_VALUE = "defaultValue";
public static final String KEY_COPY_MIN_SCORE = "copyMinScore";
public static final String KEY_ALLOW_POST_SUBMIT = "allowPostSubmit";
+ public static final String KEY_IGNORE_SELF_APPROVAL = "ignoreSelfApproval";
public static final String KEY_COPY_MAX_SCORE = "copyMaxScore";
public static final String KEY_COPY_ALL_SCORES_ON_MERGE_FIRST_PARENT_UPDATE =
"copyAllScoresOnMergeFirstParentUpdate";
@@ -880,6 +884,8 @@
}
label.setAllowPostSubmit(
rc.getBoolean(LABEL, name, KEY_ALLOW_POST_SUBMIT, LabelType.DEF_ALLOW_POST_SUBMIT));
+ label.setIgnoreSelfApproval(
+ rc.getBoolean(LABEL, name, KEY_IGNORE_SELF_APPROVAL, LabelType.DEF_IGNORE_SELF_APPROVAL));
label.setCopyMinScore(
rc.getBoolean(LABEL, name, KEY_COPY_MIN_SCORE, LabelType.DEF_COPY_MIN_SCORE));
label.setCopyMaxScore(
@@ -1162,21 +1168,20 @@
private void saveNotifySections(Config rc, Set<AccountGroup.UUID> keepGroups) {
for (NotifyConfig nc : sort(notifySections.values())) {
- List<String> email = new ArrayList<>();
- for (GroupReference gr : nc.getGroups()) {
- if (gr.getUUID() != null) {
- keepGroups.add(gr.getUUID());
- }
- email.add(new PermissionRule(gr).asString(false));
- }
- Collections.sort(email);
+ nc.getGroups()
+ .stream()
+ .map(gr -> gr.getUUID())
+ .filter(Objects::nonNull)
+ .forEach(keepGroups::add);
+ List<String> email =
+ nc.getGroups()
+ .stream()
+ .map(gr -> new PermissionRule(gr).asString(false))
+ .sorted()
+ .collect(toList());
- List<String> addrs = new ArrayList<>();
- for (Address addr : nc.getAddresses()) {
- addrs.add(addr.toString());
- }
- Collections.sort(addrs);
- email.addAll(addrs);
+ // Separate stream operation so that emails list contains 2 sorted sub-lists.
+ nc.getAddresses().stream().map(Address::toString).sorted().forEach(email::add);
set(rc, NOTIFY, nc.getName(), KEY_HEADER, nc.getHeader(), NotifyConfig.Header.BCC);
if (email.isEmpty()) {
@@ -1320,6 +1325,13 @@
rc,
LABEL,
name,
+ KEY_IGNORE_SELF_APPROVAL,
+ label.ignoreSelfApproval(),
+ LabelType.DEF_IGNORE_SELF_APPROVAL);
+ setBooleanConfigKey(
+ rc,
+ LABEL,
+ name,
KEY_COPY_MIN_SCORE,
label.isCopyMinScore(),
LabelType.DEF_COPY_MIN_SCORE);
@@ -1450,10 +1462,8 @@
validationErrors.add(error);
}
- private static <T extends Comparable<? super T>> List<T> sort(Collection<T> m) {
- ArrayList<T> r = new ArrayList<>(m);
- Collections.sort(r);
- return r;
+ private static <T extends Comparable<? super T>> ImmutableList<T> sort(Collection<T> m) {
+ return m.stream().sorted().collect(toImmutableList());
}
public boolean hasLegacyPermissions() {
diff --git a/java/com/google/gerrit/server/project/ProjectState.java b/java/com/google/gerrit/server/project/ProjectState.java
index 3e379a6..a9b19d9 100644
--- a/java/com/google/gerrit/server/project/ProjectState.java
+++ b/java/com/google/gerrit/server/project/ProjectState.java
@@ -17,6 +17,7 @@
import static com.google.gerrit.common.data.PermissionRule.Action.ALLOW;
import static java.nio.charset.StandardCharsets.UTF_8;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
@@ -89,6 +90,7 @@
private final Map<String, ProjectLevelConfig> configs;
private final Set<AccountGroup.UUID> localOwners;
private final long globalMaxObjectSizeLimit;
+ private final boolean inheritProjectMaxObjectSizeLimit;
/** Last system time the configuration's revision was examined. */
private volatile long lastCheckGeneration;
@@ -132,6 +134,7 @@
? limitsFactory.create(config.getAccessSection(AccessSection.GLOBAL_CAPABILITIES))
: null;
this.globalMaxObjectSizeLimit = transferConfig.getMaxObjectSizeLimit();
+ this.inheritProjectMaxObjectSizeLimit = transferConfig.getInheritProjectMaxObjectSizeLimit();
if (isAllProjects && !Permission.canBeOnAllProjects(AccessSection.ALL, Permission.OWNER)) {
localOwners = Collections.emptySet();
@@ -264,13 +267,58 @@
}
}
- public long getEffectiveMaxObjectSizeLimit() {
- long local = config.getMaxObjectSizeLimit();
- if (globalMaxObjectSizeLimit > 0 && local > 0) {
- return Math.min(globalMaxObjectSizeLimit, local);
+ public static class EffectiveMaxObjectSizeLimit {
+ public long value;
+ public String summary;
+ }
+
+ private static final String MAY_NOT_SET = "This project may not set a higher limit.";
+
+ @VisibleForTesting
+ public static final String INHERITED_FROM_PARENT = "Inherited from parent project '%s'.";
+
+ @VisibleForTesting
+ public static final String OVERRIDDEN_BY_PARENT =
+ "Overridden by parent project '%s'. " + MAY_NOT_SET;
+
+ @VisibleForTesting
+ public static final String INHERITED_FROM_GLOBAL = "Inherited from the global config.";
+
+ @VisibleForTesting
+ public static final String OVERRIDDEN_BY_GLOBAL =
+ "Overridden by the global config. " + MAY_NOT_SET;
+
+ public EffectiveMaxObjectSizeLimit getEffectiveMaxObjectSizeLimit() {
+ EffectiveMaxObjectSizeLimit result = new EffectiveMaxObjectSizeLimit();
+
+ result.value = config.getMaxObjectSizeLimit();
+
+ if (inheritProjectMaxObjectSizeLimit) {
+ for (ProjectState parent : parents()) {
+ long parentValue = parent.config.getMaxObjectSizeLimit();
+ if (parentValue > 0 && result.value > 0) {
+ if (parentValue < result.value) {
+ result.value = parentValue;
+ result.summary = String.format(OVERRIDDEN_BY_PARENT, parent.config.getName());
+ }
+ } else if (parentValue > 0) {
+ result.value = parentValue;
+ result.summary = String.format(INHERITED_FROM_PARENT, parent.config.getName());
+ }
+ }
}
- // zero means "no limit", in this case the max is more limiting
- return Math.max(globalMaxObjectSizeLimit, local);
+
+ if (globalMaxObjectSizeLimit > 0 && result.value > 0) {
+ if (globalMaxObjectSizeLimit < result.value) {
+ result.value = globalMaxObjectSizeLimit;
+ result.summary = OVERRIDDEN_BY_GLOBAL;
+ }
+ } else if (globalMaxObjectSizeLimit > result.value) {
+ // zero means "no limit", in this case the max is more limiting
+ result.value = globalMaxObjectSizeLimit;
+ result.summary = INHERITED_FROM_GLOBAL;
+ }
+ return result;
}
/** Get the sections that pertain only to this project. */
diff --git a/java/com/google/gerrit/server/restapi/account/CreateAccount.java b/java/com/google/gerrit/server/restapi/account/CreateAccount.java
index 78ec540..0e8eb70 100644
--- a/java/com/google/gerrit/server/restapi/account/CreateAccount.java
+++ b/java/com/google/gerrit/server/restapi/account/CreateAccount.java
@@ -115,8 +115,7 @@
throw new BadRequestException("username must match URL");
}
if (!ExternalId.isValidUsername(username)) {
- throw new BadRequestException(
- "Username '" + username + "' must contain only letters, numbers, _, - or .");
+ throw new BadRequestException("Invalid username '" + username + "'");
}
Set<AccountGroup.UUID> groups = parseGroups(input.groups);
diff --git a/java/com/google/gerrit/server/restapi/account/GetCapabilities.java b/java/com/google/gerrit/server/restapi/account/GetCapabilities.java
index 7889f6e..5c466bf 100644
--- a/java/com/google/gerrit/server/restapi/account/GetCapabilities.java
+++ b/java/com/google/gerrit/server/restapi/account/GetCapabilities.java
@@ -26,8 +26,8 @@
import com.google.gerrit.extensions.api.access.PluginPermission;
import com.google.gerrit.extensions.config.CapabilityDefinition;
import com.google.gerrit.extensions.registration.DynamicMap;
-import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.CurrentUser;
@@ -172,9 +172,9 @@
}
@Override
- public BinaryResult apply(Capability resource) throws ResourceNotFoundException {
+ public Response<String> apply(Capability resource) throws ResourceNotFoundException {
permissionBackend.checkUsesDefaultCapabilities();
- return BinaryResult.create("ok\n");
+ return Response.ok("ok");
}
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/GetEmails.java b/java/com/google/gerrit/server/restapi/account/GetEmails.java
index 85262ee..ed3347f 100644
--- a/java/com/google/gerrit/server/restapi/account/GetEmails.java
+++ b/java/com/google/gerrit/server/restapi/account/GetEmails.java
@@ -14,6 +14,9 @@
package com.google.gerrit.server.restapi.account;
+import static java.util.Comparator.comparing;
+import static java.util.stream.Collectors.toList;
+
import com.google.gerrit.extensions.common.EmailInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.RestReadView;
@@ -25,10 +28,8 @@
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
+import java.util.Objects;
@Singleton
public class GetEmails implements RestReadView<AccountResource> {
@@ -47,24 +48,19 @@
if (!self.get().hasSameAccountId(rsrc.getUser())) {
permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
}
+ return rsrc.getUser()
+ .getEmailAddresses()
+ .stream()
+ .filter(Objects::nonNull)
+ .map(e -> toEmailInfo(rsrc, e))
+ .sorted(comparing((EmailInfo e) -> e.email))
+ .collect(toList());
+ }
- List<EmailInfo> emails = new ArrayList<>();
- for (String email : rsrc.getUser().getEmailAddresses()) {
- if (email != null) {
- EmailInfo e = new EmailInfo();
- e.email = email;
- e.preferred(rsrc.getUser().getAccount().getPreferredEmail());
- emails.add(e);
- }
- }
- Collections.sort(
- emails,
- new Comparator<EmailInfo>() {
- @Override
- public int compare(EmailInfo a, EmailInfo b) {
- return a.email.compareTo(b.email);
- }
- });
- return emails;
+ private static EmailInfo toEmailInfo(AccountResource rsrc, String email) {
+ EmailInfo e = new EmailInfo();
+ e.email = email;
+ e.preferred(rsrc.getUser().getAccount().getPreferredEmail());
+ return e;
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java b/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
index 112bb24..61021be 100644
--- a/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
+++ b/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
@@ -14,8 +14,10 @@
package com.google.gerrit.server.restapi.account;
+import static java.util.Comparator.comparing;
+import static java.util.stream.Collectors.toList;
+
import com.google.common.base.Strings;
-import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.extensions.client.ProjectWatchInfo;
import com.google.gerrit.extensions.restapi.AuthException;
@@ -36,11 +38,7 @@
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
-import java.util.Map;
import org.eclipse.jgit.errors.ConfigInvalidException;
@Singleton
@@ -67,31 +65,28 @@
Account.Id accountId = rsrc.getUser().getAccountId();
AccountState account = accounts.get(accountId).orElseThrow(ResourceNotFoundException::new);
- List<ProjectWatchInfo> projectWatchInfos = new ArrayList<>();
- for (Map.Entry<ProjectWatchKey, ImmutableSet<NotifyType>> e :
- account.getProjectWatches().entrySet()) {
- ProjectWatchInfo pwi = new ProjectWatchInfo();
- pwi.filter = e.getKey().filter();
- pwi.project = e.getKey().project().get();
- pwi.notifyAbandonedChanges = toBoolean(e.getValue().contains(NotifyType.ABANDONED_CHANGES));
- pwi.notifyNewChanges = toBoolean(e.getValue().contains(NotifyType.NEW_CHANGES));
- pwi.notifyNewPatchSets = toBoolean(e.getValue().contains(NotifyType.NEW_PATCHSETS));
- pwi.notifySubmittedChanges = toBoolean(e.getValue().contains(NotifyType.SUBMITTED_CHANGES));
- pwi.notifyAllComments = toBoolean(e.getValue().contains(NotifyType.ALL_COMMENTS));
- projectWatchInfos.add(pwi);
- }
- Collections.sort(
- projectWatchInfos,
- new Comparator<ProjectWatchInfo>() {
- @Override
- public int compare(ProjectWatchInfo pwi1, ProjectWatchInfo pwi2) {
- return ComparisonChain.start()
- .compare(pwi1.project, pwi2.project)
- .compare(Strings.nullToEmpty(pwi1.filter), Strings.nullToEmpty(pwi2.filter))
- .result();
- }
- });
- return projectWatchInfos;
+ return account
+ .getProjectWatches()
+ .entrySet()
+ .stream()
+ .map(e -> toProjectWatchInfo(e.getKey(), e.getValue()))
+ .sorted(
+ comparing((ProjectWatchInfo pwi) -> pwi.project)
+ .thenComparing(pwi -> Strings.nullToEmpty(pwi.filter)))
+ .collect(toList());
+ }
+
+ private static ProjectWatchInfo toProjectWatchInfo(
+ ProjectWatchKey key, ImmutableSet<NotifyType> watchTypes) {
+ ProjectWatchInfo pwi = new ProjectWatchInfo();
+ pwi.filter = key.filter();
+ pwi.project = key.project().get();
+ pwi.notifyAbandonedChanges = toBoolean(watchTypes.contains(NotifyType.ABANDONED_CHANGES));
+ pwi.notifyNewChanges = toBoolean(watchTypes.contains(NotifyType.NEW_CHANGES));
+ pwi.notifyNewPatchSets = toBoolean(watchTypes.contains(NotifyType.NEW_PATCHSETS));
+ pwi.notifySubmittedChanges = toBoolean(watchTypes.contains(NotifyType.SUBMITTED_CHANGES));
+ pwi.notifyAllComments = toBoolean(watchTypes.contains(NotifyType.ALL_COMMENTS));
+ return pwi;
}
private static Boolean toBoolean(boolean value) {
diff --git a/java/com/google/gerrit/server/restapi/change/CommentJson.java b/java/com/google/gerrit/server/restapi/change/CommentJson.java
index e92abe1..a562592 100644
--- a/java/com/google/gerrit/server/restapi/change/CommentJson.java
+++ b/java/com/google/gerrit/server/restapi/change/CommentJson.java
@@ -14,11 +14,13 @@
package com.google.gerrit.server.restapi.change;
+import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.gerrit.server.CommentsUtil.COMMENT_INFO_ORDER;
import static java.util.stream.Collectors.toList;
import com.google.common.base.Strings;
-import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Streams;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.client.Comment.Range;
import com.google.gerrit.extensions.client.Side;
@@ -35,7 +37,6 @@
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.inject.Inject;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
@@ -96,9 +97,7 @@
list.add(o);
}
- for (List<T> list : out.values()) {
- Collections.sort(list, COMMENT_INFO_ORDER);
- }
+ out.values().forEach(l -> l.sort(COMMENT_INFO_ORDER));
if (loader != null) {
loader.fill();
@@ -106,13 +105,14 @@
return out;
}
- public List<T> formatAsList(Iterable<F> comments) throws PermissionBackendException {
+ public ImmutableList<T> formatAsList(Iterable<F> comments) throws PermissionBackendException {
AccountLoader loader = fillAccounts ? accountLoaderFactory.create(true) : null;
- List<T> out =
- FluentIterable.from(comments)
- .transform(c -> toInfo(c, loader))
- .toSortedList(COMMENT_INFO_ORDER);
+ ImmutableList<T> out =
+ Streams.stream(comments)
+ .map(c -> toInfo(c, loader))
+ .sorted(COMMENT_INFO_ORDER)
+ .collect(toImmutableList());
if (loader != null) {
loader.fill();
diff --git a/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java b/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java
index db8ef0c..dbd0ccf 100644
--- a/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.restapi.change;
+import com.google.common.collect.ImmutableList;
import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.reviewdb.client.Comment;
@@ -61,7 +62,7 @@
.format(listComments(rsrc));
}
- public List<CommentInfo> getComments(RevisionResource rsrc)
+ public ImmutableList<CommentInfo> getComments(RevisionResource rsrc)
throws OrmException, PermissionBackendException {
return commentJson
.get()
diff --git a/java/com/google/gerrit/server/restapi/change/ListRobotComments.java b/java/com/google/gerrit/server/restapi/change/ListRobotComments.java
index 66138ab..99366aa 100644
--- a/java/com/google/gerrit/server/restapi/change/ListRobotComments.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRobotComments.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.restapi.change;
+import com.google.common.collect.ImmutableList;
import com.google.gerrit.extensions.common.RobotCommentInfo;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.reviewdb.client.RobotComment;
@@ -52,7 +53,7 @@
.format(listComments(rsrc));
}
- public List<RobotCommentInfo> getComments(RevisionResource rsrc)
+ public ImmutableList<RobotCommentInfo> getComments(RevisionResource rsrc)
throws OrmException, PermissionBackendException {
return commentJson
.get()
diff --git a/java/com/google/gerrit/server/restapi/config/CachesCollection.java b/java/com/google/gerrit/server/restapi/config/CachesCollection.java
index 152fef9..a4b8802 100644
--- a/java/com/google/gerrit/server/restapi/config/CachesCollection.java
+++ b/java/com/google/gerrit/server/restapi/config/CachesCollection.java
@@ -20,6 +20,7 @@
import com.google.common.cache.Cache;
import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.registration.PluginName;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.IdString;
@@ -66,7 +67,7 @@
permissionBackend.currentUser().check(GlobalPermission.VIEW_CACHES);
String cacheName = id.get();
- String pluginName = "gerrit";
+ String pluginName = PluginName.GERRIT;
int i = cacheName.lastIndexOf('-');
if (i != -1) {
pluginName = cacheName.substring(0, i);
diff --git a/java/com/google/gerrit/server/restapi/config/ListCaches.java b/java/com/google/gerrit/server/restapi/config/ListCaches.java
index c0a9d71..38664fb 100644
--- a/java/com/google/gerrit/server/restapi/config/ListCaches.java
+++ b/java/com/google/gerrit/server/restapi/config/ListCaches.java
@@ -14,14 +14,16 @@
package com.google.gerrit.server.restapi.config;
+import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.gerrit.common.data.GlobalCapability.MAINTAIN_SERVER;
import static com.google.gerrit.common.data.GlobalCapability.VIEW_CACHES;
import static com.google.gerrit.server.config.CacheResource.cacheNameOf;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.stream.Collectors.joining;
-import com.google.common.base.Joiner;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheStats;
+import com.google.common.collect.Streams;
import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.BinaryResult;
@@ -29,11 +31,9 @@
import com.google.gerrit.server.cache.PersistentCache;
import com.google.gerrit.server.config.ConfigResource;
import com.google.inject.Inject;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
import java.util.Map;
import java.util.TreeMap;
+import java.util.stream.Stream;
import org.kohsuke.args4j.Option;
@RequiresAnyCapability({VIEW_CACHES, MAINTAIN_SERVER})
@@ -72,19 +72,17 @@
if (format == null) {
return getCacheInfos();
}
- List<String> cacheNames = new ArrayList<>();
- for (DynamicMap.Entry<Cache<?, ?>> e : cacheMap) {
- cacheNames.add(cacheNameOf(e.getPluginName(), e.getExportName()));
- }
- Collections.sort(cacheNames);
-
+ Stream<String> cacheNames =
+ Streams.stream(cacheMap)
+ .map(e -> cacheNameOf(e.getPluginName(), e.getExportName()))
+ .sorted();
if (OutputFormat.TEXT_LIST.equals(format)) {
- return BinaryResult.create(Joiner.on('\n').join(cacheNames))
+ return BinaryResult.create(cacheNames.collect(joining("\n")))
.base64()
.setContentType("text/plain")
.setCharacterEncoding(UTF_8);
}
- return cacheNames;
+ return cacheNames.collect(toImmutableList());
}
public enum CacheType {
diff --git a/java/com/google/gerrit/server/restapi/config/ListTasks.java b/java/com/google/gerrit/server/restapi/config/ListTasks.java
index 7b69831..f77cda4 100644
--- a/java/com/google/gerrit/server/restapi/config/ListTasks.java
+++ b/java/com/google/gerrit/server/restapi/config/ListTasks.java
@@ -14,7 +14,9 @@
package com.google.gerrit.server.restapi.config;
-import com.google.common.collect.ComparisonChain;
+import static java.util.Comparator.comparing;
+import static java.util.stream.Collectors.toList;
+
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.reviewdb.client.Project;
@@ -35,8 +37,6 @@
import com.google.inject.Singleton;
import java.sql.Timestamp;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -106,20 +106,14 @@
}
private List<TaskInfo> getTasks() {
- List<TaskInfo> taskInfos = workQueue.getTaskInfos(TaskInfo::new);
- Collections.sort(
- taskInfos,
- new Comparator<TaskInfo>() {
- @Override
- public int compare(TaskInfo a, TaskInfo b) {
- return ComparisonChain.start()
- .compare(a.state.ordinal(), b.state.ordinal())
- .compare(a.delay, b.delay)
- .compare(a.command, b.command)
- .result();
- }
- });
- return taskInfos;
+ return workQueue
+ .getTaskInfos(TaskInfo::new)
+ .stream()
+ .sorted(
+ comparing((TaskInfo t) -> t.state.ordinal())
+ .thenComparing(t -> t.delay)
+ .thenComparing(t -> t.command))
+ .collect(toList());
}
public static class TaskInfo {
diff --git a/java/com/google/gerrit/server/restapi/config/PostCaches.java b/java/com/google/gerrit/server/restapi/config/PostCaches.java
index 7f9b756..57ba097 100644
--- a/java/com/google/gerrit/server/restapi/config/PostCaches.java
+++ b/java/com/google/gerrit/server/restapi/config/PostCaches.java
@@ -20,6 +20,7 @@
import com.google.common.cache.Cache;
import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.registration.PluginName;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.Response;
@@ -110,7 +111,7 @@
List<CacheResource> cacheResources = new ArrayList<>(cacheNames.size());
for (String n : cacheNames) {
- String pluginName = "gerrit";
+ String pluginName = PluginName.GERRIT;
String cacheName = n;
int i = cacheName.lastIndexOf('-');
if (i != -1) {
diff --git a/java/com/google/gerrit/server/restapi/group/GetAuditLog.java b/java/com/google/gerrit/server/restapi/group/GetAuditLog.java
index 7af4284..dcdd8a8 100644
--- a/java/com/google/gerrit/server/restapi/group/GetAuditLog.java
+++ b/java/com/google/gerrit/server/restapi/group/GetAuditLog.java
@@ -41,7 +41,6 @@
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -138,7 +137,7 @@
accountLoader.fill();
// sort by date and then reverse so that the newest audit event comes first
- Collections.sort(auditEvents, comparing((GroupAuditEventInfo a) -> a.date).reversed());
+ auditEvents.sort(comparing((GroupAuditEventInfo a) -> a.date).reversed());
return auditEvents;
}
}
diff --git a/java/com/google/gerrit/server/restapi/group/ListSubgroups.java b/java/com/google/gerrit/server/restapi/group/ListSubgroups.java
index 97a260e..864b01b 100644
--- a/java/com/google/gerrit/server/restapi/group/ListSubgroups.java
+++ b/java/com/google/gerrit/server/restapi/group/ListSubgroups.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.group;
import static com.google.common.base.Strings.nullToEmpty;
+import static java.util.Comparator.comparing;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.GroupDescription;
@@ -29,8 +30,6 @@
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
@Singleton
@@ -72,18 +71,8 @@
continue;
}
}
- Collections.sort(
- included,
- new Comparator<GroupInfo>() {
- @Override
- public int compare(GroupInfo a, GroupInfo b) {
- int cmp = nullToEmpty(a.name).compareTo(nullToEmpty(b.name));
- if (cmp != 0) {
- return cmp;
- }
- return nullToEmpty(a.id).compareTo(nullToEmpty(b.id));
- }
- });
+ included.sort(
+ comparing((GroupInfo g) -> nullToEmpty(g.name)).thenComparing(g -> nullToEmpty(g.id)));
return included;
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java b/java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java
index 076bf78..60b5dee 100644
--- a/java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java
+++ b/java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java
@@ -33,10 +33,10 @@
import com.google.gerrit.server.config.PluginConfigFactory;
import com.google.gerrit.server.config.ProjectConfigEntry;
import com.google.gerrit.server.extensions.webui.UiActions;
-import com.google.gerrit.server.git.TransferConfig;
import com.google.gerrit.server.project.BooleanProjectConfigTransformations;
import com.google.gerrit.server.project.ProjectResource;
import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.project.ProjectState.EffectiveMaxObjectSizeLimit;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -48,7 +48,6 @@
boolean serverEnableSignedPush,
ProjectState projectState,
CurrentUser user,
- TransferConfig transferConfig,
DynamicMap<ProjectConfigEntry> pluginConfigEntries,
PluginConfigFactory cfgFactory,
AllProjectsName allProjects,
@@ -72,7 +71,7 @@
this.requireSignedPush = null;
}
- this.maxObjectSizeLimit = getMaxObjectSizeLimit(projectState, transferConfig, p);
+ this.maxObjectSizeLimit = getMaxObjectSizeLimit(projectState, p);
this.defaultSubmitType = new SubmitTypeInfo();
this.defaultSubmitType.value = projectState.getSubmitType();
@@ -107,13 +106,13 @@
this.extensionPanelNames = projectState.getConfig().getExtensionPanelSections();
}
- private MaxObjectSizeLimitInfo getMaxObjectSizeLimit(
- ProjectState projectState, TransferConfig transferConfig, Project p) {
+ private MaxObjectSizeLimitInfo getMaxObjectSizeLimit(ProjectState projectState, Project p) {
MaxObjectSizeLimitInfo info = new MaxObjectSizeLimitInfo();
- long value = projectState.getEffectiveMaxObjectSizeLimit();
+ EffectiveMaxObjectSizeLimit limit = projectState.getEffectiveMaxObjectSizeLimit();
+ long value = limit.value;
info.value = value == 0 ? null : String.valueOf(value);
info.configuredValue = p.getMaxObjectSizeLimit();
- info.inheritedValue = transferConfig.getFormattedMaxObjectSizeLimit();
+ info.summary = limit.summary;
return info;
}
diff --git a/java/com/google/gerrit/server/restapi/project/GetAccess.java b/java/com/google/gerrit/server/restapi/project/GetAccess.java
index d545f92..a6b9404 100644
--- a/java/com/google/gerrit/server/restapi/project/GetAccess.java
+++ b/java/com/google/gerrit/server/restapi/project/GetAccess.java
@@ -16,6 +16,7 @@
import static com.google.gerrit.server.permissions.GlobalPermission.ADMINISTRATE_SERVER;
import static com.google.gerrit.server.permissions.ProjectPermission.CREATE_REF;
+import static com.google.gerrit.server.permissions.ProjectPermission.CREATE_TAG_REF;
import static com.google.gerrit.server.permissions.RefPermission.CREATE_CHANGE;
import static com.google.gerrit.server.permissions.RefPermission.READ;
import static com.google.gerrit.server.permissions.RefPermission.WRITE_CONFIG;
@@ -270,6 +271,7 @@
|| (canReadConfig
&& perm.ref(RefNames.REFS_CONFIG).testOrFalse(CREATE_CHANGE))));
info.canAdd = toBoolean(perm.testOrFalse(CREATE_REF));
+ info.canAddTags = toBoolean(perm.testOrFalse(CREATE_TAG_REF));
info.configVisible = canReadConfig || canWriteConfig;
info.groups =
diff --git a/java/com/google/gerrit/server/restapi/project/GetConfig.java b/java/com/google/gerrit/server/restapi/project/GetConfig.java
index 7fedc8f..b3ad962 100644
--- a/java/com/google/gerrit/server/restapi/project/GetConfig.java
+++ b/java/com/google/gerrit/server/restapi/project/GetConfig.java
@@ -23,7 +23,6 @@
import com.google.gerrit.server.config.PluginConfigFactory;
import com.google.gerrit.server.config.ProjectConfigEntry;
import com.google.gerrit.server.extensions.webui.UiActions;
-import com.google.gerrit.server.git.TransferConfig;
import com.google.gerrit.server.project.ProjectResource;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -31,7 +30,6 @@
@Singleton
public class GetConfig implements RestReadView<ProjectResource> {
private final boolean serverEnableSignedPush;
- private final TransferConfig transferConfig;
private final DynamicMap<ProjectConfigEntry> pluginConfigEntries;
private final PluginConfigFactory cfgFactory;
private final AllProjectsName allProjects;
@@ -41,14 +39,12 @@
@Inject
public GetConfig(
@EnableSignedPush boolean serverEnableSignedPush,
- TransferConfig transferConfig,
DynamicMap<ProjectConfigEntry> pluginConfigEntries,
PluginConfigFactory cfgFactory,
AllProjectsName allProjects,
UiActions uiActions,
DynamicMap<RestView<ProjectResource>> views) {
this.serverEnableSignedPush = serverEnableSignedPush;
- this.transferConfig = transferConfig;
this.pluginConfigEntries = pluginConfigEntries;
this.allProjects = allProjects;
this.cfgFactory = cfgFactory;
@@ -62,7 +58,6 @@
serverEnableSignedPush,
resource.getProjectState(),
resource.getUser(),
- transferConfig,
pluginConfigEntries,
cfgFactory,
allProjects,
diff --git a/java/com/google/gerrit/server/restapi/project/ListBranches.java b/java/com/google/gerrit/server/restapi/project/ListBranches.java
index bf4a547..a0d2528 100644
--- a/java/com/google/gerrit/server/restapi/project/ListBranches.java
+++ b/java/com/google/gerrit/server/restapi/project/ListBranches.java
@@ -46,7 +46,6 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
@@ -226,7 +225,7 @@
// Do nothing.
}
}
- Collections.sort(branches, new BranchComparator());
+ branches.sort(new BranchComparator());
return branches;
}
diff --git a/java/com/google/gerrit/server/restapi/project/ListTags.java b/java/com/google/gerrit/server/restapi/project/ListTags.java
index e79fdca..f59e984 100644
--- a/java/com/google/gerrit/server/restapi/project/ListTags.java
+++ b/java/com/google/gerrit/server/restapi/project/ListTags.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.restapi.project;
import static com.google.gerrit.reviewdb.client.RefNames.isConfigRef;
+import static java.util.Comparator.comparing;
import com.google.common.collect.ImmutableMap;
import com.google.gerrit.extensions.api.projects.ProjectApi.ListRefsRequest;
@@ -39,8 +40,6 @@
import java.io.IOException;
import java.sql.Timestamp;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
import java.util.Map;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
@@ -135,14 +134,7 @@
}
}
- Collections.sort(
- tags,
- new Comparator<TagInfo>() {
- @Override
- public int compare(TagInfo a, TagInfo b) {
- return a.ref.compareTo(b.ref);
- }
- });
+ tags.sort(comparing(t -> t.ref));
return new RefFilter<TagInfo>(Constants.R_TAGS)
.start(start)
diff --git a/java/com/google/gerrit/server/restapi/project/PutConfig.java b/java/com/google/gerrit/server/restapi/project/PutConfig.java
index f4eb781..76ea0c9 100644
--- a/java/com/google/gerrit/server/restapi/project/PutConfig.java
+++ b/java/com/google/gerrit/server/restapi/project/PutConfig.java
@@ -38,7 +38,6 @@
import com.google.gerrit.server.config.PluginConfigFactory;
import com.google.gerrit.server.config.ProjectConfigEntry;
import com.google.gerrit.server.extensions.webui.UiActions;
-import com.google.gerrit.server.git.TransferConfig;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -71,7 +70,6 @@
private final Provider<MetaDataUpdate.User> metaDataUpdateFactory;
private final ProjectCache projectCache;
private final ProjectState.Factory projectStateFactory;
- private final TransferConfig transferConfig;
private final DynamicMap<ProjectConfigEntry> pluginConfigEntries;
private final PluginConfigFactory cfgFactory;
private final AllProjectsName allProjects;
@@ -86,7 +84,6 @@
Provider<MetaDataUpdate.User> metaDataUpdateFactory,
ProjectCache projectCache,
ProjectState.Factory projectStateFactory,
- TransferConfig transferConfig,
DynamicMap<ProjectConfigEntry> pluginConfigEntries,
PluginConfigFactory cfgFactory,
AllProjectsName allProjects,
@@ -98,7 +95,6 @@
this.metaDataUpdateFactory = metaDataUpdateFactory;
this.projectCache = projectCache;
this.projectStateFactory = projectStateFactory;
- this.transferConfig = transferConfig;
this.pluginConfigEntries = pluginConfigEntries;
this.cfgFactory = cfgFactory;
this.allProjects = allProjects;
@@ -173,7 +169,6 @@
serverEnableSignedPush,
state,
user.get(),
- transferConfig,
pluginConfigEntries,
cfgFactory,
allProjects,
diff --git a/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java b/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java
new file mode 100644
index 0000000..b9ddbc6
--- /dev/null
+++ b/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java
@@ -0,0 +1,171 @@
+// Copyright (C) 2018 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.rules;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.common.data.LabelFunction;
+import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.common.data.SubmitRequirement;
+import com.google.gerrit.extensions.annotations.Exports;
+import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.PatchSetApproval;
+import com.google.gerrit.server.project.SubmitRuleOptions;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Rule to require an approval from a user that did not upload the current patch set or block
+ * submission.
+ */
+@Singleton
+public class IgnoreSelfApprovalRule implements SubmitRule {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+ private static final String E_UNABLE_TO_FETCH_UPLOADER = "Unable to fetch uploader";
+ private static final String E_UNABLE_TO_FETCH_LABELS =
+ "Unable to fetch labels and approvals for the change";
+
+ public static class Module extends FactoryModule {
+ @Override
+ public void configure() {
+ bind(SubmitRule.class)
+ .annotatedWith(Exports.named("IgnoreSelfApprovalRule"))
+ .to(IgnoreSelfApprovalRule.class);
+ }
+ }
+
+ @Inject
+ IgnoreSelfApprovalRule() {}
+
+ @Override
+ public Collection<SubmitRecord> evaluate(ChangeData cd, SubmitRuleOptions options) {
+ List<LabelType> labelTypes;
+ List<PatchSetApproval> approvals;
+ try {
+ labelTypes = cd.getLabelTypes().getLabelTypes();
+ approvals = cd.currentApprovals();
+ } catch (OrmException e) {
+ logger.atWarning().withCause(e).log(E_UNABLE_TO_FETCH_LABELS);
+ return singletonRuleError(E_UNABLE_TO_FETCH_LABELS);
+ }
+
+ boolean shouldIgnoreSelfApproval = labelTypes.stream().anyMatch(l -> l.ignoreSelfApproval());
+ if (!shouldIgnoreSelfApproval) {
+ // Shortcut to avoid further processing if no label should ignore uploader approvals
+ return ImmutableList.of();
+ }
+
+ Account.Id uploader;
+ try {
+ uploader = cd.currentPatchSet().getUploader();
+ } catch (OrmException e) {
+ logger.atWarning().withCause(e).log(E_UNABLE_TO_FETCH_UPLOADER);
+ return singletonRuleError(E_UNABLE_TO_FETCH_UPLOADER);
+ }
+
+ SubmitRecord submitRecord = new SubmitRecord();
+ submitRecord.status = SubmitRecord.Status.OK;
+ submitRecord.labels = new ArrayList<>(labelTypes.size());
+ submitRecord.requirements = new ArrayList<>();
+
+ for (LabelType t : labelTypes) {
+ if (!t.ignoreSelfApproval()) {
+ // The default rules are enough in this case.
+ continue;
+ }
+
+ LabelFunction labelFunction = t.getFunction();
+ if (labelFunction == null) {
+ continue;
+ }
+
+ Collection<PatchSetApproval> allApprovalsForLabel = filterApprovalsByLabel(approvals, t);
+ SubmitRecord.Label allApprovalsCheckResult = labelFunction.check(t, allApprovalsForLabel);
+ SubmitRecord.Label ignoreSelfApprovalCheckResult =
+ labelFunction.check(t, filterOutPositiveApprovalsOfUser(allApprovalsForLabel, uploader));
+
+ if (labelCheckPassed(allApprovalsCheckResult)
+ && !labelCheckPassed(ignoreSelfApprovalCheckResult)) {
+ // The label has a valid approval from the uploader and no other valid approval. Set the
+ // label
+ // to NOT_READY and indicate the need for non-uploader approval as requirement.
+ submitRecord.labels.add(ignoreSelfApprovalCheckResult);
+ submitRecord.status = SubmitRecord.Status.NOT_READY;
+ // Add an additional requirement to be more descriptive on why the label counts as not
+ // approved.
+ submitRecord.requirements.add(
+ SubmitRequirement.builder()
+ .setFallbackText("Approval from non-uploader required")
+ .setType("non_uploader_approval")
+ .build());
+ }
+ }
+
+ if (submitRecord.labels.isEmpty()) {
+ return ImmutableList.of();
+ }
+
+ return ImmutableList.of(submitRecord);
+ }
+
+ private static boolean labelCheckPassed(SubmitRecord.Label label) {
+ switch (label.status) {
+ case OK:
+ case MAY:
+ return true;
+
+ case NEED:
+ case REJECT:
+ case IMPOSSIBLE:
+ return false;
+ }
+ return false;
+ }
+
+ private static Collection<SubmitRecord> singletonRuleError(String reason) {
+ SubmitRecord submitRecord = new SubmitRecord();
+ submitRecord.errorMessage = reason;
+ submitRecord.status = SubmitRecord.Status.RULE_ERROR;
+ return ImmutableList.of(submitRecord);
+ }
+
+ @VisibleForTesting
+ static Collection<PatchSetApproval> filterOutPositiveApprovalsOfUser(
+ Collection<PatchSetApproval> approvals, Account.Id user) {
+ return approvals
+ .stream()
+ .filter(input -> input.getValue() < 0 || !input.getAccountId().equals(user))
+ .collect(toImmutableList());
+ }
+
+ @VisibleForTesting
+ static Collection<PatchSetApproval> filterApprovalsByLabel(
+ Collection<PatchSetApproval> approvals, LabelType t) {
+ return approvals
+ .stream()
+ .filter(input -> input.getLabelId().get().equals(t.getLabelId().get()))
+ .collect(toImmutableList());
+ }
+}
diff --git a/java/com/google/gerrit/server/submit/MergeOp.java b/java/com/google/gerrit/server/submit/MergeOp.java
index 9a8895b..43d5f75 100644
--- a/java/com/google/gerrit/server/submit/MergeOp.java
+++ b/java/com/google/gerrit/server/submit/MergeOp.java
@@ -457,7 +457,7 @@
this.caller = caller;
this.ts = TimeUtil.nowTs();
this.db = db;
- this.submissionId = RequestId.forChange(change);
+ this.submissionId = new RequestId(change.getId().toString());
try (TraceContext traceContext =
TraceContext.open().addTag(RequestId.Type.SUBMISSION_ID, submissionId)) {
diff --git a/java/com/google/gerrit/server/submit/SubmitStrategyOp.java b/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
index 290e917..51dad5b 100644
--- a/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
+++ b/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
@@ -53,7 +53,6 @@
import com.google.gwtorm.server.OrmException;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -184,8 +183,7 @@
continue; // Bogus ref, can't be merged into tip so we don't care.
}
}
- Collections.sort(
- commits,
+ commits.sort(
ReviewDbUtil.intKeyOrdering().reverse().onResultOf(CodeReviewCommit::getPatchsetId));
CodeReviewCommit result = MergeUtil.findAnyMergedInto(rw, commits, tip);
if (result == null) {
diff --git a/java/com/google/gerrit/server/update/RetryHelper.java b/java/com/google/gerrit/server/update/RetryHelper.java
index 132c04b..10e3455 100644
--- a/java/com/google/gerrit/server/update/RetryHelper.java
+++ b/java/com/google/gerrit/server/update/RetryHelper.java
@@ -31,6 +31,7 @@
import com.google.common.base.Predicate;
import com.google.common.base.Throwables;
import com.google.common.collect.Maps;
+import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.metrics.Counter1;
@@ -52,6 +53,8 @@
@Singleton
public class RetryHelper {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
@FunctionalInterface
public interface ChangeAction<T> {
T call(BatchUpdate.Factory batchUpdateFactory) throws Exception;
@@ -277,6 +280,9 @@
retryerBuilder.withRetryListener(listener);
return executeWithTimeoutCount(actionType, action, retryerBuilder.build());
} finally {
+ if (listener.getAttemptCount() > 1) {
+ logger.atFine().log("%s was attempted %d times", actionType, listener.getAttemptCount());
+ }
metrics.attemptCounts.record(actionType, listener.getAttemptCount());
}
}
diff --git a/java/com/google/gerrit/sshd/BUILD b/java/com/google/gerrit/sshd/BUILD
index 4743b35..47b1d89 100644
--- a/java/com/google/gerrit/sshd/BUILD
+++ b/java/com/google/gerrit/sshd/BUILD
@@ -15,6 +15,7 @@
"//java/com/google/gerrit/server/cache/h2",
"//java/com/google/gerrit/server/git/receive",
"//java/com/google/gerrit/server/ioutil",
+ "//java/com/google/gerrit/server/logging",
"//java/com/google/gerrit/server/restapi",
"//java/com/google/gerrit/server/schema",
"//java/com/google/gerrit/util/cli",
diff --git a/java/com/google/gerrit/sshd/CommandFactoryProvider.java b/java/com/google/gerrit/sshd/CommandFactoryProvider.java
index 68ea7bb..1fdf7d8 100644
--- a/java/com/google/gerrit/sshd/CommandFactoryProvider.java
+++ b/java/com/google/gerrit/sshd/CommandFactoryProvider.java
@@ -22,7 +22,7 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.WorkQueue;
-import com.google.gerrit.server.logging.LoggingContextAwareThreadFactory;
+import com.google.gerrit.server.logging.LoggingContextAwareExecutorService;
import com.google.gerrit.sshd.SshScope.Context;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
@@ -78,12 +78,12 @@
int threads = cfg.getInt("sshd", "commandStartThreads", 2);
startExecutor = workQueue.createQueue(threads, "SshCommandStart", true);
destroyExecutor =
- Executors.newSingleThreadExecutor(
- new ThreadFactoryBuilder()
- .setThreadFactory(new LoggingContextAwareThreadFactory())
- .setNameFormat("SshCommandDestroy-%s")
- .setDaemon(true)
- .build());
+ new LoggingContextAwareExecutorService(
+ Executors.newSingleThreadExecutor(
+ new ThreadFactoryBuilder()
+ .setNameFormat("SshCommandDestroy-%s")
+ .setDaemon(true)
+ .build()));
}
@Override
diff --git a/java/com/google/gerrit/sshd/SshCommand.java b/java/com/google/gerrit/sshd/SshCommand.java
index 96d89ab..99c8724 100644
--- a/java/com/google/gerrit/sshd/SshCommand.java
+++ b/java/com/google/gerrit/sshd/SshCommand.java
@@ -14,7 +14,6 @@
package com.google.gerrit.sshd;
-import com.google.gerrit.server.logging.RequestId;
import com.google.gerrit.server.logging.TraceContext;
import java.io.IOException;
import java.io.PrintWriter;
@@ -25,6 +24,9 @@
@Option(name = "--trace", usage = "enable request tracing")
private boolean trace;
+ @Option(name = "--trace-id", usage = "trace ID (can only be set if --trace was set too)")
+ private String traceId;
+
protected PrintWriter stdout;
protected PrintWriter stderr;
@@ -49,12 +51,13 @@
protected abstract void run() throws UnloggedFailure, Failure, Exception;
- private TraceContext enableTracing() {
- if (trace) {
- RequestId traceId = new RequestId();
- stderr.println(String.format("%s: %s", RequestId.Type.TRACE_ID, traceId));
- return TraceContext.open().forceLogging().addTag(RequestId.Type.TRACE_ID, traceId);
+ private TraceContext enableTracing() throws UnloggedFailure {
+ if (!trace && traceId != null) {
+ throw die("A trace ID can only be set if --trace was specified.");
}
- return TraceContext.DISABLED;
+ return TraceContext.newTrace(
+ trace,
+ traceId,
+ (tagName, traceId) -> stderr.println(String.format("%s: %s", tagName, traceId)));
}
}
diff --git a/java/com/google/gerrit/sshd/SshKeyCacheImpl.java b/java/com/google/gerrit/sshd/SshKeyCacheImpl.java
index 3cd1a0c..81ce91d 100644
--- a/java/com/google/gerrit/sshd/SshKeyCacheImpl.java
+++ b/java/com/google/gerrit/sshd/SshKeyCacheImpl.java
@@ -86,6 +86,7 @@
@Override
public void evict(String username) {
if (username != null) {
+ logger.atFine().log("Evict SSH key for username %s", username);
cache.invalidate(username);
}
}
diff --git a/java/com/google/gerrit/sshd/commands/ShowConnections.java b/java/com/google/gerrit/sshd/commands/ShowConnections.java
index 9b517c6..baadf02 100644
--- a/java/com/google/gerrit/sshd/commands/ShowConnections.java
+++ b/java/com/google/gerrit/sshd/commands/ShowConnections.java
@@ -15,8 +15,10 @@
package com.google.gerrit.sshd.commands;
import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
+import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
@@ -33,11 +35,7 @@
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.Date;
-import java.util.List;
import java.util.Optional;
import org.apache.sshd.common.io.IoAcceptor;
import org.apache.sshd.common.io.IoSession;
@@ -92,25 +90,27 @@
throw new Failure(1, "fatal: sshd no longer running");
}
- final List<IoSession> list = new ArrayList<>(acceptor.getManagedSessions().values());
- Collections.sort(
- list,
- new Comparator<IoSession>() {
- @Override
- public int compare(IoSession arg0, IoSession arg1) {
- if (arg0 instanceof MinaSession) {
- MinaSession mArg0 = (MinaSession) arg0;
- MinaSession mArg1 = (MinaSession) arg1;
- if (mArg0.getSession().getCreationTime() < mArg1.getSession().getCreationTime()) {
- return -1;
- } else if (mArg0.getSession().getCreationTime()
- > mArg1.getSession().getCreationTime()) {
- return 1;
- }
- }
- return (int) (arg0.getId() - arg1.getId());
- }
- });
+ final ImmutableList<IoSession> list =
+ acceptor
+ .getManagedSessions()
+ .values()
+ .stream()
+ .sorted(
+ (arg0, arg1) -> {
+ if (arg0 instanceof MinaSession) {
+ MinaSession mArg0 = (MinaSession) arg0;
+ MinaSession mArg1 = (MinaSession) arg1;
+ if (mArg0.getSession().getCreationTime()
+ < mArg1.getSession().getCreationTime()) {
+ return -1;
+ } else if (mArg0.getSession().getCreationTime()
+ > mArg1.getSession().getCreationTime()) {
+ return 1;
+ }
+ }
+ return (int) (arg0.getId() - arg1.getId());
+ })
+ .collect(toImmutableList());
hostNameWidth = wide ? Integer.MAX_VALUE : columns - 9 - 9 - 10 - 32;
diff --git a/java/com/google/gerrit/sshd/commands/StreamEvents.java b/java/com/google/gerrit/sshd/commands/StreamEvents.java
index c97372c..ffd98d5 100644
--- a/java/com/google/gerrit/sshd/commands/StreamEvents.java
+++ b/java/com/google/gerrit/sshd/commands/StreamEvents.java
@@ -151,6 +151,7 @@
stdout = toPrintWriter(out);
eventListenerRegistration =
eventListeners.add(
+ "gerrit",
new UserScopedEventListener() {
@Override
public void onEvent(Event event) {
diff --git a/java/com/google/gerrit/testing/BUILD b/java/com/google/gerrit/testing/BUILD
index cf65908..15ceb77 100644
--- a/java/com/google/gerrit/testing/BUILD
+++ b/java/com/google/gerrit/testing/BUILD
@@ -29,6 +29,7 @@
"//java/com/google/gerrit/server/audit",
"//java/com/google/gerrit/server/cache/h2",
"//java/com/google/gerrit/server/cache/mem",
+ "//java/com/google/gerrit/server/logging",
"//java/com/google/gerrit/server/restapi",
"//java/com/google/gerrit/server/schema",
"//lib:guava",
diff --git a/java/com/google/gerrit/testing/ConfigSuite.java b/java/com/google/gerrit/testing/ConfigSuite.java
index ff87fd8..9e45b7c 100644
--- a/java/com/google/gerrit/testing/ConfigSuite.java
+++ b/java/com/google/gerrit/testing/ConfigSuite.java
@@ -159,7 +159,7 @@
@Override
public Object createTest() throws Exception {
- Object test = getTestClass().getJavaClass().newInstance();
+ Object test = getTestClass().getJavaClass().getDeclaredConstructor().newInstance();
parameterField.set(test, new org.eclipse.jgit.lib.Config(cfg));
if (nameField != null) {
nameField.set(test, name);
diff --git a/java/com/google/gwtexpui/globalkey/client/KeyHelpPopup.java b/java/com/google/gwtexpui/globalkey/client/KeyHelpPopup.java
index 1318125..20d093e 100644
--- a/java/com/google/gwtexpui/globalkey/client/KeyHelpPopup.java
+++ b/java/com/google/gwtexpui/globalkey/client/KeyHelpPopup.java
@@ -14,6 +14,9 @@
package com.google.gwtexpui.globalkey.client;
+import static java.util.Comparator.comparing;
+import static java.util.stream.Collectors.toList;
+
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
@@ -31,10 +34,7 @@
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwtexpui.safehtml.client.SafeHtml;
import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
@@ -228,15 +228,6 @@
}
private List<KeyCommand> sort(KeyCommandSet set) {
- final List<KeyCommand> keys = new ArrayList<>(set.getKeys());
- Collections.sort(
- keys,
- new Comparator<KeyCommand>() {
- @Override
- public int compare(KeyCommand arg0, KeyCommand arg1) {
- return arg0.getHelpText().compareTo(arg1.getHelpText());
- }
- });
- return keys;
+ return set.getKeys().stream().sorted(comparing(KeyCommand::getHelpText)).collect(toList());
}
}
diff --git a/java/com/google/gwtexpui/safehtml/client/HighlightSuggestOracle.java b/java/com/google/gwtexpui/safehtml/client/HighlightSuggestOracle.java
index ef80cdb..758521f 100644
--- a/java/com/google/gwtexpui/safehtml/client/HighlightSuggestOracle.java
+++ b/java/com/google/gwtexpui/safehtml/client/HighlightSuggestOracle.java
@@ -14,11 +14,12 @@
package com.google.gwtexpui.safehtml.client;
+import static java.util.Comparator.comparing;
+import static java.util.stream.Collectors.toList;
+
import com.google.gwt.user.client.ui.SuggestOracle;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
/**
@@ -115,15 +116,8 @@
* terms.
*/
private static List<String> splitQuery(String query) {
- List<String> queryTerms = Arrays.asList(query.split("\\s+"));
- Collections.sort(
- queryTerms,
- new Comparator<String>() {
- @Override
- public int compare(String s1, String s2) {
- return Integer.compare(s2.length(), s1.length());
- }
- });
+ List<String> queryTerms =
+ Arrays.stream(query.split("\\s+")).sorted(comparing(String::length)).collect(toList());
List<String> result = new ArrayList<>();
for (String s : queryTerms) {
diff --git a/javatests/com/google/gerrit/acceptance/TestGroupBackendTest.java b/javatests/com/google/gerrit/acceptance/TestGroupBackendTest.java
index 3c7b966..bf387fd 100644
--- a/javatests/com/google/gerrit/acceptance/TestGroupBackendTest.java
+++ b/javatests/com/google/gerrit/acceptance/TestGroupBackendTest.java
@@ -39,7 +39,7 @@
@Test
public void universalGroupBackendHandlesTestGroup() throws Exception {
- RegistrationHandle registrationHandle = groupBackends.add(testGroupBackend);
+ RegistrationHandle registrationHandle = groupBackends.add("gerrit", testGroupBackend);
try {
assertThat(universalGroupBackend.handles(testUUID)).isTrue();
} finally {
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index 05642c9..de66b87 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -230,7 +230,7 @@
@Before
public void addAccountIndexEventCounter() {
accountIndexedCounter = new AccountIndexedCounter();
- accountIndexEventCounterHandle = accountIndexedListeners.add(accountIndexedCounter);
+ accountIndexEventCounterHandle = accountIndexedListeners.add("gerrit", accountIndexedCounter);
}
@After
@@ -243,7 +243,7 @@
@Before
public void addRefUpdateCounter() {
refUpdateCounter = new RefUpdateCounter();
- refUpdateCounterHandle = refUpdateListeners.add(refUpdateCounter);
+ refUpdateCounterHandle = refUpdateListeners.add("gerrit", refUpdateCounter);
}
@After
@@ -526,12 +526,13 @@
@Test
public void validateAccountActivation() throws Exception {
- com.google.gerrit.acceptance.testsuite.account.TestAccount activatableAccount =
+ Account.Id activatableAccountId =
accountOperations.newAccount().inactive().preferredEmail("foo@activatable.com").create();
- com.google.gerrit.acceptance.testsuite.account.TestAccount deactivatableAccount =
+ Account.Id deactivatableAccountId =
accountOperations.newAccount().preferredEmail("foo@deactivatable.com").create();
RegistrationHandle registrationHandle =
accountActivationValidationListeners.add(
+ "gerrit",
new AccountActivationValidationListener() {
@Override
public void validateActivation(AccountState account) throws ValidationException {
@@ -553,61 +554,56 @@
/* Test account that can be activated, but not deactivated */
// Deactivate account that is already inactive
try {
- gApi.accounts().id(activatableAccount.accountId().get()).setActive(false);
+ gApi.accounts().id(activatableAccountId.get()).setActive(false);
fail("Expected exception");
} catch (ResourceConflictException e) {
assertThat(e.getMessage()).isEqualTo("account not active");
}
- assertThat(accountOperations.account(activatableAccount.accountId()).get().active())
- .isFalse();
+ assertThat(accountOperations.account(activatableAccountId).get().active()).isFalse();
// Activate account that can be activated
- gApi.accounts().id(activatableAccount.accountId().get()).setActive(true);
- assertThat(accountOperations.account(activatableAccount.accountId()).get().active()).isTrue();
+ gApi.accounts().id(activatableAccountId.get()).setActive(true);
+ assertThat(accountOperations.account(activatableAccountId).get().active()).isTrue();
// Activate account that is already active
- gApi.accounts().id(activatableAccount.accountId().get()).setActive(true);
- assertThat(accountOperations.account(activatableAccount.accountId()).get().active()).isTrue();
+ gApi.accounts().id(activatableAccountId.get()).setActive(true);
+ assertThat(accountOperations.account(activatableAccountId).get().active()).isTrue();
// Try deactivating account that cannot be deactivated
try {
- gApi.accounts().id(activatableAccount.accountId().get()).setActive(false);
+ gApi.accounts().id(activatableAccountId.get()).setActive(false);
fail("Expected exception");
} catch (ResourceConflictException e) {
assertThat(e.getMessage()).isEqualTo("not allowed to deactive account");
}
- assertThat(accountOperations.account(activatableAccount.accountId()).get().active()).isTrue();
+ assertThat(accountOperations.account(activatableAccountId).get().active()).isTrue();
/* Test account that can be deactivated, but not activated */
// Activate account that is already inactive
- gApi.accounts().id(deactivatableAccount.accountId().get()).setActive(true);
- assertThat(accountOperations.account(deactivatableAccount.accountId()).get().active())
- .isTrue();
+ gApi.accounts().id(deactivatableAccountId.get()).setActive(true);
+ assertThat(accountOperations.account(deactivatableAccountId).get().active()).isTrue();
// Deactivate account that can be deactivated
- gApi.accounts().id(deactivatableAccount.accountId().get()).setActive(false);
- assertThat(accountOperations.account(deactivatableAccount.accountId()).get().active())
- .isFalse();
+ gApi.accounts().id(deactivatableAccountId.get()).setActive(false);
+ assertThat(accountOperations.account(deactivatableAccountId).get().active()).isFalse();
// Deactivate account that is already inactive
try {
- gApi.accounts().id(deactivatableAccount.accountId().get()).setActive(false);
+ gApi.accounts().id(deactivatableAccountId.get()).setActive(false);
fail("Expected exception");
} catch (ResourceConflictException e) {
assertThat(e.getMessage()).isEqualTo("account not active");
}
- assertThat(accountOperations.account(deactivatableAccount.accountId()).get().active())
- .isFalse();
+ assertThat(accountOperations.account(deactivatableAccountId).get().active()).isFalse();
// Try activating account that cannot be activated
try {
- gApi.accounts().id(deactivatableAccount.accountId().get()).setActive(true);
+ gApi.accounts().id(deactivatableAccountId.get()).setActive(true);
fail("Expected exception");
} catch (ResourceConflictException e) {
assertThat(e.getMessage()).isEqualTo("not allowed to active account");
}
- assertThat(accountOperations.account(deactivatableAccount.accountId()).get().active())
- .isFalse();
+ assertThat(accountOperations.account(deactivatableAccountId).get().active()).isFalse();
} finally {
registrationHandle.remove();
}
@@ -2168,6 +2164,38 @@
}
@Test
+ public void createUserWithValidUsername() throws Exception {
+ ImmutableList<String> names =
+ ImmutableList.of(
+ "user@domain",
+ "user-name",
+ "user_name",
+ "1234",
+ "user1234",
+ "1234@domain",
+ "user!+alias{*}#$%&’^=~|@domain");
+ for (String name : names) {
+ gApi.accounts().create(name);
+ }
+ }
+
+ @Test
+ public void createUserWithInvalidUsername() throws Exception {
+ ImmutableList<String> invalidNames =
+ ImmutableList.of(
+ "@", "@foo", "-", "-foo", "_", "_foo", "!", "+", "{", "}", "*", "%", "#", "$", "&", "’",
+ "^", "=", "~");
+ for (String name : invalidNames) {
+ try {
+ gApi.accounts().create(name);
+ fail(String.format("Expected BadRequestException for username [%s]", name));
+ } catch (BadRequestException e) {
+ assertThat(e).hasMessageThat().isEqualTo(String.format("Invalid username '%s'", name));
+ }
+ }
+ }
+
+ @Test
public void allGroupsForAUserAccountCanBeRetrieved() throws Exception {
String username = name("user1");
accountOperations.newAccount().username(username).create();
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 6fd9545..b3a5e2d 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -68,7 +68,6 @@
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestProjectInput;
import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
-import com.google.gerrit.acceptance.testsuite.account.TestAccount;
import com.google.gerrit.common.FooterConstants;
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.common.data.LabelFunction;
@@ -203,7 +202,7 @@
@Before
public void addChangeIndexedCounter() {
changeIndexedCounter = new ChangeIndexedCounter();
- changeIndexedCounterHandle = changeIndexedListeners.add(changeIndexedCounter);
+ changeIndexedCounterHandle = changeIndexedListeners.add("gerrit", changeIndexedCounter);
}
@After
@@ -1688,7 +1687,7 @@
// create a group named "ab" with one user: testUser
String email = "abcd@test.com";
String fullname = "abcd";
- TestAccount testUser =
+ Account.Id accountIdOfTestUser =
accountOperations
.newAccount()
.username("abcd")
@@ -1721,7 +1720,7 @@
Collection<AccountInfo> reviewers = c.reviewers.get(REVIEWER);
assertThat(reviewers).isNotNull();
assertThat(reviewers).hasSize(1);
- assertThat(reviewers.iterator().next()._accountId).isEqualTo(testUser.accountId().get());
+ assertThat(reviewers.iterator().next()._accountId).isEqualTo(accountIdOfTestUser.get());
// Ensure ETag and lastUpdatedOn are updated.
rsrc = parseResource(r);
@@ -1748,7 +1747,7 @@
String myGroupUserEmail = "lee@test.com";
String myGroupUserFullname = "lee";
- TestAccount myGroupUser =
+ Account.Id accountIdOfGroupUser =
accountOperations
.newAccount()
.username("lee")
@@ -1785,7 +1784,7 @@
Collection<AccountInfo> reviewers = c.reviewers.get(REVIEWER);
assertThat(reviewers).isNotNull();
assertThat(reviewers).hasSize(1);
- assertThat(reviewers.iterator().next()._accountId).isEqualTo(myGroupUser.accountId().get());
+ assertThat(reviewers.iterator().next()._accountId).isEqualTo(accountIdOfGroupUser.get());
// Ensure ETag and lastUpdatedOn are updated.
rsrc = parseResource(r);
@@ -2215,13 +2214,12 @@
// notify unrelated account as TO
String email = "user2@example.com";
- TestAccount user2 =
- accountOperations
- .newAccount()
- .username("user2")
- .preferredEmail(email)
- .fullname("User2")
- .create();
+ accountOperations
+ .newAccount()
+ .username("user2")
+ .preferredEmail(email)
+ .fullname("User2")
+ .create();
setApiUser(user);
recommend(r.getChangeId());
setApiUser(admin);
@@ -2229,7 +2227,7 @@
in.notifyDetails = new HashMap<>();
in.notifyDetails.put(RecipientType.TO, new NotifyInfo(ImmutableList.of(email)));
gApi.changes().id(r.getChangeId()).reviewer(user.getId().toString()).deleteVote(in);
- assertNotifyTo(user2);
+ assertNotifyTo(email, "User2");
// notify unrelated account as CC
setApiUser(user);
@@ -2239,7 +2237,7 @@
in.notifyDetails = new HashMap<>();
in.notifyDetails.put(RecipientType.CC, new NotifyInfo(ImmutableList.of(email)));
gApi.changes().id(r.getChangeId()).reviewer(user.getId().toString()).deleteVote(in);
- assertNotifyCc(user2);
+ assertNotifyCc(email, "User2");
// notify unrelated account as BCC
setApiUser(user);
@@ -2249,7 +2247,7 @@
in.notifyDetails = new HashMap<>();
in.notifyDetails.put(RecipientType.BCC, new NotifyInfo(ImmutableList.of(email)));
gApi.changes().id(r.getChangeId()).reviewer(user.getId().toString()).deleteVote(in);
- assertNotifyBcc(user2);
+ assertNotifyBcc(email, "User2");
}
@Test
@@ -2599,6 +2597,7 @@
PushOneCommit.Result change = createChange();
RegistrationHandle handle =
changeMessageModifiers.add(
+ "gerrit",
new ChangeMessageModifier() {
@Override
public String onSubmit(
diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
index 4e3f048..d6be960 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
@@ -183,24 +183,23 @@
@Test
public void cachedGroupsForMemberAreUpdatedOnMemberAdditionAndRemoval() throws Exception {
String username = name("user");
- com.google.gerrit.acceptance.testsuite.account.TestAccount account =
- accountOperations.newAccount().username(username).create();
+ Account.Id accountId = accountOperations.newAccount().username(username).create();
// Fill the cache for the observed account.
- groupIncludeCache.getGroupsWithMember(account.accountId());
+ groupIncludeCache.getGroupsWithMember(accountId);
String groupName = createGroup("users");
AccountGroup.UUID groupUuid = new AccountGroup.UUID(gApi.groups().id(groupName).get().id);
gApi.groups().id(groupName).addMembers(username);
Collection<AccountGroup.UUID> groupsWithMemberAfterAddition =
- groupIncludeCache.getGroupsWithMember(account.accountId());
+ groupIncludeCache.getGroupsWithMember(accountId);
assertThat(groupsWithMemberAfterAddition).contains(groupUuid);
gApi.groups().id(groupName).removeMembers(username);
Collection<AccountGroup.UUID> groupsWithMemberAfterRemoval =
- groupIncludeCache.getGroupsWithMember(account.accountId());
+ groupIncludeCache.getGroupsWithMember(accountId);
assertThat(groupsWithMemberAfterRemoval).doesNotContain(groupUuid);
}
@@ -411,19 +410,17 @@
@Test
public void cachedGroupsForMemberAreUpdatedOnGroupCreation() throws Exception {
- com.google.gerrit.acceptance.testsuite.account.TestAccount account =
- accountOperations.newAccount().create();
+ Account.Id accountId = accountOperations.newAccount().create();
// Fill the cache for the observed account.
- groupIncludeCache.getGroupsWithMember(account.accountId());
+ groupIncludeCache.getGroupsWithMember(accountId);
GroupInput groupInput = new GroupInput();
groupInput.name = name("Users");
- groupInput.members = ImmutableList.of(String.valueOf(account.accountId().get()));
+ groupInput.members = ImmutableList.of(String.valueOf(accountId.get()));
GroupInfo group = gApi.groups().create(groupInput).get();
- Collection<AccountGroup.UUID> groups =
- groupIncludeCache.getGroupsWithMember(account.accountId());
+ Collection<AccountGroup.UUID> groups = groupIncludeCache.getGroupsWithMember(accountId);
assertThat(groups).containsExactly(new AccountGroup.UUID(group.id));
}
@@ -1298,7 +1295,7 @@
GroupIndexedCounter groupIndexedCounter = new GroupIndexedCounter();
RegistrationHandle groupIndexEventCounterHandle =
- groupIndexedListeners.add(groupIndexedCounter);
+ groupIndexedListeners.add("gerrit", groupIndexedCounter);
try {
// Running the reindexer right after startup should not need to reindex any group since
// reindexing was already done on startup.
@@ -1355,7 +1352,7 @@
GroupIndexedCounter groupIndexedCounter = new GroupIndexedCounter();
RegistrationHandle groupIndexEventCounterHandle =
- groupIndexedListeners.add(groupIndexedCounter);
+ groupIndexedListeners.add("gerrit", groupIndexedCounter);
try {
// No group indexing happened on startup. All groups should be reindexed now.
slaveGroupIndexer.run();
diff --git a/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java b/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java
index 2b1416a..e4194a3 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java
@@ -20,16 +20,18 @@
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.testsuite.group.GroupOperations;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.config.AccessCheckInfo;
import com.google.gerrit.extensions.api.config.AccessCheckInput;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
+import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.group.SystemGroupBackend;
+import com.google.inject.Inject;
import java.util.List;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
@@ -39,35 +41,29 @@
public class CheckAccessIT extends AbstractDaemonTest {
+ @Inject private GroupOperations groupOperations;
+
private Project.NameKey normalProject;
private Project.NameKey secretProject;
private Project.NameKey secretRefProject;
private TestAccount privilegedUser;
- private InternalGroup privilegedGroup;
@Before
public void setUp() throws Exception {
normalProject = createProject("normal");
secretProject = createProject("secret");
secretRefProject = createProject("secretRef");
- privilegedGroup = group(createGroup("privilegedGroup"));
+ AccountGroup.UUID privilegedGroupUuid =
+ groupOperations.newGroup().name(name("privilegedGroup")).create();
privilegedUser = accountCreator.create("privilegedUser", "snowden@nsa.gov", "Ed Snowden");
- gApi.groups().id(privilegedGroup.getGroupUUID().get()).addMembers(privilegedUser.username);
+ groupOperations.group(privilegedGroupUuid).forUpdate().addMember(privilegedUser.id).update();
- assertThat(gApi.groups().id(privilegedGroup.getGroupUUID().get()).members().get(0).email)
- .contains("snowden");
-
- grant(secretProject, "refs/*", Permission.READ, false, privilegedGroup.getGroupUUID());
+ grant(secretProject, "refs/*", Permission.READ, false, privilegedGroupUuid);
block(secretProject, "refs/*", Permission.READ, SystemGroupBackend.REGISTERED_USERS);
deny(secretRefProject, "refs/*", Permission.READ, SystemGroupBackend.ANONYMOUS_USERS);
- grant(
- secretRefProject,
- "refs/heads/secret/*",
- Permission.READ,
- false,
- privilegedGroup.getGroupUUID());
+ grant(secretRefProject, "refs/heads/secret/*", Permission.READ, false, privilegedGroupUuid);
block(
secretRefProject,
"refs/heads/secret/*",
@@ -81,13 +77,8 @@
SystemGroupBackend.REGISTERED_USERS);
// Ref permission
- grant(
- normalProject,
- "refs/*",
- Permission.VIEW_PRIVATE_CHANGES,
- false,
- privilegedGroup.getGroupUUID());
- grant(normalProject, "refs/*", Permission.FORGE_SERVER, false, privilegedGroup.getGroupUUID());
+ grant(normalProject, "refs/*", Permission.VIEW_PRIVATE_CHANGES, false, privilegedGroupUuid);
+ grant(normalProject, "refs/*", Permission.FORGE_SERVER, false, privilegedGroupUuid);
}
@Test
diff --git a/javatests/com/google/gerrit/acceptance/api/project/ProjectIT.java b/javatests/com/google/gerrit/acceptance/api/project/ProjectIT.java
index 5be8dfd..18888ea 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/ProjectIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/ProjectIT.java
@@ -16,6 +16,10 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.server.git.QueueProvider.QueueType.BATCH;
+import static com.google.gerrit.server.project.ProjectState.INHERITED_FROM_GLOBAL;
+import static com.google.gerrit.server.project.ProjectState.INHERITED_FROM_PARENT;
+import static com.google.gerrit.server.project.ProjectState.OVERRIDDEN_BY_GLOBAL;
+import static com.google.gerrit.server.project.ProjectState.OVERRIDDEN_BY_PARENT;
import static java.util.stream.Collectors.toSet;
import com.google.common.collect.ImmutableList;
@@ -68,7 +72,7 @@
@Before
public void addProjectIndexedCounter() {
projectIndexedCounter = new ProjectIndexedCounter();
- projectIndexedCounterHandle = projectIndexedListeners.add(projectIndexedCounter);
+ projectIndexedCounterHandle = projectIndexedListeners.add("gerrit", projectIndexedCounter);
}
@After
@@ -416,7 +420,7 @@
ConfigInfo info = getConfig();
assertThat(info.maxObjectSizeLimit.value).isNull();
assertThat(info.maxObjectSizeLimit.configuredValue).isNull();
- assertThat(info.maxObjectSizeLimit.inheritedValue).isNull();
+ assertThat(info.maxObjectSizeLimit.summary).isNull();
}
@Test
@@ -425,13 +429,30 @@
ConfigInfo info = setMaxObjectSize("100k");
assertThat(info.maxObjectSizeLimit.value).isEqualTo("102400");
assertThat(info.maxObjectSizeLimit.configuredValue).isEqualTo("100k");
- assertThat(info.maxObjectSizeLimit.inheritedValue).isNull();
+ assertThat(info.maxObjectSizeLimit.summary).isNull();
// Clear the value
info = setMaxObjectSize("0");
assertThat(info.maxObjectSizeLimit.value).isNull();
assertThat(info.maxObjectSizeLimit.configuredValue).isNull();
- assertThat(info.maxObjectSizeLimit.inheritedValue).isNull();
+ assertThat(info.maxObjectSizeLimit.summary).isNull();
+ }
+
+ @Test
+ @GerritConfig(name = "receive.inheritProjectMaxObjectSizeLimit", value = "true")
+ public void maxObjectSizeIsInheritedFromParentProject() throws Exception {
+ Project.NameKey child = createProject(name("child"), project);
+
+ ConfigInfo info = setMaxObjectSize("100k");
+ assertThat(info.maxObjectSizeLimit.value).isEqualTo("102400");
+ assertThat(info.maxObjectSizeLimit.configuredValue).isEqualTo("100k");
+ assertThat(info.maxObjectSizeLimit.summary).isNull();
+
+ info = getConfig(child);
+ assertThat(info.maxObjectSizeLimit.value).isEqualTo("102400");
+ assertThat(info.maxObjectSizeLimit.configuredValue).isNull();
+ assertThat(info.maxObjectSizeLimit.summary)
+ .isEqualTo(String.format(INHERITED_FROM_PARENT, project));
}
@Test
@@ -441,21 +462,75 @@
ConfigInfo info = setMaxObjectSize("100k");
assertThat(info.maxObjectSizeLimit.value).isEqualTo("102400");
assertThat(info.maxObjectSizeLimit.configuredValue).isEqualTo("100k");
- assertThat(info.maxObjectSizeLimit.inheritedValue).isNull();
+ assertThat(info.maxObjectSizeLimit.summary).isNull();
info = getConfig(child);
assertThat(info.maxObjectSizeLimit.value).isNull();
assertThat(info.maxObjectSizeLimit.configuredValue).isNull();
- assertThat(info.maxObjectSizeLimit.inheritedValue).isNull();
+ assertThat(info.maxObjectSizeLimit.summary).isNull();
+ }
+
+ @Test
+ public void maxObjectSizeOverridesParentProjectWhenNotSetOnParent() throws Exception {
+ Project.NameKey child = createProject(name("child"), project);
+
+ ConfigInfo info = setMaxObjectSize("0");
+ assertThat(info.maxObjectSizeLimit.value).isNull();
+ assertThat(info.maxObjectSizeLimit.configuredValue).isNull();
+ assertThat(info.maxObjectSizeLimit.summary).isNull();
+
+ info = setMaxObjectSize(child, "100k");
+ assertThat(info.maxObjectSizeLimit.value).isEqualTo("102400");
+ assertThat(info.maxObjectSizeLimit.configuredValue).isEqualTo("100k");
+ assertThat(info.maxObjectSizeLimit.summary).isNull();
+ }
+
+ @Test
+ public void maxObjectSizeOverridesParentProjectWhenLower() throws Exception {
+ Project.NameKey child = createProject(name("child"), project);
+
+ ConfigInfo info = setMaxObjectSize("200k");
+ assertThat(info.maxObjectSizeLimit.value).isEqualTo("204800");
+ assertThat(info.maxObjectSizeLimit.configuredValue).isEqualTo("200k");
+ assertThat(info.maxObjectSizeLimit.summary).isNull();
+
+ info = setMaxObjectSize(child, "100k");
+ assertThat(info.maxObjectSizeLimit.value).isEqualTo("102400");
+ assertThat(info.maxObjectSizeLimit.configuredValue).isEqualTo("100k");
+ assertThat(info.maxObjectSizeLimit.summary).isNull();
+ }
+
+ @Test
+ @GerritConfig(name = "receive.inheritProjectMaxObjectSizeLimit", value = "true")
+ public void maxObjectSizeDoesNotOverrideParentProjectWhenHigher() throws Exception {
+ Project.NameKey child = createProject(name("child"), project);
+
+ ConfigInfo info = setMaxObjectSize("100k");
+ assertThat(info.maxObjectSizeLimit.value).isEqualTo("102400");
+ assertThat(info.maxObjectSizeLimit.configuredValue).isEqualTo("100k");
+ assertThat(info.maxObjectSizeLimit.summary).isNull();
+
+ info = setMaxObjectSize(child, "200k");
+ assertThat(info.maxObjectSizeLimit.value).isEqualTo("102400");
+ assertThat(info.maxObjectSizeLimit.configuredValue).isEqualTo("200k");
+ assertThat(info.maxObjectSizeLimit.summary)
+ .isEqualTo(String.format(OVERRIDDEN_BY_PARENT, project));
}
@Test
@GerritConfig(name = "receive.maxObjectSizeLimit", value = "200k")
public void maxObjectSizeIsInheritedFromGlobalConfig() throws Exception {
+ Project.NameKey child = createProject(name("child"), project);
+
ConfigInfo info = getConfig();
assertThat(info.maxObjectSizeLimit.value).isEqualTo("204800");
assertThat(info.maxObjectSizeLimit.configuredValue).isNull();
- assertThat(info.maxObjectSizeLimit.inheritedValue).isEqualTo("200k");
+ assertThat(info.maxObjectSizeLimit.summary).isEqualTo(INHERITED_FROM_GLOBAL);
+
+ info = getConfig(child);
+ assertThat(info.maxObjectSizeLimit.value).isEqualTo("204800");
+ assertThat(info.maxObjectSizeLimit.configuredValue).isNull();
+ assertThat(info.maxObjectSizeLimit.summary).isEqualTo(INHERITED_FROM_GLOBAL);
}
@Test
@@ -464,16 +539,40 @@
ConfigInfo info = setMaxObjectSize("100k");
assertThat(info.maxObjectSizeLimit.value).isEqualTo("102400");
assertThat(info.maxObjectSizeLimit.configuredValue).isEqualTo("100k");
- assertThat(info.maxObjectSizeLimit.inheritedValue).isEqualTo("200k");
+ assertThat(info.maxObjectSizeLimit.summary).isNull();
+ }
+
+ @Test
+ @GerritConfig(name = "receive.maxObjectSizeLimit", value = "300k")
+ public void inheritedMaxObjectSizeOverridesGlobalConfigWhenLower() throws Exception {
+ Project.NameKey child = createProject(name("child"), project);
+
+ ConfigInfo info = setMaxObjectSize("200k");
+ assertThat(info.maxObjectSizeLimit.value).isEqualTo("204800");
+ assertThat(info.maxObjectSizeLimit.configuredValue).isEqualTo("200k");
+ assertThat(info.maxObjectSizeLimit.summary).isNull();
+
+ info = setMaxObjectSize(child, "100k");
+ assertThat(info.maxObjectSizeLimit.value).isEqualTo("102400");
+ assertThat(info.maxObjectSizeLimit.configuredValue).isEqualTo("100k");
+ assertThat(info.maxObjectSizeLimit.summary).isNull();
}
@Test
@GerritConfig(name = "receive.maxObjectSizeLimit", value = "200k")
+ @GerritConfig(name = "receive.inheritProjectMaxObjectSizeLimit", value = "true")
public void maxObjectSizeDoesNotOverrideGlobalConfigWhenHigher() throws Exception {
+ Project.NameKey child = createProject(name("child"), project);
+
ConfigInfo info = setMaxObjectSize("300k");
assertThat(info.maxObjectSizeLimit.value).isEqualTo("204800");
assertThat(info.maxObjectSizeLimit.configuredValue).isEqualTo("300k");
- assertThat(info.maxObjectSizeLimit.inheritedValue).isEqualTo("200k");
+ assertThat(info.maxObjectSizeLimit.summary).isEqualTo(OVERRIDDEN_BY_GLOBAL);
+
+ info = getConfig(child);
+ assertThat(info.maxObjectSizeLimit.value).isEqualTo("204800");
+ assertThat(info.maxObjectSizeLimit.configuredValue).isNull();
+ assertThat(info.maxObjectSizeLimit.summary).isEqualTo(OVERRIDDEN_BY_GLOBAL);
}
@Test
@@ -487,10 +586,6 @@
return gApi.projects().name(name.get()).config(input);
}
- private ConfigInfo setConfig(ConfigInput input) throws Exception {
- return setConfig(project, input);
- }
-
private ConfigInfo getConfig(Project.NameKey name) throws Exception {
return gApi.projects().name(name.get()).config();
}
@@ -517,9 +612,13 @@
}
private ConfigInfo setMaxObjectSize(String value) throws Exception {
+ return setMaxObjectSize(project, value);
+ }
+
+ private ConfigInfo setMaxObjectSize(Project.NameKey name, String value) throws Exception {
ConfigInput input = new ConfigInput();
input.maxObjectSizeLimit = value;
- return setConfig(input);
+ return setConfig(name, input);
}
private static class ProjectIndexedCounter implements ProjectIndexedListener {
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
index 8a3d0f3..ca4304e 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
@@ -920,6 +920,7 @@
CountDownLatch reindexed = new CountDownLatch(1);
RegistrationHandle handle =
changeIndexedListeners.add(
+ "gerrit",
new ChangeIndexedListener() {
@Override
public void onChangeIndexed(String projectName, int id) {
@@ -1086,6 +1087,7 @@
WebLinkInfo expectedWebLinkInfo = new WebLinkInfo("foo", "imageUrl", "url");
RegistrationHandle handle =
patchSetLinks.add(
+ "gerrit",
new PatchSetWebLink() {
@Override
public WebLinkInfo getPatchSetWebLink(String projectName, String commit) {
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
index 7ad34a6..f1e67c1 100644
--- a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
@@ -213,6 +213,24 @@
}
@Test
+ @TestProjectInput(createEmptyCommit = false)
+ public void validateConnected() throws Exception {
+ RevCommit c = testRepo.commit().message("Initial commit").insertChangeId().create();
+ testRepo.reset(c);
+
+ String r = "refs/heads/master";
+ PushResult pr = pushHead(testRepo, r, false);
+ assertPushOk(pr, r);
+
+ RevCommit amended =
+ testRepo.amend(c).message("different initial commit").insertChangeId().create();
+ testRepo.reset(amended);
+ r = "refs/for/master";
+ pr = pushHead(testRepo, r, false);
+ assertPushRejected(pr, r, "no common ancestry");
+ }
+
+ @Test
public void pushInitialCommitForRefsMetaConfigBranch() throws Exception {
// delete refs/meta/config
try (Repository repo = repoManager.openRepository(project);
diff --git a/javatests/com/google/gerrit/acceptance/pgm/ElasticReindexIT.java b/javatests/com/google/gerrit/acceptance/pgm/ElasticReindexIT.java
index c90b3d3..0d5d2cd 100644
--- a/javatests/com/google/gerrit/acceptance/pgm/ElasticReindexIT.java
+++ b/javatests/com/google/gerrit/acceptance/pgm/ElasticReindexIT.java
@@ -47,13 +47,8 @@
}
@ConfigSuite.Config
- public static Config elasticsearchV6_2() {
- return getConfig(ElasticVersion.V6_2);
- }
-
- @ConfigSuite.Config
- public static Config elasticsearchV6_3() {
- return getConfig(ElasticVersion.V6_3);
+ public static Config elasticsearchV6() {
+ return getConfig(ElasticVersion.V6_4);
}
@Override
diff --git a/javatests/com/google/gerrit/acceptance/rest/BUILD b/javatests/com/google/gerrit/acceptance/rest/BUILD
index b4940bc..b94a98d 100644
--- a/javatests/com/google/gerrit/acceptance/rest/BUILD
+++ b/javatests/com/google/gerrit/acceptance/rest/BUILD
@@ -4,7 +4,10 @@
srcs = glob(["*IT.java"]),
group = "rest_bindings",
labels = ["rest"],
- deps = [":util"],
+ deps = [
+ ":util",
+ "//java/com/google/gerrit/server/logging",
+ ],
)
java_library(
diff --git a/javatests/com/google/gerrit/acceptance/rest/TraceIT.java b/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
index 1369eb3..137dc21 100644
--- a/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
@@ -18,6 +18,9 @@
import static org.apache.http.HttpStatus.SC_CREATED;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.truth.Expect;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.RestResponse;
@@ -26,22 +29,31 @@
import com.google.gerrit.httpd.restapi.ParameterParser;
import com.google.gerrit.httpd.restapi.RestApiServlet;
import com.google.gerrit.server.events.CommitReceivedEvent;
+import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.server.git.validators.CommitValidationException;
import com.google.gerrit.server.git.validators.CommitValidationListener;
import com.google.gerrit.server.git.validators.CommitValidationMessage;
import com.google.gerrit.server.logging.LoggingContext;
+import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.project.CreateProjectArgs;
import com.google.gerrit.server.validators.ProjectCreationValidationListener;
import com.google.gerrit.server.validators.ValidationException;
import com.google.inject.Inject;
import java.util.List;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import org.apache.http.message.BasicHeader;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
public class TraceIT extends AbstractDaemonTest {
+ @Rule public final Expect expect = Expect.create();
+
@Inject private DynamicSet<ProjectCreationValidationListener> projectCreationValidationListeners;
@Inject private DynamicSet<CommitValidationListener> commitValidationListeners;
+ @Inject private WorkQueue workQueue;
private TraceValidatingProjectCreationValidationListener projectCreationListener;
private RegistrationHandle projectCreationListenerRegistrationHandle;
@@ -52,9 +64,10 @@
public void setup() {
projectCreationListener = new TraceValidatingProjectCreationValidationListener();
projectCreationListenerRegistrationHandle =
- projectCreationValidationListeners.add(projectCreationListener);
+ projectCreationValidationListeners.add("gerrit", projectCreationListener);
commitValidationListener = new TraceValidatingCommitValidationListener();
- commitValidationRegistrationHandle = commitValidationListeners.add(commitValidationListener);
+ commitValidationRegistrationHandle =
+ commitValidationListeners.add("gerrit", commitValidationListener);
}
@After
@@ -68,38 +81,92 @@
RestResponse response = adminRestSession.put("/projects/new1");
assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
- assertThat(projectCreationListener.foundTraceId).isFalse();
+ assertThat(projectCreationListener.traceId).isNull();
assertThat(projectCreationListener.isLoggingForced).isFalse();
}
@Test
- public void restCallWithTrace() throws Exception {
+ public void restCallWithTraceRequestParam() throws Exception {
RestResponse response =
adminRestSession.put("/projects/new2?" + ParameterParser.TRACE_PARAMETER);
assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNotNull();
- assertThat(projectCreationListener.foundTraceId).isTrue();
+ assertThat(projectCreationListener.traceId).isNotNull();
assertThat(projectCreationListener.isLoggingForced).isTrue();
}
@Test
- public void restCallWithTraceTrue() throws Exception {
+ public void restCallWithTraceRequestParamAndProvidedTraceId() throws Exception {
RestResponse response =
- adminRestSession.put("/projects/new3?" + ParameterParser.TRACE_PARAMETER + "=true");
+ adminRestSession.put("/projects/new3?" + ParameterParser.TRACE_PARAMETER + "=issue/123");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isEqualTo("issue/123");
+ assertThat(projectCreationListener.traceId).isEqualTo("issue/123");
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+ }
+
+ @Test
+ public void restCallWithTraceHeader() throws Exception {
+ RestResponse response =
+ adminRestSession.putWithHeader(
+ "/projects/new4", new BasicHeader(RestApiServlet.X_GERRIT_TRACE, null));
assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNotNull();
- assertThat(projectCreationListener.foundTraceId).isTrue();
+ assertThat(projectCreationListener.traceId).isNotNull();
assertThat(projectCreationListener.isLoggingForced).isTrue();
}
@Test
- public void restCallWithTraceFalse() throws Exception {
+ public void restCallWithTraceHeaderAndProvidedTraceId() throws Exception {
RestResponse response =
- adminRestSession.put("/projects/new4?" + ParameterParser.TRACE_PARAMETER + "=false");
+ adminRestSession.putWithHeader(
+ "/projects/new5", new BasicHeader(RestApiServlet.X_GERRIT_TRACE, "issue/123"));
assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
- assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
- assertThat(projectCreationListener.foundTraceId).isFalse();
- assertThat(projectCreationListener.isLoggingForced).isFalse();
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isEqualTo("issue/123");
+ assertThat(projectCreationListener.traceId).isEqualTo("issue/123");
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+ }
+
+ @Test
+ public void restCallWithTraceRequestParamAndTraceHeader() throws Exception {
+ // trace ID only specified by trace header
+ RestResponse response =
+ adminRestSession.putWithHeader(
+ "/projects/new6?trace", new BasicHeader(RestApiServlet.X_GERRIT_TRACE, "issue/123"));
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isEqualTo("issue/123");
+ assertThat(projectCreationListener.traceId).isEqualTo("issue/123");
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+
+ // trace ID only specified by trace request parameter
+ response =
+ adminRestSession.putWithHeader(
+ "/projects/new7?trace=issue/123", new BasicHeader(RestApiServlet.X_GERRIT_TRACE, null));
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isEqualTo("issue/123");
+ assertThat(projectCreationListener.traceId).isEqualTo("issue/123");
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+
+ // same trace ID specified by trace header and trace request parameter
+ response =
+ adminRestSession.putWithHeader(
+ "/projects/new8?trace=issue/123",
+ new BasicHeader(RestApiServlet.X_GERRIT_TRACE, "issue/123"));
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isEqualTo("issue/123");
+ assertThat(projectCreationListener.traceId).isEqualTo("issue/123");
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+
+ // different trace IDs specified by trace header and trace request parameter
+ response =
+ adminRestSession.putWithHeader(
+ "/projects/new9?trace=issue/123",
+ new BasicHeader(RestApiServlet.X_GERRIT_TRACE, "issue/456"));
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeaders(RestApiServlet.X_GERRIT_TRACE))
+ .containsExactly("issue/123", "issue/456");
+ assertThat(projectCreationListener.traceIds).containsExactly("issue/123", "issue/456");
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
}
@Test
@@ -107,7 +174,7 @@
PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
PushOneCommit.Result r = push.to("refs/heads/master");
r.assertOkStatus();
- assertThat(commitValidationListener.foundTraceId).isFalse();
+ assertThat(commitValidationListener.traceId).isNull();
assertThat(commitValidationListener.isLoggingForced).isFalse();
}
@@ -117,36 +184,26 @@
push.setPushOptions(ImmutableList.of("trace"));
PushOneCommit.Result r = push.to("refs/heads/master");
r.assertOkStatus();
- assertThat(commitValidationListener.foundTraceId).isTrue();
+ assertThat(commitValidationListener.traceId).isNotNull();
assertThat(commitValidationListener.isLoggingForced).isTrue();
}
@Test
- public void pushWithTraceTrue() throws Exception {
+ public void pushWithTraceAndProvidedTraceId() throws Exception {
PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
- push.setPushOptions(ImmutableList.of("trace=true"));
+ push.setPushOptions(ImmutableList.of("trace=issue/123"));
PushOneCommit.Result r = push.to("refs/heads/master");
r.assertOkStatus();
- assertThat(commitValidationListener.foundTraceId).isTrue();
+ assertThat(commitValidationListener.traceId).isEqualTo("issue/123");
assertThat(commitValidationListener.isLoggingForced).isTrue();
}
@Test
- public void pushWithTraceFalse() throws Exception {
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
- push.setPushOptions(ImmutableList.of("trace=false"));
- PushOneCommit.Result r = push.to("refs/heads/master");
- r.assertOkStatus();
- assertThat(commitValidationListener.foundTraceId).isFalse();
- assertThat(commitValidationListener.isLoggingForced).isFalse();
- }
-
- @Test
public void pushForReviewWithoutTrace() throws Exception {
PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
PushOneCommit.Result r = push.to("refs/for/master");
r.assertOkStatus();
- assertThat(commitValidationListener.foundTraceId).isFalse();
+ assertThat(commitValidationListener.traceId).isNull();
assertThat(commitValidationListener.isLoggingForced).isFalse();
}
@@ -156,50 +213,85 @@
push.setPushOptions(ImmutableList.of("trace"));
PushOneCommit.Result r = push.to("refs/for/master");
r.assertOkStatus();
- assertThat(commitValidationListener.foundTraceId).isTrue();
+ assertThat(commitValidationListener.traceId).isNotNull();
assertThat(commitValidationListener.isLoggingForced).isTrue();
}
@Test
- public void pushForReviewWithTraceTrue() throws Exception {
+ public void pushForReviewWithTraceAndProvidedTraceId() throws Exception {
PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
- push.setPushOptions(ImmutableList.of("trace=true"));
+ push.setPushOptions(ImmutableList.of("trace=issue/123"));
PushOneCommit.Result r = push.to("refs/for/master");
r.assertOkStatus();
- assertThat(commitValidationListener.foundTraceId).isTrue();
+ assertThat(commitValidationListener.traceId).isEqualTo("issue/123");
assertThat(commitValidationListener.isLoggingForced).isTrue();
}
@Test
- public void pushForReviewWithTraceFalse() throws Exception {
- PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
- push.setPushOptions(ImmutableList.of("trace=false"));
- PushOneCommit.Result r = push.to("refs/for/master");
- r.assertOkStatus();
- assertThat(commitValidationListener.foundTraceId).isFalse();
- assertThat(commitValidationListener.isLoggingForced).isFalse();
+ public void workQueueCopyLoggingContext() throws Exception {
+ assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
+ assertForceLogging(false);
+ try (TraceContext traceContext = TraceContext.open().forceLogging().addTag("foo", "bar")) {
+ SortedMap<String, SortedSet<Object>> tagMap = LoggingContext.getInstance().getTags().asMap();
+ assertThat(tagMap.keySet()).containsExactly("foo");
+ assertThat(tagMap.get("foo")).containsExactly("bar");
+ assertForceLogging(true);
+
+ workQueue
+ .createQueue(1, "test-queue")
+ .submit(
+ () -> {
+ // Verify that the tags and force logging flag have been propagated to the new
+ // thread.
+ SortedMap<String, SortedSet<Object>> threadTagMap =
+ LoggingContext.getInstance().getTags().asMap();
+ expect.that(threadTagMap.keySet()).containsExactly("foo");
+ expect.that(threadTagMap.get("foo")).containsExactly("bar");
+ expect
+ .that(LoggingContext.getInstance().shouldForceLogging(null, null, false))
+ .isTrue();
+ })
+ .get();
+
+ // Verify that tags and force logging flag in the outer thread are still set.
+ tagMap = LoggingContext.getInstance().getTags().asMap();
+ assertThat(tagMap.keySet()).containsExactly("foo");
+ assertThat(tagMap.get("foo")).containsExactly("bar");
+ assertForceLogging(true);
+ }
+ assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
+ assertForceLogging(false);
+ }
+
+ private void assertForceLogging(boolean expected) {
+ assertThat(LoggingContext.getInstance().shouldForceLogging(null, null, false))
+ .isEqualTo(expected);
}
private static class TraceValidatingProjectCreationValidationListener
implements ProjectCreationValidationListener {
- Boolean foundTraceId;
+ String traceId;
+ ImmutableSet<String> traceIds;
Boolean isLoggingForced;
@Override
public void validateNewProject(CreateProjectArgs args) throws ValidationException {
- this.foundTraceId = LoggingContext.getInstance().getTagsAsMap().containsKey("TRACE_ID");
+ this.traceId =
+ Iterables.getFirst(LoggingContext.getInstance().getTagsAsMap().get("TRACE_ID"), null);
+ this.traceIds = LoggingContext.getInstance().getTagsAsMap().get("TRACE_ID");
this.isLoggingForced = LoggingContext.getInstance().shouldForceLogging(null, null, false);
}
}
private static class TraceValidatingCommitValidationListener implements CommitValidationListener {
- Boolean foundTraceId;
+ String traceId;
Boolean isLoggingForced;
@Override
public List<CommitValidationMessage> onCommitReceived(CommitReceivedEvent receiveEvent)
throws CommitValidationException {
- this.foundTraceId = LoggingContext.getInstance().getTagsAsMap().containsKey("TRACE_ID");
+ this.traceId =
+ Iterables.getFirst(LoggingContext.getInstance().getTagsAsMap().get("TRACE_ID"), null);
this.isLoggingForced = LoggingContext.getInstance().shouldForceLogging(null, null, false);
return ImmutableList.of();
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java b/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
index a9182ef..b74a0d7 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
@@ -60,7 +60,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
@@ -105,8 +104,6 @@
.fromJson(
response.getReader(), new TypeToken<List<AccountExternalIdInfo>>() {}.getType());
- Collections.sort(expectedIdInfos);
- Collections.sort(results);
assertThat(results).containsExactlyElementsIn(expectedIdInfos);
}
@@ -133,8 +130,6 @@
.fromJson(
response.getReader(), new TypeToken<List<AccountExternalIdInfo>>() {}.getType());
- Collections.sort(expectedIdInfos);
- Collections.sort(results);
assertThat(results).containsExactlyElementsIn(expectedIdInfos);
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
index 5580279..af11149 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
@@ -1324,7 +1324,7 @@
protected void addOnSubmitValidationListener(OnSubmitValidationListener listener) {
assertThat(onSubmitValidatorHandle).isNull();
- onSubmitValidatorHandle = onSubmitValidationListeners.add(listener);
+ onSubmitValidatorHandle = onSubmitValidationListeners.add("gerrit", listener);
}
private String getLatestDiff(Repository repo) throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ActionsIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ActionsIT.java
index 171babd..f45f9dc 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ActionsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ActionsIT.java
@@ -331,7 +331,7 @@
assertThat(origActions.get("abandon").label).isEqualTo("Abandon");
Visitor v = new Visitor();
- visitorHandle = actionVisitors.add(v);
+ visitorHandle = actionVisitors.add("gerrit", v);
Map<String, ActionInfo> newActions =
gApi.changes().id(id).get(EnumSet.of(ListChangesOption.CHANGE_ACTIONS)).actions;
@@ -380,7 +380,7 @@
assertThat(origActions.get("rebase").label).isEqualTo("Rebase");
Visitor v = new Visitor();
- visitorHandle = actionVisitors.add(v);
+ visitorHandle = actionVisitors.add("gerrit", v);
// Test different codepaths within ActionJson...
// ...via revision API.
@@ -443,7 +443,7 @@
assertThat(origActions.get("description").label).isEqualTo("Edit Description");
Visitor v = new Visitor();
- visitorHandle = actionVisitors.add(v);
+ visitorHandle = actionVisitors.add("gerrit", v);
// Unlike for the current revision, actions for old revisions are only available via the
// revision API.
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
index 8cd1770..2182b2f 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
@@ -97,6 +97,7 @@
PushOneCommit.Result change = createChange();
RegistrationHandle handle =
changeMessageModifiers.add(
+ "gerrit",
new ChangeMessageModifier() {
@Override
public String onSubmit(
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java
index e8b8fe8..3d8d06e 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java
@@ -87,6 +87,7 @@
RegistrationHandle handle =
changeMessageModifiers.add(
+ "gerrit",
new ChangeMessageModifier() {
@Override
public String onSubmit(
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/AccessIT.java b/javatests/com/google/gerrit/acceptance/rest/project/AccessIT.java
index 3534959..a64305c 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/AccessIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/AccessIT.java
@@ -89,6 +89,7 @@
public void webLink() throws Exception {
RegistrationHandle handle =
fileHistoryWebLinkDynamicSet.add(
+ "gerrit",
new FileHistoryWebLink() {
@Override
public WebLinkInfo getFileHistoryWebLink(
@@ -111,6 +112,7 @@
public void webLinkNoRefsMetaConfig() throws Exception {
RegistrationHandle handle =
fileHistoryWebLinkDynamicSet.add(
+ "gerrit",
new FileHistoryWebLink() {
@Override
public WebLinkInfo getFileHistoryWebLink(
diff --git a/javatests/com/google/gerrit/acceptance/server/event/CommentAddedEventIT.java b/javatests/com/google/gerrit/acceptance/server/event/CommentAddedEventIT.java
index 14b3858..f4a833f 100644
--- a/javatests/com/google/gerrit/acceptance/server/event/CommentAddedEventIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/event/CommentAddedEventIT.java
@@ -76,6 +76,7 @@
eventListenerRegistration =
source.add(
+ "gerrit",
new CommentAddedListener() {
@Override
public void onCommentAdded(Event event) {
diff --git a/javatests/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java b/javatests/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java
index a5d78c6..87c5ace 100644
--- a/javatests/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java
@@ -610,7 +610,7 @@
}
private void addListener(NotesMigrationStateListener listener) {
- addedListeners.add(listeners.add(listener));
+ addedListeners.add(listeners.add("gerrit", listener));
}
private ImmutableSortedSet<String> getObjectFiles(Project.NameKey project) throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/server/project/CustomLabelIT.java b/javatests/com/google/gerrit/acceptance/server/project/CustomLabelIT.java
index 45b7767..8b0c56b 100644
--- a/javatests/com/google/gerrit/acceptance/server/project/CustomLabelIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/project/CustomLabelIT.java
@@ -82,6 +82,7 @@
eventListenerRegistration =
source.add(
+ "gerrit",
new CommentAddedListener() {
@Override
public void onCommentAdded(Event event) {
diff --git a/javatests/com/google/gerrit/acceptance/server/rules/IgnoreSelfApprovalRuleIT.java b/javatests/com/google/gerrit/acceptance/server/rules/IgnoreSelfApprovalRuleIT.java
new file mode 100644
index 0000000..d1b05e3
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/server/rules/IgnoreSelfApprovalRuleIT.java
@@ -0,0 +1,99 @@
+// Copyright (C) 2018 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.acceptance.server.rules;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.common.data.SubmitRequirement;
+import com.google.gerrit.server.project.SubmitRuleOptions;
+import com.google.gerrit.server.rules.IgnoreSelfApprovalRule;
+import com.google.inject.Inject;
+import java.util.Collection;
+import java.util.Map;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.junit.TestRepository;
+import org.junit.Test;
+
+@NoHttpd
+public class IgnoreSelfApprovalRuleIT extends AbstractDaemonTest {
+ @Inject private IgnoreSelfApprovalRule rule;
+
+ @Test
+ public void blocksWhenUploaderIsOnlyApprover() throws Exception {
+ enableRule("Code-Review", true);
+
+ PushOneCommit.Result r = createChange();
+ approve(r.getChangeId());
+
+ Collection<SubmitRecord> submitRecords =
+ rule.evaluate(r.getChange(), SubmitRuleOptions.defaults());
+
+ assertThat(submitRecords).hasSize(1);
+ SubmitRecord result = submitRecords.iterator().next();
+ assertThat(result.status).isEqualTo(SubmitRecord.Status.NOT_READY);
+ assertThat(result.labels).isNotEmpty();
+ assertThat(result.requirements)
+ .containsExactly(
+ SubmitRequirement.builder()
+ .setFallbackText("Approval from non-uploader required")
+ .setType("non_uploader_approval")
+ .build());
+ }
+
+ @Test
+ public void allowsSubmissionWhenChangeHasNonUploaderApproval() throws Exception {
+ enableRule("Code-Review", true);
+
+ // Create change as user
+ TestRepository<InMemoryRepository> userTestRepo = cloneProject(project, user);
+ PushOneCommit push = pushFactory.create(db, user.getIdent(), userTestRepo);
+ PushOneCommit.Result r = push.to("refs/for/master");
+
+ // Approve as admin
+ approve(r.getChangeId());
+
+ Collection<SubmitRecord> submitRecords =
+ rule.evaluate(r.getChange(), SubmitRuleOptions.defaults());
+ assertThat(submitRecords).isEmpty();
+ }
+
+ @Test
+ public void doesNothingByDefault() throws Exception {
+ enableRule("Code-Review", false);
+
+ PushOneCommit.Result r = createChange();
+ approve(r.getChangeId());
+
+ Collection<SubmitRecord> submitRecords =
+ rule.evaluate(r.getChange(), SubmitRuleOptions.defaults());
+ assertThat(submitRecords).isEmpty();
+ }
+
+ private void enableRule(String labelName, boolean newState) throws Exception {
+ try (ProjectConfigUpdate u = updateProject(project)) {
+ Map<String, LabelType> localLabelSections = u.getConfig().getLabelSections();
+ if (localLabelSections.isEmpty()) {
+ localLabelSections.putAll(projectCache.getAllProjects().getConfig().getLabelSections());
+ }
+ localLabelSections.get(labelName).setIgnoreSelfApproval(newState);
+ u.save();
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/ssh/AbstractIndexTests.java b/javatests/com/google/gerrit/acceptance/ssh/AbstractIndexTests.java
index 25bb7a6..ed3cdbc 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/AbstractIndexTests.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/AbstractIndexTests.java
@@ -50,7 +50,7 @@
@Before
public void addChangeIndexedCounter() {
changeIndexedCounter = new ChangeIndexedCounter();
- changeIndexedCounterHandle = changeIndexedListeners.add(changeIndexedCounter);
+ changeIndexedCounterHandle = changeIndexedListeners.add("gerrit", changeIndexedCounter);
}
@After
diff --git a/javatests/com/google/gerrit/acceptance/ssh/BUILD b/javatests/com/google/gerrit/acceptance/ssh/BUILD
index b195ecc..a01cd3e 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/BUILD
+++ b/javatests/com/google/gerrit/acceptance/ssh/BUILD
@@ -17,6 +17,7 @@
vm_args = ["-Xmx512m"],
deps = [
":util",
+ "//java/com/google/gerrit/server/logging",
"//lib/commons:compress",
],
)
diff --git a/javatests/com/google/gerrit/acceptance/ssh/ElasticIndexIT.java b/javatests/com/google/gerrit/acceptance/ssh/ElasticIndexIT.java
index 95da5a6..9d69955 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/ElasticIndexIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/ElasticIndexIT.java
@@ -46,13 +46,8 @@
}
@ConfigSuite.Config
- public static Config elasticsearchV6_2() {
- return getConfig(ElasticVersion.V6_2);
- }
-
- @ConfigSuite.Config
- public static Config elasticsearchV6_3() {
- return getConfig(ElasticVersion.V6_3);
+ public static Config elasticsearchV6() {
+ return getConfig(ElasticVersion.V6_4);
}
@Override
diff --git a/javatests/com/google/gerrit/acceptance/ssh/SshCommandsIT.java b/javatests/com/google/gerrit/acceptance/ssh/SshCommandsIT.java
index 126b0ee..e603413 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/SshCommandsIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/SshCommandsIT.java
@@ -14,12 +14,14 @@
package com.google.gerrit.acceptance.ssh;
+import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Streams;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
@@ -28,7 +30,6 @@
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.sshd.Commands;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.junit.Test;
@@ -82,16 +83,9 @@
private static final ImmutableMap<String, List<String>> MASTER_COMMANDS =
ImmutableMap.of(
Commands.ROOT,
- ImmutableList.copyOf(
- new ArrayList<String>() {
- private static final long serialVersionUID = 1L;
-
- {
- addAll(COMMON_ROOT_COMMANDS);
- addAll(MASTER_ONLY_ROOT_COMMANDS);
- Collections.sort(this);
- }
- }),
+ Streams.concat(COMMON_ROOT_COMMANDS.stream(), MASTER_ONLY_ROOT_COMMANDS.stream())
+ .sorted()
+ .collect(toImmutableList()),
"index",
ImmutableList.of(
"changes", "changes-in-project"), // "activate" and "start" are not included
diff --git a/javatests/com/google/gerrit/acceptance/ssh/SshTraceIT.java b/javatests/com/google/gerrit/acceptance/ssh/SshTraceIT.java
index b01c432..899b0cf 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/SshTraceIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/SshTraceIT.java
@@ -2,6 +2,7 @@
import static com.google.common.truth.Truth.assertThat;
+import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.UseSsh;
import com.google.gerrit.extensions.registration.DynamicSet;
@@ -27,7 +28,7 @@
public void setup() {
projectCreationListener = new TraceValidatingProjectCreationValidationListener();
projectCreationListenerRegistrationHandle =
- projectCreationValidationListeners.add(projectCreationListener);
+ projectCreationValidationListeners.add("gerrit", projectCreationListener);
}
@After
@@ -39,6 +40,7 @@
public void sshCallWithoutTrace() throws Exception {
adminSshSession.exec("gerrit create-project new1");
adminSshSession.assertSuccess();
+ assertThat(projectCreationListener.traceId).isNull();
assertThat(projectCreationListener.foundTraceId).isFalse();
assertThat(projectCreationListener.isLoggingForced).isFalse();
}
@@ -50,18 +52,40 @@
// The trace ID is written to stderr.
adminSshSession.assertFailure(RequestId.Type.TRACE_ID.name());
+ assertThat(projectCreationListener.traceId).isNotNull();
assertThat(projectCreationListener.foundTraceId).isTrue();
assertThat(projectCreationListener.isLoggingForced).isTrue();
}
+ @Test
+ public void sshCallWithTraceAndProvidedTraceId() throws Exception {
+ adminSshSession.exec("gerrit create-project --trace --trace-id issue/123 new3");
+
+ // The trace ID is written to stderr.
+ adminSshSession.assertFailure(RequestId.Type.TRACE_ID.name());
+
+ assertThat(projectCreationListener.traceId).isEqualTo("issue/123");
+ assertThat(projectCreationListener.foundTraceId).isTrue();
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+ }
+
+ @Test
+ public void sshCallWithTraceIdAndWithoutTraceFails() throws Exception {
+ adminSshSession.exec("gerrit create-project --trace-id issue/123 new3");
+ adminSshSession.assertFailure("A trace ID can only be set if --trace was specified.");
+ }
+
private static class TraceValidatingProjectCreationValidationListener
implements ProjectCreationValidationListener {
+ String traceId;
Boolean foundTraceId;
Boolean isLoggingForced;
@Override
public void validateNewProject(CreateProjectArgs args) throws ValidationException {
- this.foundTraceId = LoggingContext.getInstance().getTagsAsMap().containsKey("TRACE_ID");
+ this.traceId =
+ Iterables.getFirst(LoggingContext.getInstance().getTagsAsMap().get("TRACE_ID"), null);
+ this.foundTraceId = traceId != null;
this.isLoggingForced = LoggingContext.getInstance().shouldForceLogging(null, null, false);
}
}
diff --git a/javatests/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImplTest.java b/javatests/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImplTest.java
new file mode 100644
index 0000000..954b0e6
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImplTest.java
@@ -0,0 +1,656 @@
+// Copyright (C) 2018 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.acceptance.testsuite.group;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth8.assertThat;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.truth.Correspondence;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
+import com.google.gerrit.extensions.api.groups.GroupInput;
+import com.google.gerrit.extensions.common.AccountInfo;
+import com.google.gerrit.extensions.common.GroupInfo;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.inject.Inject;
+import java.sql.Timestamp;
+import java.util.Objects;
+import java.util.Optional;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class GroupOperationsImplTest extends AbstractDaemonTest {
+
+ @Rule public ExpectedException expectedException = ExpectedException.none();
+
+ @Inject private AccountOperations accountOperations;
+
+ @Inject private GroupOperationsImpl groupOperations;
+
+ private int uniqueGroupNameIndex;
+
+ @Test
+ public void groupCanBeCreatedWithoutSpecifyingAnyParameters() throws Exception {
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().create();
+
+ GroupInfo foundGroup = getGroupFromServer(groupUuid);
+ assertThat(foundGroup.id).isEqualTo(groupUuid.get());
+ assertThat(foundGroup.name).isNotEmpty();
+ }
+
+ @Test
+ public void twoGroupsWithoutAnyParametersDoNotClash() throws Exception {
+ AccountGroup.UUID groupUuid1 = groupOperations.newGroup().create();
+ AccountGroup.UUID groupUuid2 = groupOperations.newGroup().create();
+
+ TestGroup group1 = groupOperations.group(groupUuid1).get();
+ TestGroup group2 = groupOperations.group(groupUuid2).get();
+ assertThat(group1.groupUuid()).isNotEqualTo(group2.groupUuid());
+ }
+
+ @Test
+ public void groupCreatedByTestApiCanBeRetrievedViaOfficialApi() throws Exception {
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().name("unique group created via test API").create();
+
+ GroupInfo foundGroup = getGroupFromServer(groupUuid);
+ assertThat(foundGroup.id).isEqualTo(groupUuid.get());
+ assertThat(foundGroup.name).isEqualTo("unique group created via test API");
+ }
+
+ @Test
+ public void specifiedNameIsRespectedForGroupCreation() throws Exception {
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().name("XYZ-123-this-name-must-be-unique").create();
+
+ GroupInfo group = getGroupFromServer(groupUuid);
+ assertThat(group.name).isEqualTo("XYZ-123-this-name-must-be-unique");
+ }
+
+ @Test
+ public void specifiedDescriptionIsRespectedForGroupCreation() throws Exception {
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().description("All authenticated users").create();
+
+ GroupInfo group = getGroupFromServer(groupUuid);
+ assertThat(group.description).isEqualTo("All authenticated users");
+ }
+
+ @Test
+ public void requestingNoDescriptionIsPossibleForGroupCreation() throws Exception {
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().clearDescription().create();
+
+ GroupInfo group = getGroupFromServer(groupUuid);
+ assertThat(group.description).isNull();
+ }
+
+ @Test
+ public void specifiedOwnerIsRespectedForGroupCreation() throws Exception {
+ AccountGroup.UUID ownerGroupUuid = groupOperations.newGroup().create();
+
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().ownerGroupUuid(ownerGroupUuid).create();
+
+ GroupInfo foundGroup = getGroupFromServer(groupUuid);
+ assertThat(foundGroup.ownerId).isEqualTo(ownerGroupUuid.get());
+ }
+
+ @Test
+ public void specifiedVisibilityIsRespectedForGroupCreation() throws Exception {
+ AccountGroup.UUID group1Uuid = groupOperations.newGroup().visibleToAll(true).create();
+ AccountGroup.UUID group2Uuid = groupOperations.newGroup().visibleToAll(false).create();
+
+ GroupInfo foundGroup1 = getGroupFromServer(group1Uuid);
+ GroupInfo foundGroup2 = getGroupFromServer(group2Uuid);
+ assertThat(foundGroup1.options.visibleToAll).isTrue();
+ // False == null
+ assertThat(foundGroup2.options.visibleToAll).isNull();
+ }
+
+ @Test
+ public void specifiedMembersAreRespectedForGroupCreation() throws Exception {
+ Account.Id account1Id = accountOperations.newAccount().create();
+ Account.Id account2Id = accountOperations.newAccount().create();
+ Account.Id account3Id = accountOperations.newAccount().create();
+ Account.Id account4Id = accountOperations.newAccount().create();
+
+ AccountGroup.UUID groupUuid =
+ groupOperations
+ .newGroup()
+ .members(account1Id, account2Id)
+ .addMember(account3Id)
+ .addMember(account4Id)
+ .create();
+
+ GroupInfo foundGroup = getGroupFromServer(groupUuid);
+ assertThat(foundGroup.members)
+ .comparingElementsUsing(getAccountToIdCorrespondence())
+ .containsExactly(account1Id, account2Id, account3Id, account4Id);
+ }
+
+ @Test
+ public void directlyAddingMembersIsPossibleForGroupCreation() throws Exception {
+ Account.Id account1Id = accountOperations.newAccount().create();
+ Account.Id account2Id = accountOperations.newAccount().create();
+
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().addMember(account1Id).addMember(account2Id).create();
+
+ GroupInfo foundGroup = getGroupFromServer(groupUuid);
+ assertThat(foundGroup.members)
+ .comparingElementsUsing(getAccountToIdCorrespondence())
+ .containsExactly(account1Id, account2Id);
+ }
+
+ @Test
+ public void requestingNoMembersIsPossibleForGroupCreation() throws Exception {
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().clearMembers().create();
+
+ GroupInfo foundGroup = getGroupFromServer(groupUuid);
+ assertThat(foundGroup.members).isEmpty();
+ }
+
+ @Test
+ public void specifiedSubgroupsAreRespectedForGroupCreation() throws Exception {
+ AccountGroup.UUID group1Uuid = groupOperations.newGroup().create();
+ AccountGroup.UUID group2Uuid = groupOperations.newGroup().create();
+ AccountGroup.UUID group3Uuid = groupOperations.newGroup().create();
+ AccountGroup.UUID group4Uuid = groupOperations.newGroup().create();
+
+ AccountGroup.UUID groupUuid =
+ groupOperations
+ .newGroup()
+ .subgroups(group1Uuid, group2Uuid)
+ .addSubgroup(group3Uuid)
+ .addSubgroup(group4Uuid)
+ .create();
+
+ GroupInfo foundGroup = getGroupFromServer(groupUuid);
+ assertThat(foundGroup.includes)
+ .comparingElementsUsing(getGroupToUuidCorrespondence())
+ .containsExactly(group1Uuid, group2Uuid, group3Uuid, group4Uuid);
+ }
+
+ @Test
+ public void directlyAddingSubgroupsIsPossibleForGroupCreation() throws Exception {
+ AccountGroup.UUID group1Uuid = groupOperations.newGroup().create();
+ AccountGroup.UUID group2Uuid = groupOperations.newGroup().create();
+
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().addSubgroup(group1Uuid).addSubgroup(group2Uuid).create();
+
+ GroupInfo foundGroup = getGroupFromServer(groupUuid);
+ assertThat(foundGroup.includes)
+ .comparingElementsUsing(getGroupToUuidCorrespondence())
+ .containsExactly(group1Uuid, group2Uuid);
+ }
+
+ @Test
+ public void requestingNoSubgroupsIsPossibleForGroupCreation() throws Exception {
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().clearSubgroups().create();
+
+ GroupInfo foundGroup = getGroupFromServer(groupUuid);
+ assertThat(foundGroup.includes).isEmpty();
+ }
+
+ @Test
+ public void existingGroupCanBeCheckedForExistence() throws Exception {
+ AccountGroup.UUID groupUuid = createGroupInServer(createArbitraryGroupInput());
+
+ boolean exists = groupOperations.group(groupUuid).exists();
+
+ assertThat(exists).isTrue();
+ }
+
+ @Test
+ public void notExistingGroupCanBeCheckedForExistence() throws Exception {
+ AccountGroup.UUID notExistingGroupUuid = new AccountGroup.UUID("not-existing-group");
+
+ boolean exists = groupOperations.group(notExistingGroupUuid).exists();
+
+ assertThat(exists).isFalse();
+ }
+
+ @Test
+ public void retrievingNotExistingGroupFails() throws Exception {
+ AccountGroup.UUID notExistingGroupUuid = new AccountGroup.UUID("not-existing-group");
+
+ expectedException.expect(IllegalStateException.class);
+ groupOperations.group(notExistingGroupUuid).get();
+ }
+
+ @Test
+ public void groupNotCreatedByTestApiCanBeRetrieved() throws Exception {
+ GroupInput input = createArbitraryGroupInput();
+ input.name = "unique group not created via test API";
+ AccountGroup.UUID groupUuid = createGroupInServer(input);
+
+ TestGroup foundGroup = groupOperations.group(groupUuid).get();
+
+ assertThat(foundGroup.groupUuid()).isEqualTo(groupUuid);
+ assertThat(foundGroup.name()).isEqualTo("unique group not created via test API");
+ }
+
+ @Test
+ public void uuidOfExistingGroupCanBeRetrieved() throws Exception {
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().create();
+
+ AccountGroup.UUID foundGroupUuid = groupOperations.group(groupUuid).get().groupUuid();
+
+ assertThat(foundGroupUuid).isEqualTo(groupUuid);
+ }
+
+ @Test
+ public void nameOfExistingGroupCanBeRetrieved() throws Exception {
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().name("ABC-789-this-name-must-be-unique").create();
+
+ String groupName = groupOperations.group(groupUuid).get().name();
+
+ assertThat(groupName).isEqualTo("ABC-789-this-name-must-be-unique");
+ }
+
+ @Test
+ public void nameKeyOfExistingGroupCanBeRetrieved() throws Exception {
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().name("ABC-789-this-name-must-be-unique").create();
+
+ AccountGroup.NameKey groupName = groupOperations.group(groupUuid).get().nameKey();
+
+ assertThat(groupName).isEqualTo(new AccountGroup.NameKey("ABC-789-this-name-must-be-unique"));
+ }
+
+ @Test
+ public void descriptionOfExistingGroupCanBeRetrieved() throws Exception {
+ AccountGroup.UUID groupUuid =
+ groupOperations
+ .newGroup()
+ .description("This is a very detailed description of this group.")
+ .create();
+
+ Optional<String> description = groupOperations.group(groupUuid).get().description();
+
+ assertThat(description).hasValue("This is a very detailed description of this group.");
+ }
+
+ @Test
+ public void emptyDescriptionOfExistingGroupCanBeRetrieved() throws Exception {
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().clearDescription().create();
+
+ Optional<String> description = groupOperations.group(groupUuid).get().description();
+
+ assertThat(description).isEmpty();
+ }
+
+ @Test
+ public void ownerGroupUuidOfExistingGroupCanBeRetrieved() throws Exception {
+ AccountGroup.UUID originalOwnerGroupUuid = new AccountGroup.UUID("owner group");
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().ownerGroupUuid(originalOwnerGroupUuid).create();
+
+ AccountGroup.UUID ownerGroupUuid = groupOperations.group(groupUuid).get().ownerGroupUuid();
+
+ assertThat(ownerGroupUuid).isEqualTo(originalOwnerGroupUuid);
+ }
+
+ @Test
+ public void visibilityOfExistingGroupCanBeRetrieved() throws Exception {
+ AccountGroup.UUID visibleGroupUuid = groupOperations.newGroup().visibleToAll(true).create();
+ AccountGroup.UUID invisibleGroupUuid = groupOperations.newGroup().visibleToAll(false).create();
+
+ TestGroup visibleGroup = groupOperations.group(visibleGroupUuid).get();
+ TestGroup invisibleGroup = groupOperations.group(invisibleGroupUuid).get();
+
+ assertThat(visibleGroup.visibleToAll()).named("visibility of visible group").isTrue();
+ assertThat(invisibleGroup.visibleToAll()).named("visibility of invisible group").isFalse();
+ }
+
+ @Test
+ public void createdOnOfExistingGroupCanBeRetrieved() throws Exception {
+ GroupInfo group = gApi.groups().create(createArbitraryGroupInput()).detail();
+ AccountGroup.UUID groupUuid = new AccountGroup.UUID(group.id);
+
+ Timestamp createdOn = groupOperations.group(groupUuid).get().createdOn();
+
+ assertThat(createdOn).isEqualTo(group.createdOn);
+ }
+
+ @Test
+ public void membersOfExistingGroupCanBeRetrieved() throws Exception {
+ Account.Id memberId1 = new Account.Id(1000);
+ Account.Id memberId2 = new Account.Id(2000);
+ Account.Id memberId3 = new Account.Id(3000);
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().members(memberId1, memberId2, memberId3).create();
+
+ ImmutableSet<Account.Id> members = groupOperations.group(groupUuid).get().members();
+
+ assertThat(members).containsExactly(memberId1, memberId2, memberId3);
+ }
+
+ @Test
+ public void emptyMembersOfExistingGroupCanBeRetrieved() throws Exception {
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().clearMembers().create();
+
+ ImmutableSet<Account.Id> members = groupOperations.group(groupUuid).get().members();
+
+ assertThat(members).isEmpty();
+ }
+
+ @Test
+ public void subgroupsOfExistingGroupCanBeRetrieved() throws Exception {
+ AccountGroup.UUID subgroupUuid1 = new AccountGroup.UUID("subgroup 1");
+ AccountGroup.UUID subgroupUuid2 = new AccountGroup.UUID("subgroup 2");
+ AccountGroup.UUID subgroupUuid3 = new AccountGroup.UUID("subgroup 3");
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().subgroups(subgroupUuid1, subgroupUuid2, subgroupUuid3).create();
+
+ ImmutableSet<AccountGroup.UUID> subgroups = groupOperations.group(groupUuid).get().subgroups();
+
+ assertThat(subgroups).containsExactly(subgroupUuid1, subgroupUuid2, subgroupUuid3);
+ }
+
+ @Test
+ public void emptySubgroupsOfExistingGroupCanBeRetrieved() throws Exception {
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().clearSubgroups().create();
+
+ ImmutableSet<AccountGroup.UUID> subgroups = groupOperations.group(groupUuid).get().subgroups();
+
+ assertThat(subgroups).isEmpty();
+ }
+
+ @Test
+ public void updateWithoutAnyParametersIsANoop() throws Exception {
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().create();
+ TestGroup originalGroup = groupOperations.group(groupUuid).get();
+
+ groupOperations.group(groupUuid).forUpdate().update();
+
+ TestGroup updatedGroup = groupOperations.group(groupUuid).get();
+ assertThat(updatedGroup).isEqualTo(originalGroup);
+ }
+
+ @Test
+ public void updateWritesToInternalGroupSystem() throws Exception {
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().description("original description").create();
+
+ groupOperations.group(groupUuid).forUpdate().description("updated description").update();
+
+ String currentDescription = getGroupFromServer(groupUuid).description;
+ assertThat(currentDescription).isEqualTo("updated description");
+ }
+
+ @Test
+ public void nameCanBeUpdated() throws Exception {
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().name("original name").create();
+
+ groupOperations.group(groupUuid).forUpdate().name("updated name").update();
+
+ String currentName = groupOperations.group(groupUuid).get().name();
+ assertThat(currentName).isEqualTo("updated name");
+ }
+
+ @Test
+ public void descriptionCanBeUpdated() throws Exception {
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().description("original description").create();
+
+ groupOperations.group(groupUuid).forUpdate().description("updated description").update();
+
+ Optional<String> currentDescription = groupOperations.group(groupUuid).get().description();
+ assertThat(currentDescription).hasValue("updated description");
+ }
+
+ @Test
+ public void descriptionCanBeCleared() throws Exception {
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().description("original description").create();
+
+ groupOperations.group(groupUuid).forUpdate().clearDescription().update();
+
+ Optional<String> currentDescription = groupOperations.group(groupUuid).get().description();
+ assertThat(currentDescription).isEmpty();
+ }
+
+ @Test
+ public void ownerGroupUuidCanBeUpdated() throws Exception {
+ AccountGroup.UUID originalOwnerGroupUuid = new AccountGroup.UUID("original owner");
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().ownerGroupUuid(originalOwnerGroupUuid).create();
+
+ AccountGroup.UUID updatedOwnerGroupUuid = new AccountGroup.UUID("updated owner");
+ groupOperations.group(groupUuid).forUpdate().ownerGroupUuid(updatedOwnerGroupUuid).update();
+
+ AccountGroup.UUID currentOwnerGroupUuid =
+ groupOperations.group(groupUuid).get().ownerGroupUuid();
+ assertThat(currentOwnerGroupUuid).isEqualTo(updatedOwnerGroupUuid);
+ }
+
+ @Test
+ public void visibilityCanBeUpdated() throws Exception {
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().visibleToAll(true).create();
+
+ groupOperations.group(groupUuid).forUpdate().visibleToAll(false).update();
+
+ boolean visibleToAll = groupOperations.group(groupUuid).get().visibleToAll();
+ assertThat(visibleToAll).isFalse();
+ }
+
+ @Test
+ public void membersCanBeAdded() throws Exception {
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().clearMembers().create();
+
+ Account.Id memberId1 = new Account.Id(1000);
+ Account.Id memberId2 = new Account.Id(2000);
+ groupOperations.group(groupUuid).forUpdate().addMember(memberId1).addMember(memberId2).update();
+
+ ImmutableSet<Account.Id> members = groupOperations.group(groupUuid).get().members();
+ assertThat(members).containsExactly(memberId1, memberId2);
+ }
+
+ @Test
+ public void membersCanBeRemoved() throws Exception {
+ Account.Id memberId1 = new Account.Id(1000);
+ Account.Id memberId2 = new Account.Id(2000);
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().members(memberId1, memberId2).create();
+
+ groupOperations.group(groupUuid).forUpdate().removeMember(memberId2).update();
+
+ ImmutableSet<Account.Id> members = groupOperations.group(groupUuid).get().members();
+ assertThat(members).containsExactly(memberId1);
+ }
+
+ @Test
+ public void memberAdditionAndRemovalCanBeMixed() throws Exception {
+ Account.Id memberId1 = new Account.Id(1000);
+ Account.Id memberId2 = new Account.Id(2000);
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().members(memberId1, memberId2).create();
+
+ Account.Id memberId3 = new Account.Id(3000);
+ groupOperations
+ .group(groupUuid)
+ .forUpdate()
+ .removeMember(memberId1)
+ .addMember(memberId3)
+ .update();
+
+ ImmutableSet<Account.Id> members = groupOperations.group(groupUuid).get().members();
+ assertThat(members).containsExactly(memberId2, memberId3);
+ }
+
+ @Test
+ public void membersCanBeCleared() throws Exception {
+ Account.Id memberId1 = new Account.Id(1000);
+ Account.Id memberId2 = new Account.Id(2000);
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().members(memberId1, memberId2).create();
+
+ groupOperations.group(groupUuid).forUpdate().clearMembers().update();
+
+ ImmutableSet<Account.Id> members = groupOperations.group(groupUuid).get().members();
+ assertThat(members).isEmpty();
+ }
+
+ @Test
+ public void furtherMembersCanBeAddedAfterClearingAll() throws Exception {
+ Account.Id memberId1 = new Account.Id(1000);
+ Account.Id memberId2 = new Account.Id(2000);
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().members(memberId1, memberId2).create();
+
+ Account.Id memberId3 = new Account.Id(3000);
+ groupOperations.group(groupUuid).forUpdate().clearMembers().addMember(memberId3).update();
+
+ ImmutableSet<Account.Id> members = groupOperations.group(groupUuid).get().members();
+ assertThat(members).containsExactly(memberId3);
+ }
+
+ @Test
+ public void subgroupsCanBeAdded() throws Exception {
+ AccountGroup.UUID groupUuid = groupOperations.newGroup().clearSubgroups().create();
+
+ AccountGroup.UUID subgroupUuid1 = new AccountGroup.UUID("subgroup 1");
+ AccountGroup.UUID subgroupUuid2 = new AccountGroup.UUID("subgroup 2");
+ groupOperations
+ .group(groupUuid)
+ .forUpdate()
+ .addSubgroup(subgroupUuid1)
+ .addSubgroup(subgroupUuid2)
+ .update();
+
+ ImmutableSet<AccountGroup.UUID> subgroups = groupOperations.group(groupUuid).get().subgroups();
+ assertThat(subgroups).containsExactly(subgroupUuid1, subgroupUuid2);
+ }
+
+ @Test
+ public void subgroupsCanBeRemoved() throws Exception {
+ AccountGroup.UUID subgroupUuid1 = new AccountGroup.UUID("subgroup 1");
+ AccountGroup.UUID subgroupUuid2 = new AccountGroup.UUID("subgroup 2");
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().subgroups(subgroupUuid1, subgroupUuid2).create();
+
+ groupOperations.group(groupUuid).forUpdate().removeSubgroup(subgroupUuid2).update();
+
+ ImmutableSet<AccountGroup.UUID> subgroups = groupOperations.group(groupUuid).get().subgroups();
+ assertThat(subgroups).containsExactly(subgroupUuid1);
+ }
+
+ @Test
+ public void subgroupAdditionAndRemovalCanBeMixed() throws Exception {
+ AccountGroup.UUID subgroupUuid1 = new AccountGroup.UUID("subgroup 1");
+ AccountGroup.UUID subgroupUuid2 = new AccountGroup.UUID("subgroup 2");
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().subgroups(subgroupUuid1, subgroupUuid2).create();
+
+ AccountGroup.UUID subgroupUuid3 = new AccountGroup.UUID("subgroup 3");
+ groupOperations
+ .group(groupUuid)
+ .forUpdate()
+ .removeSubgroup(subgroupUuid1)
+ .addSubgroup(subgroupUuid3)
+ .update();
+
+ ImmutableSet<AccountGroup.UUID> subgroups = groupOperations.group(groupUuid).get().subgroups();
+ assertThat(subgroups).containsExactly(subgroupUuid2, subgroupUuid3);
+ }
+
+ @Test
+ public void subgroupsCanBeCleared() throws Exception {
+ AccountGroup.UUID subgroupUuid1 = new AccountGroup.UUID("subgroup 1");
+ AccountGroup.UUID subgroupUuid2 = new AccountGroup.UUID("subgroup 2");
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().subgroups(subgroupUuid1, subgroupUuid2).create();
+
+ groupOperations.group(groupUuid).forUpdate().clearSubgroups().update();
+
+ ImmutableSet<AccountGroup.UUID> subgroups = groupOperations.group(groupUuid).get().subgroups();
+ assertThat(subgroups).isEmpty();
+ }
+
+ @Test
+ public void furtherSubgroupsCanBeAddedAfterClearingAll() throws Exception {
+ AccountGroup.UUID subgroupUuid1 = new AccountGroup.UUID("subgroup 1");
+ AccountGroup.UUID subgroupUuid2 = new AccountGroup.UUID("subgroup 2");
+ AccountGroup.UUID groupUuid =
+ groupOperations.newGroup().subgroups(subgroupUuid1, subgroupUuid2).create();
+
+ AccountGroup.UUID subgroupUuid3 = new AccountGroup.UUID("subgroup 3");
+ groupOperations
+ .group(groupUuid)
+ .forUpdate()
+ .clearSubgroups()
+ .addSubgroup(subgroupUuid3)
+ .update();
+
+ ImmutableSet<AccountGroup.UUID> subgroups = groupOperations.group(groupUuid).get().subgroups();
+ assertThat(subgroups).containsExactly(subgroupUuid3);
+ }
+
+ private GroupInput createArbitraryGroupInput() {
+ GroupInput groupInput = new GroupInput();
+ groupInput.name = name("verifiers-" + uniqueGroupNameIndex++);
+ return groupInput;
+ }
+
+ private GroupInfo getGroupFromServer(AccountGroup.UUID groupUuid) throws RestApiException {
+ return gApi.groups().id(groupUuid.get()).detail();
+ }
+
+ private AccountGroup.UUID createGroupInServer(GroupInput input) throws RestApiException {
+ GroupInfo group = gApi.groups().create(input).detail();
+ return new AccountGroup.UUID(group.id);
+ }
+
+ private static Correspondence<AccountInfo, Account.Id> getAccountToIdCorrespondence() {
+ return new Correspondence<AccountInfo, Account.Id>() {
+ @Override
+ public boolean compare(AccountInfo actualAccount, Account.Id expectedId) {
+ Account.Id accountId =
+ Optional.ofNullable(actualAccount)
+ .map(account -> account._accountId)
+ .map(Account.Id::new)
+ .orElse(null);
+ return Objects.equals(accountId, expectedId);
+ }
+
+ @Override
+ public String toString() {
+ return "has ID";
+ }
+ };
+ }
+
+ private static Correspondence<GroupInfo, AccountGroup.UUID> getGroupToUuidCorrespondence() {
+ return new Correspondence<GroupInfo, AccountGroup.UUID>() {
+ @Override
+ public boolean compare(GroupInfo actualGroup, AccountGroup.UUID expectedUuid) {
+ AccountGroup.UUID groupUuid =
+ Optional.ofNullable(actualGroup)
+ .map(group -> group.id)
+ .map(AccountGroup.UUID::new)
+ .orElse(null);
+ return Objects.equals(groupUuid, expectedUuid);
+ }
+
+ @Override
+ public String toString() {
+ return "has UUID";
+ }
+ };
+ }
+}
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java b/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java
index f5614b4..8b3c08f 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java
@@ -45,11 +45,13 @@
case V2_4:
return "elasticsearch:2.4.6-alpine";
case V5_6:
- return "docker.elastic.co/elasticsearch/elasticsearch:5.6.10";
+ return "docker.elastic.co/elasticsearch/elasticsearch:5.6.11";
case V6_2:
return "docker.elastic.co/elasticsearch/elasticsearch-oss:6.2.4";
case V6_3:
return "docker.elastic.co/elasticsearch/elasticsearch-oss:6.3.2";
+ case V6_4:
+ return "docker.elastic.co/elasticsearch/elasticsearch-oss:6.4.0";
}
throw new IllegalStateException("No tests for version: " + version.name());
}
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryAccountsTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryAccountsTest.java
index 1d17b5b..b8154ce 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryAccountsTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryAccountsTest.java
@@ -41,7 +41,7 @@
return;
}
- container = ElasticContainer.createAndStart(ElasticVersion.V6_3);
+ container = ElasticContainer.createAndStart(ElasticVersion.V6_4);
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
}
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryChangesTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryChangesTest.java
index 7c5d2e2..3445b36 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryChangesTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryChangesTest.java
@@ -41,7 +41,7 @@
return;
}
- container = ElasticContainer.createAndStart(ElasticVersion.V6_3);
+ container = ElasticContainer.createAndStart(ElasticVersion.V6_4);
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
}
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryGroupsTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryGroupsTest.java
index 15b58e0..851b27d 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryGroupsTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryGroupsTest.java
@@ -41,7 +41,7 @@
return;
}
- container = ElasticContainer.createAndStart(ElasticVersion.V6_3);
+ container = ElasticContainer.createAndStart(ElasticVersion.V6_4);
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
}
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticVersionTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticVersionTest.java
index 860dca6..b598a0a 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticVersionTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticVersionTest.java
@@ -29,21 +29,31 @@
assertThat(ElasticVersion.forVersion("2.4.6")).isEqualTo(ElasticVersion.V2_4);
assertThat(ElasticVersion.forVersion("5.6.0")).isEqualTo(ElasticVersion.V5_6);
- assertThat(ElasticVersion.forVersion("5.6.9")).isEqualTo(ElasticVersion.V5_6);
- assertThat(ElasticVersion.forVersion("5.6.10")).isEqualTo(ElasticVersion.V5_6);
+ assertThat(ElasticVersion.forVersion("5.6.11")).isEqualTo(ElasticVersion.V5_6);
assertThat(ElasticVersion.forVersion("6.2.0")).isEqualTo(ElasticVersion.V6_2);
assertThat(ElasticVersion.forVersion("6.2.4")).isEqualTo(ElasticVersion.V6_2);
assertThat(ElasticVersion.forVersion("6.3.0")).isEqualTo(ElasticVersion.V6_3);
- assertThat(ElasticVersion.forVersion("6.3.1")).isEqualTo(ElasticVersion.V6_3);
+ assertThat(ElasticVersion.forVersion("6.3.2")).isEqualTo(ElasticVersion.V6_3);
+
+ assertThat(ElasticVersion.forVersion("6.4.0")).isEqualTo(ElasticVersion.V6_4);
+ assertThat(ElasticVersion.forVersion("6.4.1")).isEqualTo(ElasticVersion.V6_4);
}
@Test
public void unsupportedVersion() throws Exception {
- exception.expect(ElasticVersion.InvalidVersion.class);
+ exception.expect(ElasticVersion.UnsupportedVersion.class);
exception.expectMessage(
- "Invalid version: [4.0.0]. Supported versions: " + ElasticVersion.supportedVersions());
+ "Unsupported version: [4.0.0]. Supported versions: " + ElasticVersion.supportedVersions());
ElasticVersion.forVersion("4.0.0");
}
+
+ @Test
+ public void version6() throws Exception {
+ assertThat(ElasticVersion.V6_2.isV6()).isTrue();
+ assertThat(ElasticVersion.V6_3.isV6()).isTrue();
+ assertThat(ElasticVersion.V6_4.isV6()).isTrue();
+ assertThat(ElasticVersion.V5_6.isV6()).isFalse();
+ }
}
diff --git a/javatests/com/google/gerrit/extensions/registration/DynamicSetTest.java b/javatests/com/google/gerrit/extensions/registration/DynamicSetTest.java
index 117e474..c86160f 100644
--- a/javatests/com/google/gerrit/extensions/registration/DynamicSetTest.java
+++ b/javatests/com/google/gerrit/extensions/registration/DynamicSetTest.java
@@ -15,9 +15,12 @@
package com.google.gerrit.extensions.registration;
import static com.google.common.truth.Truth.assertThat;
+import static java.util.stream.Collectors.toSet;
import com.google.inject.Key;
+import com.google.inject.Provider;
import com.google.inject.util.Providers;
+import java.util.Iterator;
import org.junit.Test;
public class DynamicSetTest {
@@ -40,7 +43,7 @@
@Test
public void containsTrueWithSingleElement() throws Exception {
DynamicSet<Integer> ds = new DynamicSet<>();
- ds.add(2);
+ ds.add("gerrit", 2);
assertThat(ds.contains(2)).isTrue(); // See above comment about ds.contains
}
@@ -48,7 +51,7 @@
@Test
public void containsFalseWithSingleElement() throws Exception {
DynamicSet<Integer> ds = new DynamicSet<>();
- ds.add(2);
+ ds.add("gerrit", 2);
assertThat(ds.contains(3)).isFalse(); // See above comment about ds.contains
}
@@ -56,8 +59,8 @@
@Test
public void containsTrueWithTwoElements() throws Exception {
DynamicSet<Integer> ds = new DynamicSet<>();
- ds.add(2);
- ds.add(4);
+ ds.add("gerrit", 2);
+ ds.add("gerrit", 4);
assertThat(ds.contains(4)).isTrue(); // See above comment about ds.contains
}
@@ -65,8 +68,8 @@
@Test
public void containsFalseWithTwoElements() throws Exception {
DynamicSet<Integer> ds = new DynamicSet<>();
- ds.add(2);
- ds.add(4);
+ ds.add("gerrit", 2);
+ ds.add("gerrit", 4);
assertThat(ds.contains(3)).isFalse(); // See above comment about ds.contains
}
@@ -74,12 +77,12 @@
@Test
public void containsDynamic() throws Exception {
DynamicSet<Integer> ds = new DynamicSet<>();
- ds.add(2);
+ ds.add("gerrit", 2);
Key<Integer> key = Key.get(Integer.class);
- ReloadableRegistrationHandle<Integer> handle = ds.add(key, Providers.of(4));
+ ReloadableRegistrationHandle<Integer> handle = ds.add("gerrit", key, Providers.of(4));
- ds.add(6);
+ ds.add("gerrit", 6);
// At first, 4 is contained.
assertThat(ds.contains(4)).isTrue(); // See above comment about ds.contains
@@ -90,4 +93,49 @@
// And now 4 should no longer be contained.
assertThat(ds.contains(4)).isFalse(); // See above comment about ds.contains
}
+
+ @Test
+ public void plugins() {
+ DynamicSet<Integer> ds = new DynamicSet<>();
+ ds.add("foo", 1);
+ ds.add("bar", 2);
+ ds.add("bar", 3);
+
+ assertThat(ds.plugins()).containsExactly("bar", "foo").inOrder();
+ }
+
+ @Test
+ public void byPlugin() {
+ DynamicSet<Integer> ds = new DynamicSet<>();
+ ds.add("foo", 1);
+ ds.add("bar", 2);
+ ds.add("bar", 3);
+
+ assertThat(ds.byPlugin("foo").stream().map(Provider::get).collect(toSet())).containsExactly(1);
+ assertThat(ds.byPlugin("bar").stream().map(Provider::get).collect(toSet()))
+ .containsExactly(2, 3);
+ }
+
+ @Test
+ public void entryIterator() {
+ DynamicSet<Integer> ds = new DynamicSet<>();
+ ds.add("foo", 1);
+ ds.add("bar", 2);
+ ds.add("bar", 3);
+
+ Iterator<DynamicSet.Entry<Integer>> entryIterator = ds.entries().iterator();
+ DynamicSet.Entry<Integer> next = entryIterator.next();
+ assertThat(next.getPluginName()).isEqualTo("foo");
+ assertThat(next.getProvider().get()).isEqualTo(1);
+
+ next = entryIterator.next();
+ assertThat(next.getPluginName()).isEqualTo("bar");
+ assertThat(next.getProvider().get()).isEqualTo(2);
+
+ next = entryIterator.next();
+ assertThat(next.getPluginName()).isEqualTo("bar");
+ assertThat(next.getProvider().get()).isEqualTo(3);
+
+ assertThat(entryIterator.hasNext()).isFalse();
+ }
}
diff --git a/javatests/com/google/gerrit/gpg/PushCertificateCheckerTest.java b/javatests/com/google/gerrit/gpg/PushCertificateCheckerTest.java
index ad8f4311..266f868 100644
--- a/javatests/com/google/gerrit/gpg/PushCertificateCheckerTest.java
+++ b/javatests/com/google/gerrit/gpg/PushCertificateCheckerTest.java
@@ -184,7 +184,7 @@
}
String cert = payload + new String(bout.toByteArray(), UTF_8);
- Reader reader = new InputStreamReader(new ByteArrayInputStream(cert.getBytes(UTF_8)));
+ Reader reader = new InputStreamReader(new ByteArrayInputStream(cert.getBytes(UTF_8)), UTF_8);
PushCertificateParser parser = new PushCertificateParser(repo, signedPushConfig);
return parser.parse(reader);
}
diff --git a/javatests/com/google/gerrit/httpd/AllRequestFilterFilterProxyTest.java b/javatests/com/google/gerrit/httpd/AllRequestFilterFilterProxyTest.java
index 086dcc2..1c6559b0 100644
--- a/javatests/com/google/gerrit/httpd/AllRequestFilterFilterProxyTest.java
+++ b/javatests/com/google/gerrit/httpd/AllRequestFilterFilterProxyTest.java
@@ -76,7 +76,7 @@
*/
private ReloadableRegistrationHandle<AllRequestFilter> addFilter(AllRequestFilter filter) {
Key<AllRequestFilter> key = Key.get(AllRequestFilter.class);
- return filters.add(key, Providers.of(filter));
+ return filters.add("gerrit", key, Providers.of(filter));
}
@Test
diff --git a/javatests/com/google/gerrit/httpd/raw/IndexServletTest.java b/javatests/com/google/gerrit/httpd/raw/IndexServletTest.java
index 07399c6..307a23e 100644
--- a/javatests/com/google/gerrit/httpd/raw/IndexServletTest.java
+++ b/javatests/com/google/gerrit/httpd/raw/IndexServletTest.java
@@ -15,6 +15,7 @@
package com.google.gerrit.httpd.raw;
import static com.google.common.truth.Truth.assertThat;
+import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.template.soy.data.SoyMapData;
import java.net.URISyntaxException;
@@ -30,7 +31,7 @@
}
String getIndexSource() {
- return new String(indexSource);
+ return new String(indexSource, UTF_8);
}
}
diff --git a/javatests/com/google/gerrit/index/BUILD b/javatests/com/google/gerrit/index/BUILD
index 14a7048..5597ed1 100644
--- a/javatests/com/google/gerrit/index/BUILD
+++ b/javatests/com/google/gerrit/index/BUILD
@@ -6,9 +6,9 @@
srcs = glob(["**/*.java"]),
visibility = ["//visibility:public"],
deps = [
+ "//antlr3:query_parser",
"//java/com/google/gerrit/index",
"//java/com/google/gerrit/index:query_exception",
- "//java/com/google/gerrit/index:query_parser",
"//lib:guava",
"//lib:junit",
"//lib/antlr:java-runtime",
diff --git a/javatests/com/google/gerrit/server/BUILD b/javatests/com/google/gerrit/server/BUILD
index 29e9a0b..7be1827 100644
--- a/javatests/com/google/gerrit/server/BUILD
+++ b/javatests/com/google/gerrit/server/BUILD
@@ -49,6 +49,7 @@
"//java/com/google/gerrit/server/cache/testing",
"//java/com/google/gerrit/server/group/testing",
"//java/com/google/gerrit/server/ioutil",
+ "//java/com/google/gerrit/server/logging",
"//java/com/google/gerrit/server/project/testing:project-test-util",
"//java/com/google/gerrit/server/restapi",
"//java/com/google/gerrit/server/schema",
diff --git a/javatests/com/google/gerrit/server/account/UniversalGroupBackendTest.java b/javatests/com/google/gerrit/server/account/UniversalGroupBackendTest.java
index 91cc2b7..6dd0f3e 100644
--- a/javatests/com/google/gerrit/server/account/UniversalGroupBackendTest.java
+++ b/javatests/com/google/gerrit/server/account/UniversalGroupBackendTest.java
@@ -55,7 +55,7 @@
user = createNiceMock(IdentifiedUser.class);
replay(user);
backends = new DynamicSet<>();
- backends.add(new SystemGroupBackend(new Config()));
+ backends.add("gerrit", new SystemGroupBackend(new Config()));
backend = new UniversalGroupBackend(backends);
}
@@ -123,7 +123,7 @@
replay(member, notMember, backend);
backends = new DynamicSet<>();
- backends.add(backend);
+ backends.add("gerrit", backend);
backend = new UniversalGroupBackend(backends);
GroupMembership checker = backend.membershipsOf(member);
diff --git a/javatests/com/google/gerrit/server/change/IncludedInResolverTest.java b/javatests/com/google/gerrit/server/change/IncludedInResolverTest.java
index e91c3b4..dca2dcb 100644
--- a/javatests/com/google/gerrit/server/change/IncludedInResolverTest.java
+++ b/javatests/com/google/gerrit/server/change/IncludedInResolverTest.java
@@ -14,10 +14,9 @@
package com.google.gerrit.server.change;
+import static com.google.common.truth.Truth.assertThat;
+
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.MergeCommand.FastForwardMode;
import org.eclipse.jgit.junit.RepositoryTestCase;
@@ -27,7 +26,6 @@
import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevWalk;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -55,9 +53,6 @@
private RevCommit commit_v1_3;
private RevCommit commit_v2_5;
- private List<String> expTags = new ArrayList<>();
- private List<String> expBranches = new ArrayList<>();
-
private RevWalk revWalk;
@Override
@@ -140,12 +135,8 @@
IncludedInResolver.Result detail = resolve(commit_v2_5);
// Check that only tags and branches which refer the tip are returned
- expTags.add(TAG_2_5);
- expTags.add(TAG_2_5_ANNOTATED);
- expTags.add(TAG_2_5_ANNOTATED_TWICE);
- assertEquals(expTags, detail.getTags());
- expBranches.add(BRANCH_2_5);
- assertEquals(expBranches, detail.getBranches());
+ assertThat(detail.tags()).containsExactly(TAG_2_5, TAG_2_5_ANNOTATED, TAG_2_5_ANNOTATED_TWICE);
+ assertThat(detail.branches()).containsExactly(BRANCH_2_5);
}
@Test
@@ -154,22 +145,18 @@
IncludedInResolver.Result detail = resolve(commit_initial);
// Check whether all tags and branches are returned
- expTags.add(TAG_1_0);
- expTags.add(TAG_1_0_1);
- expTags.add(TAG_1_3);
- expTags.add(TAG_2_0);
- expTags.add(TAG_2_0_1);
- expTags.add(TAG_2_5);
- expTags.add(TAG_2_5_ANNOTATED);
- expTags.add(TAG_2_5_ANNOTATED_TWICE);
- assertEquals(expTags, detail.getTags());
-
- expBranches.add(BRANCH_MASTER);
- expBranches.add(BRANCH_1_0);
- expBranches.add(BRANCH_1_3);
- expBranches.add(BRANCH_2_0);
- expBranches.add(BRANCH_2_5);
- assertEquals(expBranches, detail.getBranches());
+ assertThat(detail.tags())
+ .containsExactly(
+ TAG_1_0,
+ TAG_1_0_1,
+ TAG_1_3,
+ TAG_2_0,
+ TAG_2_0_1,
+ TAG_2_5,
+ TAG_2_5_ANNOTATED,
+ TAG_2_5_ANNOTATED_TWICE);
+ assertThat(detail.branches())
+ .containsExactly(BRANCH_MASTER, BRANCH_1_0, BRANCH_1_3, BRANCH_2_0, BRANCH_2_5);
}
@Test
@@ -178,27 +165,15 @@
IncludedInResolver.Result detail = resolve(commit_v1_3);
// Check whether all succeeding tags and branches are returned
- expTags.add(TAG_1_3);
- expTags.add(TAG_2_5);
- expTags.add(TAG_2_5_ANNOTATED);
- expTags.add(TAG_2_5_ANNOTATED_TWICE);
- assertEquals(expTags, detail.getTags());
-
- expBranches.add(BRANCH_1_3);
- expBranches.add(BRANCH_2_5);
- assertEquals(expBranches, detail.getBranches());
+ assertThat(detail.tags())
+ .containsExactly(TAG_1_3, TAG_2_5, TAG_2_5_ANNOTATED, TAG_2_5_ANNOTATED_TWICE);
+ assertThat(detail.branches()).containsExactly(BRANCH_1_3, BRANCH_2_5);
}
private IncludedInResolver.Result resolve(RevCommit commit) throws Exception {
return IncludedInResolver.resolve(db, revWalk, commit);
}
- private void assertEquals(List<String> list1, List<String> list2) {
- Collections.sort(list1);
- Collections.sort(list2);
- Assert.assertEquals(list1, list2);
- }
-
private void createAndCheckoutBranch(ObjectId objectId, String branchName) throws IOException {
String fullBranchName = "refs/heads/" + branchName;
super.createBranch(objectId, fullBranchName);
diff --git a/javatests/com/google/gerrit/server/logging/LoggingContextAwareExecutorServiceTest.java b/javatests/com/google/gerrit/server/logging/LoggingContextAwareExecutorServiceTest.java
new file mode 100644
index 0000000..5117c01
--- /dev/null
+++ b/javatests/com/google/gerrit/server/logging/LoggingContextAwareExecutorServiceTest.java
@@ -0,0 +1,71 @@
+// Copyright (C) 2018 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.logging;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.truth.Expect;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class LoggingContextAwareExecutorServiceTest {
+ @Rule public final Expect expect = Expect.create();
+
+ @Test
+ public void loggingContextPropagationToBackgroundThread() throws Exception {
+ assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
+ assertForceLogging(false);
+ try (TraceContext traceContext = TraceContext.open().forceLogging().addTag("foo", "bar")) {
+ SortedMap<String, SortedSet<Object>> tagMap = LoggingContext.getInstance().getTags().asMap();
+ assertThat(tagMap.keySet()).containsExactly("foo");
+ assertThat(tagMap.get("foo")).containsExactly("bar");
+ assertForceLogging(true);
+
+ ExecutorService executor =
+ new LoggingContextAwareExecutorService(Executors.newFixedThreadPool(1));
+ executor
+ .submit(
+ () -> {
+ // Verify that the tags and force logging flag have been propagated to the new
+ // thread.
+ SortedMap<String, SortedSet<Object>> threadTagMap =
+ LoggingContext.getInstance().getTags().asMap();
+ expect.that(threadTagMap.keySet()).containsExactly("foo");
+ expect.that(threadTagMap.get("foo")).containsExactly("bar");
+ expect
+ .that(LoggingContext.getInstance().shouldForceLogging(null, null, false))
+ .isTrue();
+ })
+ .get();
+
+ // Verify that tags and force logging flag in the outer thread are still set.
+ tagMap = LoggingContext.getInstance().getTags().asMap();
+ assertThat(tagMap.keySet()).containsExactly("foo");
+ assertThat(tagMap.get("foo")).containsExactly("bar");
+ assertForceLogging(true);
+ }
+ assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
+ assertForceLogging(false);
+ }
+
+ private void assertForceLogging(boolean expected) {
+ assertThat(LoggingContext.getInstance().shouldForceLogging(null, null, false))
+ .isEqualTo(expected);
+ }
+}
diff --git a/javatests/com/google/gerrit/server/logging/LoggingContextAwareThreadFactoryTest.java b/javatests/com/google/gerrit/server/logging/LoggingContextAwareThreadFactoryTest.java
deleted file mode 100644
index 1164e27..0000000
--- a/javatests/com/google/gerrit/server/logging/LoggingContextAwareThreadFactoryTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright (C) 2018 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.logging;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.google.common.truth.Expect;
-import java.util.SortedMap;
-import java.util.SortedSet;
-import org.junit.Rule;
-import org.junit.Test;
-
-public class LoggingContextAwareThreadFactoryTest {
- @Rule public final Expect expect = Expect.create();
-
- @Test
- public void loggingContextPropagationToNewThread() throws Exception {
- assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
- assertForceLogging(false);
- try (TraceContext traceContext = TraceContext.open().forceLogging().addTag("foo", "bar")) {
- SortedMap<String, SortedSet<Object>> tagMap = LoggingContext.getInstance().getTags().asMap();
- assertThat(tagMap.keySet()).containsExactly("foo");
- assertThat(tagMap.get("foo")).containsExactly("bar");
- assertForceLogging(true);
-
- Thread thread =
- new LoggingContextAwareThreadFactory(r -> new Thread(r, "test-thread"))
- .newThread(
- () -> {
- // Verify that the tags and force logging flag have been propagated to the new
- // thread.
- SortedMap<String, SortedSet<Object>> threadTagMap =
- LoggingContext.getInstance().getTags().asMap();
- expect.that(threadTagMap.keySet()).containsExactly("foo");
- expect.that(threadTagMap.get("foo")).containsExactly("bar");
- expect
- .that(LoggingContext.getInstance().shouldForceLogging(null, null, false))
- .isTrue();
- });
-
- // Execute in background.
- thread.start();
- thread.join();
-
- // Verify that tags and force logging flag in the outer thread are still set.
- tagMap = LoggingContext.getInstance().getTags().asMap();
- assertThat(tagMap.keySet()).containsExactly("foo");
- assertThat(tagMap.get("foo")).containsExactly("bar");
- assertForceLogging(true);
- }
- assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
- assertForceLogging(false);
- }
-
- @Test
- public void loggingContextPropagationToSameThread() throws Exception {
- assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
- assertForceLogging(false);
- try (TraceContext traceContext = TraceContext.open().forceLogging().addTag("foo", "bar")) {
- SortedMap<String, SortedSet<Object>> tagMap = LoggingContext.getInstance().getTags().asMap();
- assertThat(tagMap.keySet()).containsExactly("foo");
- assertThat(tagMap.get("foo")).containsExactly("bar");
- assertForceLogging(true);
-
- Thread thread =
- new LoggingContextAwareThreadFactory()
- .newThread(
- () -> {
- // Verify that the tags and force logging flag have been propagated to the new
- // thread.
- SortedMap<String, SortedSet<Object>> threadTagMap =
- LoggingContext.getInstance().getTags().asMap();
- expect.that(threadTagMap.keySet()).containsExactly("foo");
- expect.that(threadTagMap.get("foo")).containsExactly("bar");
- expect
- .that(LoggingContext.getInstance().shouldForceLogging(null, null, false))
- .isTrue();
- });
-
- // Execute in the same thread.
- thread.run();
-
- // Verify that tags and force logging flag in the outer thread are still set.
- tagMap = LoggingContext.getInstance().getTags().asMap();
- assertThat(tagMap.keySet()).containsExactly("foo");
- assertThat(tagMap.get("foo")).containsExactly("bar");
- assertForceLogging(true);
- }
- assertThat(LoggingContext.getInstance().getTags().isEmpty()).isTrue();
- assertForceLogging(false);
- }
-
- private void assertForceLogging(boolean expected) {
- assertThat(LoggingContext.getInstance().shouldForceLogging(null, null, false))
- .isEqualTo(expected);
- }
-}
diff --git a/javatests/com/google/gerrit/server/logging/TraceContextTest.java b/javatests/com/google/gerrit/server/logging/TraceContextTest.java
index 7dfe14c..044d237 100644
--- a/javatests/com/google/gerrit/server/logging/TraceContextTest.java
+++ b/javatests/com/google/gerrit/server/logging/TraceContextTest.java
@@ -18,6 +18,7 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.server.logging.TraceContext.TraceIdConsumer;
import java.util.Map;
import java.util.SortedMap;
import java.util.SortedSet;
@@ -154,6 +155,87 @@
assertForceLogging(false);
}
+ @Test
+ public void newTrace() {
+ TestTraceIdConsumer traceIdConsumer = new TestTraceIdConsumer();
+ try (TraceContext traceContext = TraceContext.newTrace(true, null, traceIdConsumer)) {
+ assertForceLogging(true);
+ assertThat(LoggingContext.getInstance().getTagsAsMap().keySet())
+ .containsExactly(RequestId.Type.TRACE_ID.name());
+ }
+ assertThat(traceIdConsumer.tagName).isEqualTo(RequestId.Type.TRACE_ID.name());
+ assertThat(traceIdConsumer.traceId).isNotNull();
+ }
+
+ @Test
+ public void newTraceWithProvidedTraceId() {
+ TestTraceIdConsumer traceIdConsumer = new TestTraceIdConsumer();
+ String traceId = "foo";
+ try (TraceContext traceContext = TraceContext.newTrace(true, traceId, traceIdConsumer)) {
+ assertForceLogging(true);
+ assertTags(ImmutableMap.of(RequestId.Type.TRACE_ID.name(), ImmutableSet.of(traceId)));
+ }
+ assertThat(traceIdConsumer.tagName).isEqualTo(RequestId.Type.TRACE_ID.name());
+ assertThat(traceIdConsumer.traceId).isEqualTo(traceId);
+ }
+
+ @Test
+ public void newTraceDisabled() {
+ TestTraceIdConsumer traceIdConsumer = new TestTraceIdConsumer();
+ try (TraceContext traceContext = TraceContext.newTrace(false, null, traceIdConsumer)) {
+ assertForceLogging(false);
+ assertTags(ImmutableMap.of());
+ }
+ assertThat(traceIdConsumer.tagName).isNull();
+ assertThat(traceIdConsumer.traceId).isNull();
+ }
+
+ @Test
+ public void newTraceDisabledWithProvidedTraceId() {
+ TestTraceIdConsumer traceIdConsumer = new TestTraceIdConsumer();
+ try (TraceContext traceContext = TraceContext.newTrace(false, "foo", traceIdConsumer)) {
+ assertForceLogging(false);
+ assertTags(ImmutableMap.of());
+ }
+ assertThat(traceIdConsumer.tagName).isNull();
+ assertThat(traceIdConsumer.traceId).isNull();
+ }
+
+ @Test
+ public void onlyOneTraceId() {
+ TestTraceIdConsumer traceIdConsumer1 = new TestTraceIdConsumer();
+ try (TraceContext traceContext1 = TraceContext.newTrace(true, null, traceIdConsumer1)) {
+ String expectedTraceId = traceIdConsumer1.traceId;
+ assertThat(expectedTraceId).isNotNull();
+
+ TestTraceIdConsumer traceIdConsumer2 = new TestTraceIdConsumer();
+ try (TraceContext traceContext2 = TraceContext.newTrace(true, null, traceIdConsumer2)) {
+ assertForceLogging(true);
+ assertTags(
+ ImmutableMap.of(RequestId.Type.TRACE_ID.name(), ImmutableSet.of(expectedTraceId)));
+ }
+ assertThat(traceIdConsumer2.tagName).isEqualTo(RequestId.Type.TRACE_ID.name());
+ assertThat(traceIdConsumer2.traceId).isEqualTo(expectedTraceId);
+ }
+ }
+
+ @Test
+ public void multipleTraceIdsIfTraceIdProvided() {
+ String traceId1 = "foo";
+ try (TraceContext traceContext1 =
+ TraceContext.newTrace(true, traceId1, (tagName, traceId) -> {})) {
+ TestTraceIdConsumer traceIdConsumer = new TestTraceIdConsumer();
+ String traceId2 = "bar";
+ try (TraceContext traceContext2 = TraceContext.newTrace(true, traceId2, traceIdConsumer)) {
+ assertForceLogging(true);
+ assertTags(
+ ImmutableMap.of(RequestId.Type.TRACE_ID.name(), ImmutableSet.of(traceId1, traceId2)));
+ }
+ assertThat(traceIdConsumer.tagName).isEqualTo(RequestId.Type.TRACE_ID.name());
+ assertThat(traceIdConsumer.traceId).isEqualTo(traceId2);
+ }
+ }
+
private void assertTags(ImmutableMap<String, ImmutableSet<String>> expectedTagMap) {
SortedMap<String, SortedSet<Object>> actualTagMap =
LoggingContext.getInstance().getTags().asMap();
@@ -168,4 +250,15 @@
assertThat(LoggingContext.getInstance().shouldForceLogging(null, null, false))
.isEqualTo(expected);
}
+
+ private static class TestTraceIdConsumer implements TraceIdConsumer {
+ String tagName;
+ String traceId;
+
+ @Override
+ public void accept(String tagName, String traceId) {
+ this.tagName = tagName;
+ this.traceId = traceId;
+ }
+ }
}
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
index c774fc5..8cd2753 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
@@ -426,7 +426,7 @@
@Test
public void approvalsPostSubmit() throws Exception {
Change c = newChange();
- RequestId submissionId = RequestId.forChange(c);
+ RequestId submissionId = submissionId(c);
ChangeUpdate update = newUpdate(c, changeOwner);
update.putApproval("Code-Review", (short) 1);
update.putApproval("Verified", (short) 1);
@@ -461,7 +461,7 @@
@Test
public void approvalsDuringSubmit() throws Exception {
Change c = newChange();
- RequestId submissionId = RequestId.forChange(c);
+ RequestId submissionId = submissionId(c);
ChangeUpdate update = newUpdate(c, changeOwner);
update.putApproval("Code-Review", (short) 1);
update.putApproval("Verified", (short) 1);
@@ -598,7 +598,7 @@
@Test
public void submitRecords() throws Exception {
Change c = newChange();
- RequestId submissionId = RequestId.forChange(c);
+ RequestId submissionId = submissionId(c);
ChangeUpdate update = newUpdate(c, changeOwner);
update.setSubjectForCommit("Submit patch set 1");
@@ -640,7 +640,7 @@
@Test
public void latestSubmitRecordsOnly() throws Exception {
Change c = newChange();
- RequestId submissionId = RequestId.forChange(c);
+ RequestId submissionId = submissionId(c);
ChangeUpdate update = newUpdate(c, changeOwner);
update.setSubjectForCommit("Submit patch set 1");
update.merge(
@@ -941,7 +941,7 @@
// Finish off by merging the change.
update = newUpdate(c, changeOwner);
update.merge(
- RequestId.forChange(c),
+ submissionId(c),
ImmutableList.of(
submitRecord(
"NOT_READY",
@@ -3141,4 +3141,8 @@
update.commit();
return tr.parseBody(commit);
}
+
+ private RequestId submissionId(Change c) {
+ return new RequestId(c.getId().toString());
+ }
}
diff --git a/javatests/com/google/gerrit/server/notedb/CommitMessageOutputTest.java b/javatests/com/google/gerrit/server/notedb/CommitMessageOutputTest.java
index 43e2602..8daf67f 100644
--- a/javatests/com/google/gerrit/server/notedb/CommitMessageOutputTest.java
+++ b/javatests/com/google/gerrit/server/notedb/CommitMessageOutputTest.java
@@ -151,7 +151,7 @@
ChangeUpdate update = newUpdate(c, changeOwner);
update.setSubjectForCommit("Submit patch set 1");
- RequestId submissionId = RequestId.forChange(c);
+ RequestId submissionId = submissionId(c);
update.merge(
submissionId,
ImmutableList.of(
@@ -220,7 +220,7 @@
ChangeUpdate update = newUpdate(c, changeOwner);
update.setSubjectForCommit("Submit patch set 1");
- RequestId submissionId = RequestId.forChange(c);
+ RequestId submissionId = submissionId(c);
update.merge(
submissionId, ImmutableList.of(submitRecord("RULE_ERROR", "Problem with patch set:\n1")));
update.commit();
@@ -424,4 +424,8 @@
RevCommit commit = parseCommit(commitId);
assertThat(commit.getFullMessage()).isEqualTo(expected);
}
+
+ private RequestId submissionId(Change c) {
+ return new RequestId(c.getId().toString());
+ }
}
diff --git a/javatests/com/google/gerrit/server/rules/BUILD b/javatests/com/google/gerrit/server/rules/BUILD
index 42452df..8f4c90d 100644
--- a/javatests/com/google/gerrit/server/rules/BUILD
+++ b/javatests/com/google/gerrit/server/rules/BUILD
@@ -7,9 +7,11 @@
resources = ["//prologtests:gerrit_common_test"],
deps = [
"//java/com/google/gerrit/common:server",
+ "//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/project/testing:project-test-util",
"//java/com/google/gerrit/testing:gerrit-test-util",
+ "//lib:guava",
"//lib/guice",
"//lib/jgit/org.eclipse.jgit:jgit",
"//lib/prolog:runtime",
diff --git a/javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java b/javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java
new file mode 100644
index 0000000..27f4423
--- /dev/null
+++ b/javatests/com/google/gerrit/server/rules/IgnoreSelfApprovalRuleTest.java
@@ -0,0 +1,96 @@
+// Copyright (C) 2018 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.rules;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.common.data.LabelValue;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.LabelId;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.reviewdb.client.PatchSetApproval;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import org.junit.Test;
+
+public class IgnoreSelfApprovalRuleTest {
+ private static final Change.Id CHANGE_ID = new Change.Id(100);
+ private static final PatchSet.Id PS_ID = new PatchSet.Id(CHANGE_ID, 1);
+ private static final LabelType VERIFIED = makeLabel("Verified");
+ private static final Account.Id USER1 = makeAccount(100001);
+
+ @Test
+ public void filtersByLabel() {
+ LabelType codeReview = makeLabel("Code-Review");
+ PatchSetApproval approvalVerified = makeApproval(VERIFIED.getLabelId(), USER1, 2);
+ PatchSetApproval approvalCr = makeApproval(codeReview.getLabelId(), USER1, 2);
+
+ Collection<PatchSetApproval> filteredApprovals =
+ IgnoreSelfApprovalRule.filterApprovalsByLabel(
+ ImmutableList.of(approvalVerified, approvalCr), VERIFIED);
+
+ assertThat(filteredApprovals).containsExactly(approvalVerified);
+ }
+
+ @Test
+ public void filtersVotesFromUser() {
+ PatchSetApproval approvalM2 = makeApproval(VERIFIED.getLabelId(), USER1, -2);
+ PatchSetApproval approvalM1 = makeApproval(VERIFIED.getLabelId(), USER1, -1);
+
+ ImmutableList<PatchSetApproval> approvals =
+ ImmutableList.of(
+ approvalM2,
+ approvalM1,
+ makeApproval(VERIFIED.getLabelId(), USER1, 0),
+ makeApproval(VERIFIED.getLabelId(), USER1, +1),
+ makeApproval(VERIFIED.getLabelId(), USER1, +2));
+
+ Collection<PatchSetApproval> filteredApprovals =
+ IgnoreSelfApprovalRule.filterOutPositiveApprovalsOfUser(approvals, USER1);
+
+ assertThat(filteredApprovals).containsExactly(approvalM1, approvalM2);
+ }
+
+ private static LabelType makeLabel(String labelName) {
+ List<LabelValue> values = new ArrayList<>();
+ // The label text is irrelevant here, only the numerical value is used
+ values.add(new LabelValue((short) -2, "-2"));
+ values.add(new LabelValue((short) -1, "-1"));
+ values.add(new LabelValue((short) 0, "No vote."));
+ values.add(new LabelValue((short) 1, "+1"));
+ values.add(new LabelValue((short) 2, "+2"));
+ return new LabelType(labelName, values);
+ }
+
+ private static PatchSetApproval makeApproval(LabelId labelId, Account.Id accountId, int value) {
+ PatchSetApproval.Key key = makeKey(PS_ID, accountId, labelId);
+ return new PatchSetApproval(key, (short) value, Date.from(Instant.now()));
+ }
+
+ private static PatchSetApproval.Key makeKey(
+ PatchSet.Id psId, Account.Id accountId, LabelId labelId) {
+ return new PatchSetApproval.Key(psId, accountId, labelId);
+ }
+
+ private static Account.Id makeAccount(int account) {
+ return new Account.Id(account);
+ }
+}
diff --git a/javatests/com/google/gerrit/server/schema/Schema_166_to_167_WithGroupsInReviewDbTest.java b/javatests/com/google/gerrit/server/schema/Schema_166_to_167_WithGroupsInReviewDbTest.java
index 9256ee4..75f9307 100644
--- a/javatests/com/google/gerrit/server/schema/Schema_166_to_167_WithGroupsInReviewDbTest.java
+++ b/javatests/com/google/gerrit/server/schema/Schema_166_to_167_WithGroupsInReviewDbTest.java
@@ -586,7 +586,7 @@
AccountGroup group = createInReviewDb("group");
TestGroupBackend testGroupBackend = new TestGroupBackend();
- backends.add(testGroupBackend);
+ backends.add("gerrit", testGroupBackend);
AccountGroup.UUID subgroupUuid = testGroupBackend.create("test").getGroupUUID();
assertThat(groupBackend.handles(subgroupUuid)).isTrue();
addSubgroupsInReviewDb(group.getId(), subgroupUuid);
diff --git a/javatests/com/google/gerrit/util/http/testutil/FakeHttpServletResponse.java b/javatests/com/google/gerrit/util/http/testutil/FakeHttpServletResponse.java
index 2b1a07e..f6b3e30 100644
--- a/javatests/com/google/gerrit/util/http/testutil/FakeHttpServletResponse.java
+++ b/javatests/com/google/gerrit/util/http/testutil/FakeHttpServletResponse.java
@@ -25,6 +25,7 @@
import com.google.common.net.HttpHeaders;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.util.Collection;
@@ -106,7 +107,7 @@
public synchronized PrintWriter getWriter() {
checkState(outputStream == null, "getOutputStream() already called");
if (writer == null) {
- writer = new PrintWriter(actualBody);
+ writer = new PrintWriter(new OutputStreamWriter(actualBody, UTF_8));
}
return writer;
}
diff --git a/lib/LICENSE-Apache1.1 b/lib/LICENSE-Apache1.1
deleted file mode 100644
index 8eda4fc..0000000
--- a/lib/LICENSE-Apache1.1
+++ /dev/null
@@ -1,51 +0,0 @@
-The Apache Software License, Version 1.1
-
-Copyright (c) 2000-2002 The Apache Software Foundation. All rights
-reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
-
-3. The end-user documentation included with the redistribution,
- if any, must include the following acknowledgment:
- "This product includes software developed by the
- Apache Software Foundation (http://www.apache.org/)."
- Alternately, this acknowledgment may appear in the software itself,
- if and wherever such third-party acknowledgments normally appear.
-
-4. The names "Apache" and "Apache Software Foundation", "Jakarta-Oro"
- must not be used to endorse or promote products derived from this
- software without prior written permission. For written
- permission, please contact apache@apache.org.
-
-5. Products derived from this software may not be called "Apache"
- or "Jakarta-Oro", nor may "Apache" or "Jakarta-Oro" appear in their
- name, without prior written permission of the Apache Software Foundation.
-
-THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
-WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
-ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
-USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-====================================================================
-
-This software consists of voluntary contributions made by many
-individuals on behalf of the Apache Software Foundation. For more
-information on the Apache Software Foundation, please see
-<http://www.apache.org/>.
diff --git a/lib/jgit/jgit.bzl b/lib/jgit/jgit.bzl
index 6ada5bd..7191901 100644
--- a/lib/jgit/jgit.bzl
+++ b/lib/jgit/jgit.bzl
@@ -1,6 +1,6 @@
load("//tools/bzl:maven_jar.bzl", "GERRIT", "MAVEN_CENTRAL", "MAVEN_LOCAL", "maven_jar")
-_JGIT_VERS = "5.0.2.201807311906-r"
+_JGIT_VERS = "5.0.3.201809091024-r"
_DOC_VERS = _JGIT_VERS # Set to _JGIT_VERS unless using a snapshot
@@ -40,28 +40,28 @@
name = "jgit-lib",
artifact = "org.eclipse.jgit:org.eclipse.jgit:" + _JGIT_VERS,
repository = _JGIT_REPO,
- sha1 = "a81d7c8d153a8a744b6be1d9c6d698270beec1c0",
- src_sha1 = "c89f8f38cebaf75d13f9b2f7a1da71206d8c38f7",
+ sha1 = "0afec2df3ff8835bc4d5c279d14fad0daae6dd93",
+ src_sha1 = "e2c978064e2a46b260bbda0d8c393ed741046420",
unsign = True,
)
maven_jar(
name = "jgit-servlet",
artifact = "org.eclipse.jgit:org.eclipse.jgit.http.server:" + _JGIT_VERS,
repository = _JGIT_REPO,
- sha1 = "ab3d0c85bc2008da513c1127ab4acf3df8ef414e",
+ sha1 = "8fb0f9b6c38ac6fce60f2ead740e03dd79c3c288",
unsign = True,
)
maven_jar(
name = "jgit-archive",
artifact = "org.eclipse.jgit:org.eclipse.jgit.archive:" + _JGIT_VERS,
repository = _JGIT_REPO,
- sha1 = "ba6e0aaf3f733f2f460e227145526e1737ca160f",
+ sha1 = "72a157ce261f3eb938d9e0ee83d7c9700aa7d736",
)
maven_jar(
name = "jgit-junit",
artifact = "org.eclipse.jgit:org.eclipse.jgit.junit:" + _JGIT_VERS,
repository = _JGIT_REPO,
- sha1 = "fe28963520e19c918eb26747e678ec9772ba800f",
+ sha1 = "eb430358d96dedd923e4075cd54a7db4cab51ca2",
unsign = True,
)
diff --git a/plugins/BUILD b/plugins/BUILD
index ef18e01..a7622b2 100644
--- a/plugins/BUILD
+++ b/plugins/BUILD
@@ -27,17 +27,19 @@
]
EXPORTS = [
+ "//antlr3:query_parser",
"//java/com/google/gerrit/common:annotations",
"//java/com/google/gerrit/common:server",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/index",
"//java/com/google/gerrit/index:query_exception",
- "//java/com/google/gerrit/index:query_parser",
"//java/com/google/gerrit/lifecycle",
"//java/com/google/gerrit/metrics",
"//java/com/google/gerrit/metrics/dropwizard",
"//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server/audit",
+ "//java/com/google/gerrit/server/logging",
+ "//java/com/google/gerrit/server/schema",
"//java/com/google/gerrit/util/http",
"//lib/commons:compress",
"//lib/commons:dbcp",
@@ -100,13 +102,13 @@
main_class = "Dummy",
visibility = ["//visibility:public"],
runtime_deps = [
+ "//antlr3:libquery_parser-src.jar",
"//java/com/google/gerrit/common:libannotations-src.jar",
"//java/com/google/gerrit/common:libserver-src.jar",
"//java/com/google/gerrit/extensions:libapi-src.jar",
"//java/com/google/gerrit/httpd:libhttpd-src.jar",
"//java/com/google/gerrit/index:libindex-src.jar",
"//java/com/google/gerrit/index:libquery_exception-src.jar",
- "//java/com/google/gerrit/index:libquery_parser-src.jar",
"//java/com/google/gerrit/pgm/init/api:libapi-src.jar",
"//java/com/google/gerrit/reviewdb:libserver-src.jar",
"//java/com/google/gerrit/server:libserver-src.jar",
@@ -121,9 +123,9 @@
java_doc(
name = "plugin-api-javadoc",
libs = PLUGIN_API + [
+ "//antlr3:query_parser",
"//java/com/google/gerrit/index",
"//java/com/google/gerrit/index:query_exception",
- "//java/com/google/gerrit/index:query_parser",
"//java/com/google/gerrit/common:annotations",
"//java/com/google/gerrit/common:server",
"//java/com/google/gerrit/extensions:api",
diff --git a/plugins/codemirror-editor b/plugins/codemirror-editor
index 4ebf98c..22342a6 160000
--- a/plugins/codemirror-editor
+++ b/plugins/codemirror-editor
@@ -1 +1 @@
-Subproject commit 4ebf98c77086477a5fa63e339a539b47d4e8d202
+Subproject commit 22342a6da26c75b14bc629331c339d1b820b4d39
diff --git a/plugins/hooks b/plugins/hooks
index 07672f3..cc74144 160000
--- a/plugins/hooks
+++ b/plugins/hooks
@@ -1 +1 @@
-Subproject commit 07672f31880ba80300b38492df9d0acfcd6ee00a
+Subproject commit cc74144db755a18c5a63764a336b93ab3d1be1fe
diff --git a/plugins/replication b/plugins/replication
index b62f006..d557ccc 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit b62f006b1350180de0af02c82fb18fb290a2548f
+Subproject commit d557ccc642c59a55750f560ce0d98870e1550d65
diff --git a/plugins/singleusergroup b/plugins/singleusergroup
index e4024e9..cc636d7 160000
--- a/plugins/singleusergroup
+++ b/plugins/singleusergroup
@@ -1 +1 @@
-Subproject commit e4024e9d8d8139fc4c658c3af1a5e11e19b2d476
+Subproject commit cc636d7e36afb62455a9f045b125d246fd84afd0
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
index e905e038..ebd72ad 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
@@ -122,7 +122,7 @@
_changeComments: Object,
_canStartReview: {
type: Boolean,
- computed: '_computeCanStartReview(_loggedIn, _change, _account)',
+ computed: '_computeCanStartReview(_change)',
},
_comments: Object,
/** @type {?} */
@@ -1341,9 +1341,9 @@
});
},
- _computeCanStartReview(loggedIn, change, account) {
- return !!(loggedIn && change.work_in_progress &&
- change.owner._account_id === account._account_id);
+ _computeCanStartReview(change) {
+ return !!(change.actions && change.actions.ready &&
+ change.actions.ready.enabled);
},
_computeReplyDisabled() { return false; },
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
index b5b8cd9..4e6cb6e 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
@@ -1411,18 +1411,24 @@
});
test('canStartReview computation', () => {
- const account1 = {_account_id: 1};
- const account2 = {_account_id: 2};
- const change = {
- owner: {_account_id: 1},
+ const change1 = {};
+ const change2 = {
+ actions: {
+ ready: {
+ enabled: true,
+ },
+ },
};
- assert.isFalse(element._computeCanStartReview(true, change, account1));
- change.work_in_progress = false;
- assert.isFalse(element._computeCanStartReview(true, change, account1));
- change.work_in_progress = true;
- assert.isTrue(element._computeCanStartReview(true, change, account1));
- assert.isFalse(element._computeCanStartReview(false, change, account1));
- assert.isFalse(element._computeCanStartReview(true, change, account2));
+ const change3 = {
+ actions: {
+ ready: {
+ label: 'Ready for Review',
+ },
+ },
+ };
+ assert.isFalse(element._computeCanStartReview(change1));
+ assert.isTrue(element._computeCanStartReview(change2));
+ assert.isFalse(element._computeCanStartReview(change3));
});
});
diff --git a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.html b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.html
index 8ca40e7..9996abc 100644
--- a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.html
@@ -40,7 +40,7 @@
setup(() => {
element = fixture('basic');
sandbox = sinon.sandbox.create();
- sandbox.stub(Gerrit.Nav, 'overrideCommentlinks', x => x);
+ sandbox.stub(Gerrit.Nav, 'mapCommentlinks', x => x);
});
teardown(() => { sandbox.restore(); });
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message.html b/polygerrit-ui/app/elements/change/gr-message/gr-message.html
index 5608dec..00157ab 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message.html
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message.html
@@ -178,34 +178,20 @@
<gr-account-label
account="[[author]]"
hide-avatar></gr-account-label>
- <template is="dom-if" if="[[_successfulParse]]">
- <template is="dom-if" if="[[_parsedVotes.length]]">voted</template>
- <template is="dom-repeat" items="[[_parsedVotes]]" as="score">
- <span class$="score [[_computeScoreClass(score, labelExtremes)]]">
- [[score.label]] [[score.value]]
- </span>
- </template>
- [[_computeConversationalString(_parsedVotes, _parsedPatchNum, _parsedCommentCount)]]
+ <template is="dom-repeat" items="[[_getScores(message)]]" as="score">
+ <span class$="score [[_computeScoreClass(score, labelExtremes)]]">
+ [[score.label]] [[score.value]]
+ </span>
</template>
</div>
<template is="dom-if" if="[[message.message]]">
<div class="content">
- <template is="dom-if" if="[[_successfulParse]]">
- <div class="message hideOnOpen">[[_parsedChangeMessage]]</div>
- <gr-formatted-text
- no-trailing-margin
- class="message hideOnCollapsed"
- content="[[_parsedChangeMessage]]"
- config="[[_projectConfig.commentlinks]]"></gr-formatted-text>
- </template>
- <template is="dom-if" if="[[!_successfulParse]]">
- <div class="message hideOnOpen">[[message.message]]</div>
- <gr-formatted-text
- no-trailing-margin
- class="message hideOnCollapsed"
- content="[[message.message]]"
- config="[[_projectConfig.commentlinks]]"></gr-formatted-text>
- </template>
+ <div class="message hideOnOpen">[[message.message]]</div>
+ <gr-formatted-text
+ no-trailing-margin
+ class="message hideOnCollapsed"
+ content="[[message.message]]"
+ config="[[_projectConfig.commentlinks]]"></gr-formatted-text>
<div class="replyContainer" hidden$="[[!showReplyButton]]" hidden>
<gr-button link small on-tap="_handleReplyTap">Reply</gr-button>
</div>
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message.js b/polygerrit-ui/app/elements/change/gr-message/gr-message.js
index addd660..0590c73 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message.js
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message.js
@@ -17,8 +17,7 @@
(function() {
'use strict';
- const PATCH_SET_PREFIX_PATTERN = /^Patch Set (\d)+:[ ]?/;
- const COMMENTS_COUNT_PATTERN = /^\((\d+)( inline)? comments?\)$/;
+ const PATCH_SET_PREFIX_PATTERN = /^Patch Set \d+: /;
const LABEL_TITLE_SCORE_PATTERN = /^([A-Za-z0-9-]+)([+-]\d+)$/;
Polymer({
@@ -102,17 +101,10 @@
type: Boolean,
value: false,
},
-
- _parsedPatchNum: String,
- _parsedCommentCount: String,
- _parsedVotes: Array,
- _parsedChangeMessage: String,
- _successfulParse: Boolean,
},
observers: [
'_updateExpandedClass(message.expanded)',
- '_consumeMessage(message.message)',
],
ready() {
@@ -192,6 +184,19 @@
return event.type === 'REVIEWER_UPDATE';
},
+ _getScores(message) {
+ if (!message.message) { return []; }
+ const line = message.message.split('\n', 1)[0];
+ const patchSetPrefix = PATCH_SET_PREFIX_PATTERN;
+ if (!line.match(patchSetPrefix)) { return []; }
+ const scoresRaw = line.split(patchSetPrefix)[1];
+ if (!scoresRaw) { return []; }
+ return scoresRaw.split(' ')
+ .map(s => s.match(LABEL_TITLE_SCORE_PATTERN))
+ .filter(ms => ms && ms.length === 3)
+ .map(ms => ({label: ms[1], value: ms[2]}));
+ },
+
_computeScoreClass(score, labelExtremes) {
const classes = [];
if (score.value > 0) {
@@ -255,73 +260,5 @@
e.stopPropagation();
this.set('message.expanded', !this.message.expanded);
},
-
- /**
- * Attempts to consume a change message to create a shorter and more legible
- * format. If the function encounters unexpected characters at any point, it
- * sets the _successfulParse flag to false and terminates, causing the UI to
- * fall back to displaying the entirety of the change message.
- *
- * A successful parse results in a one-liner that reads:
- * `${AVATAR} voted ${VOTES} and left ${NUM} comment(s) on ${PATCHSET}`
- *
- * @param {string} text
- */
- _consumeMessage(text) {
- this._parsedPatchNum = '';
- this._parsedCommentCount = '';
- this._parsedChangeMessage = '';
- this._parsedVotes = [];
- if (!text) {
- // No message body means nothing to parse.
- this._successfulParse = false;
- return;
- }
- const lines = text.split('\n');
- const messageLines = lines.shift().split(PATCH_SET_PREFIX_PATTERN);
- if (!messageLines[1]) {
- // Message is in an unexpected format.
- this._successfulParse = false;
- return;
- }
- this._parsedPatchNum = messageLines[1];
- if (messageLines[2]) {
- // Content after the colon is always vote information. If it is in the
- // most up to date schema, parse it. Otherwise, cancel the parsing
- // completely.
- let match;
- for (const score of messageLines[2].split(' ')) {
- match = score.match(LABEL_TITLE_SCORE_PATTERN);
- if (!match || match.length !== 3) {
- this._successfulParse = false;
- return;
- }
- this._parsedVotes.push({label: match[1], value: match[2]});
- }
- }
- // Remove empty line.
- lines.shift();
- if (lines.length) {
- const commentMatch = lines[0].match(COMMENTS_COUNT_PATTERN);
- if (commentMatch) {
- this._parsedCommentCount = commentMatch[1];
- // Remove comment line and the following empty line.
- lines.splice(0, 2);
- }
- this._parsedChangeMessage = lines.join('\n');
- }
- this._successfulParse = true;
- },
-
- _computeConversationalString(votes, patchNum, commentCount) {
- let clause = ' on Patch Set ' + patchNum;
- if (commentCount) {
- let commentStr = ' comment';
- if (parseInt(commentCount, 10) > 1) { commentStr += 's'; }
- clause = ' left ' + commentCount + commentStr + clause;
- if (votes.length) { clause = ' and' + clause; }
- }
- return clause;
- },
});
})();
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html b/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html
index 6bb4618..870f366 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html
@@ -169,9 +169,7 @@
author: {},
expanded: false,
message: 'Patch Set 1: Verified+1 Code-Review-2 Trybot-Ready+1',
- _revision_number: 1,
};
- element.comments = {};
element.labelExtremes = {
'Verified': {max: 1, min: -1},
'Code-Review': {max: 2, min: -2},
@@ -201,145 +199,5 @@
const scoreChips = Polymer.dom(element.root).querySelectorAll('.score');
assert.equal(scoreChips.length, 0);
});
-
- suite('_consumeMessage', () => {
- const assertConsumeFailed = str => {
- element._consumeMessage(str);
- assert.isFalse(element._successfulParse);
- };
-
- test('no message body', () => {
- assertConsumeFailed('');
- });
-
- test('known old schema', () => {
- const str = 'Patch Set 1: Looks good to me, approved; Verified';
- assertConsumeFailed(str);
- });
-
- test('known old schema 2', () => {
- const str = [
- 'Patch Set 2: Looks good to me',
- '',
- 'Patch set 2 compiles, and runs as expected.',
- ].join('\n');
- assertConsumeFailed(str);
- });
-
- test('known old schema 3', () => {
- const str = 'Patch Set 2: (1 inline comment)';
- assertConsumeFailed(str);
- });
-
- test('known old schema 4', () => {
- const str = [
- 'Patch Set 2: Looks good to me',
- '',
- '(1 inline comment)',
- ].join('\n');
- assertConsumeFailed(str);
- });
-
- test('just change message', () => {
- const str = [
- 'Patch Set 2:',
- '',
- 'I think you should reconsider this approach.',
- 'It really makes no sense.',
- ].join('\n');
-
- element._consumeMessage(str);
-
- assert.isTrue(element._successfulParse);
- assert.equal(element._parsedPatchNum, '2');
- assert.equal(element._parsedCommentCount, '');
- assert.deepEqual(element._parsedVotes, []);
- assert.equal(element._parsedChangeMessage, [
- 'I think you should reconsider this approach.',
- 'It really makes no sense.',
- ].join('\n'));
- });
-
- test('just votes', () => {
- element._consumeMessage('Patch Set 2: Code-Review-Label+1 Verified+1');
-
- assert.isTrue(element._successfulParse);
- assert.equal(element._parsedPatchNum, '2');
- assert.equal(element._parsedCommentCount, '');
- assert.deepEqual(element._parsedVotes, [
- {label: 'Code-Review-Label', value: '+1'},
- {label: 'Verified', value: '+1'},
- ]);
- assert.equal(element._parsedChangeMessage, '');
- });
-
- test('just comments', () => {
- const str = [
- 'Patch Set 2:',
- '',
- '(8 comments)',
- ].join('\n');
-
- element._consumeMessage(str);
-
- assert.isTrue(element._successfulParse);
- assert.equal(element._parsedPatchNum, '2');
- assert.equal(element._parsedCommentCount, '8');
- assert.deepEqual(element._parsedVotes, []);
- assert.equal(element._parsedChangeMessage, '');
- });
-
- test('vote with comments and change message', () => {
- const str = [
- 'Patch Set 2: Code-Review-Label+1 Verified+1',
- '',
- '(1 comment)',
- '',
- 'LGTM',
- '',
- 'Nice work, just one nit.',
- ].join('\n');
-
- element._consumeMessage(str);
-
- assert.isTrue(element._successfulParse);
- assert.equal(element._parsedPatchNum, '2');
- assert.equal(element._parsedCommentCount, '1');
- assert.deepEqual(element._parsedVotes, [
- {label: 'Code-Review-Label', value: '+1'},
- {label: 'Verified', value: '+1'},
- ]);
- assert.equal(element._parsedChangeMessage, [
- 'LGTM',
- '',
- 'Nice work, just one nit.',
- ].join('\n'));
- });
-
- test('vote with change message', () => {
- const str = [
- 'Patch Set 2: Code-Review-Label+2 Verified+1',
- '',
- 'LGTM',
- '',
- 'Nice work.',
- ].join('\n');
-
- element._consumeMessage(str);
-
- assert.isTrue(element._successfulParse);
- assert.equal(element._parsedPatchNum, '2');
- assert.equal(element._parsedCommentCount, '');
- assert.deepEqual(element._parsedVotes, [
- {label: 'Code-Review-Label', value: '+2'},
- {label: 'Verified', value: '+1'},
- ]);
- assert.equal(element._parsedChangeMessage, [
- 'LGTM',
- '',
- 'Nice work.',
- ].join('\n'));
- });
- });
});
</script>
diff --git a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
index aa04194..c5e32cc 100644
--- a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
+++ b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
@@ -138,7 +138,7 @@
_generateWeblinks: uninitialized,
/** @type {Function} */
- overrideCommentlinks: uninitialized,
+ mapCommentlinks: uninitialized,
/**
* @param {number=} patchNum
@@ -152,23 +152,38 @@
/**
* Setup router implementation.
- * @param {Function} navigate
- * @param {Function} generateUrl
- * @param {Function} generateWeblinks
- * @param {Function} overrideCommentlinks
+ * @param {function(!string)} navigate the router-abstracted equivalent of
+ * `window.location.href = ...`. Takes a string.
+ * @param {function(!Object): string} generateUrl generates a URL given
+ * navigation parameters, detailed in the file header.
+ * @param {function(!Object): string} generateWeblinks weblinks generator
+ * function takes single payload parameter with type property that
+ * determines which
+ * part of the UI is the consumer of the weblinks. type property can
+ * be one of file, change, or patchset.
+ * - For file type, payload will also contain string properties: repo,
+ * commit, file.
+ * - For patchset type, payload will also contain string properties:
+ * repo, commit.
+ * - For change type, payload will also contain string properties:
+ * repo, commit. If server provides weblinks, those will be passed
+ * as options.weblinks property on the main payload object.
+ * @param {function(!Object): Object} mapCommentlinks provides an escape
+ * hatch to modify the commentlinks object, e.g. if it contains any
+ * relative URLs.
*/
- setup(navigate, generateUrl, generateWeblinks, overrideCommentlinks) {
+ setup(navigate, generateUrl, generateWeblinks, mapCommentlinks) {
this._navigate = navigate;
this._generateUrl = generateUrl;
this._generateWeblinks = generateWeblinks;
- this.overrideCommentlinks = overrideCommentlinks;
+ this.mapCommentlinks = mapCommentlinks;
},
destroy() {
this._navigate = uninitialized;
this._generateUrl = uninitialized;
this._generateWeblinks = uninitialized;
- this.overrideCommentlinks = uninitialized;
+ this.mapCommentlinks = uninitialized;
},
/**
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
index 4f0a73b..9f1b412 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
@@ -96,7 +96,10 @@
reflectToAttribute: true,
},
noRenderOnPrefsChange: Boolean,
- comments: Object,
+ comments: {
+ type: Object,
+ value: {left: [], right: []},
+ },
lineWrapping: {
type: Boolean,
value: false,
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.js b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.js
index 091cb75..530da02 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.js
+++ b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.js
@@ -60,7 +60,7 @@
* commentLink patterns
*/
_contentOrConfigChanged(content, config) {
- config = Gerrit.Nav.overrideCommentlinks(config);
+ config = Gerrit.Nav.mapCommentlinks(config);
const output = Polymer.dom(this.$.output);
output.textContent = '';
const parser = new GrLinkTextParser(config,
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html
index fc76da5..23c1442 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html
@@ -44,7 +44,7 @@
setup(() => {
element = fixture('basic');
sandbox = sinon.sandbox.create();
- sandbox.stub(Gerrit.Nav, 'overrideCommentlinks', x => x);
+ sandbox.stub(Gerrit.Nav, 'mapCommentlinks', x => x);
element.config = {
ph: {
match: '([Bb]ug|[Ii]ssue)\\s*#?(\\d+)',
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index 84c1803..af10b8371 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -1498,11 +1498,28 @@
*/
getRepos(filter, reposPerPage, opt_offset) {
const defaultFilter = 'state:active OR state:read-only';
+ const namePartDelimiters = /[@.\-\s\/_]/g;
const offset = opt_offset || 0;
+ if (filter && !filter.includes(':') && filter.match(namePartDelimiters)) {
+ // The query language specifies hyphens as operators. Split the string
+ // by hyphens and 'AND' the parts together as 'inname:' queries.
+ // If the filter includes a semicolon, the user is using a more complex
+ // query so we trust them and don't do any magic under the hood.
+ const originalFilter = filter;
+ filter = '';
+ originalFilter.split(namePartDelimiters).forEach(part => {
+ if (part) {
+ filter += (filter === '' ? 'inname:' : ' AND inname:') + part;
+ }
+ });
+ }
+ // Check if filter is now empty which could be either because the user did
+ // not provide it or because the user provided only a split character.
if (!filter) {
filter = defaultFilter;
}
+
filter = filter.trim();
const encodedFilter = encodeURIComponent(filter);
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
index b7466ef..d9656e4 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
@@ -951,16 +951,46 @@
'/projects/?n=26&S=25&query=test');
});
- test('with filter', () => {
- element.getRepos('test/test/test', 25);
+ test('with blank', () => {
+ element.getRepos('test/test', 25);
assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
- '/projects/?n=26&S=0&query=test%2Ftest%2Ftest');
+ '/projects/?n=26&S=0&query=inname%3Atest%20AND%20inname%3Atest');
});
- test('with regex filter', () => {
- element.getRepos('^test.*', 25);
+ test('with hyphen', () => {
+ element.getRepos('foo-bar', 25);
assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
- '/projects/?n=26&S=0&query=%5Etest.*');
+ '/projects/?n=26&S=0&query=inname%3Afoo%20AND%20inname%3Abar');
+ });
+
+ test('with leading hyphen', () => {
+ element.getRepos('-bar', 25);
+ assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ '/projects/?n=26&S=0&query=inname%3Abar');
+ });
+
+ test('with trailing hyphen', () => {
+ element.getRepos('foo-bar-', 25);
+ assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ '/projects/?n=26&S=0&query=inname%3Afoo%20AND%20inname%3Abar');
+ });
+
+ test('with underscore', () => {
+ element.getRepos('foo_bar', 25);
+ assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ '/projects/?n=26&S=0&query=inname%3Afoo%20AND%20inname%3Abar');
+ });
+
+ test('with underscore', () => {
+ element.getRepos('foo_bar', 25);
+ assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ '/projects/?n=26&S=0&query=inname%3Afoo%20AND%20inname%3Abar');
+ });
+
+ test('hyphen only', () => {
+ element.getRepos('-', 25);
+ assert.equal(element._fetchSharedCacheURL.lastCall.args[0].url,
+ `/projects/?n=26&S=0&query=${defaultQuery}`);
});
});
diff --git a/tools/BUILD b/tools/BUILD
index 53f441a..73ecfb9 100644
--- a/tools/BUILD
+++ b/tools/BUILD
@@ -68,7 +68,7 @@
"-Xep:CannotMockFinalClass:WARN",
"-Xep:ClassCanBeStatic:WARN",
"-Xep:ClassNewInstance:WARN",
- "-Xep:DefaultCharset:WARN",
+ "-Xep:DefaultCharset:ERROR",
"-Xep:DoubleCheckedLocking:WARN",
"-Xep:ElementsCountedInLoop:WARN",
"-Xep:EqualsHashCode:WARN",
diff --git a/tools/bzl/gwt.bzl b/tools/bzl/gwt.bzl
index 2adb7dd..b185214 100644
--- a/tools/bzl/gwt.bzl
+++ b/tools/bzl/gwt.bzl
@@ -15,7 +15,7 @@
load("//tools/bzl:genrule2.bzl", "genrule2")
load("//tools/bzl:java.bzl", "java_library2")
-jar_filetype = FileType([".jar"])
+jar_filetype = [".jar"]
BROWSERS = [
"chrome",
@@ -225,7 +225,7 @@
default = Label("@bazel_tools//tools/zip:zipper"),
cfg = "host",
executable = True,
- single_file = True,
+ allow_single_file = True,
),
},
outputs = {
diff --git a/tools/bzl/js.bzl b/tools/bzl/js.bzl
index d6d0c95..0997bcb 100644
--- a/tools/bzl/js.bzl
+++ b/tools/bzl/js.bzl
@@ -426,7 +426,7 @@
def bundle_assets(*args, **kwargs):
"""Combine html, js, css files and optionally split into js and html bundles."""
- _bundle_rule(*args, pkg = PACKAGE_NAME, **kwargs)
+ _bundle_rule(*args, pkg = native.package_name(), **kwargs)
def polygerrit_plugin(name, app, srcs = [], assets = None, **kwargs):
"""Bundles plugin dependencies for deployment.
@@ -447,7 +447,7 @@
name = name + "_combined",
app = app,
srcs = srcs if app in srcs else srcs + [app],
- pkg = PACKAGE_NAME,
+ pkg = native.package_name(),
**kwargs
)
diff --git a/tools/bzl/license-map.py b/tools/bzl/license-map.py
index 476ccb9..ebe57f2 100644
--- a/tools/bzl/license-map.py
+++ b/tools/bzl/license-map.py
@@ -35,7 +35,7 @@
continue
handled_rules.append(rule_name)
- for c in child.getchildren():
+ for c in list(child):
if c.tag != "rule-input":
continue
diff --git a/tools/bzl/license.bzl b/tools/bzl/license.bzl
index f011446..d059216 100644
--- a/tools/bzl/license.bzl
+++ b/tools/bzl/license.bzl
@@ -39,7 +39,7 @@
if target[0] not in ":/":
target = ":" + target
if target[0] != "/":
- target = "//" + PACKAGE_NAME + target
+ target = "//" + native.package_name() + target
forbidden = "//lib:LICENSE-DO_NOT_DISTRIBUTE"
native.genquery(
diff --git a/tools/bzl/pkg_war.bzl b/tools/bzl/pkg_war.bzl
index 1a376e9..40dd769 100644
--- a/tools/bzl/pkg_war.bzl
+++ b/tools/bzl/pkg_war.bzl
@@ -14,7 +14,7 @@
# War packaging.
-jar_filetype = FileType([".jar"])
+jar_filetype = [".jar"]
LIBS = [
"//java/com/google/gerrit/common:version",
diff --git a/tools/eclipse/project.py b/tools/eclipse/project.py
index b99c04e..64d837a 100755
--- a/tools/eclipse/project.py
+++ b/tools/eclipse/project.py
@@ -52,10 +52,12 @@
action='store', default='gerrit', dest='project_name')
opts.add_option('-b', '--batch', action='store_true',
dest='batch', help='Bazel batch option')
+opts.add_option('-j', '--java', action='store',
+ dest='java', help='Post Java 8 support (9|10|11|...)')
args, _ = opts.parse_args()
batch_option = '--batch' if args.batch else None
-
+custom_java = args.java
def _build_bazel_cmd(*args):
cmd = ['bazel']
@@ -63,6 +65,9 @@
cmd.append('--batch')
for arg in args:
cmd.append(arg)
+ if custom_java:
+ cmd.append('--host_java_toolchain=@bazel_tools//tools/jdk:toolchain_java%s' % custom_java)
+ cmd.append('--java_toolchain=@bazel_tools//tools/jdk:toolchain_java%s' % custom_java)
return cmd
@@ -70,9 +75,10 @@
return check_output(_build_bazel_cmd('info', 'output_base')).strip()
-def gen_bazel_path():
+def gen_bazel_path(ext_location):
bazel = check_output(['which', 'bazel']).strip().decode('UTF-8')
with open(path.join(ROOT, ".bazel_path"), 'w') as fd:
+ fd.write("output_base=%s\n" % ext_location)
fd.write("bazel=%s\n" % bazel)
fd.write("PATH=%s\n" % environ["PATH"])
@@ -301,7 +307,7 @@
gen_project(args.project_name)
gen_classpath(ext_location)
gen_factorypath(ext_location)
- gen_bazel_path()
+ gen_bazel_path(ext_location)
# TODO(davido): Remove this when GWT gone
gwt_working_dir = ".gwt_work_dir"