Merge "Remove deprecated API from tests"
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index d899b97..a0e7b3e 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -4783,47 +4783,6 @@
 link:#schedule-configuration-examples[Schedule examples] can be found
 in the link:#schedule-configuration[Schedule Configuration] section.
 
-[[urlAlias]]
-=== Section urlAlias
-
-URL aliases define regular expressions for URL tokens that are mapped
-to target URL tokens.
-
-Each URL alias must be specified in its own subsection. The subsection
-name should be a descriptive name. It must be unique, but is not
-interpreted in any way.
-
-The URL aliases are applied in no particular order. The first matching
-URL alias is used and further matches are ignored.
-
-URL aliases can be used to map plugin screens into the Gerrit URL
-namespace, or to replace Gerrit screens by plugin screens.
-
-Example:
-
-----
-[urlAlias "MyPluginScreen"]
-  match = /myscreen/(.*)
-  token = /x/myplugin/myscreen/$1
-[urlAlias "MyChangeScreen"]
-  match = /c/(.*)
-  token = /x/myplugin/c/$1
-----
-
-[[urlAlias.match]]urlAlias.match::
-+
-A regular expression for a URL token.
-+
-The matched URL token is replaced by `urlAlias.token`.
-
-[[urlAlias.token]]urlAlias.token::
-+
-The target URL token.
-+
-It can contain placeholders for the groups matched by the
-`urlAlias.match` regular expression: `$1` for the first matched group,
-`$2` for the second matched group, etc.
-
 [[submodule]]
 === Section submodule
 
diff --git a/Documentation/js-api.txt b/Documentation/js-api.txt
index d909c00..258ded2 100644
--- a/Documentation/js-api.txt
+++ b/Documentation/js-api.txt
@@ -24,123 +24,17 @@
 The plugin instance is passed to the plugin's initialization function
 and provides a number of utility services to plugin authors.
 
-[[self_delete]]
-=== self.delete() / self.del()
-Issues a DELETE REST API request to the Gerrit server.
-
-.Signature
-[source,javascript]
-----
-Gerrit.delete(url, callback)
-Gerrit.del(url, callback)
-----
-
-* url: URL relative to the plugin's URL space. The JavaScript
-  library prefixes the supplied URL with `/plugins/{getPluginName}/`.
-
-* callback: JavaScript function to be invoked with the parsed
-  JSON result of the API call. DELETE methods often return
-  `204 No Content`, which is passed as null.
-
-[[self_get]]
-=== self.get()
-Issues a GET REST API request to the Gerrit server.
-
-.Signature
-[source,javascript]
-----
-self.get(url, callback)
-----
-
-* url: URL relative to the plugin's URL space. The JavaScript
-  library prefixes the supplied URL with `/plugins/{getPluginName}/`.
-
-* callback: JavaScript function to be invoked with the parsed JSON
-  result of the API call. If the API returns a string the result is
-  a string, otherwise the result is a JavaScript object or array,
-  as described in the relevant REST API documentation.
-
 [[self_getServerInfo]]
 === self.getServerInfo()
 Returns the server's link:rest-api-config.html#server-info[ServerInfo]
 data.
 
-[[self_getCurrentUser]]
-=== self.getCurrentUser()
-Returns the currently signed in user's AccountInfo data; empty account
-data if no user is currently signed in.
-
-[[Gerrit_getUserPreferences]]
-=== Gerrit.getUserPreferences()
-Returns the preferences of the currently signed in user; the default
-preferences if no user is currently signed in.
-
-[[Gerrit_refreshUserPreferences]]
-=== Gerrit.refreshUserPreferences()
-Refreshes the preferences of the current user.
-
 [[self_getPluginName]]
 === self.getPluginName()
 Returns the name this plugin was installed as by the server
 administrator. The plugin name is required to access REST API
 views installed by the plugin, or to access resources.
 
-[[self_post]]
-=== self.post()
-Issues a POST REST API request to the Gerrit server.
-
-.Signature
-[source,javascript]
-----
-self.post(url, input, callback)
-----
-
-* url: URL relative to the plugin's URL space. The JavaScript
-  library prefixes the supplied URL with `/plugins/{getPluginName}/`.
-
-* input: JavaScript object to serialize as the request payload.
-
-* callback: JavaScript function to be invoked with the parsed JSON
-  result of the API call. If the API returns a string the result is
-  a string, otherwise the result is a JavaScript object or array,
-  as described in the relevant REST API documentation.
-
-[source,javascript]
-----
-self.post(
-  '/my-servlet',
-  {start_build: true, platform_type: 'Linux'},
-  function (r) {});
-----
-
-[[self_put]]
-=== self.put()
-Issues a PUT REST API request to the Gerrit server.
-
-.Signature
-[source,javascript]
-----
-self.put(url, input, callback)
-----
-
-* url: URL relative to the plugin's URL space. The JavaScript
-  library prefixes the supplied URL with `/plugins/{getPluginName}/`.
-
-* input: JavaScript object to serialize as the request payload.
-
-* callback: JavaScript function to be invoked with the parsed JSON
-  result of the API call. If the API returns a string the result is
-  a string, otherwise the result is a JavaScript object or array,
-  as described in the relevant REST API documentation.
-
-[source,javascript]
-----
-self.put(
-  '/builds',
-  {start_build: true, platform_type: 'Linux'},
-  function (r) {});
-----
-
 [[self_on]]
 === self.on()
 Register a JavaScript callback to be invoked when events occur within
@@ -149,7 +43,7 @@
 .Signature
 [source,javascript]
 ----
-Gerrit.on(event, callback);
+self.on(event, callback);
 ----
 
 * event: A supported event type. See below for description.
@@ -194,39 +88,26 @@
   This event can be used to register a new language highlighter with
   the highlight.js library before syntax highlighting begins.
 
-[[self_onAction]]
-=== self.onAction()
-Register a JavaScript callback to be invoked when the user clicks
-on a button associated with a server side `UiAction`.
+[[self_changeActions]]
+=== self.changeActions()
+Returns an instance of ChangeActions API.
 
 .Signature
 [source,javascript]
 ----
-self.onAction(type, view_name, callback);
+self.changeActions();
 ----
 
-* type: `'change'`, `'edit'`, `'revision'`, `'project'`, or `'branch'`
-  indicating which type of resource the `UiAction` was bound to
-  in the server.
-
-* view_name: string appearing in URLs to name the view. This is the
-  second argument of the `get()`, `post()`, `put()`, and `delete()`
-  binding methods in a `RestApiModule`.
-
-* callback: JavaScript function to invoke when the user clicks. The
-  function will be passed a link:#ActionContext[action context].
-
 [[self_screen]]
 === self.screen()
-Register a JavaScript callback to be invoked when the user navigates
+Register a module to be attached when the user navigates
 to an extension screen provided by the plugin. Extension screens are
 usually linked from the link:dev-plugins.html#top-menu-extensions[top menu].
-The callback can populate the DOM with the screen's contents.
 
 .Signature
 [source,javascript]
 ----
-self.screen(pattern, callback);
+self.screen(pattern, opt_moduleName);
 ----
 
 * pattern: URL token pattern to identify the screen. Argument can be
@@ -234,52 +115,34 @@
   If a RegExp is used the matching groups will be available inside of
   the context as `token_match`.
 
-* callback: JavaScript function to invoke when the user navigates to
+* opt_moduleName: The module to load when the user navigates to
   the screen. The function will be passed a link:#ScreenContext[screen context].
 
-[[self_settingsScreen]]
-=== self.settingsScreen()
-Register a JavaScript callback to be invoked when the user navigates
-to an extension settings screen provided by the plugin. Extension settings
-screens are automatically linked from the settings menu under the given
-menu entry.
-The callback can populate the DOM with the screen's contents.
+[[self_settings]]
+=== self.settings()
+Returns the Settings API.
 
 .Signature
 [source,javascript]
 ----
-self.settingsScreen(path, menu, callback);
+self.settings();
 ----
 
-* path: URL path to identify the settings screen.
-
-* menu: The name of the menu entry in the settings menu that should
-  link to the settings screen.
-
-* callback: JavaScript function to invoke when the user navigates to
-  the settings screen. The function will be passed a
-  link:#SettingsScreenContext[settings screen context].
-
-[[self_panel]]
-=== self.panel()
-Register a JavaScript callback to be invoked when a screen with the
-given extension point is loaded.
-The callback can populate the DOM with the panel's contents.
+[[self_registerCustomComponent]]
+=== self.registerCustomComponent()
+Register a custom component to a specific endpoint.
 
 .Signature
 [source,javascript]
 ----
-self.panel(extensionpoint, callback);
+self.registerCustomComponent(endpointName, opt_moduleName, opt_options);
 ----
 
-* extensionpoint: The name of the extension point that marks the
-  position where the panel is added to an existing screen. The
-  available extension points are described in the
-  link:dev-plugins.html#panels[plugin development documentation].
+* endpointName: The endpoint this plugin should be reigistered to.
 
-* callback: JavaScript function to invoke when a screen with the
-  extension point is loaded. The function will be passed a
-  link:#PanelContext[panel context].
+* opt_moduleName: The module name the custom component will use.
+
+* opt_options: Options to register this custom component.
 
 [[self_url]]
 === self.url()
@@ -293,398 +156,260 @@
 self.url('/static/icon.png');  // "https://gerrit-review.googlesource.com/plugins/demo/static/icon.png"
 ----
 
-
-[[ActionContext]]
-== Action Context
-A new action context is passed to the `onAction` callback function
-each time the associated action button is clicked by the user. A
-context is initialized with sufficient state to issue the associated
-REST API RPC.
-
-[[context_action]]
-=== context.action
-An link:rest-api-changes.html#action-info[ActionInfo] object instance
-supplied by the server describing the UI button the user used to
-invoke the action.
-
-[[context_call]]
-=== context.call()
-Issues the REST API call associated with the action. The HTTP method
-used comes from `context.action.method`, hiding the JavaScript from
-needing to care.
+[[self_restApi]]
+=== self.restApi()
+Returns an instance of the Plugin REST API.
 
 .Signature
 [source,javascript]
 ----
-context.call(input, callback)
+self.restApi(prefix_url)
 ----
 
-* input: JavaScript object to serialize as the request payload. This
-  parameter is ignored for GET and DELETE methods.
+* prefix_url: Base url for subsequent .get(), .post() etc requests.
 
-* callback: JavaScript function to be invoked with the parsed JSON
-  result of the API call. If the API returns a string the result is
-  a string, otherwise the result is a JavaScript object or array,
-  as described in the relevant REST API documentation.
+[[PluginRestAPI]]
+== Plugin Rest API
 
-[source,javascript]
-----
-context.call(
-  {message: "..."},
-  function (result) {
-    // ... use result here ...
-  });
-----
-
-[[context_change]]
-=== context.change
-When the action is invoked on a change a
-link:rest-api-changes.html#change-info[ChangeInfo] object instance
-describing the change. Available fields of the ChangeInfo may vary
-based on the options used by the UI when it loaded the change.
-
-[[context_delete]]
-=== context.delete()
-Issues a DELETE REST API call to the URL associated with the action.
+[[plugin_rest_delete]]
+=== restApi.delete()
+Issues a DELETE REST API request to the Gerrit server.
+Returns a promise with the response of the request.
 
 .Signature
 [source,javascript]
 ----
-context.delete(callback)
+restApi.delete(url)
 ----
 
-* callback: JavaScript function to be invoked with the parsed
-  JSON result of the API call. DELETE methods often return
-  `204 No Content`, which is passed as null.
+* url: URL relative to the base url.
 
-[source,javascript]
-----
-context.delete(function () {});
-----
-
-[[context_get]]
-=== context.get()
-Issues a GET REST API call to the URL associated with the action.
+[[plugin_rest_get]]
+=== restApi.get()
+Issues a GET REST API request to the Gerrit server.
+Returns a promise with the response of the request.
 
 .Signature
 [source,javascript]
 ----
-context.get(callback)
+restApi.get(url)
 ----
 
-* callback: JavaScript function to be invoked with the parsed JSON
-  result of the API call. If the API returns a string the result is
-  a string, otherwise the result is a JavaScript object or array,
-  as described in the relevant REST API documentation.
+* url: URL relative to the base url.
 
-[source,javascript]
-----
-context.get(function (result) {
-  // ... use result here ...
-});
-----
-
-[[context_go]]
-=== context.go()
-Go to a screen. Shorthand for link:#Gerrit_go[`Gerrit.go()`].
-
-[[context_hide]]
-=== context.hide()
-Hide the currently visible popup displayed by
-link:#context_popup[`context.popup()`].
-
-[[context_post]]
-=== context.post()
-Issues a POST REST API call to the URL associated with the action.
+[[plugin_rest_post]]
+=== restApi.post()
+Issues a POST REST API request to the Gerrit server.
+Returns a promise with the response of the request.
 
 .Signature
 [source,javascript]
 ----
-context.post(input, callback)
+restApi.post(url, opt_payload, opt_errFn, opt_contentType)
 ----
 
-* input: JavaScript object to serialize as the request payload.
+* url: URL relative to the base url.
 
-* callback: JavaScript function to be invoked with the parsed JSON
-  result of the API call. If the API returns a string the result is
-  a string, otherwise the result is a JavaScript object or array,
-  as described in the relevant REST API documentation.
+* opt_payload: JavaScript object to serialize as the request payload.
+
+* opt_errFn: JavaScript function to be invoked when error occured.
+
+* opt_contentType: Content-Type to be sent along with the request.
 
 [source,javascript]
 ----
-context.post(
-  {message: "..."},
-  function (result) {
-    // ... use result here ...
-  });
+restApi.post(
+  '/my-servlet',
+  {start_build: true, platform_type: 'Linux'});
 ----
 
-[[context_popup]]
-=== context.popup()
-
-Displays a small popup near the activation button to gather
-additional input from the user before executing the REST API RPC.
-
-The caller is always responsible for closing the popup with
-link#context_hide[`context.hide()`]. Gerrit will handle closing a
-popup if the user presses `Escape` while keyboard focus is within
-the popup.
+[[plugin_rest_put]]
+=== restApi.put()
+Issues a PUT REST API request to the Gerrit server.
+Returns a promise with the response of the request.
 
 .Signature
 [source,javascript]
 ----
-context.popup(element)
+restApi.put(url, opt_payload, opt_errFn, opt_contentType)
 ----
 
-* element: an HTML DOM element to display as the body of the
-  popup. This is typically a `div` element but can be any valid HTML
-  element. CSS can be used to style the element beyond the defaults.
+* url: URL relative to the base url.
 
-A common usage is to gather more input:
+* opt_payload: JavaScript object to serialize as the request payload.
+
+* opt_errFn: JavaScript function to be invoked when error occured.
+
+* opt_contentType: Content-Type to be sent along with the request.
 
 [source,javascript]
 ----
-self.onAction('revision', 'start-build', function (c) {
-  var l = c.checkbox();
-  var m = c.checkbox();
-  c.popup(c.div(
-    c.div(c.label(l, 'Linux')),
-    c.div(c.label(m, 'Mac OS X')),
-    c.button('Build', {onclick: function() {
-      c.call(
-        {
-          commit: c.revision.name,
-          linux: l.checked,
-          mac: m.checked,
-        },
-        function() { c.hide() });
-    });
-});
+restApi.put(
+  '/builds',
+  {start_build: true, platform_type: 'Linux'});
 ----
 
-[[context_put]]
-=== context.put()
-Issues a PUT REST API call to the URL associated with the action.
+[[ChangeActions]]
+== Change Actions API
+A new Change Actions API instance will be created when `changeActions()`
+is invoked.
+
+[[change_actions_add]]
+=== changeActions.add()
+Adds a new action to the change actions section.
+Returns the key of the newly added action.
 
 .Signature
 [source,javascript]
 ----
-context.put(input, callback)
+changeActions.add(type, label)
 ----
 
-* input: JavaScript object to serialize as the request payload.
+* type: The type of the action, either `change` or `revision`.
 
-* callback: JavaScript function to be invoked with the parsed JSON
-  result of the API call. If the API returns a string the result is
-  a string, otherwise the result is a JavaScript object or array,
-  as described in the relevant REST API documentation.
+* label: The label to be used in UI for this action.
 
 [source,javascript]
 ----
-context.put(
-  {message: "..."},
-  function (result) {
-    // ... use result here ...
-  });
+changeActions.add("change", "test")
 ----
 
-[[context_refresh]]
-=== context.refresh()
-Refresh the current display. Shorthand for
-link:#Gerrit_refresh[`Gerrit.refresh()`].
-
-[[context_revision]]
-=== context.revision
-When the action is invoked on a specific revision of a change,
-a link:rest-api-changes.html#revision-info[RevisionInfo]
-object instance describing the revision. Available fields of the
-RevisionInfo may vary based on the options used by the UI when it
-loaded the change.
-
-[[context_project]]
-=== context.project
-When the action is invoked on a specific project,
-the name of the project.
-
-=== HTML Helpers
-The link:#ActionContext[action context] includes some HTML helper
-functions to make working with DOM based widgets less painful.
-
-* `br()`: new `<br>` element.
-
-* `button(label, options)`: new `<button>` with the string `label`
-  wrapped inside of a `div`. The optional `options` object may
-  define `onclick` as a function to be invoked upon clicking. This
-  calling pattern avoids circular references between the element
-  and the onclick handler.
-
-* `checkbox()`: new `<input type='checkbox'>` element.
-* `div(...)`: a new `<div>` wrapping the (optional) arguments.
-* `hr()`: new `<hr>` element.
-
-* `label(c, label)`: a new `<label>` element wrapping element `c`
-  and the string `label`. Used to wrap a checkbox with its label,
-  `label(checkbox(), 'Click Me')`.
-
-* `prependLabel(label, c)`: a new `<label>` element wrapping element `c`
-  and the string `label`. Used to wrap an input field with its label,
-  `prependLabel('Greeting message', textfield())`.
-
-* `textarea(options)`: new `<textarea>` element. The options
-  object may optionally include `rows` and `cols`. The textarea
-  comes with an onkeypress handler installed to play nicely with
-  Gerrit's keyboard binding system.
-
-* `textfield()`: new `<input type='text'>` element.  The text field
-  comes with an onkeypress handler installed to play nicely with
-  Gerrit's keyboard binding system.
-
-* `select(a,i)`: a new `<select>` element containing one `<option>`
-  element for each entry in the provided array `a`.  The option with
-  the index `i` will be pre-selected in the drop-down-list.
-
-* `selected(s)`: returns the text of the `<option>` element that is
-  currently selected in the provided `<select>` element `s`.
-
-* `span(...)`: a new `<span>` wrapping the (optional) arguments.
-
-* `msg(label)`: a new label.
-
-
-[[ScreenContext]]
-== Screen Context
-A new screen context is passed to the `screen` callback function
-each time the user navigates to a matching URL.
-
-[[screen_body]]
-=== screen.body
-Empty HTML `<div>` node the plugin should add its content to.  The
-node is already attached to the document, but is invisible.  Plugins
-must call `screen.show()` to display the DOM node.  Deferred display
-allows an implementor to partially populate the DOM, make remote HTTP
-requests, finish populating when the callbacks arrive, and only then
-make the view visible to the user.
-
-[[screen_token]]
-=== screen.token
-URL token fragment that activated this screen.  The value is identical
-to `screen.token_match[0]`.  If the URL is `/#/x/hello/list` the token
-will be `"list"`.
-
-[[screen_token_match]]
-=== screen.token_match
-Array of matching subgroups from the pattern specified to `screen()`.
-This is identical to the result of RegExp.exec. Index 0 contains the
-entire matching expression; index 1 the first matching group, etc.
-
-[[screen_onUnload]]
-=== screen.onUnload()
-Configures an optional callback to be invoked just before the screen
-is deleted from the browser DOM.  Plugins can use this callback to
-remove event listeners from DOM nodes, preventing memory leaks.
+[[change_actions_remove]]
+=== changeActions.remove()
+Removes an action from the change actions section.
 
 .Signature
 [source,javascript]
 ----
-screen.onUnload(callback)
+changeActions.remove(key)
 ----
 
-* callback: JavaScript function to be invoked just before the
-  `screen.body` DOM element is removed from the browser DOM.
-  This event happens when the user navigates to another screen.
+* key: The key of the action.
 
-[[screen.setTitle]]
-=== screen.setTitle()
-Sets the heading text to be displayed when the screen is visible.
-This is presented in a large bold font below the menus, but above the
-content in `screen.body`.  Setting the title also sets the window
-title to the same string, if it has not already been set.
+[[change_actions_addTapListener]]
+=== changeActions.addTapListener()
+Adds a tap listener to an action that will be invoked when the action
+is tapped.
 
 .Signature
 [source,javascript]
 ----
-screen.setPageTitle(titleText)
+changeActions.addTapListener(key, callback)
 ----
 
-[[screen.setWindowTitle]]
-=== screen.setWindowTitle()
-Sets the text to be displayed in the browser's title bar when the
-screen is visible.  Plugins should always prefer this method over
-trying to set `window.title` directly.  The window title defaults to
-the title given to `setTitle`.
+* key: The key of the action.
+
+* callback: JavaScript function to be invoked when action tapped.
+
+[source,javascript]
+----
+changeActions.addTapListener("__key_for_my_action__", () => {
+  // do something when my action gets clicked
+})
+----
+
+[[change_actions_removeTapListener]]
+=== changeActions.removeTapListener()
+Removes an existing tap listener on an action.
 
 .Signature
 [source,javascript]
 ----
-screen.setWindowTitle(titleText)
+changeActions.removeTapListener(key, callback)
 ----
 
-[[screen_show]]
-=== screen.show()
-Destroy the currently visible screen and display the plugin's screen.
-This method must be called after adding content to `screen.body`.
+* key: The key of the action.
 
-[[SettingsScreenContext]]
-== Settings Screen Context
-A new settings screen context is passed to the `settingsScreen` callback
-function each time the user navigates to a matching URL.
+* callback: JavaScript function to be removed.
 
-[[settingsScreen_body]]
-=== settingsScreen.body
-Empty HTML `<div>` node the plugin should add its content to.  The
-node is already attached to the document, but is invisible.  Plugins
-must call `settingsScreen.show()` to display the DOM node.  Deferred
-display allows an implementor to partially populate the DOM, make
-remote HTTP requests, finish populating when the callbacks arrive, and
-only then make the view visible to the user.
-
-[[settingsScreen_onUnload]]
-=== settingsScreen.onUnload()
-Configures an optional callback to be invoked just before the screen
-is deleted from the browser DOM.  Plugins can use this callback to
-remove event listeners from DOM nodes, preventing memory leaks.
+[[change_actions_setLabel]]
+=== changeActions.setLabel()
+Sets the label for an action.
 
 .Signature
 [source,javascript]
 ----
-settingsScreen.onUnload(callback)
+changeActions.setLabel(key, label)
 ----
 
-* callback: JavaScript function to be invoked just before the
-  `settingsScreen.body` DOM element is removed from the browser DOM.
-  This event happens when the user navigates to another screen.
+* key: The key of the action.
 
-[[settingsScreen.setTitle]]
-=== settingsScreen.setTitle()
-Sets the heading text to be displayed when the screen is visible.
-This is presented in a large bold font below the menus, but above the
-content in `settingsScreen.body`. Setting the title also sets the
-window title to the same string, if it has not already been set.
+* label: The label of the action.
+
+[[change_actions_setTitle]]
+=== changeActions.setTitle()
+Sets the title for an action.
 
 .Signature
 [source,javascript]
 ----
-settingsScreen.setPageTitle(titleText)
+changeActions.setTitle(key, title)
 ----
 
-[[settingsScreen.setWindowTitle]]
-=== settingsScreen.setWindowTitle()
-Sets the text to be displayed in the browser's title bar when the
-screen is visible.  Plugins should always prefer this method over
-trying to set `window.title` directly.  The window title defaults to
-the title given to `setTitle`.
+* key: The key of the action.
+
+* title: The title of the action.
+
+[[change_actions_setIcon]]
+=== changeActions.setIcon()
+Sets an icon for an action.
 
 .Signature
 [source,javascript]
 ----
-settingsScreen.setWindowTitle(titleText)
+changeActions.setIcon(key, icon)
 ----
 
-[[settingsScreen_show]]
-=== settingsScreen.show()
-Destroy the currently visible screen and display the plugin's screen.
-This method must be called after adding content to
-`settingsScreen.body`.
+* key: The key of the action.
+
+* icon: The name of the icon.
+
+[[change_actions_setEnabled]]
+=== changeActions.setEnabled()
+Sets an action to enabled or disabled.
+
+.Signature
+[source,javascript]
+----
+changeActions.setEnabled(key, enabled)
+----
+
+* key: The key of the action.
+
+* enabled: The status of the action, true to enable.
+
+[[change_actions_setActionHidden]]
+=== changeActions.setActionHidden()
+Sets an action to be hidden.
+
+.Signature
+[source,javascript]
+----
+changeActions.setActionHidden(type, key, hidden)
+----
+
+* type: The type of the action.
+
+* key: The key of the action.
+
+* hidden: True to hide the action, false to show the action.
+
+[[change_actions_setActionOverflow]]
+=== changeActions.setActionOverflow()
+Sets an action to show in overflow menu.
+
+.Signature
+[source,javascript]
+----
+changeActions.setActionOverflow(type, key, overflow)
+----
+
+* type: The type of the action.
+
+* key: The key of the action.
+
+* overflow: True to move the action to overflow menu, false to move
+  the action out of the overflow menu.
 
 [[PanelContext]]
 == Panel Context
@@ -734,59 +459,6 @@
 });
 ----
 
-[[Gerrit_delete]]
-=== Gerrit.delete()
-Issues a DELETE REST API request to the Gerrit server. For plugin
-private REST API URLs see link:#self_delete[self.delete()].
-
-.Signature
-[source,javascript]
-----
-Gerrit.delete(url, callback)
-----
-
-* url: URL relative to the Gerrit server. For example to access the
-  link:rest-api-changes.html[changes REST API] use `'/changes/'`.
-
-* callback: JavaScript function to be invoked with the parsed
-  JSON result of the API call. DELETE methods often return
-  `204 No Content`, which is passed as null.
-
-[source,javascript]
-----
-Gerrit.delete(
-  '/changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/topic',
-  function () {});
-----
-
-[[Gerrit_get]]
-=== Gerrit.get()
-Issues a GET REST API request to the Gerrit server. For plugin
-private REST API URLs see link:#self_get[self.get()].
-
-.Signature
-[source,javascript]
-----
-Gerrit.get(url, callback)
-----
-
-* url: URL relative to the Gerrit server. For example to access the
-  link:rest-api-changes.html[changes REST API] use `'/changes/'`.
-
-* callback: JavaScript function to be invoked with the parsed JSON
-  result of the API call. If the API returns a string the result is
-  a string, otherwise the result is a JavaScript object or array,
-  as described in the relevant REST API documentation.
-
-[source,javascript]
-----
-Gerrit.get('/changes/?q=status:open', function (open) {
-  for (var i = 0; i < open.length; i++) {
-    console.log(open[i].change_id);
-  }
-});
-----
-
 [[Gerrit_getCurrentUser]]
 === Gerrit.getCurrentUser()
 Returns the currently signed in user's AccountInfo data; empty account
@@ -828,136 +500,6 @@
 });
 ----
 
-[[Gerrit_post]]
-=== Gerrit.post()
-Issues a POST REST API request to the Gerrit server. For plugin
-private REST API URLs see link:#self_post[self.post()].
-
-.Signature
-[source,javascript]
-----
-Gerrit.post(url, input, callback)
-----
-
-* url: URL relative to the Gerrit server. For example to access the
-  link:rest-api-changes.html[changes REST API] use `'/changes/'`.
-
-* input: JavaScript object to serialize as the request payload.
-
-* callback: JavaScript function to be invoked with the parsed JSON
-  result of the API call. If the API returns a string the result is
-  a string, otherwise the result is a JavaScript object or array,
-  as described in the relevant REST API documentation.
-
-[source,javascript]
-----
-Gerrit.post(
-  '/changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/topic',
-  {topic: 'tests', message: 'Classify work as for testing.'},
-  function (r) {});
-----
-
-[[Gerrit_put]]
-=== Gerrit.put()
-Issues a PUT REST API request to the Gerrit server. For plugin
-private REST API URLs see link:#self_put[self.put()].
-
-.Signature
-[source,javascript]
-----
-Gerrit.put(url, input, callback)
-----
-
-* url: URL relative to the Gerrit server. For example to access the
-  link:rest-api-changes.html[changes REST API] use `'/changes/'`.
-
-* input: JavaScript object to serialize as the request payload.
-
-* callback: JavaScript function to be invoked with the parsed JSON
-  result of the API call. If the API returns a string the result is
-  a string, otherwise the result is a JavaScript object or array,
-  as described in the relevant REST API documentation.
-
-[source,javascript]
-----
-Gerrit.put(
-  '/changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/topic',
-  {topic: 'tests', message: 'Classify work as for testing.'},
-  function (r) {});
-----
-
-[[Gerrit_onAction]]
-=== Gerrit.onAction()
-Register a JavaScript callback to be invoked when the user clicks
-on a button associated with a server side `UiAction`.
-
-.Signature
-[source,javascript]
-----
-Gerrit.onAction(type, view_name, callback);
-----
-
-* type: `'change'`, `'edit'`, `'revision'`, `'project'` or `'branch'`
-  indicating what sort of resource the `UiAction` was bound to in the server.
-
-* view_name: string appearing in URLs to name the view. This is the
-  second argument of the `get()`, `post()`, `put()`, and `delete()`
-  binding methods in a `RestApiModule`.
-
-* callback: JavaScript function to invoke when the user clicks. The
-  function will be passed a link:#ActionContext[ActionContext].
-
-[[Gerrit_screen]]
-=== Gerrit.screen()
-Register a JavaScript callback to be invoked when the user navigates
-to an extension screen provided by the plugin. Extension screens are
-usually linked from the link:dev-plugins.html#top-menu-extensions[top menu].
-The callback can populate the DOM with the screen's contents.
-
-.Signature
-[source,javascript]
-----
-Gerrit.screen(pattern, callback);
-----
-
-* pattern: URL token pattern to identify the screen. Argument can be
-  either a string (`'index'`) or a RegExp object (`/list\/(.*)/`).
-  If a RegExp is used the matching groups will be available inside of
-  the context as `token_match`.
-
-* callback: JavaScript function to invoke when the user navigates to
-  the screen. The function will be passed link:#ScreenContext[screen context].
-
-[[Gerrit_refresh]]
-=== Gerrit.refresh()
-Redisplays the current web UI view, refreshing all information.
-
-[[Gerrit_refreshMenuBar]]
-=== Gerrit.refreshMenuBar()
-Refreshes Gerrit's menu bar.
-
-[[Gerrit_isSignedIn]]
-=== Gerrit.isSignedIn()
-Checks if user is signed in.
-
-[[Gerrit_url]]
-=== Gerrit.url()
-Returns the URL of the Gerrit Code Review server. If invoked with
-no parameter the URL of the site is returned. If passed a string
-the argument is appended to the site URL.
-
-[source,javascript]
-----
-Gerrit.url();        // "https://gerrit-review.googlesource.com/"
-Gerrit.url('/123');  // "https://gerrit-review.googlesource.com/123"
-----
-
-For a plugin specific version see link:#self_url()[`self.url()`].
-
-[[Gerrit_showError]]
-=== Gerrit.showError(message)
-Displays the given message in the Gerrit ErrorDialog.
-
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index 6fb9220..7097a16 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -2764,9 +2764,6 @@
 |`change_table`                           ||
 The columns to display in the change table (PolyGerrit only). The default is
 empty, which will default columns as determined by the frontend.
-|`url_aliases`                  |optional|
-A map of URL path pairs, where the first URL path is an alias for the
-second URL path.
 |`email_strategy`               ||
 The type of email strategy to use. On `ENABLED`, the user will receive emails
 from Gerrit. On `CC_ON_OWN_COMMENTS` the user will also receive emails for
@@ -2829,9 +2826,6 @@
 |`change_table`                           ||
 The columns to display in the change table (PolyGerrit only). The default is
 empty, which will default columns as determined by the frontend.
-|`url_aliases`                  |optional|
-A map of URL path pairs, where the first URL path is an alias for the
-second URL path.
 |`email_strategy`               |optional|
 The type of email strategy to use. On `ENABLED`, the user will receive emails
 from Gerrit. On `CC_ON_OWN_COMMENTS` the user will also receive emails for
diff --git a/Documentation/rest-api-config.txt b/Documentation/rest-api-config.txt
index 2b10e33..021a1bb 100644
--- a/Documentation/rest-api-config.txt
+++ b/Documentation/rest-api-config.txt
@@ -1963,11 +1963,6 @@
 Information about the configuration from the
 link:config-gerrit.html#suggest[suggest] section as link:#suggest-info[
 SuggestInfo] entity.
-|`url_aliases`             |optional|
-A map of URL aliases, where a regular expression for an URL token is
-mapped to a target URL token. The target URL token can contain
-placeholders for the groups matched by the regular expression: `$1` for
-the first matched group, `$2` for the second matched group, etc.
 |`user`                    ||
 Information about the configuration from the
 link:config-gerrit.html#user[user] section as link:#user-config-info[
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 97394c7..c631aca 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -805,7 +805,7 @@
   }
 
   protected Account getAccount(Account.Id accountId) {
-    return getAccountState(accountId).getAccount();
+    return getAccountState(accountId).account();
   }
 
   protected AccountState getAccountState(Account.Id accountId) {
diff --git a/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java
index ec2d75e..d46cb97 100644
--- a/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java
+++ b/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java
@@ -61,9 +61,9 @@
   private Account.Id createAccount(TestAccountCreation accountCreation) throws Exception {
     AccountsUpdate.AccountUpdater accountUpdater =
         (account, updateBuilder) ->
-            fillBuilder(updateBuilder, accountCreation, account.getAccount().id());
+            fillBuilder(updateBuilder, accountCreation, account.account().id());
     AccountState createdAccount = createAccount(accountUpdater);
-    return createdAccount.getAccount().id();
+    return createdAccount.account().id();
   }
 
   private AccountState createAccount(AccountsUpdate.AccountUpdater accountUpdater)
@@ -129,13 +129,13 @@
     }
 
     private TestAccount toTestAccount(AccountState accountState) {
-      Account account = accountState.getAccount();
+      Account account = accountState.account();
       return TestAccount.builder()
           .accountId(account.id())
           .preferredEmail(Optional.ofNullable(account.preferredEmail()))
           .fullname(Optional.ofNullable(account.fullName()))
-          .username(accountState.getUserName())
-          .active(accountState.getAccount().isActive())
+          .username(accountState.userName())
+          .active(accountState.account().isActive())
           .build();
     }
 
diff --git a/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java b/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java
index 10ecd68..f7533a4 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java
@@ -85,7 +85,7 @@
       throw new StorageException(
           String.format(
               "Failed to replace account %s in index %s: %s",
-              as.getAccount().id(), indexName, statusCode));
+              as.account().id(), indexName, statusCode));
     }
   }
 
@@ -108,7 +108,7 @@
 
   @Override
   protected String getId(AccountState as) {
-    return as.getAccount().id().toString();
+    return as.account().id().toString();
   }
 
   @Override
diff --git a/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java b/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java
index f5a740e..458bcf5 100644
--- a/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java
+++ b/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.extensions.client;
 
 import java.util.List;
-import java.util.Map;
 
 /** Preferences about a single user. */
 public class GeneralPreferencesInfo {
@@ -145,7 +144,6 @@
   public Boolean workInProgressByDefault;
   public List<MenuItem> my;
   public List<String> changeTable;
-  public Map<String, String> urlAliases;
 
   public DateFormat getDateFormat() {
     if (dateFormat == null) {
diff --git a/java/com/google/gerrit/extensions/common/ServerInfo.java b/java/com/google/gerrit/extensions/common/ServerInfo.java
index 8904f0a..82d5bc8 100644
--- a/java/com/google/gerrit/extensions/common/ServerInfo.java
+++ b/java/com/google/gerrit/extensions/common/ServerInfo.java
@@ -14,8 +14,6 @@
 
 package com.google.gerrit.extensions.common;
 
-import java.util.Map;
-
 public class ServerInfo {
   public AccountsInfo accounts;
   public AuthInfo auth;
@@ -26,7 +24,6 @@
   public PluginConfigInfo plugin;
   public SshdInfo sshd;
   public SuggestInfo suggest;
-  public Map<String, String> urlAliases;
   public UserConfigInfo user;
   public ReceiveInfo receive;
   public String defaultTheme;
diff --git a/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java b/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
index 9c08857..9477cb6 100644
--- a/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
+++ b/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
@@ -201,7 +201,7 @@
   private Set<String> getAllowedUserIds(IdentifiedUser user) {
     Set<String> result = new HashSet<>();
     result.addAll(user.getEmailAddresses());
-    for (ExternalId extId : user.state().getExternalIds()) {
+    for (ExternalId extId : user.state().externalIds()) {
       if (extId.isScheme(SCHEME_GPGKEY)) {
         continue; // Omit GPG keys.
       }
diff --git a/java/com/google/gerrit/gpg/server/PostGpgKeys.java b/java/com/google/gerrit/gpg/server/PostGpgKeys.java
index bfd7d27..62c8660 100644
--- a/java/com/google/gerrit/gpg/server/PostGpgKeys.java
+++ b/java/com/google/gerrit/gpg/server/PostGpgKeys.java
@@ -304,12 +304,12 @@
       String msg = "GPG key " + extIdKey.get() + " associated with multiple accounts: [";
       msg =
           accountStates.stream()
-              .map(a -> a.getAccount().id().toString())
+              .map(a -> a.account().id().toString())
               .collect(joining(", ", msg, "]"));
       throw new IllegalStateException(msg);
     }
 
-    return accountStates.get(0).getAccount();
+    return accountStates.get(0).account();
   }
 
   private Map<String, GpgKeyInfo> toJson(
diff --git a/java/com/google/gerrit/httpd/ContainerAuthFilter.java b/java/com/google/gerrit/httpd/ContainerAuthFilter.java
index 03ed90d..517d5db 100644
--- a/java/com/google/gerrit/httpd/ContainerAuthFilter.java
+++ b/java/com/google/gerrit/httpd/ContainerAuthFilter.java
@@ -112,13 +112,13 @@
       username = username.toLowerCase(Locale.US);
     }
     Optional<AccountState> who =
-        accountCache.getByUsername(username).filter(a -> a.getAccount().isActive());
+        accountCache.getByUsername(username).filter(a -> a.account().isActive());
     if (!who.isPresent()) {
       rsp.sendError(SC_UNAUTHORIZED);
       return false;
     }
     WebSession ws = session.get();
-    ws.setUserAccountId(who.get().getAccount().id());
+    ws.setUserAccountId(who.get().account().id());
     ws.setAccessPathOk(AccessPath.GIT, true);
     ws.setAccessPathOk(AccessPath.REST_API, true);
     return true;
diff --git a/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java b/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java
index aa38c27..95b0447 100644
--- a/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java
+++ b/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java
@@ -129,7 +129,7 @@
     }
 
     Optional<AccountState> accountState =
-        accountCache.getByUsername(username).filter(a -> a.getAccount().isActive());
+        accountCache.getByUsername(username).filter(a -> a.account().isActive());
     if (!accountState.isPresent()) {
       logger.atWarning().log(
           "Authentication failed for %s: account inactive or not provisioned in Gerrit", username);
@@ -141,7 +141,7 @@
     GitBasicAuthPolicy gitBasicAuthPolicy = authConfig.getGitBasicAuthPolicy();
     if (gitBasicAuthPolicy == GitBasicAuthPolicy.HTTP
         || gitBasicAuthPolicy == GitBasicAuthPolicy.HTTP_LDAP) {
-      if (PasswordVerifier.checkPassword(who.getExternalIds(), username, password)) {
+      if (PasswordVerifier.checkPassword(who.externalIds(), username, password)) {
         return succeedAuthentication(who);
       }
     }
@@ -158,7 +158,7 @@
       setUserIdentified(whoAuthResult.getAccountId());
       return true;
     } catch (NoSuchUserException e) {
-      if (PasswordVerifier.checkPassword(who.getExternalIds(), username, password)) {
+      if (PasswordVerifier.checkPassword(who.externalIds(), username, password)) {
         return succeedAuthentication(who);
       }
       logger.atWarning().withCause(e).log(authenticationFailedMsg(username, req));
@@ -178,7 +178,7 @@
   }
 
   private boolean succeedAuthentication(AccountState who) {
-    setUserIdentified(who.getAccount().id());
+    setUserIdentified(who.account().id());
     return true;
   }
 
diff --git a/java/com/google/gerrit/httpd/ProjectOAuthFilter.java b/java/com/google/gerrit/httpd/ProjectOAuthFilter.java
index 3bb728f..0aa9c79 100644
--- a/java/com/google/gerrit/httpd/ProjectOAuthFilter.java
+++ b/java/com/google/gerrit/httpd/ProjectOAuthFilter.java
@@ -152,7 +152,7 @@
     }
 
     Optional<AccountState> who =
-        accountCache.getByUsername(authInfo.username).filter(a -> a.getAccount().isActive());
+        accountCache.getByUsername(authInfo.username).filter(a -> a.account().isActive());
     if (!who.isPresent()) {
       logger.atWarning().log(
           authenticationFailedMsg(authInfo.username, req)
@@ -161,7 +161,7 @@
       return false;
     }
 
-    Account account = who.get().getAccount();
+    Account account = who.get().account();
     AuthRequest authRequest = AuthRequest.forExternalUser(authInfo.username);
     authRequest.setEmailAddress(account.preferredEmail());
     authRequest.setDisplayName(account.fullName());
diff --git a/java/com/google/gerrit/httpd/RunAsFilter.java b/java/com/google/gerrit/httpd/RunAsFilter.java
index 0055fc7..b985741 100644
--- a/java/com/google/gerrit/httpd/RunAsFilter.java
+++ b/java/com/google/gerrit/httpd/RunAsFilter.java
@@ -105,7 +105,7 @@
 
       Account.Id target;
       try {
-        target = accountResolver.resolve(runas).asUnique().getAccount().id();
+        target = accountResolver.resolve(runas).asUnique().account().id();
       } catch (UnprocessableEntityException e) {
         replyError(req, res, SC_FORBIDDEN, "no account matches " + RUN_AS, null);
         return;
diff --git a/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java b/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
index 3eb4bcc..a600454 100644
--- a/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
+++ b/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
@@ -153,10 +153,10 @@
       if (!accountState.isPresent()) {
         continue;
       }
-      Account account = accountState.get().getAccount();
+      Account account = accountState.get().account();
       String displayName;
-      if (accountState.get().getUserName().isPresent()) {
-        displayName = accountState.get().getUserName().get();
+      if (accountState.get().userName().isPresent()) {
+        displayName = accountState.get().userName().get();
       } else if (account.fullName() != null && !account.fullName().isEmpty()) {
         displayName = account.fullName();
       } else if (account.preferredEmail() != null) {
@@ -176,7 +176,7 @@
   }
 
   private Optional<AuthResult> auth(Optional<AccountState> account) {
-    return account.map(a -> new AuthResult(a.getAccount().id(), null, false));
+    return account.map(a -> new AuthResult(a.account().id(), null, false));
   }
 
   private AuthResult auth(Account.Id account) {
@@ -196,7 +196,7 @@
       getServletContext().log("Multiple accounts with username " + userName + " found");
       return null;
     }
-    return auth(accountStates.get(0).getAccount().id());
+    return auth(accountStates.get(0).account().id());
   }
 
   private Optional<AuthResult> byPreferredEmail(String email) {
diff --git a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
index 407a4bc..7af7f45 100644
--- a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
+++ b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
@@ -210,6 +210,7 @@
   public static final String XD_AUTHORIZATION = "access_token";
   public static final String XD_CONTENT_TYPE = "$ct";
   public static final String XD_METHOD = "$m";
+  public static final int SC_TOO_MANY_REQUESTS = 429;
 
   private static final int HEAP_EST_SIZE = 10 * 8 * 1024; // Presize 10 blocks.
   private static final String PLAIN_TEXT = "text/plain";
@@ -651,7 +652,13 @@
         }
       } catch (QuotaException e) {
         responseBytes =
-            replyError(req, res, status = 429, messageOr(e, "Quota limit reached"), e.caching(), e);
+            replyError(
+                req,
+                res,
+                status = SC_TOO_MANY_REQUESTS,
+                messageOr(e, "Quota limit reached"),
+                e.caching(),
+                e);
       } catch (Exception e) {
         status = SC_INTERNAL_SERVER_ERROR;
         responseBytes = handleException(e, req, res);
diff --git a/java/com/google/gerrit/index/query/TooManyTermsInQueryException.java b/java/com/google/gerrit/index/query/TooManyTermsInQueryException.java
new file mode 100644
index 0000000..b0a394e
--- /dev/null
+++ b/java/com/google/gerrit/index/query/TooManyTermsInQueryException.java
@@ -0,0 +1,29 @@
+// Copyright (C) 2019 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.index.query;
+
+public class TooManyTermsInQueryException extends QueryParseException {
+  private static final long serialVersionUID = 1L;
+
+  private static final String MESSAGE = "too many terms in query";
+
+  public TooManyTermsInQueryException() {
+    super(MESSAGE);
+  }
+
+  public TooManyTermsInQueryException(Throwable why) {
+    super(MESSAGE, why);
+  }
+}
diff --git a/java/com/google/gerrit/lucene/LuceneAccountIndex.java b/java/com/google/gerrit/lucene/LuceneAccountIndex.java
index 8e67fda..27aa37f 100644
--- a/java/com/google/gerrit/lucene/LuceneAccountIndex.java
+++ b/java/com/google/gerrit/lucene/LuceneAccountIndex.java
@@ -62,7 +62,7 @@
   private static final String ID_SORT_FIELD = sortFieldName(ID);
 
   private static Term idTerm(AccountState as) {
-    return idTerm(as.getAccount().id());
+    return idTerm(as.account().id());
   }
 
   private static Term idTerm(Account.Id id) {
diff --git a/java/com/google/gerrit/server/IdentifiedUser.java b/java/com/google/gerrit/server/IdentifiedUser.java
index 7e18280..f29850a 100644
--- a/java/com/google/gerrit/server/IdentifiedUser.java
+++ b/java/com/google/gerrit/server/IdentifiedUser.java
@@ -234,7 +234,7 @@
         groupBackend,
         enableReverseDnsLookup,
         remotePeerProvider,
-        state.getAccount().id(),
+        state.account().id(),
         realUser);
     this.state = state;
   }
@@ -323,7 +323,7 @@
    */
   @Override
   public Optional<String> getUserName() {
-    return state().getUserName();
+    return state().userName();
   }
 
   /** @return unique name of the user for logging, never {@code null} */
@@ -339,7 +339,7 @@
    * @return the account of the identified user, an empty account if the account is missing
    */
   public Account getAccount() {
-    return state().getAccount();
+    return state().account();
   }
 
   public boolean hasEmailAddress(String email) {
@@ -376,7 +376,7 @@
   @Override
   public GroupMembership getEffectiveGroups() {
     if (effectiveGroups == null) {
-      if (authConfig.isIdentityTrustable(state().getExternalIds())) {
+      if (authConfig.isIdentityTrustable(state().externalIds())) {
         effectiveGroups = groupBackend.membershipsOf(this);
         logger.atFinest().log(
             "Known groups of %s: %s", getLoggableName(), lazy(effectiveGroups::getKnownGroups));
diff --git a/java/com/google/gerrit/server/account/AbstractRealm.java b/java/com/google/gerrit/server/account/AbstractRealm.java
index e61736d..380001d 100644
--- a/java/com/google/gerrit/server/account/AbstractRealm.java
+++ b/java/com/google/gerrit/server/account/AbstractRealm.java
@@ -53,7 +53,7 @@
 
   @Override
   public boolean hasEmailAddress(IdentifiedUser user, String email) {
-    for (ExternalId ext : user.state().getExternalIds()) {
+    for (ExternalId ext : user.state().externalIds()) {
       if (email != null && email.equalsIgnoreCase(ext.email())) {
         return true;
       }
@@ -63,7 +63,7 @@
 
   @Override
   public Set<String> getEmailAddresses(IdentifiedUser user) {
-    Collection<ExternalId> ids = user.state().getExternalIds();
+    Collection<ExternalId> ids = user.state().externalIds();
     Set<String> emails = Sets.newHashSetWithExpectedSize(ids.size());
     for (ExternalId ext : ids) {
       if (!Strings.isNullOrEmpty(ext.email())) {
diff --git a/java/com/google/gerrit/server/account/AccountCacheImpl.java b/java/com/google/gerrit/server/account/AccountCacheImpl.java
index fe386ee..0decc91 100644
--- a/java/com/google/gerrit/server/account/AccountCacheImpl.java
+++ b/java/com/google/gerrit/server/account/AccountCacheImpl.java
@@ -133,7 +133,7 @@
     }
     for (Future<Optional<AccountState>> f : futures) {
       try {
-        f.get().ifPresent(s -> accountStates.put(s.getAccount().id(), s));
+        f.get().ifPresent(s -> accountStates.put(s.account().id(), s));
       } catch (InterruptedException | ExecutionException e) {
         logger.atSevere().withCause(e).log("Cannot load AccountState");
       }
diff --git a/java/com/google/gerrit/server/account/AccountControl.java b/java/com/google/gerrit/server/account/AccountControl.java
index fc0bfd0..05749a1 100644
--- a/java/com/google/gerrit/server/account/AccountControl.java
+++ b/java/com/google/gerrit/server/account/AccountControl.java
@@ -133,7 +133,7 @@
         new OtherUser() {
           @Override
           Account.Id getId() {
-            return otherUser.getAccount().id();
+            return otherUser.account().id();
           }
 
           @Override
diff --git a/java/com/google/gerrit/server/account/AccountDeactivator.java b/java/com/google/gerrit/server/account/AccountDeactivator.java
index 1bd17bc..3465459 100644
--- a/java/com/google/gerrit/server/account/AccountDeactivator.java
+++ b/java/com/google/gerrit/server/account/AccountDeactivator.java
@@ -100,15 +100,15 @@
   }
 
   private boolean processAccount(AccountState accountState) {
-    if (!accountState.getUserName().isPresent()) {
+    if (!accountState.userName().isPresent()) {
       return false;
     }
 
-    String userName = accountState.getUserName().get();
+    String userName = accountState.userName().get();
     logger.atFine().log("processing account %s", userName);
     try {
-      if (realm.accountBelongsToRealm(accountState.getExternalIds()) && !realm.isActive(userName)) {
-        sif.deactivate(accountState.getAccount().id());
+      if (realm.accountBelongsToRealm(accountState.externalIds()) && !realm.isActive(userName)) {
+        sif.deactivate(accountState.account().id());
         logger.atInfo().log("deactivated account %s", userName);
         return true;
       }
@@ -117,7 +117,7 @@
     } catch (Exception e) {
       logger.atSevere().withCause(e).log(
           "Error deactivating account: %s (%s) %s",
-          userName, accountState.getAccount().id(), e.getMessage());
+          userName, accountState.account().id(), e.getMessage());
     }
     return false;
   }
diff --git a/java/com/google/gerrit/server/account/AccountManager.java b/java/com/google/gerrit/server/account/AccountManager.java
index 09757eb..c5d291e 100644
--- a/java/com/google/gerrit/server/account/AccountManager.java
+++ b/java/com/google/gerrit/server/account/AccountManager.java
@@ -151,7 +151,7 @@
       }
 
       // Account exists
-      Optional<Account> act = updateAccountActiveStatus(who, accountState.get().getAccount());
+      Optional<Account> act = updateAccountActiveStatus(who, accountState.get().account());
       if (!act.isPresent()) {
         // The account was deleted since we checked for it last time. This should never happen
         // since we don't support deletion of accounts.
@@ -207,7 +207,7 @@
         throw new AccountException("Unable to deactivate account " + account.id(), e);
       }
     }
-    return byIdCache.get(account.id()).map(AccountState::getAccount);
+    return byIdCache.get(account.id()).map(AccountState::account);
   }
 
   private boolean shouldUpdateActiveStatus(AuthRequest authRequest) {
@@ -337,7 +337,7 @@
       addGroupMember(adminGroupUuid, user);
     }
 
-    realm.onCreateAccount(who, accountState.getAccount());
+    realm.onCreateAccount(who, accountState.account());
     return new AuthResult(newId, extId.key(), true);
   }
 
@@ -421,7 +421,7 @@
               to,
               (a, u) -> {
                 u.addExternalId(newExtId);
-                if (who.getEmailAddress() != null && a.getAccount().preferredEmail() == null) {
+                if (who.getEmailAddress() != null && a.account().preferredEmail() == null) {
                   u.setPreferredEmail(who.getEmailAddress());
                 }
               });
@@ -450,7 +450,7 @@
             to,
             (a, u) -> {
               Set<ExternalId> filteredExtIdsByScheme =
-                  a.getExternalIds().stream()
+                  a.externalIds().stream()
                       .filter(e -> e.key().isScheme(who.getExternalIdKey().scheme()))
                       .collect(toImmutableSet());
               if (filteredExtIdsByScheme.isEmpty()) {
@@ -514,9 +514,9 @@
             from,
             (a, u) -> {
               u.deleteExternalIds(extIds);
-              if (a.getAccount().preferredEmail() != null
+              if (a.account().preferredEmail() != null
                   && extIds.stream()
-                      .anyMatch(e -> a.getAccount().preferredEmail().equals(e.email()))) {
+                      .anyMatch(e -> a.account().preferredEmail().equals(e.email()))) {
                 u.setPreferredEmail(null);
               }
             });
diff --git a/java/com/google/gerrit/server/account/AccountResolver.java b/java/com/google/gerrit/server/account/AccountResolver.java
index d07765c..058cb62 100644
--- a/java/com/google/gerrit/server/account/AccountResolver.java
+++ b/java/com/google/gerrit/server/account/AccountResolver.java
@@ -113,9 +113,9 @@
   }
 
   private static String formatForException(Result result, AccountState state) {
-    return state.getAccount().id()
+    return state.account().id()
         + ": "
-        + state.getAccount().getNameEmail(result.accountResolver().anonymousCowardName);
+        + state.account().getNameEmail(result.accountResolver().anonymousCowardName);
   }
 
   public static boolean isSelf(String input) {
@@ -135,7 +135,7 @@
     }
 
     private ImmutableList<AccountState> canonicalize(List<AccountState> list) {
-      TreeSet<AccountState> set = new TreeSet<>(comparing(a -> a.getAccount().id().get()));
+      TreeSet<AccountState> set = new TreeSet<>(comparing(a -> a.account().id().get()));
       set.addAll(requireNonNull(list));
       return ImmutableList.copyOf(set);
     }
@@ -160,7 +160,7 @@
     }
 
     public ImmutableSet<Account.Id> asIdSet() {
-      return list.stream().map(a -> a.getAccount().id()).collect(toImmutableSet());
+      return list.stream().map(a -> a.account().id()).collect(toImmutableSet());
     }
 
     public AccountState asUnique() throws UnresolvableAccountException {
@@ -192,7 +192,7 @@
         return self.get().asIdentifiedUser();
       }
       return userFactory.runAs(
-          null, list.get(0).getAccount().id(), requireNonNull(caller).getRealUser());
+          null, list.get(0).account().id(), requireNonNull(caller).getRealUser());
     }
 
     @VisibleForTesting
@@ -349,7 +349,7 @@
       String name = nameOrEmail.substring(0, lt - 1);
       ImmutableList<AccountState> nameMatches =
           allMatches.stream()
-              .filter(a -> name.equals(a.getAccount().fullName()))
+              .filter(a -> name.equals(a.account().fullName()))
               .collect(toImmutableList());
       return !nameMatches.isEmpty() ? nameMatches.stream() : allMatches.stream();
     }
@@ -558,7 +558,7 @@
   }
 
   private Predicate<AccountState> accountActivityPredicate() {
-    return (AccountState accountState) -> accountState.getAccount().isActive();
+    return (AccountState accountState) -> accountState.account().isActive();
   }
 
   @VisibleForTesting
diff --git a/java/com/google/gerrit/server/account/AccountState.java b/java/com/google/gerrit/server/account/AccountState.java
index dd81c93..745f197 100644
--- a/java/com/google/gerrit/server/account/AccountState.java
+++ b/java/com/google/gerrit/server/account/AccountState.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.account;
 
+import com.google.auto.value.AutoValue;
 import com.google.common.base.MoreObjects;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
@@ -39,7 +40,8 @@
  * <p>Most callers should not construct AccountStates directly but rather lookup accounts via the
  * account cache (see {@link AccountCache#get(Account.Id)}).
  */
-public class AccountState {
+@AutoValue
+public abstract class AccountState {
   /**
    * Creates an AccountState from the given account config.
    *
@@ -90,13 +92,22 @@
     // an open Repository instance.
     ImmutableMap<ProjectWatchKey, ImmutableSet<NotifyType>> projectWatches =
         accountConfig.getProjectWatches();
-    GeneralPreferencesInfo generalPreferences = accountConfig.getGeneralPreferences();
-    DiffPreferencesInfo diffPreferences = accountConfig.getDiffPreferences();
-    EditPreferencesInfo editPreferences = accountConfig.getEditPreferences();
+    Preferences.General generalPreferences =
+        Preferences.General.fromInfo(accountConfig.getGeneralPreferences());
+    Preferences.Diff diffPreferences =
+        Preferences.Diff.fromInfo(accountConfig.getDiffPreferences());
+    Preferences.Edit editPreferences =
+        Preferences.Edit.fromInfo(accountConfig.getEditPreferences());
 
     return Optional.of(
-        new AccountState(
-            account, extIds, projectWatches, generalPreferences, diffPreferences, editPreferences));
+        new AutoValue_AccountState(
+            account,
+            extIds,
+            ExternalId.getUserName(extIds),
+            projectWatches,
+            generalPreferences,
+            diffPreferences,
+            editPreferences));
   }
 
   /**
@@ -118,44 +129,20 @@
    * @return the account state
    */
   public static AccountState forAccount(Account account, Collection<ExternalId> extIds) {
-    return new AccountState(
+    return new AutoValue_AccountState(
         account,
         ImmutableSet.copyOf(extIds),
+        ExternalId.getUserName(extIds),
         ImmutableMap.of(),
-        GeneralPreferencesInfo.defaults(),
-        DiffPreferencesInfo.defaults(),
-        EditPreferencesInfo.defaults());
-  }
-
-  private final Account account;
-  private final ImmutableSet<ExternalId> externalIds;
-  private final Optional<String> userName;
-  private final ImmutableMap<ProjectWatchKey, ImmutableSet<NotifyType>> projectWatches;
-  private final Preferences.General generalPreferences;
-  private final Preferences.Diff diffPreferences;
-  private final Preferences.Edit editPreferences;
-
-  private AccountState(
-      Account account,
-      ImmutableSet<ExternalId> externalIds,
-      ImmutableMap<ProjectWatchKey, ImmutableSet<NotifyType>> projectWatches,
-      GeneralPreferencesInfo generalPreferences,
-      DiffPreferencesInfo diffPreferences,
-      EditPreferencesInfo editPreferences) {
-    this.account = account;
-    this.externalIds = externalIds;
-    this.userName = ExternalId.getUserName(externalIds);
-    this.projectWatches = projectWatches;
-    this.generalPreferences = Preferences.General.fromInfo(generalPreferences);
-    this.diffPreferences = Preferences.Diff.fromInfo(diffPreferences);
-    this.editPreferences = Preferences.Edit.fromInfo(editPreferences);
+        Preferences.General.fromInfo(GeneralPreferencesInfo.defaults()),
+        Preferences.Diff.fromInfo(DiffPreferencesInfo.defaults()),
+        Preferences.Edit.fromInfo(EditPreferencesInfo.defaults()));
   }
 
   /** Get the cached account metadata. */
-  public Account getAccount() {
-    return account;
-  }
-
+  public abstract Account account();
+  /** The external identities that identify the account holder. */
+  public abstract ImmutableSet<ExternalId> externalIds();
   /**
    * Get the username, if one has been declared for this user.
    *
@@ -164,39 +151,36 @@
    * @return the username, {@link Optional#empty()} if the user has no username, or if the username
    *     is empty
    */
-  public Optional<String> getUserName() {
-    return userName;
-  }
-
-  /** The external identities that identify the account holder. */
-  public ImmutableSet<ExternalId> getExternalIds() {
-    return externalIds;
-  }
-
+  public abstract Optional<String> userName();
   /** The project watches of the account. */
-  public ImmutableMap<ProjectWatchKey, ImmutableSet<NotifyType>> getProjectWatches() {
-    return projectWatches;
-  }
+  public abstract ImmutableMap<ProjectWatchKey, ImmutableSet<NotifyType>> projectWatches();
+  /** The general preferences of the account. */
 
   /** The general preferences of the account. */
-  public GeneralPreferencesInfo getGeneralPreferences() {
-    return generalPreferences.toInfo();
+  public GeneralPreferencesInfo generalPreferences() {
+    return immutableGeneralPreferences().toInfo();
   }
 
   /** The diff preferences of the account. */
-  public DiffPreferencesInfo getDiffPreferences() {
-    return diffPreferences.toInfo();
+  public DiffPreferencesInfo diffPreferences() {
+    return immutableDiffPreferences().toInfo();
   }
 
   /** The edit preferences of the account. */
-  public EditPreferencesInfo getEditPreferences() {
-    return editPreferences.toInfo();
+  public EditPreferencesInfo editPreferences() {
+    return immutableEditPreferences().toInfo();
   }
 
   @Override
-  public String toString() {
+  public final String toString() {
     MoreObjects.ToStringHelper h = MoreObjects.toStringHelper(this);
-    h.addValue(getAccount().id());
+    h.addValue(account().id());
     return h.toString();
   }
+
+  protected abstract Preferences.General immutableGeneralPreferences();
+
+  protected abstract Preferences.Diff immutableDiffPreferences();
+
+  protected abstract Preferences.Edit immutableEditPreferences();
 }
diff --git a/java/com/google/gerrit/server/account/AccountsConsistencyChecker.java b/java/com/google/gerrit/server/account/AccountsConsistencyChecker.java
index 6ec3a05..289a587 100644
--- a/java/com/google/gerrit/server/account/AccountsConsistencyChecker.java
+++ b/java/com/google/gerrit/server/account/AccountsConsistencyChecker.java
@@ -35,9 +35,9 @@
     List<ConsistencyProblemInfo> problems = new ArrayList<>();
 
     for (AccountState accountState : accounts.all()) {
-      Account account = accountState.getAccount();
+      Account account = accountState.account();
       if (account.preferredEmail() != null) {
-        if (!accountState.getExternalIds().stream()
+        if (!accountState.externalIds().stream()
             .anyMatch(e -> account.preferredEmail().equals(e.email()))) {
           addError(
               String.format(
diff --git a/java/com/google/gerrit/server/account/Emails.java b/java/com/google/gerrit/server/account/Emails.java
index 1d53ed2..ee2b672 100644
--- a/java/com/google/gerrit/server/account/Emails.java
+++ b/java/com/google/gerrit/server/account/Emails.java
@@ -82,7 +82,7 @@
     }
 
     return executeIndexQuery(() -> queryProvider.get().byPreferredEmail(email).stream())
-        .map(a -> a.getAccount().id())
+        .map(a -> a.account().id())
         .collect(toImmutableSet());
   }
 
@@ -102,7 +102,7 @@
     if (!emailsToBackfill.isEmpty()) {
       executeIndexQuery(
               () -> queryProvider.get().byPreferredEmail(emailsToBackfill).entries().stream())
-          .forEach(e -> result.put(e.getKey(), e.getValue().getAccount().id()));
+          .forEach(e -> result.put(e.getKey(), e.getValue().account().id()));
     }
     return ImmutableSetMultimap.copyOf(result);
   }
diff --git a/java/com/google/gerrit/server/account/GroupMembers.java b/java/com/google/gerrit/server/account/GroupMembers.java
index d7e97ba..1ce3ccf 100644
--- a/java/com/google/gerrit/server/account/GroupMembers.java
+++ b/java/com/google/gerrit/server/account/GroupMembers.java
@@ -131,7 +131,7 @@
             .filter(groupControl::canSeeMember)
             .map(accountCache::get)
             .flatMap(Streams::stream)
-            .map(AccountState::getAccount)
+            .map(AccountState::account)
             .collect(toImmutableSet());
 
     Set<Account> indirectMembers = new HashSet<>();
diff --git a/java/com/google/gerrit/server/account/InternalAccountDirectory.java b/java/com/google/gerrit/server/account/InternalAccountDirectory.java
index dde8f25..160e355 100644
--- a/java/com/google/gerrit/server/account/InternalAccountDirectory.java
+++ b/java/com/google/gerrit/server/account/InternalAccountDirectory.java
@@ -105,7 +105,7 @@
       AccountState state = accountStates.get(id);
       if (state != null) {
         if (!options.contains(FillOptions.SECONDARY_EMAILS)
-            || Objects.equals(currentUserId, state.getAccount().id())
+            || Objects.equals(currentUserId, state.account().id())
             || canModifyAccount) {
           fill(info, accountStates.get(id), options);
         } else {
@@ -120,7 +120,7 @@
   }
 
   private void fill(AccountInfo info, AccountState accountState, Set<FillOptions> options) {
-    Account account = accountState.getAccount();
+    Account account = accountState.account();
     if (options.contains(FillOptions.ID)) {
       info._accountId = account.id().get();
     } else {
@@ -130,17 +130,17 @@
     if (options.contains(FillOptions.NAME)) {
       info.name = Strings.emptyToNull(account.fullName());
       if (info.name == null) {
-        info.name = accountState.getUserName().orElse(null);
+        info.name = accountState.userName().orElse(null);
       }
     }
     if (options.contains(FillOptions.EMAIL)) {
       info.email = account.preferredEmail();
     }
     if (options.contains(FillOptions.SECONDARY_EMAILS)) {
-      info.secondaryEmails = getSecondaryEmails(account, accountState.getExternalIds());
+      info.secondaryEmails = getSecondaryEmails(account, accountState.externalIds());
     }
     if (options.contains(FillOptions.USERNAME)) {
-      info.username = accountState.getUserName().orElse(null);
+      info.username = accountState.userName().orElse(null);
     }
 
     if (options.contains(FillOptions.STATUS)) {
diff --git a/java/com/google/gerrit/server/account/Preferences.java b/java/com/google/gerrit/server/account/Preferences.java
index 63ff9e3..c15e6b0 100644
--- a/java/com/google/gerrit/server/account/Preferences.java
+++ b/java/com/google/gerrit/server/account/Preferences.java
@@ -15,7 +15,6 @@
 
 import com.google.auto.value.AutoValue;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.extensions.client.DiffPreferencesInfo;
 import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
@@ -77,8 +76,6 @@
 
     public abstract Optional<ImmutableList<String>> changeTable();
 
-    public abstract Optional<ImmutableMap<String, String>> urlAliases();
-
     @AutoValue.Builder
     public abstract static class Builder {
       abstract Builder changesPerPage(@Nullable Integer val);
@@ -121,8 +118,6 @@
 
       abstract Builder changeTable(@Nullable ImmutableList<String> val);
 
-      abstract Builder urlAliases(@Nullable ImmutableMap<String, String> val);
-
       abstract General build();
     }
 
@@ -148,7 +143,6 @@
           .workInProgressByDefault(info.workInProgressByDefault)
           .my(info.my == null ? null : ImmutableList.copyOf(info.my))
           .changeTable(info.changeTable == null ? null : ImmutableList.copyOf(info.changeTable))
-          .urlAliases(info.urlAliases == null ? null : ImmutableMap.copyOf(info.urlAliases))
           .build();
     }
 
@@ -174,7 +168,6 @@
       info.workInProgressByDefault = workInProgressByDefault().orElse(null);
       info.my = my().orElse(null);
       info.changeTable = changeTable().orElse(null);
-      info.urlAliases = urlAliases().orElse(null);
       return info;
     }
   }
diff --git a/java/com/google/gerrit/server/account/SetInactiveFlag.java b/java/com/google/gerrit/server/account/SetInactiveFlag.java
index da2d640..5d0b6ec 100644
--- a/java/com/google/gerrit/server/account/SetInactiveFlag.java
+++ b/java/com/google/gerrit/server/account/SetInactiveFlag.java
@@ -56,7 +56,7 @@
             "Deactivate Account via API",
             accountId,
             (a, u) -> {
-              if (!a.getAccount().isActive()) {
+              if (!a.account().isActive()) {
                 alreadyInactive.set(true);
               } else {
                 try {
@@ -89,7 +89,7 @@
             "Activate Account via API",
             accountId,
             (a, u) -> {
-              if (a.getAccount().isActive()) {
+              if (a.account().isActive()) {
                 alreadyActive.set(true);
               } else {
                 try {
diff --git a/java/com/google/gerrit/server/account/StoredPreferences.java b/java/com/google/gerrit/server/account/StoredPreferences.java
index 31705db..05b8f41 100644
--- a/java/com/google/gerrit/server/account/StoredPreferences.java
+++ b/java/com/google/gerrit/server/account/StoredPreferences.java
@@ -21,11 +21,8 @@
 import static com.google.gerrit.server.git.UserConfigSections.CHANGE_TABLE;
 import static com.google.gerrit.server.git.UserConfigSections.CHANGE_TABLE_COLUMN;
 import static com.google.gerrit.server.git.UserConfigSections.KEY_ID;
-import static com.google.gerrit.server.git.UserConfigSections.KEY_MATCH;
 import static com.google.gerrit.server.git.UserConfigSections.KEY_TARGET;
-import static com.google.gerrit.server.git.UserConfigSections.KEY_TOKEN;
 import static com.google.gerrit.server.git.UserConfigSections.KEY_URL;
-import static com.google.gerrit.server.git.UserConfigSections.URL_ALIAS;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.base.Strings;
@@ -47,9 +44,7 @@
 import java.io.IOException;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.Optional;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.lib.CommitBuilder;
@@ -151,7 +146,6 @@
           parseDefaultGeneralPreferences(defaultCfg, null));
       setChangeTable(cfg, mergedGeneralPreferencesInput.changeTable);
       setMy(cfg, mergedGeneralPreferencesInput.my);
-      setUrlAliases(cfg, mergedGeneralPreferencesInput.urlAliases);
 
       // evict the cached general preferences
       this.generalPreferences = null;
@@ -246,11 +240,9 @@
     if (input != null) {
       r.changeTable = input.changeTable;
       r.my = input.my;
-      r.urlAliases = input.urlAliases;
     } else {
       r.changeTable = parseChangeTableColumns(cfg, defaultCfg);
       r.my = parseMyMenus(cfg, defaultCfg);
-      r.urlAliases = parseUrlAliases(cfg, defaultCfg);
     }
     return r;
   }
@@ -404,14 +396,6 @@
     return my;
   }
 
-  private static Map<String, String> parseUrlAliases(Config cfg, @Nullable Config defaultCfg) {
-    Map<String, String> urlAliases = urlAliases(cfg);
-    if (urlAliases == null && defaultCfg != null) {
-      urlAliases = urlAliases(defaultCfg);
-    }
-    return urlAliases;
-  }
-
   public static GeneralPreferencesInfo readDefaultGeneralPreferences(
       AllUsersName allUsersName, Repository allUsersRepo)
       throws IOException, ConfigInvalidException {
@@ -449,7 +433,6 @@
         GeneralPreferencesInfo.defaults());
     setMy(defaultPrefs.getConfig(), input.my);
     setChangeTable(defaultPrefs.getConfig(), input.changeTable);
-    setUrlAliases(defaultPrefs.getConfig(), input.urlAliases);
     defaultPrefs.commit(md);
 
     return parseGeneralPreferences(defaultPrefs.getConfig(), null, null);
@@ -554,31 +537,6 @@
     }
   }
 
-  private static Map<String, String> urlAliases(Config cfg) {
-    HashMap<String, String> urlAliases = new HashMap<>();
-    for (String subsection : cfg.getSubsections(URL_ALIAS)) {
-      urlAliases.put(
-          cfg.getString(URL_ALIAS, subsection, KEY_MATCH),
-          cfg.getString(URL_ALIAS, subsection, KEY_TOKEN));
-    }
-    return !urlAliases.isEmpty() ? urlAliases : null;
-  }
-
-  private static void setUrlAliases(Config cfg, Map<String, String> urlAliases) {
-    if (urlAliases != null) {
-      for (String subsection : cfg.getSubsections(URL_ALIAS)) {
-        cfg.unsetSection(URL_ALIAS, subsection);
-      }
-
-      int i = 1;
-      for (Map.Entry<String, String> e : urlAliases.entrySet()) {
-        cfg.setString(URL_ALIAS, URL_ALIAS + i, KEY_MATCH, e.getKey());
-        cfg.setString(URL_ALIAS, URL_ALIAS + i, KEY_TOKEN, e.getValue());
-        i++;
-      }
-    }
-  }
-
   private static void unsetSection(Config cfg, String section) {
     cfg.unsetSection(section, null);
     for (String subsection : cfg.getSubsections(section)) {
diff --git a/java/com/google/gerrit/server/api/groups/GroupsImpl.java b/java/com/google/gerrit/server/api/groups/GroupsImpl.java
index a46b59a..95dcad4 100644
--- a/java/com/google/gerrit/server/api/groups/GroupsImpl.java
+++ b/java/com/google/gerrit/server/api/groups/GroupsImpl.java
@@ -141,7 +141,7 @@
 
     if (req.getUser() != null) {
       try {
-        list.setUser(accountResolver.resolve(req.getUser()).asUnique().getAccount().id());
+        list.setUser(accountResolver.resolve(req.getUser()).asUnique().account().id());
       } catch (Exception e) {
         throw asRestApiException("Error looking up user " + req.getUser(), e);
       }
diff --git a/java/com/google/gerrit/server/args4j/AccountIdHandler.java b/java/com/google/gerrit/server/args4j/AccountIdHandler.java
index 36ae88a..26bef4c 100644
--- a/java/com/google/gerrit/server/args4j/AccountIdHandler.java
+++ b/java/com/google/gerrit/server/args4j/AccountIdHandler.java
@@ -62,7 +62,7 @@
     Account.Id accountId;
     try {
       try {
-        accountId = accountResolver.resolve(token).asUnique().getAccount().id();
+        accountId = accountResolver.resolve(token).asUnique().account().id();
       } catch (UnprocessableEntityException e) {
         switch (authType) {
           case HTTP_LDAP:
diff --git a/java/com/google/gerrit/server/auth/InternalAuthBackend.java b/java/com/google/gerrit/server/auth/InternalAuthBackend.java
index 2821bf6..2f8886b 100644
--- a/java/com/google/gerrit/server/auth/InternalAuthBackend.java
+++ b/java/com/google/gerrit/server/auth/InternalAuthBackend.java
@@ -56,14 +56,14 @@
 
     AccountState who = accountCache.getByUsername(username).orElseThrow(UnknownUserException::new);
 
-    if (!who.getAccount().isActive()) {
+    if (!who.account().isActive()) {
       throw new UserNotAllowedException(
           "Authentication failed for "
               + username
               + ": account inactive or not provisioned in Gerrit");
     }
 
-    if (!PasswordVerifier.checkPassword(who.getExternalIds(), username, req.getPassword().get())) {
+    if (!PasswordVerifier.checkPassword(who.externalIds(), username, req.getPassword().get())) {
       throw new InvalidCredentialsException();
     }
     return new AuthUser(AuthUser.UUID.create(username), username);
diff --git a/java/com/google/gerrit/server/auth/ldap/LdapGroupBackend.java b/java/com/google/gerrit/server/auth/ldap/LdapGroupBackend.java
index 2433f67..4a75158 100644
--- a/java/com/google/gerrit/server/auth/ldap/LdapGroupBackend.java
+++ b/java/com/google/gerrit/server/auth/ldap/LdapGroupBackend.java
@@ -179,7 +179,7 @@
 
   @Override
   public GroupMembership membershipsOf(IdentifiedUser user) {
-    String id = findId(user.state().getExternalIds());
+    String id = findId(user.state().externalIds());
     if (id == null) {
       return GroupMembership.EMPTY;
     }
diff --git a/java/com/google/gerrit/server/change/AbandonOp.java b/java/com/google/gerrit/server/change/AbandonOp.java
index 31332ec..230a03c 100644
--- a/java/com/google/gerrit/server/change/AbandonOp.java
+++ b/java/com/google/gerrit/server/change/AbandonOp.java
@@ -112,7 +112,7 @@
     try {
       ReplyToChangeSender cm = abandonedSenderFactory.create(ctx.getProject(), change.getId());
       if (accountState != null) {
-        cm.setFrom(accountState.getAccount().id());
+        cm.setFrom(accountState.account().id());
       }
       cm.setChangeMessage(message.getMessage(), ctx.getWhen());
       cm.setNotify(notify);
diff --git a/java/com/google/gerrit/server/change/ChangeResource.java b/java/com/google/gerrit/server/change/ChangeResource.java
index d8d82c6..4566919 100644
--- a/java/com/google/gerrit/server/change/ChangeResource.java
+++ b/java/com/google/gerrit/server/change/ChangeResource.java
@@ -223,9 +223,8 @@
   }
 
   private void hashAccount(Hasher h, AccountState accountState, byte[] buf) {
-    h.putInt(accountState.getAccount().id().get());
-    h.putString(
-        MoreObjects.firstNonNull(accountState.getAccount().metaId(), ZERO_ID_STRING), UTF_8);
-    accountState.getExternalIds().stream().forEach(e -> hashObjectId(h, e.blobId(), buf));
+    h.putInt(accountState.account().id().get());
+    h.putString(MoreObjects.firstNonNull(accountState.account().metaId(), ZERO_ID_STRING), UTF_8);
+    accountState.externalIds().stream().forEach(e -> hashObjectId(h, e.blobId(), buf));
   }
 }
diff --git a/java/com/google/gerrit/server/change/DeleteReviewerOp.java b/java/com/google/gerrit/server/change/DeleteReviewerOp.java
index 4cb06ba..7a6c11f 100644
--- a/java/com/google/gerrit/server/change/DeleteReviewerOp.java
+++ b/java/com/google/gerrit/server/change/DeleteReviewerOp.java
@@ -108,7 +108,7 @@
   @Override
   public boolean updateChange(ChangeContext ctx)
       throws AuthException, ResourceNotFoundException, PermissionBackendException, IOException {
-    Account.Id reviewerId = reviewer.getAccount().id();
+    Account.Id reviewerId = reviewer.account().id();
     // Check of removing this reviewer (even if there is no vote processed by the loop below) is OK
     removeReviewerControl.checkRemoveReviewer(ctx.getNotes(), ctx.getUser(), reviewerId);
 
@@ -125,7 +125,7 @@
     }
 
     StringBuilder msg = new StringBuilder();
-    msg.append("Removed reviewer " + reviewer.getAccount().fullName());
+    msg.append("Removed reviewer " + reviewer.account().fullName());
     StringBuilder removedVotesMsg = new StringBuilder();
     removedVotesMsg.append(" with the following votes:\n\n");
     List<PatchSetApproval> del = new ArrayList<>();
@@ -212,13 +212,13 @@
       NotifyResolver.Result notify)
       throws EmailException {
     Account.Id userId = user.get().getAccountId();
-    if (userId.equals(reviewer.getAccount().id())) {
+    if (userId.equals(reviewer.account().id())) {
       // The user knows they removed themselves, don't bother emailing them.
       return;
     }
     DeleteReviewerSender cm = deleteReviewerSenderFactory.create(projectName, change.getId());
     cm.setFrom(userId);
-    cm.addReviewers(Collections.singleton(reviewer.getAccount().id()));
+    cm.addReviewers(Collections.singleton(reviewer.account().id()));
     cm.setChangeMessage(changeMessage.getMessage(), changeMessage.getWrittenOn());
     cm.setNotify(notify);
     cm.send();
diff --git a/java/com/google/gerrit/server/change/NotifyResolver.java b/java/com/google/gerrit/server/change/NotifyResolver.java
index 62c0fdf..491885d 100644
--- a/java/com/google/gerrit/server/change/NotifyResolver.java
+++ b/java/com/google/gerrit/server/change/NotifyResolver.java
@@ -99,7 +99,7 @@
     List<String> problems = new ArrayList<>(inputs.size());
     for (String nameOrEmail : inputs) {
       try {
-        r.add(accountResolver.resolve(nameOrEmail).asUnique().getAccount().id());
+        r.add(accountResolver.resolve(nameOrEmail).asUnique().account().id());
       } catch (UnprocessableEntityException e) {
         problems.add(e.getMessage());
       }
diff --git a/java/com/google/gerrit/server/events/EventFactory.java b/java/com/google/gerrit/server/events/EventFactory.java
index bc91807..4d5f158 100644
--- a/java/com/google/gerrit/server/events/EventFactory.java
+++ b/java/com/google/gerrit/server/events/EventFactory.java
@@ -571,9 +571,9 @@
    */
   public AccountAttribute asAccountAttribute(AccountState accountState) {
     AccountAttribute who = new AccountAttribute();
-    who.name = accountState.getAccount().fullName();
-    who.email = accountState.getAccount().preferredEmail();
-    who.username = accountState.getUserName().orElse(null);
+    who.name = accountState.account().fullName();
+    who.email = accountState.account().preferredEmail();
+    who.username = accountState.userName().orElse(null);
     return who;
   }
 
diff --git a/java/com/google/gerrit/server/extensions/events/EventUtil.java b/java/com/google/gerrit/server/extensions/events/EventUtil.java
index 8b58f4f..1c4cf4f 100644
--- a/java/com/google/gerrit/server/extensions/events/EventUtil.java
+++ b/java/com/google/gerrit/server/extensions/events/EventUtil.java
@@ -89,14 +89,14 @@
   }
 
   public AccountInfo accountInfo(AccountState accountState) {
-    if (accountState == null || accountState.getAccount().id() == null) {
+    if (accountState == null || accountState.account().id() == null) {
       return null;
     }
-    Account account = accountState.getAccount();
+    Account account = accountState.account();
     AccountInfo accountInfo = new AccountInfo(account.id().get());
     accountInfo.email = account.preferredEmail();
     accountInfo.name = account.fullName();
-    accountInfo.username = accountState.getUserName().orElse(null);
+    accountInfo.username = accountState.userName().orElse(null);
     return accountInfo;
   }
 
@@ -106,8 +106,7 @@
     for (Map.Entry<String, Short> e : approvals.entrySet()) {
       Integer value = e.getValue() != null ? Integer.valueOf(e.getValue()) : null;
       result.put(
-          e.getKey(),
-          new ApprovalInfo(accountState.getAccount().id().get(), value, null, null, ts));
+          e.getKey(), new ApprovalInfo(accountState.account().id().get(), value, null, null, ts));
     }
     return result;
   }
diff --git a/java/com/google/gerrit/server/git/UserConfigSections.java b/java/com/google/gerrit/server/git/UserConfigSections.java
index 859e40d..0ef908c 100644
--- a/java/com/google/gerrit/server/git/UserConfigSections.java
+++ b/java/com/google/gerrit/server/git/UserConfigSections.java
@@ -25,9 +25,6 @@
   public static final String KEY_URL = "url";
   public static final String KEY_TARGET = "target";
   public static final String KEY_ID = "id";
-  public static final String URL_ALIAS = "urlAlias";
-  public static final String KEY_MATCH = "match";
-  public static final String KEY_TOKEN = "token";
 
   /** The table column user preferences. */
   public static final String CHANGE_TABLE = "changeTable";
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
index 3dcb695..f67f465 100644
--- a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
+++ b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
@@ -1578,10 +1578,10 @@
       this.cmd = cmd;
       this.draft = cmd.getRefName().startsWith(MagicBranch.NEW_DRAFT_CHANGE);
       this.labelTypes = labelTypes;
-      GeneralPreferencesInfo prefs = user.state().getGeneralPreferences();
+      GeneralPreferencesInfo prefs = user.state().generalPreferences();
       this.defaultPublishComments =
           prefs != null
-              ? firstNonNull(user.state().getGeneralPreferences().publishCommentsOnPush, false)
+              ? firstNonNull(user.state().generalPreferences().publishCommentsOnPush, false)
               : false;
     }
 
@@ -1696,7 +1696,7 @@
       }
 
       return projectState.is(BooleanProjectConfig.WORK_IN_PROGRESS_BY_DEFAULT)
-          || firstNonNull(user.state().getGeneralPreferences().workInProgressByDefault, false);
+          || firstNonNull(user.state().generalPreferences().workInProgressByDefault, false);
     }
 
     NotifyResolver.Result getNotifyForNewChange() {
diff --git a/java/com/google/gerrit/server/git/receive/ReplaceOp.java b/java/com/google/gerrit/server/git/receive/ReplaceOp.java
index a9a49bf..8640670 100644
--- a/java/com/google/gerrit/server/git/receive/ReplaceOp.java
+++ b/java/com/google/gerrit/server/git/receive/ReplaceOp.java
@@ -544,7 +544,7 @@
       try {
         ReplacePatchSetSender cm =
             replacePatchSetFactory.create(projectState.getNameKey(), notes.getChangeId());
-        cm.setFrom(ctx.getAccount().getAccount().id());
+        cm.setFrom(ctx.getAccount().account().id());
         cm.setPatchSet(newPatchSet, info);
         cm.setChangeMessage(msg.getMessage(), ctx.getWhen());
         cm.setNotify(ctx.getNotify(notes.getChangeId()));
diff --git a/java/com/google/gerrit/server/group/db/AuditLogFormatter.java b/java/com/google/gerrit/server/group/db/AuditLogFormatter.java
index 454ce68..f263d18 100644
--- a/java/com/google/gerrit/server/group/db/AuditLogFormatter.java
+++ b/java/com/google/gerrit/server/group/db/AuditLogFormatter.java
@@ -51,7 +51,7 @@
   }
 
   private static Optional<Account> getAccount(AccountCache accountCache, Account.Id accountId) {
-    return accountCache.get(accountId).map(AccountState::getAccount);
+    return accountCache.get(accountId).map(AccountState::account);
   }
 
   private static Optional<GroupDescription.Basic> getGroup(
diff --git a/java/com/google/gerrit/server/index/account/AccountField.java b/java/com/google/gerrit/server/index/account/AccountField.java
index f425339..c6835a0 100644
--- a/java/com/google/gerrit/server/index/account/AccountField.java
+++ b/java/com/google/gerrit/server/index/account/AccountField.java
@@ -44,7 +44,7 @@
 /** Secondary index schemas for accounts. */
 public class AccountField {
   public static final FieldDef<AccountState, Integer> ID =
-      integer("id").stored().build(a -> a.getAccount().id().get());
+      integer("id").stored().build(a -> a.account().id().get());
 
   /**
    * External IDs.
@@ -54,7 +54,7 @@
    */
   public static final FieldDef<AccountState, Iterable<String>> EXTERNAL_ID =
       exact("external_id")
-          .buildRepeatable(a -> Iterables.transform(a.getExternalIds(), id -> id.key().get()));
+          .buildRepeatable(a -> Iterables.transform(a.externalIds(), id -> id.key().get()));
 
   /**
    * Fuzzy prefix match on name and email parts.
@@ -69,7 +69,7 @@
   public static final FieldDef<AccountState, Iterable<String>> NAME_PART =
       prefix("name")
           .buildRepeatable(
-              a -> getNameParts(a, Iterables.transform(a.getExternalIds(), ExternalId::email)));
+              a -> getNameParts(a, Iterables.transform(a.externalIds(), ExternalId::email)));
 
   /**
    * Fuzzy prefix match on name and preferred email parts. Parts of secondary emails are not
@@ -77,13 +77,13 @@
    */
   public static final FieldDef<AccountState, Iterable<String>> NAME_PART_NO_SECONDARY_EMAIL =
       prefix("name2")
-          .buildRepeatable(a -> getNameParts(a, Arrays.asList(a.getAccount().preferredEmail())));
+          .buildRepeatable(a -> getNameParts(a, Arrays.asList(a.account().preferredEmail())));
 
   public static final FieldDef<AccountState, String> FULL_NAME =
-      exact("full_name").build(a -> a.getAccount().fullName());
+      exact("full_name").build(a -> a.account().fullName());
 
   public static final FieldDef<AccountState, String> ACTIVE =
-      exact("inactive").build(a -> a.getAccount().isActive() ? "1" : "0");
+      exact("inactive").build(a -> a.account().isActive() ? "1" : "0");
 
   /**
    * All emails (preferred email + secondary emails). Use this field only if the current user is
@@ -95,9 +95,9 @@
       prefix("email")
           .buildRepeatable(
               a ->
-                  FluentIterable.from(a.getExternalIds())
+                  FluentIterable.from(a.externalIds())
                       .transform(ExternalId::email)
-                      .append(Collections.singleton(a.getAccount().preferredEmail()))
+                      .append(Collections.singleton(a.account().preferredEmail()))
                       .filter(Objects::nonNull)
                       .transform(String::toLowerCase)
                       .toSet());
@@ -106,24 +106,24 @@
       prefix("preferredemail")
           .build(
               a -> {
-                String preferredEmail = a.getAccount().preferredEmail();
+                String preferredEmail = a.account().preferredEmail();
                 return preferredEmail != null ? preferredEmail.toLowerCase() : null;
               });
 
   public static final FieldDef<AccountState, String> PREFERRED_EMAIL_EXACT =
-      exact("preferredemail_exact").build(a -> a.getAccount().preferredEmail());
+      exact("preferredemail_exact").build(a -> a.account().preferredEmail());
 
   public static final FieldDef<AccountState, Timestamp> REGISTERED =
-      timestamp("registered").build(a -> a.getAccount().registeredOn());
+      timestamp("registered").build(a -> a.account().registeredOn());
 
   public static final FieldDef<AccountState, String> USERNAME =
-      exact("username").build(a -> a.getUserName().map(String::toLowerCase).orElse(""));
+      exact("username").build(a -> a.userName().map(String::toLowerCase).orElse(""));
 
   public static final FieldDef<AccountState, Iterable<String>> WATCHED_PROJECT =
       exact("watchedproject")
           .buildRepeatable(
               a ->
-                  FluentIterable.from(a.getProjectWatches().keySet())
+                  FluentIterable.from(a.projectWatches().keySet())
                       .transform(k -> k.project().get())
                       .toSet());
 
@@ -138,14 +138,14 @@
       storedOnly("ref_state")
           .buildRepeatable(
               a -> {
-                if (a.getAccount().metaId() == null) {
+                if (a.account().metaId() == null) {
                   return ImmutableList.of();
                 }
 
                 return ImmutableList.of(
                     RefState.create(
-                            RefNames.refsUsers(a.getAccount().id()),
-                            ObjectId.fromString(a.getAccount().metaId()))
+                            RefNames.refsUsers(a.account().id()),
+                            ObjectId.fromString(a.account().metaId()))
                         // We use the default AllUsers name to avoid having to pass around that
                         // variable just for indexing.
                         // This field is only used for staleness detection which will discover the
@@ -163,13 +163,13 @@
       storedOnly("external_id_state")
           .buildRepeatable(
               a ->
-                  a.getExternalIds().stream()
+                  a.externalIds().stream()
                       .filter(e -> e.blobId() != null)
                       .map(ExternalId::toByteArray)
                       .collect(toSet()));
 
   private static final Set<String> getNameParts(AccountState a, Iterable<String> emails) {
-    String fullName = a.getAccount().fullName();
+    String fullName = a.account().fullName();
     Set<String> parts = SchemaUtil.getNameParts(fullName, emails);
 
     // Additional values not currently added by getPersonParts.
diff --git a/java/com/google/gerrit/server/index/account/AccountIndexRewriter.java b/java/com/google/gerrit/server/index/account/AccountIndexRewriter.java
index 35b967c..643c249 100644
--- a/java/com/google/gerrit/server/index/account/AccountIndexRewriter.java
+++ b/java/com/google/gerrit/server/index/account/AccountIndexRewriter.java
@@ -16,22 +16,27 @@
 
 import static java.util.Objects.requireNonNull;
 
+import com.google.gerrit.index.IndexConfig;
 import com.google.gerrit.index.IndexRewriter;
 import com.google.gerrit.index.QueryOptions;
+import com.google.gerrit.index.query.IndexPredicate;
 import com.google.gerrit.index.query.Predicate;
 import com.google.gerrit.index.query.QueryParseException;
+import com.google.gerrit.index.query.TooManyTermsInQueryException;
 import com.google.gerrit.server.account.AccountState;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
+import org.eclipse.jgit.util.MutableInteger;
 
 @Singleton
 public class AccountIndexRewriter implements IndexRewriter<AccountState> {
-
   private final AccountIndexCollection indexes;
+  private final IndexConfig config;
 
   @Inject
-  AccountIndexRewriter(AccountIndexCollection indexes) {
+  AccountIndexRewriter(AccountIndexCollection indexes, IndexConfig config) {
     this.indexes = indexes;
+    this.config = config;
   }
 
   @Override
@@ -39,6 +44,32 @@
       throws QueryParseException {
     AccountIndex index = indexes.getSearchIndex();
     requireNonNull(index, "no active search index configured for accounts");
+    validateMaxTermsInQuery(in);
     return new IndexedAccountQuery(index, in, opts);
   }
+
+  /**
+   * Validates whether a query has too many terms.
+   *
+   * @param predicate the predicate for which the leaf predicates should be counted
+   * @throws QueryParseException thrown if the query has too many terms
+   */
+  public void validateMaxTermsInQuery(Predicate<AccountState> predicate)
+      throws QueryParseException {
+    MutableInteger leafTerms = new MutableInteger();
+    validateMaxTermsInQuery(predicate, leafTerms);
+  }
+
+  private void validateMaxTermsInQuery(Predicate<AccountState> predicate, MutableInteger leafTerms)
+      throws TooManyTermsInQueryException {
+    if (!(predicate instanceof IndexPredicate)) {
+      if (++leafTerms.value > config.maxTerms()) {
+        throw new TooManyTermsInQueryException();
+      }
+    }
+
+    for (Predicate<AccountState> childPredicate : predicate.getChildren()) {
+      validateMaxTermsInQuery(childPredicate, leafTerms);
+    }
+  }
 }
diff --git a/java/com/google/gerrit/server/index/change/ChangeIndexRewriter.java b/java/com/google/gerrit/server/index/change/ChangeIndexRewriter.java
index e92a0f6..476ddc9 100644
--- a/java/com/google/gerrit/server/index/change/ChangeIndexRewriter.java
+++ b/java/com/google/gerrit/server/index/change/ChangeIndexRewriter.java
@@ -31,6 +31,7 @@
 import com.google.gerrit.index.query.OrPredicate;
 import com.google.gerrit.index.query.Predicate;
 import com.google.gerrit.index.query.QueryParseException;
+import com.google.gerrit.index.query.TooManyTermsInQueryException;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Change.Status;
 import com.google.gerrit.server.query.change.AndChangeSource;
@@ -183,7 +184,7 @@
       throws QueryParseException {
     if (isIndexPredicate(in, index)) {
       if (++leafTerms.value > config.maxTerms()) {
-        throw new QueryParseException("too many terms in query");
+        throw new TooManyTermsInQueryException();
       }
       return in;
     } else if (in instanceof LimitPredicate) {
diff --git a/java/com/google/gerrit/server/mail/MailUtil.java b/java/com/google/gerrit/server/mail/MailUtil.java
index 26ebb5c..be01aa9 100644
--- a/java/com/google/gerrit/server/mail/MailUtil.java
+++ b/java/com/google/gerrit/server/mail/MailUtil.java
@@ -62,7 +62,7 @@
   @SuppressWarnings("deprecation")
   private static Account.Id toAccountId(AccountResolver accountResolver, String nameOrEmail)
       throws UnprocessableEntityException, IOException, ConfigInvalidException {
-    return accountResolver.resolveByNameOrEmail(nameOrEmail).asUnique().getAccount().id();
+    return accountResolver.resolveByNameOrEmail(nameOrEmail).asUnique().account().id();
   }
 
   private static boolean isReviewer(FooterLine candidateFooterLine) {
diff --git a/java/com/google/gerrit/server/mail/receive/MailProcessor.java b/java/com/google/gerrit/server/mail/receive/MailProcessor.java
index 034bcc9..4f23fe2 100644
--- a/java/com/google/gerrit/server/mail/receive/MailProcessor.java
+++ b/java/com/google/gerrit/server/mail/receive/MailProcessor.java
@@ -205,7 +205,7 @@
       logger.atWarning().log("Mail: Account %s doesn't exist. Will delete message.", accountId);
       return;
     }
-    if (!accountState.get().getAccount().isActive()) {
+    if (!accountState.get().account().isActive()) {
       logger.atWarning().log("Mail: Account %s is inactive. Will delete message.", accountId);
       sendRejectionEmail(message, InboundEmailRejectionSender.Error.INACTIVE_ACCOUNT);
       return;
diff --git a/java/com/google/gerrit/server/mail/send/ChangeEmail.java b/java/com/google/gerrit/server/mail/send/ChangeEmail.java
index 949541e..21c796a 100644
--- a/java/com/google/gerrit/server/mail/send/ChangeEmail.java
+++ b/java/com/google/gerrit/server/mail/send/ChangeEmail.java
@@ -334,7 +334,7 @@
   protected void removeUsersThatIgnoredTheChange() {
     for (Map.Entry<Account.Id, Collection<String>> e : stars.asMap().entrySet()) {
       if (e.getValue().contains(StarredChangesUtil.IGNORE_LABEL)) {
-        args.accountCache.get(e.getKey()).ifPresent(a -> removeUser(a.getAccount()));
+        args.accountCache.get(e.getKey()).ifPresent(a -> removeUser(a.account()));
       }
     }
   }
diff --git a/java/com/google/gerrit/server/mail/send/FromAddressGeneratorProvider.java b/java/com/google/gerrit/server/mail/send/FromAddressGeneratorProvider.java
index c5f0257..bd42c26 100644
--- a/java/com/google/gerrit/server/mail/send/FromAddressGeneratorProvider.java
+++ b/java/com/google/gerrit/server/mail/send/FromAddressGeneratorProvider.java
@@ -123,7 +123,7 @@
     public Address from(Account.Id fromId) {
       String senderName;
       if (fromId != null) {
-        Optional<Account> a = accountCache.get(fromId).map(AccountState::getAccount);
+        Optional<Account> a = accountCache.get(fromId).map(AccountState::account);
         String fullName = a.map(Account::fullName).orElse(null);
         String userEmail = a.map(Account::preferredEmail).orElse(null);
         if (canRelay(userEmail)) {
@@ -208,7 +208,7 @@
       final String senderName;
 
       if (fromId != null) {
-        String fullName = accountCache.get(fromId).map(a -> a.getAccount().fullName()).orElse(null);
+        String fullName = accountCache.get(fromId).map(a -> a.account().fullName()).orElse(null);
         if (fullName == null || "".equals(fullName)) {
           fullName = anonymousCowardName;
         }
diff --git a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
index 61b5327..3e32628 100644
--- a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
+++ b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
@@ -116,7 +116,7 @@
       if (fromId != null) {
         Optional<AccountState> fromUser = args.accountCache.get(fromId);
         if (fromUser.isPresent()) {
-          GeneralPreferencesInfo senderPrefs = fromUser.get().getGeneralPreferences();
+          GeneralPreferencesInfo senderPrefs = fromUser.get().generalPreferences();
           if (senderPrefs != null && senderPrefs.getEmailStrategy() == CC_ON_OWN_COMMENTS) {
             // If we are impersonating a user, make sure they receive a CC of
             // this message so they can always review and audit what we sent
@@ -127,7 +127,7 @@
             // If they don't want a copy, but we queued one up anyway,
             // drop them from the recipient lists.
             //
-            removeUser(fromUser.get().getAccount());
+            removeUser(fromUser.get().account());
           }
         }
       }
@@ -137,8 +137,8 @@
       for (Account.Id id : rcptTo) {
         Optional<AccountState> thisUser = args.accountCache.get(id);
         if (thisUser.isPresent()) {
-          Account thisUserAccount = thisUser.get().getAccount();
-          GeneralPreferencesInfo prefs = thisUser.get().getGeneralPreferences();
+          Account thisUserAccount = thisUser.get().account();
+          GeneralPreferencesInfo prefs = thisUser.get().generalPreferences();
           if (prefs == null || prefs.getEmailStrategy() == DISABLED) {
             removeUser(thisUserAccount);
           } else if (useHtml() && prefs.getEmailFormat() == EmailFormat.PLAINTEXT) {
@@ -248,7 +248,7 @@
 
   protected String getFromLine() {
     StringBuilder f = new StringBuilder();
-    Optional<Account> account = args.accountCache.get(fromId).map(AccountState::getAccount);
+    Optional<Account> account = args.accountCache.get(fromId).map(AccountState::account);
     if (account.isPresent()) {
       String name = account.get().fullName();
       String email = account.get().preferredEmail();
@@ -324,7 +324,7 @@
       return args.gerritPersonIdent.getName();
     }
 
-    Optional<Account> account = args.accountCache.get(accountId).map(AccountState::getAccount);
+    Optional<Account> account = args.accountCache.get(accountId).map(AccountState::account);
     String name = null;
     if (account.isPresent()) {
       name = account.get().fullName();
@@ -346,7 +346,7 @@
    * @return name/email of account, or Anonymous Coward if unset.
    */
   protected String getNameEmailFor(Account.Id accountId) {
-    Optional<Account> account = args.accountCache.get(accountId).map(AccountState::getAccount);
+    Optional<Account> account = args.accountCache.get(accountId).map(AccountState::account);
     if (account.isPresent()) {
       String name = account.get().fullName();
       String email = account.get().preferredEmail();
@@ -374,7 +374,7 @@
       return null;
     }
 
-    Account account = accountState.get().getAccount();
+    Account account = accountState.get().account();
     String name = account.fullName();
     String email = account.preferredEmail();
     if (name != null && email != null) {
@@ -384,7 +384,7 @@
     } else if (name != null) {
       return name;
     }
-    return accountState.get().getUserName().orElse(null);
+    return accountState.get().userName().orElse(null);
   }
 
   protected boolean shouldSendMessage() {
@@ -505,7 +505,7 @@
   }
 
   private Address toAddress(Account.Id id) {
-    Optional<Account> accountState = args.accountCache.get(id).map(AccountState::getAccount);
+    Optional<Account> accountState = args.accountCache.get(id).map(AccountState::account);
     if (!accountState.isPresent()) {
       return null;
     }
diff --git a/java/com/google/gerrit/server/mail/send/ProjectWatch.java b/java/com/google/gerrit/server/mail/send/ProjectWatch.java
index 8b426ac..37ef801 100644
--- a/java/com/google/gerrit/server/mail/send/ProjectWatch.java
+++ b/java/com/google/gerrit/server/mail/send/ProjectWatch.java
@@ -66,9 +66,8 @@
     Set<Account.Id> projectWatchers = new HashSet<>();
 
     for (AccountState a : args.accountQueryProvider.get().byWatchedProject(project)) {
-      Account.Id accountId = a.getAccount().id();
-      for (Map.Entry<ProjectWatchKey, ImmutableSet<NotifyType>> e :
-          a.getProjectWatches().entrySet()) {
+      Account.Id accountId = a.account().id();
+      for (Map.Entry<ProjectWatchKey, ImmutableSet<NotifyType>> e : a.projectWatches().entrySet()) {
         if (project.equals(e.getKey().project())
             && add(matching, accountId, e.getKey(), e.getValue(), type)) {
           // We only want to prevent matching All-Projects if this filter hits
@@ -78,10 +77,9 @@
     }
 
     for (AccountState a : args.accountQueryProvider.get().byWatchedProject(args.allProjectsName)) {
-      for (Map.Entry<ProjectWatchKey, ImmutableSet<NotifyType>> e :
-          a.getProjectWatches().entrySet()) {
+      for (Map.Entry<ProjectWatchKey, ImmutableSet<NotifyType>> e : a.projectWatches().entrySet()) {
         if (args.allProjectsName.equals(e.getKey().project())) {
-          Account.Id accountId = a.getAccount().id();
+          Account.Id accountId = a.account().id();
           if (!projectWatchers.contains(accountId)) {
             add(matching, accountId, e.getKey(), e.getValue(), type);
           }
diff --git a/java/com/google/gerrit/server/query/account/AccountQueryProcessor.java b/java/com/google/gerrit/server/query/account/AccountQueryProcessor.java
index 19d2215..2e29bbd 100644
--- a/java/com/google/gerrit/server/query/account/AccountQueryProcessor.java
+++ b/java/com/google/gerrit/server/query/account/AccountQueryProcessor.java
@@ -77,6 +77,6 @@
 
   @Override
   protected String formatForLogging(AccountState accountState) {
-    return accountState.getAccount().id().toString();
+    return accountState.account().id().toString();
   }
 }
diff --git a/java/com/google/gerrit/server/query/account/CanSeeChangePredicate.java b/java/com/google/gerrit/server/query/account/CanSeeChangePredicate.java
index c2d8de9..0252a06 100644
--- a/java/com/google/gerrit/server/query/account/CanSeeChangePredicate.java
+++ b/java/com/google/gerrit/server/query/account/CanSeeChangePredicate.java
@@ -37,7 +37,7 @@
   public boolean match(AccountState accountState) {
     try {
       permissionBackend
-          .absentUser(accountState.getAccount().id())
+          .absentUser(accountState.account().id())
           .change(changeNotes)
           .check(ChangePermission.READ);
       return true;
diff --git a/java/com/google/gerrit/server/query/account/InternalAccountQuery.java b/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
index 09c2d51..ef6f2cb 100644
--- a/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
+++ b/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
@@ -76,8 +76,7 @@
       msg.append("Ambiguous external ID ").append(externalId).append(" for accounts: ");
       Joiner.on(", ")
           .appendTo(
-              msg,
-              accountStates.stream().map(a -> a.getAccount().id().toString()).collect(toList()));
+              msg, accountStates.stream().map(a -> a.account().id().toString()).collect(toList()));
       logger.atWarning().log(msg.toString());
     }
     return null;
@@ -103,7 +102,7 @@
     }
 
     return query(AccountPredicates.preferredEmail(email)).stream()
-        .filter(a -> a.getAccount().preferredEmail().equals(email))
+        .filter(a -> a.account().preferredEmail().equals(email))
         .collect(toList());
   }
 
@@ -136,7 +135,7 @@
       String email = emails.get(i);
       Set<AccountState> matchingAccounts =
           r.get(i).stream()
-              .filter(a -> a.getAccount().preferredEmail().equals(email))
+              .filter(a -> a.account().preferredEmail().equals(email))
               .collect(toSet());
       accountsByEmail.putAll(email, matchingAccounts);
     }
diff --git a/java/com/google/gerrit/server/query/change/IsWatchedByPredicate.java b/java/com/google/gerrit/server/query/change/IsWatchedByPredicate.java
index 6028f2d..218a89d 100644
--- a/java/com/google/gerrit/server/query/change/IsWatchedByPredicate.java
+++ b/java/com/google/gerrit/server/query/change/IsWatchedByPredicate.java
@@ -93,7 +93,7 @@
       throws QueryParseException {
     CurrentUser user = args.getUser();
     if (user.isIdentifiedUser()) {
-      return user.asIdentifiedUser().state().getProjectWatches().keySet();
+      return user.asIdentifiedUser().state().projectWatches().keySet();
     }
     return Collections.emptySet();
   }
diff --git a/java/com/google/gerrit/server/restapi/account/GetDiffPreferences.java b/java/com/google/gerrit/server/restapi/account/GetDiffPreferences.java
index c9773f5..e6c17a9 100644
--- a/java/com/google/gerrit/server/restapi/account/GetDiffPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/GetDiffPreferences.java
@@ -59,7 +59,7 @@
     return Response.ok(
         accountCache
             .get(id)
-            .map(AccountState::getDiffPreferences)
+            .map(AccountState::diffPreferences)
             .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString()))));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/account/GetEditPreferences.java b/java/com/google/gerrit/server/restapi/account/GetEditPreferences.java
index ae3a215..ccc678f 100644
--- a/java/com/google/gerrit/server/restapi/account/GetEditPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/GetEditPreferences.java
@@ -59,7 +59,7 @@
     return Response.ok(
         accountCache
             .get(id)
-            .map(AccountState::getEditPreferences)
+            .map(AccountState::editPreferences)
             .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString()))));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/account/GetPreferences.java b/java/com/google/gerrit/server/restapi/account/GetPreferences.java
index 90884c7..508d294 100644
--- a/java/com/google/gerrit/server/restapi/account/GetPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/GetPreferences.java
@@ -65,7 +65,7 @@
     GeneralPreferencesInfo preferencesInfo =
         accountCache
             .get(id)
-            .map(AccountState::getGeneralPreferences)
+            .map(AccountState::generalPreferences)
             .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString())));
     return Response.ok(unsetDownloadSchemeIfUnsupported(preferencesInfo));
   }
diff --git a/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java b/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
index d60bfd5..fbf1770 100644
--- a/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
+++ b/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
@@ -66,7 +66,7 @@
     Account.Id accountId = rsrc.getUser().getAccountId();
     AccountState account = accounts.get(accountId).orElseThrow(ResourceNotFoundException::new);
     return Response.ok(
-        account.getProjectWatches().entrySet().stream()
+        account.projectWatches().entrySet().stream()
             .map(e -> toProjectWatchInfo(e.getKey(), e.getValue()))
             .sorted(
                 comparing((ProjectWatchInfo pwi) -> pwi.project)
diff --git a/java/com/google/gerrit/server/restapi/account/PutAgreement.java b/java/com/google/gerrit/server/restapi/account/PutAgreement.java
index 5985e17..991f43c 100644
--- a/java/com/google/gerrit/server/restapi/account/PutAgreement.java
+++ b/java/com/google/gerrit/server/restapi/account/PutAgreement.java
@@ -93,7 +93,7 @@
 
     AccountState accountState = self.get().state();
     try {
-      addMembers.addMembers(uuid, ImmutableSet.of(accountState.getAccount().id()));
+      addMembers.addMembers(uuid, ImmutableSet.of(accountState.account().id()));
     } catch (NoSuchGroupException e) {
       throw new ResourceConflictException("autoverify group not found");
     }
diff --git a/java/com/google/gerrit/server/restapi/account/PutName.java b/java/com/google/gerrit/server/restapi/account/PutName.java
index 9e8f5be..d5f6333c 100644
--- a/java/com/google/gerrit/server/restapi/account/PutName.java
+++ b/java/com/google/gerrit/server/restapi/account/PutName.java
@@ -84,8 +84,8 @@
             .get()
             .update("Set Full Name via API", user.getAccountId(), u -> u.setFullName(newName))
             .orElseThrow(() -> new ResourceNotFoundException("account not found"));
-    return Strings.isNullOrEmpty(accountState.getAccount().fullName())
+    return Strings.isNullOrEmpty(accountState.account().fullName())
         ? Response.none()
-        : Response.ok(accountState.getAccount().fullName());
+        : Response.ok(accountState.account().fullName());
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/account/PutPreferred.java b/java/com/google/gerrit/server/restapi/account/PutPreferred.java
index 3799b24..2ddea2f 100644
--- a/java/com/google/gerrit/server/restapi/account/PutPreferred.java
+++ b/java/com/google/gerrit/server/restapi/account/PutPreferred.java
@@ -85,13 +85,13 @@
             "Set Preferred Email via API",
             user.getAccountId(),
             (a, u) -> {
-              if (preferredEmail.equals(a.getAccount().preferredEmail())) {
+              if (preferredEmail.equals(a.account().preferredEmail())) {
                 alreadyPreferred.set(true);
               } else {
                 // check if the user has a matching email
                 String matchingEmail = null;
                 for (String email :
-                    a.getExternalIds().stream()
+                    a.externalIds().stream()
                         .map(ExternalId::email)
                         .filter(Objects::nonNull)
                         .collect(toSet())) {
@@ -128,7 +128,7 @@
                     }
 
                     // claim the email now
-                    u.addExternalId(ExternalId.createEmail(a.getAccount().id(), preferredEmail));
+                    u.addExternalId(ExternalId.createEmail(a.account().id(), preferredEmail));
                     matchingEmail = preferredEmail;
                   } else {
                     // Realm says that the email doesn't belong to the user. This can only happen as
diff --git a/java/com/google/gerrit/server/restapi/account/PutStatus.java b/java/com/google/gerrit/server/restapi/account/PutStatus.java
index 29f69ab..7e27489 100644
--- a/java/com/google/gerrit/server/restapi/account/PutStatus.java
+++ b/java/com/google/gerrit/server/restapi/account/PutStatus.java
@@ -73,8 +73,8 @@
             .get()
             .update("Set Status via API", user.getAccountId(), u -> u.setStatus(newStatus))
             .orElseThrow(() -> new ResourceNotFoundException("account not found"));
-    return Strings.isNullOrEmpty(accountState.getAccount().status())
+    return Strings.isNullOrEmpty(accountState.account().status())
         ? Response.none()
-        : Response.ok(accountState.getAccount().status());
+        : Response.ok(accountState.account().status());
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/account/QueryAccounts.java b/java/com/google/gerrit/server/restapi/account/QueryAccounts.java
index 55019f46..fb2d7d1 100644
--- a/java/com/google/gerrit/server/restapi/account/QueryAccounts.java
+++ b/java/com/google/gerrit/server/restapi/account/QueryAccounts.java
@@ -210,7 +210,7 @@
       }
       QueryResult<AccountState> result = queryProcessor.query(queryPred);
       for (AccountState accountState : result.entities()) {
-        Account.Id id = accountState.getAccount().id();
+        Account.Id id = accountState.account().id();
         matches.put(id, accountLoader.get(id));
       }
 
diff --git a/java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java b/java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java
index 1a63993..2d188970 100644
--- a/java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java
@@ -70,7 +70,7 @@
         accountsUpdateProvider
             .get()
             .update("Set Diff Preferences via API", id, u -> u.setDiffPreferences(input))
-            .map(AccountState::getDiffPreferences)
+            .map(AccountState::diffPreferences)
             .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString()))));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/account/SetEditPreferences.java b/java/com/google/gerrit/server/restapi/account/SetEditPreferences.java
index c85adde..b5c7305 100644
--- a/java/com/google/gerrit/server/restapi/account/SetEditPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/SetEditPreferences.java
@@ -71,7 +71,7 @@
         accountsUpdateProvider
             .get()
             .update("Set Edit Preferences via API", id, u -> u.setEditPreferences(input))
-            .map(AccountState::getEditPreferences)
+            .map(AccountState::editPreferences)
             .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString()))));
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/account/SetPreferences.java b/java/com/google/gerrit/server/restapi/account/SetPreferences.java
index 54f0d9b..ad22851 100644
--- a/java/com/google/gerrit/server/restapi/account/SetPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/SetPreferences.java
@@ -75,7 +75,7 @@
         accountsUpdateProvider
             .get()
             .update("Set General Preferences via API", id, u -> u.setGeneralPreferences(input))
-            .map(AccountState::getGeneralPreferences)
+            .map(AccountState::generalPreferences)
             .orElseThrow(() -> new ResourceNotFoundException(IdString.fromDecoded(id.toString()))));
   }
 
diff --git a/java/com/google/gerrit/server/restapi/change/CreateChange.java b/java/com/google/gerrit/server/restapi/change/CreateChange.java
index c6c9595..564f9e7 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateChange.java
@@ -258,7 +258,7 @@
         input.workInProgress = true;
       } else {
         input.workInProgress =
-            firstNonNull(me.state().getGeneralPreferences().workInProgressByDefault, false);
+            firstNonNull(me.state().generalPreferences().workInProgressByDefault, false);
       }
     }
 
@@ -426,15 +426,14 @@
       commitMessage = ChangeIdUtil.insertId(commitMessage, id);
     }
 
-    if (Boolean.TRUE.equals(me.state().getGeneralPreferences().signedOffBy)) {
+    if (Boolean.TRUE.equals(me.state().generalPreferences().signedOffBy)) {
       commitMessage =
           Joiner.on("\n")
               .join(
                   commitMessage.trim(),
                   String.format(
                       "%s%s",
-                      SIGNED_OFF_BY_TAG,
-                      me.state().getAccount().getNameEmail(anonymousCowardName)));
+                      SIGNED_OFF_BY_TAG, me.state().account().getNameEmail(anonymousCowardName)));
     }
 
     return commitMessage;
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java b/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java
index 2a4f16b..01945fd 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java
@@ -104,7 +104,7 @@
     }
 
     public Account.Id getDeletedAssignee() {
-      return deletedAssignee != null ? deletedAssignee.getAccount().id() : null;
+      return deletedAssignee != null ? deletedAssignee.account().id() : null;
     }
 
     private void addMessage(
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteVote.java b/java/com/google/gerrit/server/restapi/change/DeleteVote.java
index a80863e..3d85631 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteVote.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteVote.java
@@ -170,7 +170,7 @@
       boolean found = false;
       LabelTypes labelTypes = projectState.getLabelTypes(ctx.getNotes());
 
-      Account.Id accountId = accountState.getAccount().id();
+      Account.Id accountId = accountState.account().id();
 
       for (PatchSetApproval a :
           approvalsUtil.byPatchSetUser(
diff --git a/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java b/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
index 7fd5ecf..1aeb1a7 100644
--- a/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
+++ b/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
@@ -24,9 +24,11 @@
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.client.ReviewerState;
 import com.google.gerrit.extensions.common.GroupBaseInfo;
 import com.google.gerrit.extensions.common.SuggestedReviewerInfo;
+import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.Url;
 import com.google.gerrit.index.IndexConfig;
 import com.google.gerrit.index.QueryOptions;
@@ -34,6 +36,7 @@
 import com.google.gerrit.index.query.Predicate;
 import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.index.query.ResultSet;
+import com.google.gerrit.index.query.TooManyTermsInQueryException;
 import com.google.gerrit.metrics.Description;
 import com.google.gerrit.metrics.Description.Units;
 import com.google.gerrit.metrics.MetricMaker;
@@ -50,6 +53,7 @@
 import com.google.gerrit.server.change.ReviewerAdder;
 import com.google.gerrit.server.index.account.AccountField;
 import com.google.gerrit.server.index.account.AccountIndexCollection;
+import com.google.gerrit.server.index.account.AccountIndexRewriter;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.NoSuchProjectException;
@@ -120,6 +124,7 @@
 
   private final AccountLoader.Factory accountLoaderFactory;
   private final AccountQueryBuilder accountQueryBuilder;
+  private final AccountIndexRewriter accountIndexRewriter;
   private final GroupBackend groupBackend;
   private final GroupMembers groupMembers;
   private final ReviewerRecommender reviewerRecommender;
@@ -133,6 +138,7 @@
   ReviewersUtil(
       AccountLoader.Factory accountLoaderFactory,
       AccountQueryBuilder accountQueryBuilder,
+      AccountIndexRewriter accountIndexRewriter,
       GroupBackend groupBackend,
       GroupMembers groupMembers,
       ReviewerRecommender reviewerRecommender,
@@ -143,6 +149,7 @@
       Provider<CurrentUser> self) {
     this.accountLoaderFactory = accountLoaderFactory;
     this.accountQueryBuilder = accountQueryBuilder;
+    this.accountIndexRewriter = accountIndexRewriter;
     this.groupBackend = groupBackend;
     this.groupMembers = groupMembers;
     this.reviewerRecommender = reviewerRecommender;
@@ -164,7 +171,7 @@
       ProjectState projectState,
       VisibilityControl visibilityControl,
       boolean excludeGroups)
-      throws IOException, ConfigInvalidException, PermissionBackendException {
+      throws IOException, ConfigInvalidException, PermissionBackendException, BadRequestException {
     CurrentUser currentUser = self.get();
     if (changeNotes != null) {
       logger.atFine().log(
@@ -224,37 +231,48 @@
     return suggestedReviewers;
   }
 
-  private List<Account.Id> suggestAccounts(SuggestReviewers suggestReviewers) {
+  private List<Account.Id> suggestAccounts(SuggestReviewers suggestReviewers)
+      throws BadRequestException {
     try (Timer0.Context ctx = metrics.queryAccountsLatency.start()) {
-      try {
-        // For performance reasons we don't use AccountQueryProvider as it would always load the
-        // complete account from the cache (or worse, from NoteDb) even though we only need the ID
-        // which we can directly get from the returned results.
-        Predicate<AccountState> pred =
-            Predicate.and(
-                AccountPredicates.isActive(),
-                accountQueryBuilder.defaultQuery(suggestReviewers.getQuery()));
-        logger.atFine().log("accounts index query: %s", pred);
-        ResultSet<FieldBundle> result =
-            accountIndexes
-                .getSearchIndex()
-                .getSource(
-                    pred,
-                    QueryOptions.create(
-                        indexConfig,
-                        0,
-                        suggestReviewers.getLimit() * CANDIDATE_LIST_MULTIPLIER,
-                        ImmutableSet.of(AccountField.ID.getName())))
-                .readRaw();
-        List<Account.Id> matches =
-            result.toList().stream()
-                .map(f -> Account.id(f.getValue(AccountField.ID).intValue()))
-                .collect(toList());
-        logger.atFine().log("Matches: %s", matches);
-        return matches;
-      } catch (QueryParseException e) {
+      // For performance reasons we don't use AccountQueryProvider as it would always load the
+      // complete account from the cache (or worse, from NoteDb) even though we only need the ID
+      // which we can directly get from the returned results.
+      Predicate<AccountState> pred =
+          Predicate.and(
+              AccountPredicates.isActive(),
+              accountQueryBuilder.defaultQuery(suggestReviewers.getQuery()));
+      logger.atFine().log("accounts index query: %s", pred);
+      accountIndexRewriter.validateMaxTermsInQuery(pred);
+      ResultSet<FieldBundle> result =
+          accountIndexes
+              .getSearchIndex()
+              .getSource(
+                  pred,
+                  QueryOptions.create(
+                      indexConfig,
+                      0,
+                      suggestReviewers.getLimit() * CANDIDATE_LIST_MULTIPLIER,
+                      ImmutableSet.of(AccountField.ID.getName())))
+              .readRaw();
+      List<Account.Id> matches =
+          result.toList().stream()
+              .map(f -> Account.id(f.getValue(AccountField.ID).intValue()))
+              .collect(toList());
+      logger.atFine().log("Matches: %s", matches);
+      return matches;
+    } catch (TooManyTermsInQueryException e) {
+      throw new BadRequestException(e.getMessage());
+    } catch (QueryParseException e) {
+      logger.atWarning().withCause(e).log("Suggesting accounts failed, return empty result.");
+      return ImmutableList.of();
+    } catch (StorageException e) {
+      if (e.getCause() instanceof TooManyTermsInQueryException) {
+        throw new BadRequestException(e.getMessage());
+      }
+      if (e.getCause() instanceof QueryParseException) {
         return ImmutableList.of();
       }
+      throw e;
     }
   }
 
diff --git a/java/com/google/gerrit/server/restapi/config/GetServerInfo.java b/java/com/google/gerrit/server/restapi/config/GetServerInfo.java
index a36e75c..2d504c7 100644
--- a/java/com/google/gerrit/server/restapi/config/GetServerInfo.java
+++ b/java/com/google/gerrit/server/restapi/config/GetServerInfo.java
@@ -66,16 +66,11 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
-import java.util.Map;
 import java.util.Optional;
 import java.util.concurrent.TimeUnit;
 import org.eclipse.jgit.lib.Config;
 
 public class GetServerInfo implements RestReadView<ConfigResource> {
-  private static final String URL_ALIAS = "urlAlias";
-  private static final String KEY_MATCH = "match";
-  private static final String KEY_TOKEN = "token";
-
   private final Config config;
   private final AccountVisibilityProvider accountVisibilityProvider;
   private final AuthConfig authConfig;
@@ -152,9 +147,6 @@
     info.sshd = getSshdInfo();
     info.suggest = getSuggestInfo();
 
-    Map<String, String> urlAliases = getUrlAliasesInfo();
-    info.urlAliases = !urlAliases.isEmpty() ? urlAliases : null;
-
     info.user = getUserInfo();
     info.receive = getReceiveInfo();
     return Response.ok(info);
@@ -347,16 +339,6 @@
     return null;
   }
 
-  private Map<String, String> getUrlAliasesInfo() {
-    Map<String, String> urlAliases = new HashMap<>();
-    for (String subsection : config.getSubsections(URL_ALIAS)) {
-      urlAliases.put(
-          config.getString(URL_ALIAS, subsection, KEY_MATCH),
-          config.getString(URL_ALIAS, subsection, KEY_TOKEN));
-    }
-    return urlAliases;
-  }
-
   private SshdInfo getSshdInfo() {
     String[] addr = config.getStringList("sshd", null, "listenAddress");
     if (addr.length == 1 && isOff(addr[0])) {
diff --git a/java/com/google/gerrit/server/restapi/group/AddMembers.java b/java/com/google/gerrit/server/restapi/group/AddMembers.java
index 6efca52..4e308a0 100644
--- a/java/com/google/gerrit/server/restapi/group/AddMembers.java
+++ b/java/com/google/gerrit/server/restapi/group/AddMembers.java
@@ -146,7 +146,7 @@
       throws UnprocessableEntityException, IOException, ConfigInvalidException {
     AccountResolver.Result result = accountResolver.resolve(nameOrEmailOrId);
     try {
-      return result.asUnique().getAccount();
+      return result.asUnique().account();
     } catch (UnresolvableAccountException e) {
       switch (authType) {
         case HTTP_LDAP:
@@ -193,7 +193,7 @@
       req.setSkipAuthentication(true);
       return accountCache
           .get(accountManager.authenticate(req).getAccountId())
-          .map(AccountState::getAccount);
+          .map(AccountState::account);
     } catch (AccountException e) {
       return Optional.empty();
     }
diff --git a/java/com/google/gerrit/server/restapi/group/DeleteMembers.java b/java/com/google/gerrit/server/restapi/group/DeleteMembers.java
index 5d1d447..3428779 100644
--- a/java/com/google/gerrit/server/restapi/group/DeleteMembers.java
+++ b/java/com/google/gerrit/server/restapi/group/DeleteMembers.java
@@ -68,7 +68,7 @@
 
     Set<Account.Id> membersToRemove = new HashSet<>();
     for (String nameOrEmail : input.members) {
-      membersToRemove.add(accountResolver.resolve(nameOrEmail).asUnique().getAccount().id());
+      membersToRemove.add(accountResolver.resolve(nameOrEmail).asUnique().account().id());
     }
     AccountGroup.UUID groupUuid = internalGroup.getGroupUUID();
     try {
diff --git a/java/com/google/gerrit/server/restapi/project/CheckAccess.java b/java/com/google/gerrit/server/restapi/project/CheckAccess.java
index 516e126..78ffdda 100644
--- a/java/com/google/gerrit/server/restapi/project/CheckAccess.java
+++ b/java/com/google/gerrit/server/restapi/project/CheckAccess.java
@@ -73,7 +73,7 @@
       throw new BadRequestException("input requires 'account'");
     }
 
-    Account.Id match = accountResolver.resolve(input.account).asUnique().getAccount().id();
+    Account.Id match = accountResolver.resolve(input.account).asUnique().account().id();
 
     AccessCheckInfo info = new AccessCheckInfo();
     try {
diff --git a/java/com/google/gerrit/server/submit/SubmitStrategyOp.java b/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
index bcffbc9..b8f1966 100644
--- a/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
+++ b/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
@@ -396,7 +396,7 @@
   private String getByAccountName() {
     requireNonNull(submitter, "getByAccountName called before submitter populated");
     Optional<Account> account =
-        args.accountCache.get(submitter.accountId()).map(AccountState::getAccount);
+        args.accountCache.get(submitter.accountId()).map(AccountState::account);
     if (account.isPresent() && account.get().fullName() != null) {
       return " by " + account.get().fullName();
     }
diff --git a/java/com/google/gerrit/server/update/Context.java b/java/com/google/gerrit/server/update/Context.java
index 8704cf0..12ea986 100644
--- a/java/com/google/gerrit/server/update/Context.java
+++ b/java/com/google/gerrit/server/update/Context.java
@@ -114,7 +114,7 @@
   /**
    * Get the account of the user performing the update.
    *
-   * <p>Convenience method for {@code getIdentifiedUser().getAccount()}.
+   * <p>Convenience method for {@code getIdentifiedUser().account()}.
    *
    * @see CurrentUser#asIdentifiedUser()
    * @return account.
diff --git a/java/com/google/gerrit/sshd/GerritGSSAuthenticator.java b/java/com/google/gerrit/sshd/GerritGSSAuthenticator.java
index 01a8cb6..72a6f3a 100644
--- a/java/com/google/gerrit/sshd/GerritGSSAuthenticator.java
+++ b/java/com/google/gerrit/sshd/GerritGSSAuthenticator.java
@@ -66,7 +66,7 @@
     }
 
     Optional<Account> account =
-        accounts.getByUsername(username).map(AccountState::getAccount).filter(Account::isActive);
+        accounts.getByUsername(username).map(AccountState::account).filter(Account::isActive);
     if (!account.isPresent()) {
       return false;
     }
diff --git a/java/com/google/gerrit/sshd/commands/LsUserRefs.java b/java/com/google/gerrit/sshd/commands/LsUserRefs.java
index ae3d59e..648256a 100644
--- a/java/com/google/gerrit/sshd/commands/LsUserRefs.java
+++ b/java/com/google/gerrit/sshd/commands/LsUserRefs.java
@@ -76,7 +76,7 @@
   protected void run() throws Failure {
     Account.Id userAccountId;
     try {
-      userAccountId = accountResolver.resolve(userName).asUnique().getAccount().id();
+      userAccountId = accountResolver.resolve(userName).asUnique().account().id();
     } catch (UnprocessableEntityException e) {
       stdout.println(e.getMessage());
       stdout.flush();
diff --git a/java/com/google/gerrit/sshd/commands/SetMembersCommand.java b/java/com/google/gerrit/sshd/commands/SetMembersCommand.java
index 578f6fe..de3d4f0 100644
--- a/java/com/google/gerrit/sshd/commands/SetMembersCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetMembersCommand.java
@@ -140,7 +140,7 @@
                     return "n/a";
                   }
                   return MoreObjects.firstNonNull(
-                      accountState.get().getAccount().preferredEmail(), "n/a");
+                      accountState.get().account().preferredEmail(), "n/a");
                 })
             .collect(joining(", "));
     out.write(
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index eb59669..47bc7b3 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -433,7 +433,7 @@
                 "Create Account Atomically",
                 accountId,
                 u -> u.setFullName(fullName).addExternalId(extId));
-    assertThat(accountState.getAccount().fullName()).isEqualTo(fullName);
+    assertThat(accountState.account().fullName()).isEqualTo(fullName);
 
     AccountInfo info = gApi.accounts().id(accountId.get()).get();
     assertThat(info.name).isEqualTo(fullName);
@@ -473,7 +473,7 @@
             .get()
             .update("Set status", anonymousCoward.id(), u -> u.setStatus(status));
     assertThat(accountState).isPresent();
-    Account account = accountState.get().getAccount();
+    Account account = accountState.get().account();
     assertThat(account.fullName()).isNull();
     assertThat(account.status()).isEqualTo(status);
     assertUserBranch(anonymousCoward.id(), null, status);
@@ -596,7 +596,7 @@
             new AccountActivationValidationListener() {
               @Override
               public void validateActivation(AccountState account) throws ValidationException {
-                String preferredEmail = account.getAccount().preferredEmail();
+                String preferredEmail = account.account().preferredEmail();
                 if (preferredEmail == null || !preferredEmail.endsWith("@activatable.com")) {
                   throw new ValidationException("not allowed to active account");
                 }
@@ -604,7 +604,7 @@
 
               @Override
               public void validateDeactivation(AccountState account) throws ValidationException {
-                String preferredEmail = account.getAccount().preferredEmail();
+                String preferredEmail = account.account().preferredEmail();
                 if (preferredEmail == null || !preferredEmail.endsWith("@deactivatable.com")) {
                   throw new ValidationException("not allowed to deactive account");
                 }
@@ -2461,21 +2461,20 @@
   @Test
   public void checkMetaId() throws Exception {
     // metaId is set when account is loaded
-    assertThat(accounts.get(admin.id()).get().getAccount().metaId())
-        .isEqualTo(getMetaId(admin.id()));
+    assertThat(accounts.get(admin.id()).get().account().metaId()).isEqualTo(getMetaId(admin.id()));
 
     // metaId is set when account is created
     AccountsUpdate au = accountsUpdateProvider.get();
     Account.Id accountId = Account.id(seq.nextAccountId());
     AccountState accountState = au.insert("Create Test Account", accountId, u -> {});
-    assertThat(accountState.getAccount().metaId()).isEqualTo(getMetaId(accountId));
+    assertThat(accountState.account().metaId()).isEqualTo(getMetaId(accountId));
 
     // metaId is set when account is updated
     Optional<AccountState> updatedAccountState =
         au.update("Set Full Name", accountId, u -> u.setFullName("foo"));
     assertThat(updatedAccountState).isPresent();
-    Account updatedAccount = updatedAccountState.get().getAccount();
-    assertThat(accountState.getAccount().metaId()).isNotEqualTo(updatedAccount.metaId());
+    Account updatedAccount = updatedAccountState.get().account();
+    assertThat(accountState.account().metaId()).isNotEqualTo(updatedAccount.metaId());
     assertThat(updatedAccount.metaId()).isEqualTo(getMetaId(accountId));
   }
 
@@ -2621,7 +2620,7 @@
     assertThat(doneBgUpdate.get()).isTrue();
 
     assertThat(updatedAccountState).isPresent();
-    Account updatedAccount = updatedAccountState.get().getAccount();
+    Account updatedAccount = updatedAccountState.get().account();
     assertThat(updatedAccount.status()).isEqualTo(status);
     assertThat(updatedAccount.fullName()).isEqualTo(fullName);
 
@@ -2678,7 +2677,7 @@
         () -> update.update("Set Full Name", admin.id(), u -> u.setFullName(fullName)));
     assertThat(bgCounter.get()).isEqualTo(status.size());
 
-    Account updatedAccount = accounts.get(admin.id()).get().getAccount();
+    Account updatedAccount = accounts.get(admin.id()).get().account();
     assertThat(updatedAccount.status()).isEqualTo(Iterables.getLast(status));
     assertThat(updatedAccount.fullName()).isEqualTo(admin.fullName());
 
@@ -2730,12 +2729,12 @@
             "Set Status",
             admin.id(),
             (a, u) -> {
-              if ("A-1".equals(a.getAccount().status())) {
+              if ("A-1".equals(a.account().status())) {
                 bgCounterA1.getAndIncrement();
                 u.setStatus("B-1");
               }
 
-              if ("A-2".equals(a.getAccount().status())) {
+              if ("A-2".equals(a.account().status())) {
                 bgCounterA2.getAndIncrement();
                 u.setStatus("B-2");
               }
@@ -2745,8 +2744,8 @@
     assertThat(bgCounterA2.get()).isEqualTo(1);
 
     assertThat(updatedAccountState).isPresent();
-    assertThat(updatedAccountState.get().getAccount().status()).isEqualTo("B-2");
-    assertThat(accounts.get(admin.id()).get().getAccount().status()).isEqualTo("B-2");
+    assertThat(updatedAccountState.get().account().status()).isEqualTo("B-2");
+    assertThat(accounts.get(admin.id()).get().account().status()).isEqualTo("B-2");
     assertThat(gApi.accounts().id(admin.id().get()).get().status).isEqualTo("B-2");
   }
 
@@ -2812,12 +2811,12 @@
             "Update External ID",
             accountId,
             (a, u) -> {
-              if (a.getExternalIds().contains(extIdA1)) {
+              if (a.externalIds().contains(extIdA1)) {
                 bgCounterA1.getAndIncrement();
                 u.replaceExternalId(extIdA1, extIdB1);
               }
 
-              if (a.getExternalIds().contains(extIdA2)) {
+              if (a.externalIds().contains(extIdA2)) {
                 bgCounterA2.getAndIncrement();
                 u.replaceExternalId(extIdA2, extIdB2);
               }
@@ -2827,8 +2826,8 @@
     assertThat(bgCounterA2.get()).isEqualTo(1);
 
     assertThat(updatedAccount).isPresent();
-    assertThat(updatedAccount.get().getExternalIds()).containsExactly(extIdB2);
-    assertThat(accounts.get(accountId).get().getExternalIds()).containsExactly(extIdB2);
+    assertThat(updatedAccount.get().externalIds()).containsExactly(extIdB2);
+    assertThat(accounts.get(accountId).get().externalIds()).containsExactly(extIdB2);
     assertThat(
             gApi.accounts().id(accountId.get()).getExternalIds().stream()
                 .map(i -> i.identity)
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIndexerIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIndexerIT.java
index 75a727d..e7ae49a 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIndexerIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIndexerIT.java
@@ -66,7 +66,7 @@
     List<AccountState> matchedAccountStates =
         accountQueryProvider.get().byPreferredEmail(preferredEmail);
     assertThat(matchedAccountStates).hasSize(1);
-    assertThat(matchedAccountStates.get(0).getAccount().id()).isEqualTo(accountId);
+    assertThat(matchedAccountStates.get(0).account().id()).isEqualTo(accountId);
   }
 
   @Test
@@ -82,7 +82,7 @@
     List<AccountState> matchedAccountStates =
         accountQueryProvider.get().byPreferredEmail(preferredEmail);
     assertThat(matchedAccountStates).hasSize(1);
-    assertThat(matchedAccountStates.get(0).getAccount().id()).isEqualTo(accountId);
+    assertThat(matchedAccountStates.get(0).account().id()).isEqualTo(accountId);
   }
 
   @Test
@@ -91,10 +91,10 @@
     loadAccountToCache(accountId);
     String status = "ooo";
     updateAccountWithoutCacheOrIndex(accountId, newAccountUpdate().setStatus(status).build());
-    assertThat(accountCache.get(accountId).get().getAccount().status()).isNull();
+    assertThat(accountCache.get(accountId).get().account().status()).isNull();
 
     accountIndexer.index(accountId);
-    assertThat(accountCache.get(accountId).get().getAccount().status()).isEqualTo(status);
+    assertThat(accountCache.get(accountId).get().account().status()).isEqualTo(status);
   }
 
   @Test
@@ -109,7 +109,7 @@
     List<AccountState> matchedAccountStates =
         accountQueryProvider.get().byPreferredEmail(preferredEmail);
     assertThat(matchedAccountStates).hasSize(1);
-    assertThat(matchedAccountStates.get(0).getAccount().id()).isEqualTo(accountId);
+    assertThat(matchedAccountStates.get(0).account().id()).isEqualTo(accountId);
   }
 
   @Test
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java
index c48ee9d..9ccb74b 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountManagerIT.java
@@ -192,7 +192,7 @@
 
     Optional<AccountState> accountState = accounts.get(accountId);
     assertThat(accountState).isPresent();
-    assertThat(accountState.get().getAccount().preferredEmail()).isEqualTo(newEmail);
+    assertThat(accountState.get().account().preferredEmail()).isEqualTo(newEmail);
   }
 
   @Test
@@ -217,7 +217,7 @@
 
     Optional<AccountState> accountState = accounts.get(accountId);
     assertThat(accountState).isPresent();
-    assertThat(accountState.get().getAccount().fullName()).isEqualTo(newName);
+    assertThat(accountState.get().account().fullName()).isEqualTo(newName);
   }
 
   @Test
@@ -296,7 +296,7 @@
     assertAuthResultForExistingAccount(authResult, accountId, gerritExtIdKey);
     Optional<AccountState> accountState = accounts.get(accountId);
     assertThat(accountState).isPresent();
-    assertThat(accountState.get().getAccount().isActive()).isTrue();
+    assertThat(accountState.get().account().isActive()).isTrue();
   }
 
   @Test
@@ -317,7 +317,7 @@
     assertAuthResultForExistingAccount(authResult, accountId, gerritExtIdKey);
     Optional<AccountState> accountState = accounts.get(accountId);
     assertThat(accountState).isPresent();
-    assertThat(accountState.get().getAccount().isActive()).isTrue();
+    assertThat(accountState.get().account().isActive()).isTrue();
   }
 
   @Test
@@ -341,7 +341,7 @@
 
     Optional<AccountState> accountState = accounts.get(accountId);
     assertThat(accountState).isPresent();
-    assertThat(accountState.get().getAccount().isActive()).isFalse();
+    assertThat(accountState.get().account().isActive()).isFalse();
   }
 
   @Test
@@ -433,7 +433,7 @@
     // Verify that the preferred email was not updated.
     Optional<AccountState> accountState = accounts.get(accountId);
     assertThat(accountState).isPresent();
-    assertThat(accountState.get().getAccount().preferredEmail()).isEqualTo(email);
+    assertThat(accountState.get().account().preferredEmail()).isEqualTo(email);
   }
 
   @Test
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
index f253533..76d8044 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
@@ -38,7 +38,6 @@
 import com.google.inject.Inject;
 import com.google.inject.util.Providers;
 import java.util.ArrayList;
-import java.util.HashMap;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -90,8 +89,6 @@
     i.my.add(new MenuItem("name", "url"));
     i.changeTable = new ArrayList<>();
     i.changeTable.add("Status");
-    i.urlAliases = new HashMap<>();
-    i.urlAliases.put("foo", "bar");
 
     o = gApi.accounts().id(user42.id().toString()).setPreferences(i);
     assertPrefs(o, i, "my");
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java b/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
index e9e8b7f..2bba4e6 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
@@ -112,7 +112,7 @@
 
   @Test
   public void getExternalIds() throws Exception {
-    Collection<ExternalId> expectedIds = getAccountState(user.id()).getExternalIds();
+    Collection<ExternalId> expectedIds = getAccountState(user.id()).externalIds();
     List<AccountExternalIdInfo> expectedIdInfos = toExternalIdInfos(expectedIds);
 
     RestResponse response = userRestSession.get("/accounts/self/external.ids");
@@ -142,7 +142,7 @@
         .add(allowCapability(GlobalCapability.ACCESS_DATABASE).group(REGISTERED_USERS))
         .update();
 
-    Collection<ExternalId> expectedIds = getAccountState(admin.id()).getExternalIds();
+    Collection<ExternalId> expectedIds = getAccountState(admin.id()).externalIds();
     List<AccountExternalIdInfo> expectedIdInfos = toExternalIdInfos(expectedIds);
 
     RestResponse response = userRestSession.get("/accounts/" + admin.id() + "/external.ids");
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
index 7ac655f..42b82c5 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
@@ -21,6 +21,7 @@
 import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
 import static com.google.gerrit.common.data.Permission.READ;
 import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
 import static java.util.stream.Collectors.toList;
 
 import com.google.common.collect.ImmutableList;
@@ -39,6 +40,7 @@
 import com.google.gerrit.extensions.client.ReviewerState;
 import com.google.gerrit.extensions.common.ChangeInput;
 import com.google.gerrit.extensions.common.SuggestedReviewerInfo;
+import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
@@ -136,6 +138,27 @@
   }
 
   @Test
+  @GerritConfig(name = "index.maxTerms", value = "10")
+  public void suggestReviewersTooManyQueryTerms() throws Exception {
+    String changeId = createChange().getChangeId();
+
+    // Do a query which doesn't exceed index.maxTerms succeeds (add only 9 terms, since on
+    // 'inactive:1' term is implicitly added) and assert that a result is returned
+    StringBuilder query = new StringBuilder();
+    for (int i = 1; i <= 9; i++) {
+      query.append(name("u")).append(" ");
+    }
+    assertThat(suggestReviewers(changeId, query.toString())).isNotEmpty();
+
+    // Do a query which exceed index.maxTerms succeeds (10 terms plus 'inactive:1' term which is
+    // implicitly added).
+    query.append(name("u"));
+    BadRequestException exception =
+        assertThrows(BadRequestException.class, () -> suggestReviewers(changeId, query.toString()));
+    assertThat(exception).hasMessageThat().isEqualTo("too many terms in query");
+  }
+
+  @Test
   public void suggestReviewersWithExcludeGroups() throws Exception {
     String changeId = createChange().getChangeId();
 
diff --git a/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java b/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java
index 7627e65..68f10e6 100644
--- a/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java
@@ -340,6 +340,6 @@
         accountsUpdateProvider
             .get()
             .update("Force set preferred email", id, (s, u) -> u.setPreferredEmail(email));
-    assertThat(result.map(a -> a.getAccount().preferredEmail())).hasValue(email);
+    assertThat(result.map(a -> a.account().preferredEmail())).hasValue(email);
   }
 }
diff --git a/javatests/com/google/gerrit/acceptance/server/quota/MultipleQuotaPluginsIT.java b/javatests/com/google/gerrit/acceptance/server/quota/MultipleQuotaPluginsIT.java
index 0ad2010..c6b09cc 100644
--- a/javatests/com/google/gerrit/acceptance/server/quota/MultipleQuotaPluginsIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/quota/MultipleQuotaPluginsIT.java
@@ -17,11 +17,10 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth8.assertThat;
 import static com.google.gerrit.testing.GerritJUnit.assertThrows;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.expectLastCall;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.resetToStrict;
-import static org.easymock.EasyMock.verify;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import com.google.common.collect.ImmutableList;
 import com.google.gerrit.acceptance.AbstractDaemonTest;
@@ -35,15 +34,12 @@
 import com.google.inject.Inject;
 import com.google.inject.Module;
 import java.util.OptionalLong;
-import org.easymock.EasyMock;
 import org.junit.Before;
 import org.junit.Test;
 
 public class MultipleQuotaPluginsIT extends AbstractDaemonTest {
-  private static final QuotaEnforcer quotaEnforcerA =
-      EasyMock.createStrictMock(QuotaEnforcer.class);
-  private static final QuotaEnforcer quotaEnforcerB =
-      EasyMock.createStrictMock(QuotaEnforcer.class);
+  private static final QuotaEnforcer quotaEnforcerA = mock(QuotaEnforcer.class);
+  private static final QuotaEnforcer quotaEnforcerB = mock(QuotaEnforcer.class);
 
   private IdentifiedUser identifiedAdmin;
   @Inject private QuotaBackend quotaBackend;
@@ -67,39 +63,32 @@
   @Before
   public void setUp() {
     identifiedAdmin = identifiedUserFactory.create(admin.id());
-    resetToStrict(quotaEnforcerA);
-    resetToStrict(quotaEnforcerB);
+    clearInvocations(quotaEnforcerA);
+    clearInvocations(quotaEnforcerB);
   }
 
   @Test
   public void refillsOnError() {
     QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build();
-    expect(quotaEnforcerA.requestTokens("testGroup", ctx, 1)).andReturn(QuotaResponse.ok());
-    expect(quotaEnforcerB.requestTokens("testGroup", ctx, 1))
-        .andReturn(QuotaResponse.error("fail"));
-    quotaEnforcerA.refill("testGroup", ctx, 1);
-    expectLastCall();
-
-    replay(quotaEnforcerA);
-    replay(quotaEnforcerB);
+    when(quotaEnforcerA.requestTokens("testGroup", ctx, 1)).thenReturn(QuotaResponse.ok());
+    when(quotaEnforcerB.requestTokens("testGroup", ctx, 1)).thenReturn(QuotaResponse.error("fail"));
 
     assertThat(quotaBackend.user(identifiedAdmin).requestToken("testGroup"))
         .isEqualTo(
             QuotaResponse.Aggregated.create(
                 ImmutableList.of(QuotaResponse.ok(), QuotaResponse.error("fail"))));
+
+    verify(quotaEnforcerA).requestTokens("testGroup", ctx, 1);
+    verify(quotaEnforcerB).requestTokens("testGroup", ctx, 1);
+    verify(quotaEnforcerA).refill("testGroup", ctx, 1);
   }
 
   @Test
   public void refillsOnException() {
     NullPointerException exception = new NullPointerException();
     QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build();
-    expect(quotaEnforcerA.requestTokens("testGroup", ctx, 1)).andThrow(exception);
-    expect(quotaEnforcerB.requestTokens("testGroup", ctx, 1)).andReturn(QuotaResponse.ok());
-    quotaEnforcerB.refill("testGroup", ctx, 1);
-    expectLastCall();
-
-    replay(quotaEnforcerA);
-    replay(quotaEnforcerB);
+    when(quotaEnforcerA.requestTokens("testGroup", ctx, 1)).thenReturn(QuotaResponse.ok());
+    when(quotaEnforcerB.requestTokens("testGroup", ctx, 1)).thenThrow(exception);
 
     NullPointerException thrown =
         assertThrows(
@@ -107,52 +96,53 @@
             () -> quotaBackend.user(identifiedAdmin).requestToken("testGroup"));
     assertThat(thrown).isEqualTo(exception);
 
-    verify(quotaEnforcerA);
+    verify(quotaEnforcerA).requestTokens("testGroup", ctx, 1);
+    verify(quotaEnforcerB).requestTokens("testGroup", ctx, 1);
+    verify(quotaEnforcerA).refill("testGroup", ctx, 1);
   }
 
   @Test
   public void doesNotRefillNoOp() {
     QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build();
-    expect(quotaEnforcerA.requestTokens("testGroup", ctx, 1))
-        .andReturn(QuotaResponse.error("fail"));
-    expect(quotaEnforcerB.requestTokens("testGroup", ctx, 1)).andReturn(QuotaResponse.noOp());
-
-    replay(quotaEnforcerA);
-    replay(quotaEnforcerB);
+    when(quotaEnforcerA.requestTokens("testGroup", ctx, 1)).thenReturn(QuotaResponse.error("fail"));
+    when(quotaEnforcerB.requestTokens("testGroup", ctx, 1)).thenReturn(QuotaResponse.noOp());
 
     assertThat(quotaBackend.user(identifiedAdmin).requestToken("testGroup"))
         .isEqualTo(
             QuotaResponse.Aggregated.create(
                 ImmutableList.of(QuotaResponse.error("fail"), QuotaResponse.noOp())));
+
+    verify(quotaEnforcerA).requestTokens("testGroup", ctx, 1);
+    verify(quotaEnforcerB).requestTokens("testGroup", ctx, 1);
   }
 
   @Test
   public void minimumAvailableTokens() {
     QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build();
-    expect(quotaEnforcerA.availableTokens("testGroup", ctx)).andReturn(QuotaResponse.ok(20L));
-    expect(quotaEnforcerB.availableTokens("testGroup", ctx)).andReturn(QuotaResponse.ok(10L));
-
-    replay(quotaEnforcerA);
-    replay(quotaEnforcerB);
+    when(quotaEnforcerA.availableTokens("testGroup", ctx)).thenReturn(QuotaResponse.ok(20L));
+    when(quotaEnforcerB.availableTokens("testGroup", ctx)).thenReturn(QuotaResponse.ok(10L));
 
     OptionalLong tokens =
         quotaBackend.user(identifiedAdmin).availableTokens("testGroup").availableTokens();
     assertThat(tokens).isPresent();
     assertThat(tokens.getAsLong()).isEqualTo(10L);
+
+    verify(quotaEnforcerA).availableTokens("testGroup", ctx);
+    verify(quotaEnforcerB).availableTokens("testGroup", ctx);
   }
 
   @Test
   public void ignoreNoOpForAvailableTokens() {
     QuotaRequestContext ctx = QuotaRequestContext.builder().user(identifiedAdmin).build();
-    expect(quotaEnforcerA.availableTokens("testGroup", ctx)).andReturn(QuotaResponse.noOp());
-    expect(quotaEnforcerB.availableTokens("testGroup", ctx)).andReturn(QuotaResponse.ok(20L));
-
-    replay(quotaEnforcerA);
-    replay(quotaEnforcerB);
+    when(quotaEnforcerA.availableTokens("testGroup", ctx)).thenReturn(QuotaResponse.noOp());
+    when(quotaEnforcerB.availableTokens("testGroup", ctx)).thenReturn(QuotaResponse.ok(20L));
 
     OptionalLong tokens =
         quotaBackend.user(identifiedAdmin).availableTokens("testGroup").availableTokens();
     assertThat(tokens).isPresent();
     assertThat(tokens.getAsLong()).isEqualTo(20L);
+
+    verify(quotaEnforcerA).availableTokens("testGroup", ctx);
+    verify(quotaEnforcerB).availableTokens("testGroup", ctx);
   }
 }
diff --git a/javatests/com/google/gerrit/acceptance/server/quota/RestApiQuotaIT.java b/javatests/com/google/gerrit/acceptance/server/quota/RestApiQuotaIT.java
index 802f55f..917d597 100644
--- a/javatests/com/google/gerrit/acceptance/server/quota/RestApiQuotaIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/quota/RestApiQuotaIT.java
@@ -14,10 +14,11 @@
 
 package com.google.gerrit.acceptance.server.quota;
 
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.reset;
-import static org.easymock.EasyMock.verify;
+import static com.google.gerrit.httpd.restapi.RestApiServlet.SC_TOO_MANY_REQUESTS;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.extensions.common.ChangeInfo;
@@ -29,15 +30,14 @@
 import com.google.gerrit.server.quota.QuotaResponse;
 import com.google.inject.Module;
 import java.util.Collections;
-import org.easymock.EasyMock;
 import org.junit.Before;
 import org.junit.Test;
 
 public class RestApiQuotaIT extends AbstractDaemonTest {
   private static final QuotaBackend.WithResource quotaBackendWithResource =
-      EasyMock.createStrictMock(QuotaBackend.WithResource.class);
+      mock(QuotaBackend.WithResource.class);
   private static final QuotaBackend.WithUser quotaBackendWithUser =
-      EasyMock.createStrictMock(QuotaBackend.WithUser.class);
+      mock(QuotaBackend.WithUser.class);
 
   @Override
   public Module createModule() {
@@ -63,72 +63,65 @@
 
   @Before
   public void setUp() {
-    reset(quotaBackendWithResource);
-    reset(quotaBackendWithUser);
+    clearInvocations(quotaBackendWithResource);
+    clearInvocations(quotaBackendWithUser);
   }
 
   @Test
   public void changeDetail() throws Exception {
     Change.Id changeId = retrieveChangeId();
-    expect(quotaBackendWithResource.requestToken("/restapi/changes/detail:GET"))
-        .andReturn(singletonAggregation(QuotaResponse.ok()));
-    replay(quotaBackendWithResource);
-    expect(quotaBackendWithUser.change(changeId, project)).andReturn(quotaBackendWithResource);
-    replay(quotaBackendWithUser);
+    when(quotaBackendWithResource.requestToken("/restapi/changes/detail:GET"))
+        .thenReturn(singletonAggregation(QuotaResponse.ok()));
+    when(quotaBackendWithUser.change(changeId, project)).thenReturn(quotaBackendWithResource);
     adminRestSession.get("/changes/" + changeId + "/detail").assertOK();
-    verify(quotaBackendWithUser);
-    verify(quotaBackendWithResource);
+    verify(quotaBackendWithResource).requestToken("/restapi/changes/detail:GET");
+    verify(quotaBackendWithUser).change(changeId, project);
   }
 
   @Test
   public void revisionDetail() throws Exception {
     Change.Id changeId = retrieveChangeId();
-    expect(quotaBackendWithResource.requestToken("/restapi/changes/revisions/actions:GET"))
-        .andReturn(singletonAggregation(QuotaResponse.ok()));
-    replay(quotaBackendWithResource);
-    expect(quotaBackendWithUser.change(changeId, project)).andReturn(quotaBackendWithResource);
-    replay(quotaBackendWithUser);
+    when(quotaBackendWithResource.requestToken("/restapi/changes/revisions/actions:GET"))
+        .thenReturn(singletonAggregation(QuotaResponse.ok()));
+    when(quotaBackendWithUser.change(changeId, project)).thenReturn(quotaBackendWithResource);
     adminRestSession.get("/changes/" + changeId + "/revisions/current/actions").assertOK();
-    verify(quotaBackendWithUser);
-    verify(quotaBackendWithResource);
+    verify(quotaBackendWithResource).requestToken("/restapi/changes/revisions/actions:GET");
+    verify(quotaBackendWithUser).change(changeId, project);
   }
 
   @Test
   public void createChangePost() throws Exception {
-    expect(quotaBackendWithUser.requestToken("/restapi/changes:POST"))
-        .andReturn(singletonAggregation(QuotaResponse.ok()));
-    replay(quotaBackendWithUser);
+    when(quotaBackendWithUser.requestToken("/restapi/changes:POST"))
+        .thenReturn(singletonAggregation(QuotaResponse.ok()));
     ChangeInput changeInput = new ChangeInput(project.get(), "master", "test");
     adminRestSession.post("/changes/", changeInput).assertCreated();
-    verify(quotaBackendWithUser);
+    verify(quotaBackendWithUser).requestToken("/restapi/changes:POST");
   }
 
   @Test
   public void accountDetail() throws Exception {
-    expect(quotaBackendWithResource.requestToken("/restapi/accounts/detail:GET"))
-        .andReturn(singletonAggregation(QuotaResponse.ok()));
-    replay(quotaBackendWithResource);
-    expect(quotaBackendWithUser.account(admin.id())).andReturn(quotaBackendWithResource);
-    replay(quotaBackendWithUser);
+    when(quotaBackendWithResource.requestToken("/restapi/accounts/detail:GET"))
+        .thenReturn(singletonAggregation(QuotaResponse.ok()));
+    when(quotaBackendWithUser.account(admin.id())).thenReturn(quotaBackendWithResource);
     adminRestSession.get("/accounts/self/detail").assertOK();
-    verify(quotaBackendWithUser);
-    verify(quotaBackendWithResource);
+    verify(quotaBackendWithResource).requestToken("/restapi/accounts/detail:GET");
+    verify(quotaBackendWithUser).account(admin.id());
   }
 
   @Test
   public void config() throws Exception {
-    expect(quotaBackendWithUser.requestToken("/restapi/config/version:GET"))
-        .andReturn(singletonAggregation(QuotaResponse.ok()));
-    replay(quotaBackendWithUser);
+    when(quotaBackendWithUser.requestToken("/restapi/config/version:GET"))
+        .thenReturn(singletonAggregation(QuotaResponse.ok()));
     adminRestSession.get("/config/server/version").assertOK();
+    verify(quotaBackendWithUser).requestToken("/restapi/config/version:GET");
   }
 
   @Test
   public void outOfQuotaReturnsError() throws Exception {
-    expect(quotaBackendWithUser.requestToken("/restapi/config/version:GET"))
-        .andReturn(singletonAggregation(QuotaResponse.error("no quota")));
-    replay(quotaBackendWithUser);
-    adminRestSession.get("/config/server/version").assertStatus(429);
+    when(quotaBackendWithUser.requestToken("/restapi/config/version:GET"))
+        .thenReturn(singletonAggregation(QuotaResponse.error("no quota")));
+    adminRestSession.get("/config/server/version").assertStatus(SC_TOO_MANY_REQUESTS);
+    verify(quotaBackendWithUser).requestToken("/restapi/config/version:GET");
   }
 
   private Change.Id retrieveChangeId() throws Exception {
diff --git a/javatests/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java b/javatests/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java
index bc035af..1f101ef 100644
--- a/javatests/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java
+++ b/javatests/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java
@@ -198,7 +198,7 @@
         .update(
             "Delete External IDs",
             user.getAccountId(),
-            (a, u) -> u.deleteExternalIds(a.getExternalIds()));
+            (a, u) -> u.deleteExternalIds(a.externalIds()));
     reloadUser();
 
     TestKey key = validKeyWithSecondUserId();
diff --git a/javatests/com/google/gerrit/server/account/AccountResolverTest.java b/javatests/com/google/gerrit/server/account/AccountResolverTest.java
index e9e5e54..f5788ca 100644
--- a/javatests/com/google/gerrit/server/account/AccountResolverTest.java
+++ b/javatests/com/google/gerrit/server/account/AccountResolverTest.java
@@ -84,7 +84,7 @@
     @Override
     public String toString() {
       return accounts.stream()
-          .map(a -> a.getAccount().id().toString())
+          .map(a -> a.account().id().toString())
           .collect(joining(",", pattern + "(", ")"));
     }
   }
@@ -156,7 +156,7 @@
     // Searchers always short-circuit when finding a non-empty result list, and this one didn't
     // filter out inactive results, so the second searcher never ran.
     assertThat(result.asIdSet()).containsExactlyElementsIn(ids(1));
-    assertThat(getOnlyElement(result.asList()).getAccount().isActive()).isFalse();
+    assertThat(getOnlyElement(result.asList()).account().isActive()).isFalse();
     assertThat(filteredInactiveIds(result)).isEmpty();
   }
 
@@ -173,7 +173,7 @@
     // and this one didn't filter out inactive results,
     // so the second searcher never ran.
     assertThat(result.asIdSet()).containsExactlyElementsIn(ids(1));
-    assertThat(getOnlyElement(result.asList()).getAccount().isActive()).isFalse();
+    assertThat(getOnlyElement(result.asList()).account().isActive()).isFalse();
     assertThat(filteredInactiveIds(result)).isEmpty();
   }
 
@@ -255,8 +255,8 @@
     AccountState account = newAccount(1);
     ImmutableList<Searcher<?>> searchers =
         ImmutableList.of(new TestSearcher("foo", false, account));
-    assertThat(search("foo", searchers, allVisible()).asUnique().getAccount().id())
-        .isEqualTo(account.getAccount().id());
+    assertThat(search("foo", searchers, allVisible()).asUnique().account().id())
+        .isEqualTo(account.account().id());
   }
 
   @Test
@@ -375,18 +375,16 @@
   }
 
   private Predicate<AccountState> activityPrediate() {
-    return (AccountState accountState) -> accountState.getAccount().isActive();
+    return (AccountState accountState) -> accountState.account().isActive();
   }
 
   private static Supplier<Predicate<AccountState>> only(int... ids) {
     ImmutableSet<Account.Id> idSet =
         Arrays.stream(ids).mapToObj(Account::id).collect(toImmutableSet());
-    return () -> a -> idSet.contains(a.getAccount().id());
+    return () -> a -> idSet.contains(a.account().id());
   }
 
   private static ImmutableSet<Account.Id> filteredInactiveIds(Result result) {
-    return result.filteredInactive().stream()
-        .map(a -> a.getAccount().id())
-        .collect(toImmutableSet());
+    return result.filteredInactive().stream().map(a -> a.account().id()).collect(toImmutableSet());
   }
 }
diff --git a/javatests/com/google/gerrit/server/mail/send/FromAddressGeneratorProviderTest.java b/javatests/com/google/gerrit/server/mail/send/FromAddressGeneratorProviderTest.java
index 6b0945c..27f5938 100644
--- a/javatests/com/google/gerrit/server/mail/send/FromAddressGeneratorProviderTest.java
+++ b/javatests/com/google/gerrit/server/mail/send/FromAddressGeneratorProviderTest.java
@@ -347,8 +347,8 @@
 
   private Account.Id user(String name, String email) {
     final AccountState s = makeUser(name, email);
-    when(accountCache.get(eq(s.getAccount().id()))).thenReturn(Optional.of(s));
-    return s.getAccount().id();
+    when(accountCache.get(eq(s.account().id()))).thenReturn(Optional.of(s));
+    return s.account().id();
   }
 
   private void verifyAccountCacheGet(Account.Id id) {
@@ -357,7 +357,7 @@
 
   private Account.Id userNoLookup(String name, String email) {
     final AccountState s = makeUser(name, email);
-    return s.getAccount().id();
+    return s.account().id();
   }
 
   private AccountState makeUser(String name, String email) {
diff --git a/javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java b/javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
index 33f47b2..fce3744 100644
--- a/javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
+++ b/javatests/com/google/gerrit/server/query/account/AbstractQueryAccountsTest.java
@@ -816,7 +816,7 @@
   }
 
   protected void assertAccounts(List<AccountState> accounts, AccountInfo... expectedAccounts) {
-    assertThat(accounts.stream().map(a -> a.getAccount().id().get()).collect(toList()))
+    assertThat(accounts.stream().map(a -> a.account().id().get()).collect(toList()))
         .containsExactlyElementsIn(
             Arrays.asList(expectedAccounts).stream().map(a -> a._accountId).collect(toList()));
   }
diff --git a/plugins/replication b/plugins/replication
index 5a3519e..4ca9342 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit 5a3519e6e1733e2515900866b8db9ca98ba9da7e
+Subproject commit 4ca93421cb84b80da2c76ac6bba95117aa53543c
diff --git a/plugins/reviewnotes b/plugins/reviewnotes
index 3667220..10dd134 160000
--- a/plugins/reviewnotes
+++ b/plugins/reviewnotes
@@ -1 +1 @@
-Subproject commit 3667220b860d444406ca5fa5cc27d87858642596
+Subproject commit 10dd13408ac80985fabd1b90da81887fa0472c58
diff --git a/plugins/singleusergroup b/plugins/singleusergroup
index 731af7e..3c4e63c 160000
--- a/plugins/singleusergroup
+++ b/plugins/singleusergroup
@@ -1 +1 @@
-Subproject commit 731af7e23c5a235c5ce087aaeb44dc8a12070bcb
+Subproject commit 3c4e63c40937a9b47c9536851ae4c286ec94db3f
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.html b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.html
index b582b0e..6d638b4 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.html
@@ -160,16 +160,14 @@
     <td class="cell owner"
         hidden$="[[isColumnHidden('Owner', visibleChangeTableColumns)]]">
       <gr-account-link
-          account="[[change.owner]]"
-          additional-text="[[_computeAccountStatusString(change.owner)]]"></gr-account-link>
+          account="[[change.owner]]"></gr-account-link>
     </td>
     <td class="cell assignee"
         hidden$="[[isColumnHidden('Assignee', visibleChangeTableColumns)]]">
       <template is="dom-if" if="[[change.assignee]]">
         <gr-account-link
             id="assigneeAccountLink"
-            account="[[change.assignee]]"
-            additional-text="[[_computeAccountStatusString(change.assignee)]]"></gr-account-link>
+            account="[[change.assignee]]"></gr-account-link>
       </template>
       <template is="dom-if" if="[[!change.assignee]]">
         <span class="placeholder">--</span>
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.js b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.js
index 1f0da2b..9cf2ad6 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.js
@@ -165,10 +165,6 @@
       return str;
     },
 
-    _computeAccountStatusString(account) {
-      return account && account.status ? `(${account.status})` : '';
-    },
-
     _computeSizeTooltip(change) {
       if (change.insertions + change.deletions === 0 ||
           isNaN(change.insertions + change.deletions)) {
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.html b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.html
index df4a442..1cddbc5 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.html
@@ -189,14 +189,6 @@
       };
       flushAsynchronousOperations();
       assert.isOk(element.$$('.assignee gr-account-link'));
-      assert.equal(Polymer.dom(element.root)
-          .querySelector('#assigneeAccountLink').additionalText, '(test)');
-    });
-
-    test('_computeAccountStatusString', () => {
-      assert.equal(element._computeAccountStatusString({}), '');
-      assert.equal(element._computeAccountStatusString({status: 'Working'}),
-          '(Working)');
     });
 
     test('TShirt sizing tooltip', () => {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
index 4a98ef3..f29a5da 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
@@ -227,8 +227,10 @@
             this.$.coverageLayerLeft,
             this.$.coverageLayerRight,
           ];
-          layers.push(...this.pluginLayers);
 
+          if (this.pluginLayers) {
+            layers.push(...this.pluginLayers);
+          }
           this._layers = layers;
         },
 
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html
index 325ac05..6831a8d 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html
@@ -37,7 +37,7 @@
 
 <test-fixture id="basic">
   <template is="dom-template">
-    <gr-diff-builder plugin-layers="[[pluginLayers]]">
+    <gr-diff-builder>
       <table id="diffTable"></table>
     </gr-diff-builder>
   </template>
@@ -596,7 +596,8 @@
       let withPluginLayerCount;
       setup(() => {
         const pluginLayers = [];
-        element = fixture('basic', {pluginLayers});
+        element = fixture('basic');
+        element.pluginLayers = pluginLayers;
         element._showTrailingWhitespace = true;
         element._setupAnnotationLayers();
         initialLayersCount = element._layers.length;
@@ -610,7 +611,8 @@
       suite('with plugin layers', () => {
         const pluginLayers = [{}, {}];
         setup(() => {
-          element = fixture('basic', {pluginLayers});
+          element = fixture('basic');
+          element.pluginLayers = pluginLayers;
           element._showTrailingWhitespace = true;
           element._setupAnnotationLayers();
           withPluginLayerCount = element._layers.length;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.html b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.html
index db3f4a3..f87ef7a 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.html
@@ -267,7 +267,15 @@
       element.path = 'some/path';
       element.projectName = 'Some project';
       const threadEls = threads.map(
-          thread => element._createThreadElement(thread));
+          thread => {
+            const threadEl = element._createThreadElement(thread);
+            // Polymer 2 doesn't fire ready events and doesn't execute
+            // observers if element is not added to the Dom.
+            // See https://github.com/Polymer/old-docs-site/issues/2322
+            // and https://github.com/Polymer/polymer/issues/4526
+            element._attachThreadElement(threadEl);
+            return threadEl;
+          });
       assert.equal(threadEls.length, 2);
       assert.equal(threadEls[0].rootId, 4711);
       assert.equal(threadEls[1].rootId, 42);
diff --git a/polygerrit-ui/app/elements/gr-app_test.html b/polygerrit-ui/app/elements/gr-app_test.html
index 73d012a..ebf1304 100644
--- a/polygerrit-ui/app/elements/gr-app_test.html
+++ b/polygerrit-ui/app/elements/gr-app_test.html
@@ -24,7 +24,13 @@
 <script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
 <script src="/bower_components/web-component-tester/browser.js"></script>
 <link rel="import" href="../test/common-test-setup.html"/>
-<link rel="import" href="gr-app.html">
+
+<script>
+  const link = document.createElement('link');
+  link.setAttribute('rel', 'import');
+  link.setAttribute('href', window.POLYMER2 ? 'gr-app-p2.html' : 'gr-app.html');
+  document.head.appendChild(link);
+</script>
 
 <script>void(0);</script>
 
@@ -34,6 +40,12 @@
   </template>
 </test-fixture>
 
+<test-fixture id="basic-p2">
+  <template>
+    <gr-app-p2 id="app"></gr-app-p2>
+  </template>
+</test-fixture>
+
 <script>
   suite('gr-app tests', () => {
     let sandbox;
@@ -68,7 +80,7 @@
         probePath() { return Promise.resolve(42); },
       });
 
-      element = fixture('basic');
+      element = fixture(window.POLYMER2 ? 'basic-p2' : 'basic');
       flush(done);
     });
 
diff --git a/resources/com/google/gerrit/server/mail/Abandoned.soy b/resources/com/google/gerrit/server/mail/Abandoned.soy
index 2785ffc..d5aac0e 100644
--- a/resources/com/google/gerrit/server/mail/Abandoned.soy
+++ b/resources/com/google/gerrit/server/mail/Abandoned.soy
@@ -17,7 +17,7 @@
 {namespace com.google.gerrit.server.mail.template}
 
 /**
- * .Abandoned template will determine the contents of the email related to a
+ * The .Abandoned template will determine the contents of the email related to a
  * change being abandoned.
  */
 {template .Abandoned kind="text"}