Merge "Cleanup old java_library2 buck targets."
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
index f66a0ff..dbfdf4c 100644
--- a/.settings/org.eclipse.jdt.core.prefs
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -17,7 +17,7 @@
 org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
 org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
 org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
-org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning
 org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
 org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
 org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
diff --git a/Documentation/config-cla.txt b/Documentation/config-cla.txt
index 624135c..4c8d04a 100644
--- a/Documentation/config-cla.txt
+++ b/Documentation/config-cla.txt
@@ -29,7 +29,6 @@
 ====
   [contributor-agreement "Individual"]
     description = If you are going to be contributing code on your own, this is the one you want. You can sign this one online.
-    requireContactInformation = true
     agreementUrl = static/cla_individual.html
     autoVerify = group CLA Accepted - Individual
     accepted = group CLA Accepted - Individual
@@ -52,11 +51,6 @@
 Short text describing the contributor agreement. This text will appear
 when the user selects an agreement.
 
-[[contributor-agreement.name.requireContactInformation]]contributor-agreement.<name>.requireContactInformation::
-+
-True if the user must provide contact information when signing a
-contributor agreement. Default is false.
-
 [[contributor-agreement.name.agreementUrl]]contributor-agreement.<name>.agreementUrl::
 +
 An absolute URL or a relative path to an HTML file containing the text
diff --git a/Documentation/config-contact.txt b/Documentation/config-contact.txt
deleted file mode 100644
index e0795be..0000000
--- a/Documentation/config-contact.txt
+++ /dev/null
@@ -1,213 +0,0 @@
-= Gerrit Code Review - Contact Information
-
-To help ensure contributor privacy, but still support gathering of
-contributor agreements as necessary, Gerrit encrypts all offline
-contact information gathered from users.  This data is shipped to
-another server, typically at a different location, to make it more
-difficult for an attacker to obtain.
-
-This feature is optional.  If the crypto APIs aren't installed
-and the `contactstore.url` setting in `gerrit.config` is not set,
-Gerrit will not collect contact information from users.
-
-
-== Setup
-
-Ensure Bouncy Castle Crypto API is available in the web application's
-CLASSPATH (e.g. in `'JETTY_HOME'/lib/plus` for Jetty).  Gerrit needs
-both `bcprov-jdk\*-*.jar` and `bcpg-jdk\*-*.jar` to be provided
-for the contact encryption to work.
-
-* link:http://www.bouncycastle.org/latest_releases.html[Bouncy Castle Crypto API]
-
-Ensure a proper JCE policy file is installed.  By default most
-JRE installations forbid the use of a strong key, resulting in
-SecurityException messages when trying to encrypt the contact data.
-You need to obtain a strong JCE policy file and install it by hand.
-Look for the 'Unlimited Strength Jurisdiction Policy' download.
-
-* link:http://java.sun.com/javase/downloads/index.jsp[Java SE Downloads]
-
-Create a public/private key pair for contact data handling.
-Generate the keys on a protected system, where the resulting
-private key is unlikely to fall into the wrong hands.
-
-====
-  gpg --gen-key
-====
-
-Select to use a `DSA and Elgamal` key type, as the public key will
-be used for data encryption.
-
-The information chosen for name, email and comment fields can be
-anything reasonable which would identify the contact store of this
-Gerrit instance.  It is probably a good idea to not use a real
-person's name here, but instead some sort of organizational role.
-The actual values chosen don't matter later, and are only to help
-document the purpose of the key.
-
-Choose a fairly long expiration period, such as 20 years.  For most
-Gerrit instances, contact data will be written once, and rarely,
-if ever, read back.
-
-Export the public key for Gerrit to use during encryption.  The
-public key must be stored in a file called `contact_information.pub`
-and reside inside of the `site_config` directory.  Armoring it
-during export makes it easier to transport between systems, as
-you can easily copy-and-paste the text.  Gerrit can read both the
-armored and unarmored formats.
-
-====
-  gpg --export --armor KEYEMAIL >$site_path/etc/contact_information.pub
-====
-
-Consider storing the private key with some sort of key escrow
-service within your organization.  Without the private key it
-is impossible to recover contact records.
-
-Install a contact store implementation somewhere to receive
-the contact records.  To be really paranoid, Gerrit always
-ships the data to another HTTP server, preferably over HTTPS.
-Existing open-source server implementations can be found in the
-gerrit-contactstore project.
-
-* link:https://code.google.com/p/gerrit/source/checkout?repo=contactstore[gerrit-contactstore]
-
-Configure `'$site_path'/etc/gerrit.config` with the contact store's
-URL (in `contactstore.url`), and if needed, APPSEC value (in
-`contactstore.appsec`):
-
-====
-  git config --file $site_path/etc/gerrit.config appsec.url https://...
-  git config --file $site_path/etc/gerrit.config appsec.appsec sekret
-====
-
-
-== Contact Store Protocol
-
-To implement a new contact store, the following details are useful.
-
-Gerrit connects to the contact store by sending a standard
-`application/x-www-form-urlencoded` within an HTTP POST request
-sent to the store URL (the exact URL that is in contactstore.url)
-with the following form fields in the body:
-
-* APPSEC
-+
-A shared secret "password" that should be known only to Gerrit
-and the contact store.  The contact store should test this value to
-deter spamming of the contact store by outside parties.  Gerrit reads
-this from contactstore.appsec.
-
-* account_id
-+
-Unique account_id value from the Gerrit database for the account
-the contact information belongs to.  Base 10 integer.
-
-* email
-+
-Preferred email address of the account.  May facilitate lookups in
-the contact store at a future date.  May be omitted or the empty
-string if the user hasn't chosen a preferred email.
-
-* filed
-+
-Seconds since the UNIX epoch of when the contact information
-was filed.  May be omitted or the empty string if Gerrit
-doesn't think the supplied contact information is valid enough.
-
-* data
-+
-Encrypted account data as an armored ASCII blob.  This is usually
-several KB of text data as a single string, with embedded newlines
-to break the lines at about 70-75 characters per line.  Data can
-be decoded using GnuPG with the correct private key.
-
-Upon successful store, the contact store application should respond
-with HTTP status code `200` and a body consisting only of `OK`
-(or `OK\n`).  Any other response code or body is considered to be
-a failure by Gerrit.
-
-Using `https://` for the store URL is *highly* encouraged, as it
-prevents man-in-the-middle attacks from reading the shared secret
-APPSEC token, or messing with the data field.
-
-=== Data Format
-
-Once decrypted the `data` field looks something like the following:
-
-----
-Account-Id: 1001240
-Date: 2009-02-23 20:32:32.852 UTC
-Full-Name: John Doe
-Preferred-Email: jdoe@example.com
-Identity: jd15@some-isp.com
-Identity: jdoe@example.com <http://jdoe.blogger.com/>
-Address:
-	123 Any Street
-	Any Town, Somewhere
-Country: USA
-Phone-Number: +1 (555) 555-1212
-Fax-Number: 555.1200
-----
-
-The fields are as follows:
-
-* `Account-Id`
-+
-Value of the `account_id` field in the metadata database.  This is
-a unique key for this account, and links all data records to it.
-
-* `Date`
-+
-Date and time of when this contact record was submitted by the user.
-Written in an ISO formatted date/time string (`YYYY-MM-DD hh:mm:ss`),
-in the UTC timezone.
-
-* `Full-Name`
-+
-The `full_name` field of the account record when the user submitted
-the contact information.  This should be the user's given name and
-family name.
-
-* `Preferred-Email`
-+
-The `preferred_email` field of the account record when the user
-submitted the contact information.  This should be one of the emails
-listed in the `Identity` field.
-
-* `Identity`
-+
-This field occurs once for each `account_external_id` record
-in the database for this account.  The email address is listed,
-and if the user is using OpenID authentication, the OpenID claimed
-identity follows in brackets (`<...>`).  Identity lines without an
-OpenID identity are usually created by sending an email containing
-a unique hyperlink that the user must visit to setup the identity.
-
-* `Address`
-+
-Free form text, as entered by the user.  This should describe some
-location that physical documents could be sent to, but it is not
-verified, so users can enter pretty much anything here.  Each line
-is prefixed with a single TAB character, but is otherwise exactly
-as entered.
-
-* `Country`
-+
-Free form text, as entered by the user.  This should be some sort
-of country name or ISO country abbreviation, but it is not verified,
-so it can be pretty much anything.
-
-* `Phone-Number`, `Fax-Number`
-+
-Free form text, as entered by the user.  The format here can be
-anything, and as the example shows, may not even be consistent in
-the same record.
-
-GERRIT
-------
-Part of link:index.html[Gerrit Code Review]
-
-SEARCHBOX
----------
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 2de50ac..14523ec 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -1128,22 +1128,6 @@
 link:rest-api-projects.html#get-config[REST API].
 
 
-[[contactstore]]
-=== Section contactstore
-
-[[contactstore.url]]contactstore.url::
-+
-URL of the web based contact store Gerrit will send any offline
-contact information to when it collects the data from users as part
-of a contributor agreement.
-+
-See link:config-contact.html[Contact Information].
-
-[[contactstore.appsec]]contactstore.appsec::
-+
-Shared secret of the web based contact store.
-
-
 [[container]]
 === Section container
 
@@ -2879,28 +2863,16 @@
 [[receive]]
 === Section receive
 
-This section is used to set who can execute the 'receive-pack' and
-to limit the maximum Git object size that 'receive-pack' will accept.
-'receive-pack' is what runs on the server during a user's push or
-repo upload command. It also contains some advanced options for tuning the
-behavior of Gerrit's 'receive-pack' mechanism.
+This section is used to configure behavior of the 'receive-pack'
+handler, which responds to 'git push' requests.
 
-----
-[receive]
-  allowGroup = GROUP_ALLOWED_TO_EXECUTE
-  allowGroup = YET_ANOTHER_GROUP_ALLOWED_TO_EXECUTE
-  maxObjectSizeLimit = 40 m
-----
-
-[[receive.enableSignedPush]]receive.enableSignedPush::
+[[receive.allowGroup]]receive.allowGroup::
 +
-If true, server-side signed push validation is enabled.
+Name of the groups of users that are allowed to execute
+'receive-pack' on the server. One or more groups can be set.
 +
-When a client pushes with `git push --signed`, this ensures that the
-push certificate is valid and signed with a valid public key stored in
-the `refs/gpg-keys` branch of `All-Users`.
-+
-Defaults to false.
+If no groups are added, any user will be allowed to execute
+'receive-pack' on the server.
 
 [[receive.certNonceSeed]]receive.certNonceSeed::
 +
@@ -2926,6 +2898,18 @@
 +
 Default is 5 minutes.
 
+[[receive.changeUpdateThreads]]receive.changeUpdateThreads::
++
+Number of threads to perform change creation or patch set updates
+concurrently. Each thread uses its own database connection from
+the database connection pool, and if all threads are busy then
+main receive thread will also perform a change creation or patch
+set update.
++
+Defaults to 1, using only the main receive thread. This feature is for
+databases with very high latency that can benefit from concurrent
+operations when multiple changes are impacted at once.
+
 [[receive.checkMagicRefs]]receive.checkMagicRefs::
 +
 If true, Gerrit will verify the destination repository has
@@ -2954,13 +2938,30 @@
 +
 Default is true.
 
-[[receive.allowGroup]]receive.allowGroup::
+[[receive.enableSignedPush]]receive.enableSignedPush::
 +
-Name of the groups of users that are allowed to execute
-'receive-pack' on the server. One or more groups can be set.
+If true, server-side signed push validation is enabled.
 +
-If no groups are added, any user will be allowed to execute
-'receive-pack' on the server.
+When a client pushes with `git push --signed`, this ensures that the
+push certificate is valid and signed with a valid public key stored in
+the `refs/gpg-keys` branch of `All-Users`.
++
+Defaults to false.
+
+[[receive.maxBatchChanges]]receive.maxBatchChanges::
++
+The maximum number of changes that Gerrit allows to be pushed
+in a batch for review. When this number is exceeded Gerrit rejects
+the push with an error message.
++
+May be overridden for certain groups by specifying a limit in the
+link:access-control.html#capability_batchChangesLimit['Batch Changes Limit']
+global capability.
++
+This setting can be used to prevent users from uploading large
+number of changes for review by mistake.
++
+Default is zero, no limit.
 
 [[receive.maxObjectSizeLimit]]receive.maxObjectSizeLimit::
 +
@@ -2981,21 +2982,6 @@
 +
 Common unit suffixes of 'k', 'm', or 'g' are supported.
 
-[[receive.maxBatchChanges]]receive.maxBatchChanges::
-+
-The maximum number of changes that Gerrit allows to be pushed
-in a batch for review. When this number is exceeded Gerrit rejects
-the push with an error message.
-+
-May be overridden for certain groups by specifying a limit in the
-link:access-control.html#capability_batchChangesLimit['Batch Changes Limit']
-global capability.
-+
-This setting can be used to prevent users from uploading large
-number of changes for review by mistake.
-+
-Default is zero, no limit.
-
 [[receive.threadPoolSize]]receive.threadPoolSize::
 +
 Maximum size of the thread pool in which the change data in received packs is
@@ -3003,18 +2989,6 @@
 +
 Defaults to the number of available CPUs according to the Java runtime.
 
-[[receive.changeUpdateThreads]]receive.changeUpdateThreads::
-+
-Number of threads to perform change creation or patch set updates
-concurrently. Each thread uses its own database connection from
-the database connection pool, and if all threads are busy then
-main receive thread will also perform a change creation or patch
-set update.
-+
-Defaults to 1, using only the main receive thread. This feature is for
-databases with very high latency that can benefit from concurrent
-operations when multiple changes are impacted at once.
-
 [[receive.timeout]]receive.timeout::
 +
 Overall timeout on the time taken to process the change data in
diff --git a/Documentation/dev-buck.txt b/Documentation/dev-buck.txt
index de3e6de..49a4d85 100644
--- a/Documentation/dev-buck.txt
+++ b/Documentation/dev-buck.txt
@@ -67,17 +67,7 @@
   tools/eclipse/project.py
 ----
 
-In Eclipse, choose 'Import existing project' and select the `gerrit` project
-from the current working directory.
-
-Expand the `gerrit` project, right-click on the `buck-out` folder, select
-'Properties', and then under 'Attributes' check 'Derived'.
-
-Note that if you make any changes in the project configuration
-that get saved to the `.project` file, for example adding Resource
-Filters on a folder, they will be overwritten the next time you run
-`tools/eclipse/project.py`.
-
+and then follow the link:dev-eclipse.html#setup[setup instructions].
 
 === Refreshing the Classpath
 
diff --git a/Documentation/dev-eclipse.txt b/Documentation/dev-eclipse.txt
index 7e1ca10..b8d01e8 100644
--- a/Documentation/dev-eclipse.txt
+++ b/Documentation/dev-eclipse.txt
@@ -7,6 +7,35 @@
 runtime debugging environment.
 
 
+[[setup]]
+== Project Setup
+
+In your Eclipse installation's `eclipse.ini` file, add the following line in
+the `vmargs` section:
+
+----
+  -DmaxCompiledUnitsAtOnce=10000
+----
+
+Without this setting, annotation processing does not work reliably and the
+build is likely to fail with errors like:
+
+----
+  Could not write generated class ... javax.annotation.processing.FilerException: Source file already created
+----
+
+In Eclipse, choose 'Import existing project' and select the `gerrit` project
+from the current working directory.
+
+Expand the `gerrit` project, right-click on the `buck-out` folder, select
+'Properties', and then under 'Attributes' check 'Derived'.
+
+Note that if you make any changes in the project configuration
+that get saved to the `.project` file, for example adding Resource
+Filters on a folder, they will be overwritten the next time you run
+`tools/eclipse/project.py`.
+
+
 [[Formatting]]
 == Code Formatter Settings
 
diff --git a/Documentation/index.txt b/Documentation/index.txt
index 2a0082a..50770a9 100644
--- a/Documentation/index.txt
+++ b/Documentation/index.txt
@@ -53,7 +53,6 @@
 . link:cmd-index.html[Command Line Tools]
 . link:config-plugins.html#replication[Replication]
 . link:config-plugins.html[Plugins]
-. link:config-contact.html[User Contact Information]
 . link:config-reverseproxy.html[Reverse Proxy]
 . link:config-auto-site-initialization.html[Automatic Site Initialization on Startup]
 . link:pgm-index.html[Server Side Administrative Tools]
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index e64fa7e..c9209af 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -760,7 +760,7 @@
 Add or delete one or more GPG keys for a user.
 
 The changes must be provided in the request body as a
-link:#gpg-key-input[GpgKeyInput] entity. Each new GPG key is provided in
+link:#gpg-keys-input[GpgKeysInput] entity. Each new GPG key is provided in
 ASCII armored format, and must contain a self-signed certification
 matching a registered email or other identity of the user.
 
@@ -1452,10 +1452,6 @@
 |`registered_on`     ||
 The link:rest-api.html#timestamp[timestamp] of when the account was
 registered.
-|`contact_filed_on`  |optional|
-The link:rest-api.html#timestamp[timestamp] of when contact information
-for this account was filed. Not set if no contact information was
-filed.
 |=================================
 
 [[account-info]]
@@ -1735,9 +1731,9 @@
 |`key`        |Not set for deleted keys|ASCII armored public key material.
 |========================
 
-[[gpg-key-input]]
-=== GpgKeyInput
-The `GpgKeyInput` entity contains information for adding GPG keys.
+[[gpg-keys-input]]
+=== GpgKeysInput
+The `GpgKeysInput` entity contains information for adding/deleting GPG keys.
 
 [options="header",cols="1,6"]
 |========================
diff --git a/Documentation/rest-api-config.txt b/Documentation/rest-api-config.txt
index c641f94..7e117c5 100644
--- a/Documentation/rest-api-config.txt
+++ b/Documentation/rest-api-config.txt
@@ -1095,18 +1095,6 @@
 the whole topic is submitted].
 |=============================
 
-[[contact-store-info]]
-=== ContactStoreInfo
-The `ContactStoreInfo` entity contains information about the contact
-store.
-
-[options="header",cols="1,6"]
-|=======================
-|Field Name |Description
-|`url`      |
-The link:config-gerrit.html#contactstore.url[URL of the contact store].
-|=======================
-
 [[download-info]]
 === DownloadInfo
 The `DownloadInfo` entity contains information about supported download
@@ -1363,9 +1351,6 @@
 Information about the configuration from the
 link:config-gerrit.html#change[change] section as
 link:#change-config-info[ChangeConfigInfo] entity.
-|`contact_store`           |optional|
-Information about the contact store configuration as
-link:#contact-store-info[ContactStoreInfo] entity.
 |`download`                ||
 Information about the configured download options as
 link:#download-info[DownloadInfo] entity.
diff --git a/gerrit-acceptance-tests/BUCK b/gerrit-acceptance-tests/BUCK
index 0888216..833070b 100644
--- a/gerrit-acceptance-tests/BUCK
+++ b/gerrit-acceptance-tests/BUCK
@@ -5,6 +5,8 @@
     '//gerrit-common:annotations',
     '//gerrit-common:server',
     '//gerrit-extension-api:api',
+    '//gerrit-gpg:gpg',
+    '//gerrit-gpg:testutil',
     '//gerrit-launcher:launcher',
     '//gerrit-lucene:lucene',
     '//gerrit-httpd:httpd',
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java
index 948f178..482e040 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java
@@ -18,10 +18,10 @@
 import com.google.common.base.MoreObjects;
 import com.google.common.collect.ImmutableList;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.lucene.LuceneIndexModule;
 import com.google.gerrit.pgm.Daemon;
 import com.google.gerrit.pgm.Init;
-import com.google.gerrit.server.config.FactoryModule;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.git.AsyncReceiveCommits;
 import com.google.gerrit.server.index.ChangeSchemas;
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index 91d3f11..5baaa18 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -17,7 +17,8 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assert_;
-import static com.google.gerrit.server.git.gpg.PublicKeyStore.keyToString;
+import static com.google.gerrit.gpg.PublicKeyStore.REFS_GPG_KEYS;
+import static com.google.gerrit.gpg.PublicKeyStore.keyToString;
 import static java.nio.charset.StandardCharsets.UTF_8;
 
 import com.google.common.base.Function;
@@ -33,21 +34,22 @@
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.gpg.Fingerprint;
+import com.google.gerrit.gpg.PublicKeyStore;
+import com.google.gerrit.gpg.server.GpgKeys;
+import com.google.gerrit.gpg.testutil.TestKey;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountExternalId;
-import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.GpgKeys;
 import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.git.gpg.Fingerprint;
-import com.google.gerrit.server.git.gpg.PublicKeyStore;
-import com.google.gerrit.server.git.gpg.TestKey;
+import com.google.gerrit.testutil.ConfigSuite;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
 import org.bouncycastle.bcpg.ArmoredOutputStream;
 import org.bouncycastle.openpgp.PGPPublicKey;
 import org.bouncycastle.openpgp.PGPPublicKeyRing;
+import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.RefUpdate;
 import org.eclipse.jgit.lib.Repository;
@@ -65,6 +67,13 @@
 import java.util.Map;
 
 public class AccountIT extends AbstractDaemonTest {
+  @ConfigSuite.Default
+  public static Config enableSignedPushConfig() {
+    Config cfg = new Config();
+    cfg.setBoolean("receive", null, "enableSignedPush", true);
+    return cfg;
+  }
+
   @Inject
   private Provider<PublicKeyStore> publicKeyStoreProvider;
 
@@ -90,9 +99,9 @@
   @After
   public void clearPublicKeyStore() throws Exception {
     try (Repository repo = repoManager.openRepository(allUsers)) {
-      Ref ref = repo.getRef(RefNames.REFS_GPG_KEYS);
+      Ref ref = repo.getRef(REFS_GPG_KEYS);
       if (ref != null) {
-        RefUpdate ru = repo.updateRef(RefNames.REFS_GPG_KEYS);
+        RefUpdate ru = repo.updateRef(REFS_GPG_KEYS);
         ru.setForceUpdate(true);
         assertThat(ru.delete()).isEqualTo(RefUpdate.Result.FORCED);
       }
@@ -106,7 +115,7 @@
 
   @After
   public void deleteGpgKeys() throws Exception {
-    String ref = RefNames.REFS_GPG_KEYS;
+    String ref = REFS_GPG_KEYS;
     try (Repository repo = repoManager.openRepository(allUsers)) {
       if (repo.getRefDatabase().exactRef(ref) != null) {
         RefUpdate ru = repo.updateRef(ref);
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/GetAccountDetailIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/GetAccountDetailIT.java
index b6fc0bc..da1d3ec 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/GetAccountDetailIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/GetAccountDetailIT.java
@@ -32,6 +32,5 @@
     assertAccountInfo(admin, info);
     Account account = accountCache.get(admin.getId()).getAccount();
     assertThat(info.registeredOn).isEqualTo(account.getRegisteredOn());
-    assertThat(info.contactFiledOn).isEqualTo(account.getContactFiledOn());
   }
 }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/ServerInfoIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/ServerInfoIT.java
index f75780c..a656760 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/ServerInfoIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/config/ServerInfoIT.java
@@ -94,9 +94,6 @@
     assertThat(i.change.replyLabel).isEqualTo("Vote\u2026");
     assertThat(i.change.updateDelay).isEqualTo(50);
 
-    // contactstore
-    assertThat(i.contactStore).isNull();
-
     // download
     assertThat(i.download.archives).containsExactly("tar", "tbz2", "tgz", "txz");
     assertThat(i.download.schemes).isEmpty();
@@ -147,9 +144,6 @@
     assertThat(i.change.replyLabel).isEqualTo("Reply\u2026");
     assertThat(i.change.updateDelay).isEqualTo(30);
 
-    // contactstore
-    assertThat(i.contactStore).isNull();
-
     // download
     assertThat(i.download.archives).containsExactly("tar", "tbz2", "tgz", "txz");
     assertThat(i.download.schemes).isEmpty();
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountSecurity.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountSecurity.java
index 0ca0207..752f0d2 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountSecurity.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountSecurity.java
@@ -18,7 +18,6 @@
 import com.google.gerrit.common.auth.SignInRequired;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountExternalId;
-import com.google.gerrit.reviewdb.client.ContactInformation;
 import com.google.gwtjsonrpc.common.AsyncCallback;
 import com.google.gwtjsonrpc.common.RemoteJsonService;
 import com.google.gwtjsonrpc.common.RpcImpl;
@@ -41,7 +40,7 @@
   @Audit
   @SignInRequired
   void updateContact(String fullName, String emailAddr,
-      ContactInformation info, AsyncCallback<Account> callback);
+      AsyncCallback<Account> callback);
 
   @Audit
   @SignInRequired
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ContributorAgreement.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ContributorAgreement.java
index d475980..17f640d 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ContributorAgreement.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ContributorAgreement.java
@@ -25,7 +25,6 @@
   protected String name;
   protected String description;
   protected List<PermissionRule> accepted;
-  protected boolean requireContactInformation;
   protected GroupReference autoVerify;
   protected String agreementUrl;
 
@@ -63,14 +62,6 @@
     this.accepted = accepted;
   }
 
-  public boolean isRequireContactInformation() {
-    return requireContactInformation;
-  }
-
-  public void setRequireContactInformation(boolean requireContactInformation) {
-    this.requireContactInformation = requireContactInformation;
-  }
-
   public GroupReference getAutoVerify() {
     return autoVerify;
   }
@@ -101,7 +92,6 @@
     ContributorAgreement ca = new ContributorAgreement(name);
     ca.description = description;
     ca.accepted = Collections.emptyList();
-    ca.requireContactInformation = requireContactInformation;
     if (autoVerify != null) {
       ca.autoVerify = new GroupReference();
     }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/errors/ContactInformationStoreException.java b/gerrit-common/src/main/java/com/google/gerrit/common/errors/ContactInformationStoreException.java
deleted file mode 100644
index 956d010..0000000
--- a/gerrit-common/src/main/java/com/google/gerrit/common/errors/ContactInformationStoreException.java
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.common.errors;
-
-/** Error indicating the server cannot store contact information. */
-public class ContactInformationStoreException extends Exception {
-  private static final long serialVersionUID = 1L;
-
-  public static final String MESSAGE = "Cannot store contact information";
-
-  public ContactInformationStoreException() {
-    super(MESSAGE);
-  }
-
-  public ContactInformationStoreException(final Throwable why) {
-    super(MESSAGE, why);
-  }
-}
diff --git a/gerrit-extension-api/BUCK b/gerrit-extension-api/BUCK
index 77a6b13..307aefa 100644
--- a/gerrit-extension-api/BUCK
+++ b/gerrit-extension-api/BUCK
@@ -28,6 +28,7 @@
   exported_deps = [
     ':api',
     '//lib/guice:guice',
+    '//lib/guice:guice-assistedinject',
     '//lib/guice:guice-servlet',
     '//lib:servlet-api-3_1',
   ],
@@ -37,7 +38,10 @@
 java_library(
   name = 'api',
   srcs = glob([SRC + '**/*.java']),
-  provided_deps = ['//lib/guice:guice'],
+  provided_deps = [
+    '//lib/guice:guice',
+    '//lib/guice:guice-assistedinject',
+  ],
   visibility = ['PUBLIC'],
 )
 
@@ -47,6 +51,17 @@
   visibility = ['PUBLIC'],
 )
 
+java_test(
+  name = 'api_tests',
+  srcs = glob(['src/test/java/**/*.java']),
+  deps = [
+    ':api',
+    '//lib:truth',
+    '//lib/guice:guice',
+  ],
+  source_under_test = [':api'],
+)
+
 java_doc(
   name = 'extension-api-javadoc',
   title = 'Gerrit Review Extension API Documentation',
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/FactoryModule.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/config/FactoryModule.java
similarity index 96%
rename from gerrit-server/src/main/java/com/google/gerrit/server/config/FactoryModule.java
rename to gerrit-extension-api/src/main/java/com/google/gerrit/extensions/config/FactoryModule.java
index a0cf2ab..226a6c2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/FactoryModule.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/config/FactoryModule.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.config;
+package com.google.gerrit.extensions.config;
 
 import com.google.inject.AbstractModule;
 import com.google.inject.assistedinject.FactoryModuleBuilder;
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicSet.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicSet.java
index c99f233..28052ef 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicSet.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/registration/DynamicSet.java
@@ -182,6 +182,23 @@
   }
 
   /**
+   * Returns {@code true} if this set contains the given item.
+   *
+   * @param item item to check whether or not it is contained.
+   * @return {@code true} if this set contains the given item.
+   */
+  public boolean contains(final T item) {
+    Iterator<T> iterator = iterator();
+    while (iterator.hasNext()) {
+      T candidate = iterator.next();
+      if (candidate == item) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
    * Add one new element to the set.
    *
    * @param item the item to add to the collection. Must not be null.
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/NotImplementedException.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/NotImplementedException.java
index 10d0a14..566159d 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/NotImplementedException.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/NotImplementedException.java
@@ -19,6 +19,10 @@
   private static final long serialVersionUID = 1L;
 
   public NotImplementedException() {
-    super("Not implemented.");
+    this("Not implemented");
+  }
+
+  public NotImplementedException(String message) {
+    super(message);
   }
 }
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/RestApiModule.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/RestApiModule.java
index 7708a5c..3567300 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/RestApiModule.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/RestApiModule.java
@@ -16,14 +16,14 @@
 
 import com.google.gerrit.extensions.annotations.Export;
 import com.google.gerrit.extensions.annotations.Exports;
-import com.google.inject.AbstractModule;
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.inject.Provider;
 import com.google.inject.TypeLiteral;
 import com.google.inject.binder.LinkedBindingBuilder;
 import com.google.inject.binder.ScopedBindingBuilder;
 
 /** Guice DSL for binding {@link RestView} implementations. */
-public abstract class RestApiModule extends AbstractModule {
+public abstract class RestApiModule extends FactoryModule {
   protected static final String GET = "GET";
   protected static final String PUT = "PUT";
   protected static final String DELETE = "DELETE";
diff --git a/gerrit-extension-api/src/test/java/com/google/gerrit/extensions/registration/DynamicSetTest.java b/gerrit-extension-api/src/test/java/com/google/gerrit/extensions/registration/DynamicSetTest.java
new file mode 100644
index 0000000..dc71b12
--- /dev/null
+++ b/gerrit-extension-api/src/test/java/com/google/gerrit/extensions/registration/DynamicSetTest.java
@@ -0,0 +1,95 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.extensions.registration;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.inject.Key;
+import com.google.inject.util.Providers;
+
+import org.junit.Test;
+
+public class DynamicSetTest {
+  // In tests for {@link DynamicSet#contains(Object)}, be sure to avoid
+  // {@code assertThat(ds).contains(...) @} and
+  // {@code assertThat(ds).DoesNotContains(...) @} as (since
+  // {@link DynamicSet@} is not a {@link Collection@}) those boil down to
+  // iterating over the {@link DynamicSet@} and checking equality instead
+  // of calling {@link DynamicSet#contains(Object)}.
+  // To test for {@link DynamicSet#contains(Object)}, use
+  // {@code assertThat(ds.contains(...)).isTrue() @} and
+  // {@code assertThat(ds.contains(...)).isFalse() @} instead.
+
+  @Test
+  public void testContainsWithEmpty() throws Exception {
+    DynamicSet<Integer> ds = new DynamicSet<>();
+    assertThat(ds.contains(2)).isFalse(); //See above comment about ds.contains
+  }
+
+  @Test
+  public void testContainsTrueWithSingleElement() throws Exception {
+    DynamicSet<Integer> ds = new DynamicSet<>();
+    ds.add(2);
+
+    assertThat(ds.contains(2)).isTrue(); //See above comment about ds.contains
+  }
+
+  @Test
+  public void testContainsFalseWithSingleElement() throws Exception {
+    DynamicSet<Integer> ds = new DynamicSet<>();
+    ds.add(2);
+
+    assertThat(ds.contains(3)).isFalse(); //See above comment about ds.contains
+  }
+
+  @Test
+  public void testContainsTrueWithTwoElements() throws Exception {
+    DynamicSet<Integer> ds = new DynamicSet<>();
+    ds.add(2);
+    ds.add(4);
+
+    assertThat(ds.contains(4)).isTrue(); //See above comment about ds.contains
+  }
+
+  @Test
+  public void testContainsFalseWithTwoElements() throws Exception {
+    DynamicSet<Integer> ds = new DynamicSet<>();
+    ds.add(2);
+    ds.add(4);
+
+    assertThat(ds.contains(3)).isFalse(); //See above comment about ds.contains
+  }
+
+  @Test
+  public void testContainsDynamic() throws Exception {
+    DynamicSet<Integer> ds = new DynamicSet<>();
+    ds.add(2);
+
+    Key<Integer> key = Key.get(Integer.class);
+    ReloadableRegistrationHandle<Integer> handle = ds.add(key, Providers.of(4));
+
+    ds.add(6);
+
+    // At first, 4 is contained.
+    assertThat(ds.contains(4)).isTrue(); //See above comment about ds.contains
+
+    // Then we remove 4.
+    handle.remove();
+
+    // And now 4 should no longer be contained.
+    assertThat(ds.contains(4)).isFalse(); //See above comment about ds.contains
+  }
+}
diff --git a/gerrit-gpg/BUCK b/gerrit-gpg/BUCK
new file mode 100644
index 0000000..592a5190
--- /dev/null
+++ b/gerrit-gpg/BUCK
@@ -0,0 +1,65 @@
+java_library(
+  name = 'gpg',
+  srcs = glob(['src/main/java/**/*.java']),
+  deps = [
+    '//gerrit-common:server',
+    '//gerrit-extension-api:api',
+    '//gerrit-reviewdb:server',
+    '//gerrit-server:server',
+    '//lib:guava',
+    '//lib:gwtorm',
+    '//lib/guice:guice',
+    '//lib/guice:guice-assistedinject',
+    '//lib/guice:guice-servlet',
+    '//lib/jgit:jgit',
+    '//lib/log:api',
+  ],
+  provided_deps = [
+    '//lib/bouncycastle:bcprov',
+    '//lib/bouncycastle:bcpg',
+  ],
+  visibility = ['PUBLIC'],
+)
+
+TESTUTIL_SRCS = [
+  'src/test/java/com/google/gerrit/gpg/testutil/TestKey.java',
+]
+
+java_library(
+  name = 'testutil',
+  srcs = TESTUTIL_SRCS,
+  deps = [
+    ':gpg',
+    '//lib:guava',
+    '//lib/bouncycastle:bcpg',
+    '//lib/bouncycastle:bcprov',
+    '//lib/jgit:jgit',
+  ],
+  visibility = ['PUBLIC'],
+)
+
+java_test(
+  name = 'gpg_tests',
+  srcs = glob(
+    ['src/test/java/**/*.java'],
+    excludes = TESTUTIL_SRCS,
+  ),
+  deps = [
+    ':gpg',
+    ':testutil',
+    '//gerrit-extension-api:api',
+    '//gerrit-reviewdb:server',
+    '//gerrit-server:server',
+    '//gerrit-server:testutil',
+    '//lib:guava',
+    '//lib:gwtorm',
+    '//lib:truth',
+    '//lib/bouncycastle:bcpg',
+    '//lib/bouncycastle:bcprov',
+    '//lib/guice:guice',
+    '//lib/jgit:jgit',
+    '//lib/jgit:junit',
+  ],
+  source_under_test = [':gpg'],
+  visibility = ['//tools/eclipse:classpath'],
+)
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/util/BouncyCastleUtil.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/BouncyCastleUtil.java
similarity index 97%
rename from gerrit-server/src/main/java/com/google/gerrit/server/util/BouncyCastleUtil.java
rename to gerrit-gpg/src/main/java/com/google/gerrit/gpg/BouncyCastleUtil.java
index ba87d58..ef065a1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/util/BouncyCastleUtil.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/BouncyCastleUtil.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.util;
+package com.google.gerrit.gpg;
 
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.openpgp.PGPPublicKey;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/gpg/CheckResult.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/CheckResult.java
similarity index 97%
rename from gerrit-server/src/main/java/com/google/gerrit/server/git/gpg/CheckResult.java
rename to gerrit-gpg/src/main/java/com/google/gerrit/gpg/CheckResult.java
index 71321ba..c41ecbe 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/gpg/CheckResult.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/CheckResult.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.git.gpg;
+package com.google.gerrit.gpg;
 
 import java.util.ArrayList;
 import java.util.Arrays;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/gpg/Fingerprint.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/Fingerprint.java
similarity index 97%
rename from gerrit-server/src/main/java/com/google/gerrit/server/git/gpg/Fingerprint.java
rename to gerrit-gpg/src/main/java/com/google/gerrit/gpg/Fingerprint.java
index bc70cb3..6fd8bac 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/gpg/Fingerprint.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/Fingerprint.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.git.gpg;
+package com.google.gerrit.gpg;
 
 import static com.google.common.base.Preconditions.checkArgument;
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/gpg/GerritPublicKeyChecker.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
similarity index 97%
rename from gerrit-server/src/main/java/com/google/gerrit/server/git/gpg/GerritPublicKeyChecker.java
rename to gerrit-gpg/src/main/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
index 851808c..69a09f0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/gpg/GerritPublicKeyChecker.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
@@ -12,10 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.git.gpg;
+package com.google.gerrit.gpg;
 
+import static com.google.gerrit.gpg.PublicKeyStore.keyIdToString;
 import static com.google.gerrit.reviewdb.client.AccountExternalId.SCHEME_GPGKEY;
-import static com.google.gerrit.server.git.gpg.PublicKeyStore.keyIdToString;
 
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.Ordering;
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/GpgModule.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/GpgModule.java
new file mode 100644
index 0000000..fc1953a
--- /dev/null
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/GpgModule.java
@@ -0,0 +1,99 @@
+// Copyright (C) 2015 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.gpg;
+
+import static com.google.gerrit.gpg.server.GpgKey.GPG_KEY_KIND;
+import static com.google.gerrit.server.account.AccountResource.ACCOUNT_KIND;
+
+import com.google.gerrit.extensions.api.accounts.GpgKeyApi;
+import com.google.gerrit.extensions.common.GpgKeyInfo;
+import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.NotImplementedException;
+import com.google.gerrit.extensions.restapi.RestApiModule;
+import com.google.gerrit.gpg.api.GpgApiAdapterImpl;
+import com.google.gerrit.gpg.api.GpgKeyApiImpl;
+import com.google.gerrit.gpg.server.DeleteGpgKey;
+import com.google.gerrit.gpg.server.GpgKeys;
+import com.google.gerrit.gpg.server.PostGpgKeys;
+import com.google.gerrit.server.EnableSignedPush;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.api.accounts.GpgApiAdapter;
+
+import org.eclipse.jgit.lib.Config;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Map;
+
+public class GpgModule extends RestApiModule {
+  private static final Logger log = LoggerFactory.getLogger(GpgModule.class);
+
+  private final Config cfg;
+
+  public GpgModule(Config cfg) {
+    this.cfg = cfg;
+  }
+
+  @Override
+  protected void configure() {
+    boolean configEnableSignedPush =
+        cfg.getBoolean("receive", null, "enableSignedPush", false);
+    boolean havePgp = BouncyCastleUtil.havePGP();
+    boolean enableSignedPush = configEnableSignedPush && havePgp;
+    bindConstant().annotatedWith(EnableSignedPush.class).to(enableSignedPush);
+
+    if (configEnableSignedPush && !havePgp) {
+      log.info("Bouncy Castle PGP not installed; signed push verification is"
+          + " disabled");
+    }
+    if (!enableSignedPush) {
+      bind(GpgApiAdapter.class).to(NoGpgApi.class);
+      return;
+    }
+
+    install(new SignedPushModule());
+    bind(GpgApiAdapter.class).to(GpgApiAdapterImpl.class);
+    factory(GpgKeyApiImpl.Factory.class);
+
+    DynamicMap.mapOf(binder(), GPG_KEY_KIND);
+
+    child(ACCOUNT_KIND, "gpgkeys").to(GpgKeys.class);
+    post(ACCOUNT_KIND, "gpgkeys").to(PostGpgKeys.class);
+    get(GPG_KEY_KIND).to(GpgKeys.Get.class);
+    delete(GPG_KEY_KIND).to(DeleteGpgKey.class);
+  }
+
+  private static class NoGpgApi implements GpgApiAdapter {
+    private static final String MSG = "GPG key APIs disabled";
+
+    @Override
+    public Map<String, GpgKeyInfo> listGpgKeys(AccountResource account) {
+      throw new NotImplementedException(MSG);
+    }
+
+    @Override
+    public Map<String, GpgKeyInfo> putGpgKeys(AccountResource account,
+        List<String> add, List<String> delete) {
+      throw new NotImplementedException(MSG);
+    }
+
+    @Override
+    public GpgKeyApi gpgKey(AccountResource account, IdString idStr) {
+      throw new NotImplementedException(MSG);
+    }
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/gpg/PublicKeyChecker.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/PublicKeyChecker.java
similarity index 94%
rename from gerrit-server/src/main/java/com/google/gerrit/server/git/gpg/PublicKeyChecker.java
rename to gerrit-gpg/src/main/java/com/google/gerrit/gpg/PublicKeyChecker.java
index 62cb563..7b7aabb 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/gpg/PublicKeyChecker.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/PublicKeyChecker.java
@@ -12,9 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.git.gpg;
+package com.google.gerrit.gpg;
 
-import static com.google.gerrit.server.git.gpg.PublicKeyStore.keyIdToString;
+import static com.google.gerrit.gpg.PublicKeyStore.keyIdToString;
 
 import org.bouncycastle.openpgp.PGPPublicKey;
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/gpg/PublicKeyStore.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/PublicKeyStore.java
similarity index 96%
rename from gerrit-server/src/main/java/com/google/gerrit/server/git/gpg/PublicKeyStore.java
rename to gerrit-gpg/src/main/java/com/google/gerrit/gpg/PublicKeyStore.java
index 7151914..a36052e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/gpg/PublicKeyStore.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/PublicKeyStore.java
@@ -12,13 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.git.gpg;
+package com.google.gerrit.gpg;
 
 import static com.google.common.base.Preconditions.checkState;
 import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
 
-import com.google.gerrit.reviewdb.client.RefNames;
-
 import org.bouncycastle.bcpg.ArmoredInputStream;
 import org.bouncycastle.bcpg.ArmoredOutputStream;
 import org.bouncycastle.openpgp.PGPException;
@@ -73,6 +71,9 @@
   private static final ObjectId EMPTY_TREE =
       ObjectId.fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904");
 
+  /** Ref where GPG public keys are stored. */
+  public static final String REFS_GPG_KEYS = "refs/meta/gpg-keys";
+
   private final Repository repo;
   private ObjectReader reader;
   private RevCommit tip;
@@ -89,6 +90,10 @@
 
   @Override
   public void close() {
+    reset();
+  }
+
+  private void reset() {
     if (reader != null) {
       reader.close();
       reader = null;
@@ -97,10 +102,10 @@
   }
 
   private void load() throws IOException {
-    close();
+    reset();
     reader = repo.newObjectReader();
 
-    Ref ref = repo.getRefDatabase().exactRef(RefNames.REFS_GPG_KEYS);
+    Ref ref = repo.getRefDatabase().exactRef(REFS_GPG_KEYS);
     if (ref == null) {
       return;
     }
@@ -245,13 +250,13 @@
       ins.flush();
     }
 
-    RefUpdate ru = repo.updateRef(RefNames.REFS_GPG_KEYS);
+    RefUpdate ru = repo.updateRef(PublicKeyStore.REFS_GPG_KEYS);
     ru.setExpectedOldObjectId(tip);
     ru.setNewObjectId(newTip);
     ru.setRefLogIdent(cb.getCommitter());
     ru.setRefLogMessage("Store public keys", true);
     RefUpdate.Result result = ru.update();
-    close();
+    reset();
     switch (result) {
       case FAST_FORWARD:
       case NEW:
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/gpg/PushCertificateChecker.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/PushCertificateChecker.java
similarity index 79%
rename from gerrit-server/src/main/java/com/google/gerrit/server/git/gpg/PushCertificateChecker.java
rename to gerrit-gpg/src/main/java/com/google/gerrit/gpg/PushCertificateChecker.java
index fcef3a3..86a33ab 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/gpg/PushCertificateChecker.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/PushCertificateChecker.java
@@ -12,10 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.git.gpg;
+package com.google.gerrit.gpg;
 
-import static com.google.gerrit.server.git.gpg.PublicKeyStore.keyIdToString;
-import static com.google.gerrit.server.git.gpg.PublicKeyStore.keyToString;
+import static com.google.gerrit.gpg.PublicKeyStore.keyIdToString;
+import static com.google.gerrit.gpg.PublicKeyStore.keyToString;
 
 import org.bouncycastle.bcpg.ArmoredInputStream;
 import org.bouncycastle.openpgp.PGPException;
@@ -31,6 +31,8 @@
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.transport.PushCertificate;
 import org.eclipse.jgit.transport.PushCertificate.NonceStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
@@ -39,6 +41,9 @@
 
 /** Checker for push certificates. */
 public abstract class PushCertificateChecker {
+  private static final Logger log =
+      LoggerFactory.getLogger(PushCertificateChecker.class);
+
   private final PublicKeyChecker publicKeyChecker;
 
   protected PushCertificateChecker(PublicKeyChecker publicKeyChecker) {
@@ -49,28 +54,34 @@
    * Check a push certificate.
    *
    * @return result of the check.
-   * @throws PGPException if an error occurred during GPG checks.
-   * @throws IOException if an error occurred reading from the repository.
    */
-  public final CheckResult check(PushCertificate cert) throws PGPException, IOException {
+  public final CheckResult check(PushCertificate cert) {
     if (cert.getNonceStatus() != NonceStatus.OK) {
       return new CheckResult("Invalid nonce");
     }
-    PGPSignature sig = readSignature(cert);
-    if (sig == null) {
-      return new CheckResult("Invalid signature format");
-    }
-    Repository repo = getRepository();
     List<String> problems = new ArrayList<>();
-    try (PublicKeyStore store = new PublicKeyStore(repo)) {
-      checkSignature(sig, cert, store.get(sig.getKeyID()), problems);
-      checkCustom(repo, problems);
-      return new CheckResult(problems);
-    } finally {
-      if (shouldClose(repo)) {
-        repo.close();
+    try {
+      PGPSignature sig = readSignature(cert);
+      if (sig != null) {
+        @SuppressWarnings("resource")
+        Repository repo = getRepository();
+        try (PublicKeyStore store = new PublicKeyStore(repo)) {
+          checkSignature(sig, cert, store.get(sig.getKeyID()), problems);
+          checkCustom(repo, problems);
+        } finally {
+          if (shouldClose(repo)) {
+            repo.close();
+          }
+        }
+      } else {
+        problems.add("Invalid signature format");
       }
+    } catch (PGPException | IOException e) {
+      String msg = "Internal error checking push certificate";
+      log.error(msg, e);
+      problems.add(msg);
     }
+    return new CheckResult(problems);
   }
 
   /**
@@ -140,9 +151,9 @@
         if (result.isOk()) {
           return;
         }
-        StringBuilder err = new StringBuilder("Invalid public key (")
+        StringBuilder err = new StringBuilder("Invalid public key ")
             .append(keyToString(k))
-            .append("):");
+            .append(":");
         for (int i = 0; i < result.getProblems().size(); i++) {
           err.append('\n').append("  ").append(result.getProblems().get(i));
         }
@@ -150,13 +161,13 @@
         return;
       } catch (PGPException e) {
         deferredProblems.add(
-            "Error checking signature with public key (" + keyToString(k)
+            "Error checking signature with public key " + keyToString(k)
             + ": " + e.getMessage());
       }
     }
     if (!anyKeys) {
       problems.add(
-          "No public keys found for Key ID " + keyIdToString(sig.getKeyID()));
+          "No public keys found for key ID " + keyIdToString(sig.getKeyID()));
     } else {
       problems.addAll(deferredProblems);
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/gpg/SignedPushModule.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/SignedPushModule.java
similarity index 91%
rename from gerrit-server/src/main/java/com/google/gerrit/server/git/gpg/SignedPushModule.java
rename to gerrit-gpg/src/main/java/com/google/gerrit/gpg/SignedPushModule.java
index 88bda9a..7508806 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/gpg/SignedPushModule.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/SignedPushModule.java
@@ -12,19 +12,19 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.git.gpg;
+package com.google.gerrit.gpg;
 
 import com.google.common.base.Strings;
 import com.google.common.collect.Lists;
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.EnableSignedPush;
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.ReceivePackInitializer;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.server.util.BouncyCastleUtil;
 import com.google.inject.AbstractModule;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -44,20 +44,14 @@
 import java.security.SecureRandom;
 import java.util.Random;
 
-public class SignedPushModule extends AbstractModule {
+class SignedPushModule extends AbstractModule {
   private static final Logger log =
       LoggerFactory.getLogger(SignedPushModule.class);
 
-  public static boolean isEnabled(Config cfg) {
-    return cfg.getBoolean("receive", null, "enableSignedPush", false);
-  }
-
   @Override
   protected void configure() {
     if (!BouncyCastleUtil.havePGP()) {
-      log.info("BouncyCastle PGP not installed; signed push verification is"
-          + " disabled");
-      return;
+      throw new ProvisionException("Bouncy Castle PGP not installed");
     }
     bind(PublicKeyChecker.class).to(GerritPublicKeyChecker.class);
     bind(PublicKeyStore.class).toProvider(StoreProvider.class);
@@ -73,12 +67,13 @@
 
     @Inject
     Initializer(@GerritServerConfig Config cfg,
+        @EnableSignedPush boolean enableSignedPush,
         SignedPushPreReceiveHook hook,
         ProjectCache projectCache) {
       this.hook = hook;
       this.projectCache = projectCache;
 
-      if (isEnabled(cfg)) {
+      if (enableSignedPush) {
         String seed = cfg.getString("receive", null, "certNonceSeed");
         if (Strings.isNullOrEmpty(seed)) {
           seed = randomString(64);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/gpg/SignedPushPreReceiveHook.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/SignedPushPreReceiveHook.java
similarity index 67%
rename from gerrit-server/src/main/java/com/google/gerrit/server/git/gpg/SignedPushPreReceiveHook.java
rename to gerrit-gpg/src/main/java/com/google/gerrit/gpg/SignedPushPreReceiveHook.java
index f5e9f09..b2dca8b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/gpg/SignedPushPreReceiveHook.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/SignedPushPreReceiveHook.java
@@ -12,21 +12,18 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.git.gpg;
+package com.google.gerrit.gpg;
 
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
-import org.bouncycastle.openpgp.PGPException;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.transport.PreReceiveHook;
 import org.eclipse.jgit.transport.PushCertificate;
 import org.eclipse.jgit.transport.ReceiveCommand;
 import org.eclipse.jgit.transport.ReceivePack;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.util.Collection;
@@ -40,9 +37,6 @@
  */
 @Singleton
 public class SignedPushPreReceiveHook implements PreReceiveHook {
-  private static final Logger log =
-      LoggerFactory.getLogger(SignedPushPreReceiveHook.class);
-
   private final GitRepositoryManager repoManager;
   private final AllUsersName allUsers;
   private final PublicKeyChecker keyChecker;
@@ -60,32 +54,27 @@
   @Override
   public void onPreReceive(ReceivePack rp,
       Collection<ReceiveCommand> commands) {
-    try {
-      PushCertificate cert = rp.getPushCertificate();
-      if (cert == null) {
-        return;
+    PushCertificate cert = rp.getPushCertificate();
+    if (cert == null) {
+      return;
+    }
+    PushCertificateChecker checker = new PushCertificateChecker(keyChecker) {
+      @Override
+      protected Repository getRepository() throws IOException {
+        return repoManager.openRepository(allUsers);
       }
-      PushCertificateChecker checker = new PushCertificateChecker(keyChecker) {
-        @Override
-        protected Repository getRepository() throws IOException {
-          return repoManager.openRepository(allUsers);
-        }
 
-        @Override
-        protected boolean shouldClose(Repository repo) {
-          return true;
-        }
-      };
-      CheckResult result = checker.check(cert);
-      if (!result.isOk()) {
-        for (String problem : result.getProblems()) {
-          rp.sendMessage(problem);
-        }
-        reject(commands, "invalid push cert");
+      @Override
+      protected boolean shouldClose(Repository repo) {
+        return true;
       }
-    } catch (PGPException | IOException e) {
-      log.error("Error checking push certificate", e);
-      reject(commands, "push cert error");
+    };
+    CheckResult result = checker.check(cert);
+    if (!result.isOk()) {
+      for (String problem : result.getProblems()) {
+        rp.sendMessage(problem);
+      }
+      reject(commands, "invalid push cert");
     }
   }
 
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java
new file mode 100644
index 0000000..64b9e85
--- /dev/null
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java
@@ -0,0 +1,83 @@
+// Copyright (C) 2015 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.gpg.api;
+
+import com.google.gerrit.extensions.api.accounts.GpgKeyApi;
+import com.google.gerrit.extensions.common.GpgKeyInfo;
+import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.gpg.server.GpgKeys;
+import com.google.gerrit.gpg.server.PostGpgKeys;
+import com.google.gerrit.server.GpgException;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.gerrit.server.api.accounts.GpgApiAdapter;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+
+import org.bouncycastle.openpgp.PGPException;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+public class GpgApiAdapterImpl implements GpgApiAdapter {
+  private final PostGpgKeys postGpgKeys;
+  private final GpgKeys gpgKeys;
+  private final GpgKeyApiImpl.Factory gpgKeyApiFactory;
+
+  @Inject
+  GpgApiAdapterImpl(
+      PostGpgKeys postGpgKeys,
+      GpgKeys gpgKeys,
+      GpgKeyApiImpl.Factory gpgKeyApiFactory) {
+    this.postGpgKeys = postGpgKeys;
+    this.gpgKeys = gpgKeys;
+    this.gpgKeyApiFactory = gpgKeyApiFactory;
+  }
+
+  @Override
+  public Map<String, GpgKeyInfo> listGpgKeys(AccountResource account)
+      throws RestApiException, GpgException {
+    try {
+      return gpgKeys.list().apply(account);
+    } catch (OrmException | PGPException | IOException e) {
+      throw new GpgException(e);
+    }
+  }
+
+  @Override
+  public Map<String, GpgKeyInfo> putGpgKeys(AccountResource account,
+      List<String> add, List<String> delete)
+      throws RestApiException, GpgException {
+    PostGpgKeys.Input in = new PostGpgKeys.Input();
+    in.add = add;
+    in.delete = delete;
+    try {
+      return postGpgKeys.apply(account, in);
+    } catch (PGPException | OrmException | IOException e) {
+      throw new GpgException(e);
+    }
+  }
+
+  @Override
+  public GpgKeyApi gpgKey(AccountResource account, IdString idStr)
+      throws RestApiException, GpgException {
+    try {
+      return gpgKeyApiFactory.create(gpgKeys.parse(account, idStr));
+    } catch (PGPException | OrmException | IOException e) {
+      throw new GpgException(e);
+    }
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/GpgKeyApiImpl.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/api/GpgKeyApiImpl.java
similarity index 80%
rename from gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/GpgKeyApiImpl.java
rename to gerrit-gpg/src/main/java/com/google/gerrit/gpg/api/GpgKeyApiImpl.java
index e42c2f6..ab30184 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/GpgKeyApiImpl.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/api/GpgKeyApiImpl.java
@@ -12,14 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.api.accounts;
+package com.google.gerrit.gpg.api;
 
 import com.google.gerrit.extensions.api.accounts.GpgKeyApi;
 import com.google.gerrit.extensions.common.GpgKeyInfo;
 import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.server.account.AccountResource;
-import com.google.gerrit.server.account.DeleteGpgKey;
-import com.google.gerrit.server.account.GpgKeys;
+import com.google.gerrit.gpg.server.DeleteGpgKey;
+import com.google.gerrit.gpg.server.GpgKey;
+import com.google.gerrit.gpg.server.GpgKeys;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
@@ -28,20 +28,20 @@
 
 import java.io.IOException;
 
-class GpgKeyApiImpl implements GpgKeyApi {
-  interface Factory {
-    GpgKeyApiImpl create(AccountResource.GpgKey rsrc);
+public class GpgKeyApiImpl implements GpgKeyApi {
+  public interface Factory {
+    GpgKeyApiImpl create(GpgKey rsrc);
   }
 
   private final GpgKeys.Get get;
   private final DeleteGpgKey delete;
-  private final AccountResource.GpgKey rsrc;
+  private final GpgKey rsrc;
 
   @AssistedInject
   GpgKeyApiImpl(
       GpgKeys.Get get,
       DeleteGpgKey delete,
-      @Assisted AccountResource.GpgKey rsrc) {
+      @Assisted GpgKey rsrc) {
     this.get = get;
     this.delete = delete;
     this.rsrc = rsrc;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteGpgKey.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/DeleteGpgKey.java
similarity index 90%
rename from gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteGpgKey.java
rename to gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/DeleteGpgKey.java
index b7e63cf..baac714 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteGpgKey.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/DeleteGpgKey.java
@@ -12,20 +12,19 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.gpg.server;
 
-import static com.google.gerrit.server.git.gpg.PublicKeyStore.keyIdToString;
+import static com.google.gerrit.gpg.PublicKeyStore.keyIdToString;
 
 import com.google.common.io.BaseEncoding;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.gpg.PublicKeyStore;
+import com.google.gerrit.gpg.server.DeleteGpgKey.Input;
 import com.google.gerrit.reviewdb.client.AccountExternalId;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.account.AccountResource.GpgKey;
-import com.google.gerrit.server.account.DeleteGpgKey.Input;
-import com.google.gerrit.server.git.gpg.PublicKeyStore;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/GpgKey.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/GpgKey.java
new file mode 100644
index 0000000..2fe7eb6
--- /dev/null
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/GpgKey.java
@@ -0,0 +1,38 @@
+// Copyright (C) 2015 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.gpg.server;
+
+import com.google.gerrit.extensions.restapi.RestView;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountResource;
+import com.google.inject.TypeLiteral;
+
+import org.bouncycastle.openpgp.PGPPublicKeyRing;
+
+public class GpgKey extends AccountResource {
+  public static final TypeLiteral<RestView<GpgKey>> GPG_KEY_KIND =
+      new TypeLiteral<RestView<GpgKey>>() {};
+
+  private final PGPPublicKeyRing keyRing;
+
+  public GpgKey(IdentifiedUser user, PGPPublicKeyRing keyRing) {
+    super(user);
+    this.keyRing = keyRing;
+  }
+
+  public PGPPublicKeyRing getKeyRing() {
+    return keyRing;
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GpgKeys.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/GpgKeys.java
similarity index 92%
rename from gerrit-server/src/main/java/com/google/gerrit/server/account/GpgKeys.java
rename to gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/GpgKeys.java
index bbcdd06..b22ca0e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GpgKeys.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/GpgKeys.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.gpg.server;
 
 import static com.google.gerrit.reviewdb.client.AccountExternalId.SCHEME_GPGKEY;
 import static java.nio.charset.StandardCharsets.UTF_8;
@@ -31,13 +31,13 @@
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.extensions.restapi.RestView;
+import com.google.gerrit.gpg.BouncyCastleUtil;
+import com.google.gerrit.gpg.Fingerprint;
+import com.google.gerrit.gpg.PublicKeyStore;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountExternalId;
 import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.account.AccountResource.GpgKey;
-import com.google.gerrit.server.git.gpg.Fingerprint;
-import com.google.gerrit.server.git.gpg.PublicKeyStore;
-import com.google.gerrit.server.util.BouncyCastleUtil;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -60,17 +60,17 @@
 
 @Singleton
 public class GpgKeys implements
-    ChildCollection<AccountResource, AccountResource.GpgKey> {
+    ChildCollection<AccountResource, GpgKey> {
   private static final Logger log = LoggerFactory.getLogger(GpgKeys.class);
 
   public static String MIME_TYPE = "application/pgp-keys";
 
-  private final DynamicMap<RestView<AccountResource.GpgKey>> views;
+  private final DynamicMap<RestView<GpgKey>> views;
   private final Provider<ReviewDb> db;
   private final Provider<PublicKeyStore> storeProvider;
 
   @Inject
-  GpgKeys(DynamicMap<RestView<AccountResource.GpgKey>> views,
+  GpgKeys(DynamicMap<RestView<GpgKey>> views,
       Provider<ReviewDb> db,
       Provider<PublicKeyStore> storeProvider) {
     this.views = views;
@@ -102,7 +102,7 @@
       for (PGPPublicKeyRing keyRing : store.get(keyId)) {
         PGPPublicKey key = keyRing.getPublicKey();
         if (Arrays.equals(key.getFingerprint(), fp)) {
-          return new AccountResource.GpgKey(parent.getUser(), keyRing);
+          return new GpgKey(parent.getUser(), keyRing);
         }
       }
     }
@@ -172,7 +172,7 @@
   }
 
   @Singleton
-  public static class Get implements RestReadView<AccountResource.GpgKey> {
+  public static class Get implements RestReadView<GpgKey> {
     @Override
     public GpgKeyInfo apply(GpgKey rsrc) throws IOException {
       return toJson(rsrc.getKeyRing());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PostGpgKeys.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java
similarity index 94%
rename from gerrit-server/src/main/java/com/google/gerrit/server/account/PostGpgKeys.java
rename to gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java
index 776f983..670adba 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PostGpgKeys.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/server/PostGpgKeys.java
@@ -12,10 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.account;
+package com.google.gerrit.gpg.server;
 
-import static com.google.gerrit.server.git.gpg.PublicKeyStore.keyIdToString;
-import static com.google.gerrit.server.git.gpg.PublicKeyStore.keyToString;
+import static com.google.gerrit.gpg.PublicKeyStore.keyIdToString;
+import static com.google.gerrit.gpg.PublicKeyStore.keyToString;
 import static java.nio.charset.StandardCharsets.UTF_8;
 
 import com.google.common.base.Function;
@@ -32,14 +32,15 @@
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.gpg.CheckResult;
+import com.google.gerrit.gpg.Fingerprint;
+import com.google.gerrit.gpg.PublicKeyChecker;
+import com.google.gerrit.gpg.PublicKeyStore;
+import com.google.gerrit.gpg.server.PostGpgKeys.Input;
 import com.google.gerrit.reviewdb.client.AccountExternalId;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.account.PostGpgKeys.Input;
-import com.google.gerrit.server.git.gpg.CheckResult;
-import com.google.gerrit.server.git.gpg.Fingerprint;
-import com.google.gerrit.server.git.gpg.PublicKeyChecker;
-import com.google.gerrit.server.git.gpg.PublicKeyStore;
+import com.google.gerrit.server.account.AccountResource;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/git/gpg/GerritPublicKeyCheckerTest.java b/gerrit-gpg/src/test/java/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java
similarity index 98%
rename from gerrit-server/src/test/java/com/google/gerrit/server/git/gpg/GerritPublicKeyCheckerTest.java
rename to gerrit-gpg/src/test/java/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java
index 4b00d5c..e65ba00 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/git/gpg/GerritPublicKeyCheckerTest.java
+++ b/gerrit-gpg/src/test/java/com/google/gerrit/gpg/GerritPublicKeyCheckerTest.java
@@ -12,12 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.git.gpg;
+package com.google.gerrit.gpg;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.truth.Truth.assertThat;
 
 import com.google.common.collect.ImmutableList;
+import com.google.gerrit.gpg.testutil.TestKey;
 import com.google.gerrit.lifecycle.LifecycleManager;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountExternalId;
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/git/gpg/PublicKeyCheckerTest.java b/gerrit-gpg/src/test/java/com/google/gerrit/gpg/PublicKeyCheckerTest.java
similarity index 95%
rename from gerrit-server/src/test/java/com/google/gerrit/server/git/gpg/PublicKeyCheckerTest.java
rename to gerrit-gpg/src/test/java/com/google/gerrit/gpg/PublicKeyCheckerTest.java
index a82619c..ebc3e58 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/git/gpg/PublicKeyCheckerTest.java
+++ b/gerrit-gpg/src/test/java/com/google/gerrit/gpg/PublicKeyCheckerTest.java
@@ -12,10 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.git.gpg;
+package com.google.gerrit.gpg;
 
 import static org.junit.Assert.assertEquals;
 
+import com.google.gerrit.gpg.testutil.TestKey;
+
 import org.junit.Before;
 import org.junit.Test;
 
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/git/gpg/PublicKeyStoreTest.java b/gerrit-gpg/src/test/java/com/google/gerrit/gpg/PublicKeyStoreTest.java
similarity index 93%
rename from gerrit-server/src/test/java/com/google/gerrit/server/git/gpg/PublicKeyStoreTest.java
rename to gerrit-gpg/src/test/java/com/google/gerrit/gpg/PublicKeyStoreTest.java
index c84757e..d936a31 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/git/gpg/PublicKeyStoreTest.java
+++ b/gerrit-gpg/src/test/java/com/google/gerrit/gpg/PublicKeyStoreTest.java
@@ -12,17 +12,18 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.git.gpg;
+package com.google.gerrit.gpg;
 
-import static com.google.gerrit.server.git.gpg.PublicKeyStore.keyIdToString;
-import static com.google.gerrit.server.git.gpg.PublicKeyStore.keyObjectId;
-import static com.google.gerrit.server.git.gpg.PublicKeyStore.keyToString;
+import static com.google.gerrit.gpg.PublicKeyStore.REFS_GPG_KEYS;
+import static com.google.gerrit.gpg.PublicKeyStore.keyIdToString;
+import static com.google.gerrit.gpg.PublicKeyStore.keyObjectId;
+import static com.google.gerrit.gpg.PublicKeyStore.keyToString;
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.gpg.testutil.TestKey;
 
 import org.bouncycastle.openpgp.PGPPublicKey;
 import org.bouncycastle.openpgp.PGPPublicKeyRing;
@@ -83,13 +84,13 @@
   @Test
   public void testGet() throws Exception {
     TestKey key1 = TestKey.key1();
-    tr.branch(RefNames.REFS_GPG_KEYS)
+    tr.branch(REFS_GPG_KEYS)
         .commit()
         .add(keyObjectId(key1.getKeyId()).name(),
           key1.getPublicKeyArmored())
         .create();
     TestKey key2 = TestKey.key2();
-    tr.branch(RefNames.REFS_GPG_KEYS)
+    tr.branch(REFS_GPG_KEYS)
         .commit()
         .add(keyObjectId(key2.getKeyId()).name(),
           key2.getPublicKeyArmored())
@@ -103,7 +104,7 @@
   public void testGetMultiple() throws Exception {
     TestKey key1 = TestKey.key1();
     TestKey key2 = TestKey.key2();
-    tr.branch(RefNames.REFS_GPG_KEYS)
+    tr.branch(REFS_GPG_KEYS)
         .commit()
         .add(keyObjectId(key1.getKeyId()).name(),
             key1.getPublicKeyArmored()
@@ -130,7 +131,7 @@
   public void saveAppendsToExistingList() throws Exception {
     TestKey key1 = TestKey.key1();
     TestKey key2 = TestKey.key2();
-    tr.branch(RefNames.REFS_GPG_KEYS)
+    tr.branch(REFS_GPG_KEYS)
         .commit()
         // Mismatched for this key ID, but we can still read it out.
         .add(keyObjectId(key1.getKeyId()).name(), key2.getPublicKeyArmored())
@@ -145,7 +146,7 @@
         RevWalk rw = new RevWalk(reader)) {
       NoteMap notes = NoteMap.read(
           reader, tr.getRevWalk().parseCommit(
-            tr.getRepository().getRef(RefNames.REFS_GPG_KEYS).getObjectId()));
+            tr.getRepository().getRef(REFS_GPG_KEYS).getObjectId()));
       String contents = new String(
           reader.open(notes.get(keyObjectId(key1.getKeyId()))).getBytes(),
           UTF_8);
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/git/gpg/PushCertificateCheckerTest.java b/gerrit-gpg/src/test/java/com/google/gerrit/gpg/PushCertificateCheckerTest.java
similarity index 91%
rename from gerrit-server/src/test/java/com/google/gerrit/server/git/gpg/PushCertificateCheckerTest.java
rename to gerrit-gpg/src/test/java/com/google/gerrit/gpg/PushCertificateCheckerTest.java
index aa2a2c7..8a633ae 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/git/gpg/PushCertificateCheckerTest.java
+++ b/gerrit-gpg/src/test/java/com/google/gerrit/gpg/PushCertificateCheckerTest.java
@@ -12,14 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.git.gpg;
+package com.google.gerrit.gpg;
 
-import static com.google.gerrit.server.git.gpg.PublicKeyStore.keyIdToString;
-import static com.google.gerrit.server.git.gpg.PublicKeyStore.keyToString;
+import static com.google.gerrit.gpg.PublicKeyStore.REFS_GPG_KEYS;
+import static com.google.gerrit.gpg.PublicKeyStore.keyIdToString;
+import static com.google.gerrit.gpg.PublicKeyStore.keyToString;
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.junit.Assert.assertEquals;
 
-import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.gpg.testutil.TestKey;
 
 import org.bouncycastle.bcpg.ArmoredOutputStream;
 import org.bouncycastle.bcpg.BCPGOutputStream;
@@ -55,7 +56,7 @@
     TestKey key3 = TestKey.key3();
     tr = new TestRepository<>(new InMemoryRepository(
         new DfsRepositoryDescription("repo")));
-    tr.branch(RefNames.REFS_GPG_KEYS).commit()
+    tr.branch(REFS_GPG_KEYS).commit()
         .add(PublicKeyStore.keyObjectId(key1.getPublicKey().getKeyID()).name(),
             key1.getPublicKeyArmored())
         .add(PublicKeyStore.keyObjectId(key3.getPublicKey().getKeyID()).name(),
@@ -95,7 +96,7 @@
     TestKey key2 = TestKey.key2();
     PushCertificate cert = newSignedCert(validNonce(), key2);
     assertProblems(cert,
-        "No public keys found for Key ID " + keyIdToString(key2.getKeyId()));
+        "No public keys found for key ID " + keyIdToString(key2.getKeyId()));
   }
 
   @Test
@@ -103,8 +104,8 @@
     TestKey key3 = TestKey.key3();
     PushCertificate cert = newSignedCert(validNonce(), key3);
     assertProblems(cert,
-        "Invalid public key (" + keyToString(key3.getPublicKey())
-          + "):\n  Key is expired");
+        "Invalid public key " + keyToString(key3.getPublicKey())
+          + ":\n  Key is expired");
   }
 
   private String validNonce() {
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/git/gpg/TestKey.java b/gerrit-gpg/src/test/java/com/google/gerrit/gpg/testutil/TestKey.java
similarity index 99%
rename from gerrit-server/src/test/java/com/google/gerrit/server/git/gpg/TestKey.java
rename to gerrit-gpg/src/test/java/com/google/gerrit/gpg/testutil/TestKey.java
index 321f01b..614818e 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/git/gpg/TestKey.java
+++ b/gerrit-gpg/src/test/java/com/google/gerrit/gpg/testutil/TestKey.java
@@ -12,9 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.server.git.gpg;
+package com.google.gerrit.gpg.testutil;
 
-import static com.google.gerrit.server.git.gpg.PublicKeyStore.keyIdToString;
+import static com.google.gerrit.gpg.PublicKeyStore.keyIdToString;
 
 import com.google.common.collect.ImmutableList;
 
diff --git a/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/css/rebind/CssLinker.java b/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/css/rebind/CssLinker.java
index f48a663..753d25f 100644
--- a/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/css/rebind/CssLinker.java
+++ b/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/css/rebind/CssLinker.java
@@ -77,14 +77,15 @@
 
   private String name(final TreeLogger logger, final PublicResource r)
       throws UnableToCompleteException {
-    final ByteArrayOutputStream tmp = new ByteArrayOutputStream();
-    try (InputStream in = r.getContents(logger)) {
+    byte[] out;
+    try (ByteArrayOutputStream tmp = new ByteArrayOutputStream();
+        InputStream in = r.getContents(logger)) {
       final byte[] buf = new byte[2048];
       int n;
       while ((n = in.read(buf)) >= 0) {
         tmp.write(buf, 0, n);
       }
-      tmp.close();
+      out = tmp.toByteArray();
     } catch (IOException e) {
       final UnableToCompleteException ute = new UnableToCompleteException();
       ute.initCause(e);
@@ -98,7 +99,7 @@
     } else {
       base = "";
     }
-    return base + Util.computeStrongName(tmp.toByteArray()) + ".cache.css";
+    return base + Util.computeStrongName(out) + ".cache.css";
   }
 
   private static class CssPubRsrc extends PublicResource {
diff --git a/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/user/client/UserAgent.java b/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/user/client/UserAgent.java
index 2ffa7c5d..2070200 100644
--- a/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/user/client/UserAgent.java
+++ b/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/user/client/UserAgent.java
@@ -81,6 +81,7 @@
       try {
         return Integer.parseInt(p);
       } catch (NumberFormatException nan) {
+        // Ignored
       }
     }
     return -1;
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/DateFormatter.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/DateFormatter.java
index 37e63af..b6af366 100644
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/DateFormatter.java
+++ b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/DateFormatter.java
@@ -20,7 +20,7 @@
 import java.util.Date;
 
 public class DateFormatter {
-  private final static long ONE_YEAR = 182L * 24 * 60 * 60 * 1000;
+  private static final long ONE_YEAR = 182L * 24 * 60 * 60 * 1000;
 
   private final DateTimeFormat sTime;
   private final DateTimeFormat sDate;
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/AccountInfo.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/AccountInfo.java
index 60c7641..6ac0404 100644
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/AccountInfo.java
+++ b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/AccountInfo.java
@@ -44,22 +44,6 @@
   private final native Timestamp _getRegisteredOn() /*-{ return this._cts; }-*/;
   private final native void _setRegisteredOn(Timestamp ts) /*-{ this._cts = ts; }-*/;
 
-  public final Timestamp contactFiledOn() {
-    if (contactFiledOnRaw() != null) {
-      Timestamp ts = _getContactFiledOn();
-      if (ts == null) {
-        ts = JavaSqlTimestamp_JsonSerializer.parseTimestamp(contactFiledOnRaw());
-        _setContactFiledOn(ts);
-      }
-      return ts;
-    }
-    return null;
-  }
-
-  private final native String contactFiledOnRaw() /*-{ return this.contact_filed_on; }-*/;
-  private final native Timestamp _getContactFiledOn() /*-{ return this._cts; }-*/;
-  private final native void _setContactFiledOn(Timestamp ts) /*-{ this._cts = ts; }-*/;
-
   /**
    * @return true if the server supplied avatar information about this account.
    *         The information may be an empty list, indicating no avatars are
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ServerInfo.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ServerInfo.java
index b487f2e..b0e52fa 100644
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ServerInfo.java
+++ b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/ServerInfo.java
@@ -25,7 +25,6 @@
 public class ServerInfo extends JavaScriptObject {
   public final native AuthInfo auth() /*-{ return this.auth; }-*/;
   public final native ChangeConfigInfo change() /*-{ return this.change; }-*/;
-  public final native ContactStoreInfo contactStore() /*-{ return this.contact_store; }-*/;
   public final native DownloadInfo download() /*-{ return this.download; }-*/;
   public final native GerritInfo gerrit() /*-{ return this.gerrit; }-*/;
   public final native GitwebInfo gitweb() /*-{ return this.gitweb; }-*/;
@@ -47,10 +46,6 @@
   private final native NativeMap<NativeString> _urlAliases() /*-{ return this.url_aliases; }-*/;
 
 
-  public final boolean hasContactStore() {
-    return contactStore() != null;
-  }
-
   public final boolean hasSshd() {
     return sshd() != null;
   }
@@ -71,13 +66,6 @@
     }
   }
 
-  public static class ContactStoreInfo extends JavaScriptObject {
-    public final native String url() /*-{ return this.url; }-*/;
-
-    protected ContactStoreInfo() {
-    }
-  }
-
   public static class PluginConfigInfo extends JavaScriptObject {
     public final native boolean hasAvatars() /*-{ return this.has_avatars || false; }-*/;
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java
index c2a7637..f010879 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java
@@ -17,8 +17,6 @@
 import com.google.gwt.resources.client.CssResource;
 
 public interface GerritCss extends CssResource {
-  String accountContactOnFile();
-  String accountContactPrivacyDetails();
   String accountDashboard();
   String accountInfoBlock();
   String accountLinkPanel();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountApi.java
index 367644f..85ebeb7 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountApi.java
@@ -159,30 +159,30 @@
       .post(GpgKeysInput.add(armored), cb);
   }
 
-  public static void removeGpgKeys(String account,
+  public static void deleteGpgKeys(String account,
       Iterable<String> fingerprints, AsyncCallback<NativeMap<GpgKeyInfo>> cb) {
     new RestApi("/accounts/")
       .id(account)
       .view("gpgkeys")
-      .post(GpgKeysInput.remove(fingerprints), cb);
+      .post(GpgKeysInput.delete(fingerprints), cb);
   }
 
   private static class GpgKeysInput extends JavaScriptObject {
     static GpgKeysInput add(String key) {
-      return createAdd(Natives.arrayOf(key));
+      return createWithAdd(Natives.arrayOf(key));
     }
 
-    static GpgKeysInput remove(Iterable<String> fingerprints) {
-      return createRemove(Natives.arrayOf(fingerprints));
+    static GpgKeysInput delete(Iterable<String> fingerprints) {
+      return createWithDelete(Natives.arrayOf(fingerprints));
     }
 
-    private static native GpgKeysInput createAdd(JsArrayString keys) /*-{
+    private static native GpgKeysInput createWithAdd(JsArrayString keys) /*-{
       return {'add': keys};
     }-*/;
 
-    private static native GpgKeysInput createRemove(
+    private static native GpgKeysInput createWithDelete(
         JsArrayString fingerprints) /*-{
-      return {'remove': fingerprints};
+      return {'delete': fingerprints};
     }-*/;
 
     protected GpgKeysInput() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java
index 6234f02..1bf4034 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java
@@ -124,11 +124,6 @@
 
   String contactFieldFullName();
   String contactFieldEmail();
-  String contactPrivacyDetailsHtml();
-  String contactFieldAddress();
-  String contactFieldCountry();
-  String contactFieldPhone();
-  String contactFieldFax();
   String buttonOpenRegisterNewEmail();
   String buttonSendRegisterNewEmail();
   String buttonCancel();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties
index eee7a60..6d91807 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties
@@ -128,18 +128,6 @@
 
 contactFieldFullName = Full Name
 contactFieldEmail = Preferred Email
-contactPrivacyDetailsHtml = \
-  <b>The following offline contact information is stored encrypted.</b><br />\
-  <br />\
-  Contact information will only be made available to administrators if it is \
-  necessary to reach you through non-email based communication.  Received data \
-  is stored encrypted with a strong public/private key pair algorithm, and \
-  this site does not have the private key.  Once saved, you will be unable to \
-  retrieve previously stored contact details.
-contactFieldAddress = Mailing Address
-contactFieldCountry = Country
-contactFieldPhone = Phone Number
-contactFieldFax = Fax Number
 buttonOpenRegisterNewEmail = Register New Email ...
 buttonSendRegisterNewEmail = Register
 buttonCancel = Cancel
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.java
index e55be79..68a99e0 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.java
@@ -16,12 +16,9 @@
 
 import com.google.gwt.i18n.client.Messages;
 
-import java.util.Date;
-
 public interface AccountMessages extends Messages {
   String lines(short cnt);
   String rowsPerPage(short cnt);
   String changeScreenServerDefault(String d);
   String enterIAGREE(String iagree);
-  String contactOnFile(Date lastDate);
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.properties
index 994c236..a8d61cf 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.properties
@@ -1,7 +1,4 @@
 lines = {0} lines
 rowsPerPage = {0} rows per page
-
 changeScreenServerDefault = Server Default ({0})
-
 enterIAGREE = (enter {0} in the box to the left)
-contactOnFile = Contact information last updated on {0,date,medium} at {0,time,short}.
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelFull.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelFull.java
deleted file mode 100644
index 14bf346..0000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelFull.java
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.client.account;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.info.AccountInfo;
-import com.google.gerrit.client.ui.OnEditEnabler;
-import com.google.gerrit.reviewdb.client.ContactInformation;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwtexpui.globalkey.client.NpTextArea;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-
-import java.sql.Timestamp;
-import java.util.Date;
-
-class ContactPanelFull extends ContactPanelShort {
-  private Label hasContact;
-  private NpTextArea addressTxt;
-  private NpTextBox countryTxt;
-  private NpTextBox phoneTxt;
-  private NpTextBox faxTxt;
-
-  @Override
-  protected void onInitUI() {
-    super.onInitUI();
-
-    addressTxt = new NpTextArea();
-    addressTxt.setVisibleLines(4);
-    addressTxt.setCharacterWidth(60);
-
-    countryTxt = new NpTextBox();
-    countryTxt.setVisibleLength(40);
-    countryTxt.setMaxLength(40);
-
-    phoneTxt = new NpTextBox();
-    phoneTxt.setVisibleLength(30);
-    phoneTxt.setMaxLength(30);
-
-    faxTxt = new NpTextBox();
-    faxTxt.setVisibleLength(30);
-    faxTxt.setMaxLength(30);
-
-    final Grid infoSecure = new Grid(4, 2);
-    infoSecure.setStyleName(Gerrit.RESOURCES.css().infoBlock());
-    infoSecure.addStyleName(Gerrit.RESOURCES.css().accountInfoBlock());
-
-    final HTML privhtml = new HTML(Util.C.contactPrivacyDetailsHtml());
-    privhtml.setStyleName(Gerrit.RESOURCES.css().accountContactPrivacyDetails());
-
-    hasContact = new Label();
-    hasContact.setStyleName(Gerrit.RESOURCES.css().accountContactOnFile());
-    hasContact.setVisible(false);
-
-    if (Gerrit.info().hasContactStore()) {
-      body.add(privhtml);
-      body.add(hasContact);
-      body.add(infoSecure);
-    }
-
-    row(infoSecure, 0, Util.C.contactFieldAddress(), addressTxt);
-    row(infoSecure, 1, Util.C.contactFieldCountry(), countryTxt);
-    row(infoSecure, 2, Util.C.contactFieldPhone(), phoneTxt);
-    row(infoSecure, 3, Util.C.contactFieldFax(), faxTxt);
-
-    infoSecure.getCellFormatter().addStyleName(0, 0, Gerrit.RESOURCES.css().topmost());
-    infoSecure.getCellFormatter().addStyleName(0, 1, Gerrit.RESOURCES.css().topmost());
-    infoSecure.getCellFormatter().addStyleName(3, 0, Gerrit.RESOURCES.css().bottomheader());
-
-    final OnEditEnabler sbl = new OnEditEnabler(save);
-    sbl.listenTo(addressTxt);
-    sbl.listenTo(countryTxt);
-    sbl.listenTo(phoneTxt);
-    sbl.listenTo(faxTxt);
-  }
-
-  @Override
-  protected void display(AccountInfo account) {
-    super.display(account);
-    displayHasContact(account);
-    addressTxt.setText("");
-    countryTxt.setText("");
-    phoneTxt.setText("");
-    faxTxt.setText("");
-  }
-
-  private void displayHasContact(AccountInfo account) {
-    if (account.contactFiledOn() != null) {
-      Timestamp dt = account.contactFiledOn();
-      hasContact.setText(Util.M.contactOnFile(new Date(dt.getTime())));
-      hasContact.setVisible(true);
-    } else {
-      hasContact.setVisible(false);
-    }
-  }
-
-  @Override
-  void onSaveSuccess(AccountInfo account) {
-    super.onSaveSuccess(account);
-    displayHasContact(account);
-  }
-
-  @Override
-  ContactInformation toContactInformation() {
-    final ContactInformation info;
-    if (Gerrit.info().hasContactStore()) {
-      info = new ContactInformation();
-      info.setAddress(addressTxt.getText());
-      info.setCountry(countryTxt.getText());
-      info.setPhoneNumber(phoneTxt.getText());
-      info.setFaxNumber(faxTxt.getText());
-    } else {
-      info = null;
-    }
-    return info;
-  }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java
index 2fd53bf..901fe96 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java
@@ -25,7 +25,6 @@
 import com.google.gerrit.common.errors.EmailException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Account.FieldName;
-import com.google.gerrit.reviewdb.client.ContactInformation;
 import com.google.gwt.core.client.JsArray;
 import com.google.gwt.event.dom.client.ChangeEvent;
 import com.google.gwt.event.dom.client.ChangeHandler;
@@ -356,11 +355,10 @@
       newEmail = currentEmail;
     }
 
-    final ContactInformation info = toContactInformation();
     save.setEnabled(false);
     registerNewEmail.setEnabled(false);
 
-    Util.ACCOUNT_SEC.updateContact(newName, newEmail, info,
+    Util.ACCOUNT_SEC.updateContact(newName, newEmail,
         new GerritCallback<Account>() {
           @Override
           public void onSuccess(Account result) {
@@ -388,10 +386,6 @@
     display(me);
   }
 
-  ContactInformation toContactInformation() {
-    return null;
-  }
-
   private int emailListIndexOf(String value) {
     for (int i = 0; i < emailPick.getItemCount(); i++) {
       if (value.equalsIgnoreCase(emailPick.getValue(i))) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyContactInformationScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyContactInformationScreen.java
index c542511..4c1016a 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyContactInformationScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyContactInformationScreen.java
@@ -15,12 +15,12 @@
 package com.google.gerrit.client.account;
 
 public class MyContactInformationScreen extends SettingsScreen {
-  private ContactPanelFull panel;
+  private ContactPanelShort panel;
 
   @Override
   protected void onInitUI() {
     super.onInitUI();
-    panel = new ContactPanelFull() {
+    panel = new ContactPanelShort() {
       @Override
       void display() {
         MyContactInformationScreen.this.display();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGpgKeysScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGpgKeysScreen.java
index 6d88e38..dc35b1e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGpgKeysScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGpgKeysScreen.java
@@ -252,7 +252,7 @@
           toDelete.add(getRowItem(row).fingerprint());
         }
       }
-      AccountApi.removeGpgKeys("self", toDelete,
+      AccountApi.deleteGpgKeys("self", toDelete,
           new GerritCallback<NativeMap<GpgKeyInfo>>() {
             @Override
             public void onSuccess(NativeMap<GpgKeyInfo> result) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/NewAgreementScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/NewAgreementScreen.java
index 95ea317..14f8e2f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/NewAgreementScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/NewAgreementScreen.java
@@ -23,7 +23,6 @@
 import com.google.gerrit.common.PageLinks;
 import com.google.gerrit.common.data.AgreementInfo;
 import com.google.gerrit.common.data.ContributorAgreement;
-import com.google.gerrit.reviewdb.client.Account;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.ClickHandler;
@@ -42,7 +41,6 @@
 import com.google.gwt.user.client.ui.RadioButton;
 import com.google.gwt.user.client.ui.VerticalPanel;
 import com.google.gwtexpui.globalkey.client.NpTextBox;
-import com.google.gwtjsonrpc.common.AsyncCallback;
 import com.google.gwtjsonrpc.common.VoidResult;
 
 import java.util.HashSet;
@@ -60,9 +58,6 @@
   private Panel agreementGroup;
   private HTML agreementHtml;
 
-  private Panel contactGroup;
-  private ContactPanelFull contactPanel;
-
   private Panel finalGroup;
   private NpTextBox yesIAgreeBox;
   private Button submit;
@@ -117,11 +112,6 @@
     agreementGroup.add(agreementHtml);
     formBody.add(agreementGroup);
 
-    contactGroup = new FlowPanel();
-    contactGroup
-        .add(new SmallHeading(Util.C.newAgreementReviewContactHeading()));
-    formBody.add(contactGroup);
-
     finalGroup = new VerticalPanel();
     finalGroup.add(new SmallHeading(Util.C.newAgreementCompleteHeading()));
     final FlowPanel fp = new FlowPanel();
@@ -157,7 +147,6 @@
   private void renderSelf() {
     current = null;
     agreementGroup.setVisible(false);
-    contactGroup.setVisible(false);
     finalGroup.setVisible(false);
     radios.clear();
 
@@ -206,21 +195,7 @@
       yesIAgreeBox.setFocus(true);
       return;
     }
-
-    if (contactGroup.isVisible()) {
-      contactPanel.doSave(new AsyncCallback<Account>() {
-        @Override
-        public void onSuccess(Account result) {
-          doEnterAgreement();
-        }
-
-        @Override
-        public void onFailure(Throwable caught) {
-        }
-      });
-    } else {
-      doEnterAgreement();
-    }
+    doEnterAgreement();
   }
 
   private void doEnterAgreement() {
@@ -275,13 +250,6 @@
       agreementGroup.setVisible(false);
     }
 
-    if (contactPanel == null && cla.isRequireContactInformation()) {
-      contactPanel = new ContactPanelFull();
-      contactGroup.add(contactPanel);
-      contactPanel.hideSaveButton();
-    }
-    contactGroup.setVisible(
-        cla.isRequireContactInformation() && cla.getAutoVerify() != null);
     finalGroup.setVisible(cla.getAutoVerify() != null);
     yesIAgreeBox.setText("");
     submit.setEnabled(false);
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
index 0914efd..3ec3d28 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
@@ -1031,18 +1031,6 @@
 .accountInfoBlock .gwt-Button {
   margin-left: 10px;
 }
-.accountContactPrivacyDetails {
-  margin-left: 10px;
-  margin-top: 5px;
-  margin-bottom: 5px;
-  width: 40em;
-}
-.accountContactOnFile {
-  margin-left: 10px;
-  margin-top: 5px;
-  margin-bottom: 5px;
-  font-weight: bold;
-}
 
 .addWatchPanel {
   margin-top: 10px;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/NpIntTextBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/NpIntTextBox.java
index b57a58e..0933153 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/NpIntTextBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/NpIntTextBox.java
@@ -89,6 +89,7 @@
       try {
         intValue = Integer.parseInt(getText());
       } catch (NumberFormatException e) {
+        // Ignored
       }
     }
     return intValue;
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectListPopup.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectListPopup.java
index 86c31f2..8b58403 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectListPopup.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectListPopup.java
@@ -168,11 +168,8 @@
     } else {
       popup.setPopupPositionAndShow(popupPosition);
       GlobalKey.dialog(popup);
-      try {
-        GlobalKey.addApplication(popup, new HidePopupPanelCommand(0,
-            KeyCodes.KEY_ESCAPE, popup));
-      } catch (Throwable e) {
-      }
+      GlobalKey.addApplication(popup, new HidePopupPanelCommand(0,
+          KeyCodes.KEY_ESCAPE, popup));
       projectsTab.setRegisterKeys(true);
       projectsTab.finishDisplay();
       filterTxt.setFocus(true);
diff --git a/gerrit-httpd/BUCK b/gerrit-httpd/BUCK
index 3345018..b29bd2a 100644
--- a/gerrit-httpd/BUCK
+++ b/gerrit-httpd/BUCK
@@ -34,7 +34,7 @@
     '//lib/jgit:jgit',
     '//lib/jgit:jgit-servlet',
     '//lib/log:api',
-    '//lib/lucene:core',
+    '//lib/lucene:core-and-backward-codecs',
   ],
   provided_deps = ['//lib:servlet-api-3_1'],
   visibility = ['PUBLIC'],
@@ -64,6 +64,7 @@
     '//lib:truth',
     '//lib/easymock:easymock',
     '//lib/guice:guice',
+    '//lib/guice:guice-servlet',
     '//lib/jgit:jgit',
     '//lib/jgit:junit',
   ],
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/AllRequestFilter.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/AllRequestFilter.java
index 3b48b65..bcc5842 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/AllRequestFilter.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/AllRequestFilter.java
@@ -15,8 +15,11 @@
 package com.google.gerrit.httpd;
 
 import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.server.plugins.Plugin;
+import com.google.gerrit.server.plugins.StopPluginListener;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
+import com.google.inject.internal.UniqueAnnotations;
 import com.google.inject.servlet.ServletModule;
 
 import java.io.IOException;
@@ -37,17 +40,62 @@
       protected void configureServlets() {
         DynamicSet.setOf(binder(), AllRequestFilter.class);
         filter("/*").through(FilterProxy.class);
+
+        bind(StopPluginListener.class)
+          .annotatedWith(UniqueAnnotations.create())
+          .to(FilterProxy.class);
       }
     };
   }
 
   @Singleton
-  static class FilterProxy implements Filter {
+  static class FilterProxy implements Filter, StopPluginListener {
     private final DynamicSet<AllRequestFilter> filters;
 
+    private DynamicSet<AllRequestFilter> initializedFilters;
+    private FilterConfig filterConfig;
+
     @Inject
     FilterProxy(DynamicSet<AllRequestFilter> filters) {
       this.filters = filters;
+      this.initializedFilters = new DynamicSet<>();
+      this.filterConfig = null;
+    }
+
+    /**
+     * Initializes a filter if needed
+     *
+     * @param filter The filter that should get initialized
+     * @return {@code true} iff filter is now initialized
+     * @throws ServletException if filter itself fails to init
+     */
+    private synchronized boolean initFilterIfNeeded(AllRequestFilter filter)
+        throws ServletException {
+      boolean ret = true;
+      if (filters.contains(filter)) {
+        // Regardless of whether or not the caller checked filter's
+        // containment in initializedFilters, we better re-check as we're now
+        // synchronized.
+        if (!initializedFilters.contains(filter)) {
+          filter.init(filterConfig);
+          initializedFilters.add(filter);
+        }
+      } else {
+        ret = false;
+      }
+      return ret;
+    }
+
+    private synchronized void cleanUpInitializedFilters() {
+      Iterable<AllRequestFilter> filtersToCleanUp  = initializedFilters;
+      initializedFilters = new DynamicSet<>();
+      for (AllRequestFilter filter : filtersToCleanUp) {
+        if (filters.contains(filter)) {
+          initializedFilters.add(filter);
+        } else {
+          filter.destroy();
+        }
+      }
     }
 
     @Override
@@ -58,28 +106,67 @@
         @Override
         public void doFilter(ServletRequest req, ServletResponse res)
             throws IOException, ServletException {
-          if (itr.hasNext()) {
-            itr.next().doFilter(req, res, this);
-          } else {
-            last.doFilter(req, res);
+          while (itr.hasNext()) {
+            AllRequestFilter filter = itr.next();
+            // To avoid {@code synchronized} on the the whole filtering (and
+            // thereby killing concurrency), we start the below disjunction
+            // with an unsynchronized check for containment. This
+            // unsynchronized check is always correct if no filters got
+            // initialized/cleaned concurrently behind our back.
+            // The case of concurrently initialized filters is saved by the
+            // call to initFilterIfNeeded. So that's fine too.
+            // The case of concurrently cleaned filters between the {@code if}
+            // condition and the call to {@code doFilter} is not saved by
+            // anything. If a filter is getting removed concurrently while
+            // another thread is in those two lines, doFilter might (but need
+            // not) fail.
+            //
+            // Since this failure only occurs if a filter is deleted
+            // (e.g.: a plugin reloaded) exactly when a thread is in those
+            // two lines, and it only breaks a single request, we're ok with
+            // it, given that this is really both really improbable and also
+            // the "proper" fix for it would basically kill concurrency of
+            // webrequests.
+            if (initializedFilters.contains(filter)
+                || initFilterIfNeeded(filter)) {
+              filter.doFilter(req, res, this);
+              return;
+            }
           }
+          last.doFilter(req, res);
         }
       }.doFilter(req, res);
     }
 
     @Override
     public void init(FilterConfig config) throws ServletException {
+      // Plugins that provide AllRequestFilters might get loaded later at
+      // runtime, long after this init method had been called. To allow to
+      // correctly init such plugins' AllRequestFilters, we keep the
+      // FilterConfig around, and reuse it to lazy init the AllRequestFilters.
+      filterConfig = config;
+
       for (AllRequestFilter f: filters) {
-        f.init(config);
+        initFilterIfNeeded(f);
       }
     }
 
     @Override
-    public void destroy() {
-      for (AllRequestFilter f: filters) {
-        f.destroy();
+    public synchronized void destroy() {
+      Iterable<AllRequestFilter> filtersToDestroy  = initializedFilters;
+      initializedFilters = new DynamicSet<>();
+      for (AllRequestFilter filter: filtersToDestroy) {
+        filter.destroy();
       }
     }
+
+    @Override
+    public void onStopPlugin(Plugin plugin) {
+      // In order to allow properly garbage collection, we need to scrub
+      // initializedFilters clean of filters stemming from plugins as they
+      // get unloaded.
+      cleanUpInitializedFilters();
+    }
   }
 
   @Override
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/CatServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/CatServlet.java
index 65f42e5..3cbb68b 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/CatServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/CatServlet.java
@@ -49,6 +49,7 @@
 import org.eclipse.jgit.treewalk.TreeWalk;
 import org.eclipse.jgit.util.NB;
 
+import java.io.FilterOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
@@ -248,10 +249,21 @@
     rsp.setDateHeader("Last-Modified", when);
     CacheHeaders.setNotCacheable(rsp);
 
-    OutputStream out;
-    ZipOutputStream zo;
+    try (OutputStream out = openOutputStream(
+        req, rsp, blobLoader, fromCommit, when, path, suffix, raw)) {
+      if (raw != null) {
+        out.write(raw);
+      } else {
+        blobLoader.copyTo(out);
+      }
+    }
+  }
 
-    final MimeType contentType = registry.getMimeType(path, raw);
+  private OutputStream openOutputStream(HttpServletRequest req,
+      HttpServletResponse rsp, ObjectLoader blobLoader,
+      RevCommit fromCommit, long when, String path, String suffix, byte[] raw)
+      throws IOException {
+    MimeType contentType = registry.getMimeType(path, raw);
     if (!registry.isSafeInline(contentType)) {
       // The content may not be safe to transmit inline, as a browser might
       // interpret it as HTML or JavaScript hosted by this site. Such code
@@ -266,33 +278,30 @@
       rsp.setHeader("Content-Disposition", "attachment; filename=\""
           + safeFileName(path, suffix) + ".zip" + "\"");
 
-      zo = new ZipOutputStream(rsp.getOutputStream());
+      final ZipOutputStream zo = new ZipOutputStream(rsp.getOutputStream());
 
-      final ZipEntry e = new ZipEntry(safeFileName(path, rand(req, suffix)));
+      ZipEntry e = new ZipEntry(safeFileName(path, rand(req, suffix)));
       e.setComment(fromCommit.name() + ":" + path);
       e.setSize(blobLoader.getSize());
       e.setTime(when);
       zo.putNextEntry(e);
-      out = zo;
+      return new FilterOutputStream(zo) {
+        @Override
+        public void close() throws IOException {
+          try {
+            zo.closeEntry();
+          } finally {
+            super.close();
+          }
+        }
+      };
 
     } else {
       rsp.setContentType(contentType.toString());
       rsp.setHeader("Content-Length", "" + blobLoader.getSize());
 
-      out = rsp.getOutputStream();
-      zo = null;
+      return rsp.getOutputStream();
     }
-
-    if (raw != null) {
-      out.write(raw);
-    } else {
-      blobLoader.copyTo(out);
-    }
-
-    if (zo != null) {
-      zo.closeEntry();
-    }
-    out.close();
   }
 
   private static String safeFileName(String fileName, final String suffix) {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
index 44bf7aa..2f2575d 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
@@ -1062,10 +1062,10 @@
     int maxSize = base64MaxSize(bin.getContentLength());
     int estSize = Math.min(base64MaxSize(HEAP_EST_SIZE), maxSize);
     TemporaryBuffer.Heap buf = heap(estSize, maxSize);
-    OutputStream encoded = BaseEncoding.base64().encodingStream(
-        new OutputStreamWriter(buf, ISO_8859_1));
-    bin.writeTo(encoded);
-    encoded.close();
+    try (OutputStream encoded = BaseEncoding.base64().encodingStream(
+        new OutputStreamWriter(buf, ISO_8859_1))) {
+      bin.writeTo(encoded);
+    }
     return asBinaryResult(buf);
   }
 
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountModule.java
index d0fb504..62778eb 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountModule.java
@@ -14,9 +14,9 @@
 
 package com.google.gerrit.httpd.rpc.account;
 
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.httpd.rpc.RpcServletModule;
 import com.google.gerrit.httpd.rpc.UiRpcModule;
-import com.google.gerrit.server.config.FactoryModule;
 
 public class AccountModule extends RpcServletModule {
   public AccountModule() {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java
index 55586b0..e0d63c8 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java
@@ -17,10 +17,8 @@
 import com.google.common.base.Strings;
 import com.google.gerrit.audit.AuditService;
 import com.google.gerrit.common.ChangeHooks;
-import com.google.gerrit.common.TimeUtil;
 import com.google.gerrit.common.data.AccountSecurity;
 import com.google.gerrit.common.data.ContributorAgreement;
-import com.google.gerrit.common.errors.ContactInformationStoreException;
 import com.google.gerrit.common.errors.NoSuchEntityException;
 import com.google.gerrit.common.errors.PermissionDeniedException;
 import com.google.gerrit.httpd.rpc.BaseServiceImplementation;
@@ -28,7 +26,6 @@
 import com.google.gerrit.reviewdb.client.AccountExternalId;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.client.AccountGroupMember;
-import com.google.gerrit.reviewdb.client.ContactInformation;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
@@ -36,7 +33,6 @@
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.GroupCache;
 import com.google.gerrit.server.account.Realm;
-import com.google.gerrit.server.contact.ContactStore;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gwtjsonrpc.common.AsyncCallback;
 import com.google.gwtjsonrpc.common.VoidResult;
@@ -50,13 +46,11 @@
 
 class AccountSecurityImpl extends BaseServiceImplementation implements
     AccountSecurity {
-  private final ContactStore contactStore;
   private final Realm realm;
   private final ProjectCache projectCache;
   private final Provider<IdentifiedUser> user;
   private final AccountByEmailCache byEmailCache;
   private final AccountCache accountCache;
-  private final boolean useContactInfo;
 
   private final DeleteExternalIds.Factory deleteExternalIdsFactory;
   private final ExternalIdDetailFactory.Factory externalIdDetailFactory;
@@ -67,7 +61,7 @@
 
   @Inject
   AccountSecurityImpl(final Provider<ReviewDb> schema,
-      final Provider<CurrentUser> currentUser, final ContactStore cs,
+      final Provider<CurrentUser> currentUser,
       final Realm r, final Provider<IdentifiedUser> u,
       final ProjectCache pc,
       final AccountByEmailCache abec, final AccountCache uac,
@@ -76,16 +70,12 @@
       final ChangeHooks hooks, final GroupCache groupCache,
       final AuditService auditService) {
     super(schema, currentUser);
-    contactStore = cs;
     realm = r;
     user = u;
     projectCache = pc;
     byEmailCache = abec;
     accountCache = uac;
     this.auditService = auditService;
-
-    useContactInfo = contactStore != null && contactStore.isEnabled();
-
     this.deleteExternalIdsFactory = deleteExternalIdsFactory;
     this.externalIdDetailFactory = externalIdDetailFactory;
     this.hooks = hooks;
@@ -105,7 +95,7 @@
 
   @Override
   public void updateContact(final String name, final String emailAddr,
-      final ContactInformation info, final AsyncCallback<Account> callback) {
+      final AsyncCallback<Account> callback) {
     run(callback, new Action<Account>() {
       @Override
       public Account run(ReviewDb db) throws OrmException, Failure {
@@ -120,19 +110,6 @@
           throw new Failure(new PermissionDeniedException("Email address must be verified"));
         }
         me.setPreferredEmail(Strings.emptyToNull(emailAddr));
-        if (useContactInfo) {
-          if (ContactInformation.hasAddress(info)
-              || (me.isContactFiled() && ContactInformation.hasData(info))) {
-            me.setContactFiled(TimeUtil.nowTs());
-          }
-          if (ContactInformation.hasData(info)) {
-            try {
-              contactStore.store(me, info);
-            } catch (ContactInformationStoreException e) {
-              throw new Failure(e);
-            }
-          }
-        }
         db.accounts().update(Collections.singleton(me));
         if (!eq(oldEmail, me.getPreferredEmail())) {
           byEmailCache.evict(oldEmail);
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AgreementInfoFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AgreementInfoFactory.java
index 712f5a2..0fff8ce 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AgreementInfoFactory.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AgreementInfoFactory.java
@@ -34,7 +34,7 @@
 import java.util.Map;
 
 class AgreementInfoFactory extends Handler<AgreementInfo> {
-  private final Logger log = LoggerFactory.getLogger(getClass());
+  private static final Logger log = LoggerFactory.getLogger(AgreementInfoFactory.class);
 
   interface Factory {
     AgreementInfoFactory create();
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeModule.java
index 840137a..d8adfe3 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeModule.java
@@ -14,9 +14,9 @@
 
 package com.google.gerrit.httpd.rpc.changedetail;
 
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.httpd.rpc.RpcServletModule;
 import com.google.gerrit.httpd.rpc.UiRpcModule;
-import com.google.gerrit.server.config.FactoryModule;
 
 public class ChangeModule extends RpcServletModule {
   public ChangeModule() {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java
index bd5f940..a89f52e 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java
@@ -14,9 +14,9 @@
 
 package com.google.gerrit.httpd.rpc.project;
 
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.httpd.rpc.RpcServletModule;
 import com.google.gerrit.httpd.rpc.UiRpcModule;
-import com.google.gerrit.server.config.FactoryModule;
 
 public class ProjectModule extends RpcServletModule {
   public ProjectModule() {
diff --git a/gerrit-httpd/src/test/java/com/google/gerrit/httpd/AllRequestFilterFilterProxyTest.java b/gerrit-httpd/src/test/java/com/google/gerrit/httpd/AllRequestFilterFilterProxyTest.java
new file mode 100644
index 0000000..10a35a8
--- /dev/null
+++ b/gerrit-httpd/src/test/java/com/google/gerrit/httpd/AllRequestFilterFilterProxyTest.java
@@ -0,0 +1,366 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.httpd;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.newCapture;
+
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.registration.ReloadableRegistrationHandle;
+import com.google.gerrit.server.plugins.Plugin;
+import com.google.inject.Key;
+import com.google.inject.util.Providers;
+
+import org.easymock.Capture;
+import org.easymock.EasyMockSupport;
+import org.easymock.IMocksControl;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class AllRequestFilterFilterProxyTest {
+  /**
+   * Set of filters for FilterProxy
+   * <p/>
+   * This set is used to as set of filters when fetching an
+   * {@link AllRequestFilter.FilterProxy} instance through
+   * {@link #getFilterProxy()}.
+   */
+  private DynamicSet<AllRequestFilter> filters;
+
+  @Before
+  public void setUp() throws Exception {
+    // Force starting each test with an initially empty set of filters.
+    // Filters get added by the tests themselves.
+    filters = new DynamicSet<>();
+  }
+
+  // The wrapping of {@link #getFilterProxy()} and
+  // {@link #addFilter(AllRequestFilter)} into separate methods may seem
+  // overengineered at this point. However, if in the future we decide to not
+  // test the inner class directly, but rather test from the outside using
+  // Guice Injectors, it is now sufficient to change only those two methods,
+  // and we need not mess with the individual tests.
+
+  /**
+   * Obtain a FilterProxy with a known DynamicSet of filters
+   * <p/>
+   * The returned {@link AllRequestFilter.FilterProxy} can have new filters
+   * added dynamically by calling {@link #addFilter(AllRequestFilter)}.
+   */
+  private AllRequestFilter.FilterProxy getFilterProxy() {
+    return new AllRequestFilter.FilterProxy(filters);
+  }
+
+  /**
+   * Add a filter to created FilterProxy instances
+   * <p/>
+   * This method adds the given filter to all
+   * {@link AllRequestFilter.FilterProxy} instances created by
+   * {@link #getFilterProxy()}.
+   */
+  private ReloadableRegistrationHandle<AllRequestFilter> addFilter(
+      final AllRequestFilter filter) {
+    Key<AllRequestFilter> key = Key.get(AllRequestFilter.class);
+    return filters.add(key, Providers.of(filter));
+  }
+
+  @Test
+  public void testNoFilters() throws Exception {
+    EasyMockSupport ems = new EasyMockSupport();
+
+    FilterConfig config = ems.createMock(FilterConfig.class);
+    HttpServletRequest req = ems.createMock(HttpServletRequest.class);
+    HttpServletResponse res = ems.createMock(HttpServletResponse.class);
+
+    FilterChain chain = ems.createMock(FilterChain.class);
+    chain.doFilter(req, res);
+
+    ems.replayAll();
+
+    AllRequestFilter.FilterProxy filterProxy = getFilterProxy();
+
+    filterProxy.init(config);
+    filterProxy.doFilter(req, res, chain);
+    filterProxy.destroy();
+
+    ems.verifyAll();
+  }
+
+  @Test
+  public void testSingleFilterNoBubbling() throws Exception {
+    EasyMockSupport ems = new EasyMockSupport();
+
+    FilterConfig config = ems.createMock("config", FilterConfig.class);
+    HttpServletRequest req = ems.createMock("req", HttpServletRequest.class);
+    HttpServletResponse res = ems.createMock("res", HttpServletResponse.class);
+
+    FilterChain chain = ems.createMock("chain", FilterChain.class);
+
+    AllRequestFilter filter = ems.createStrictMock("filter", AllRequestFilter.class);
+    filter.init(config);
+    filter.doFilter(eq(req), eq(res), anyObject(FilterChain.class));
+    filter.destroy();
+
+    ems.replayAll();
+
+    AllRequestFilter.FilterProxy filterProxy = getFilterProxy();
+    addFilter(filter);
+
+    filterProxy.init(config);
+    filterProxy.doFilter(req, res, chain);
+    filterProxy.destroy();
+
+    ems.verifyAll();
+  }
+
+  @Test
+  public void testSingleFilterBubbling() throws Exception {
+    EasyMockSupport ems = new EasyMockSupport();
+
+    FilterConfig config = ems.createMock(FilterConfig.class);
+    HttpServletRequest req = ems.createMock(HttpServletRequest.class);
+    HttpServletResponse res = ems.createMock(HttpServletResponse.class);
+
+    IMocksControl mockControl = ems.createStrictControl();
+    FilterChain chain = mockControl.createMock(FilterChain.class);
+
+    Capture<FilterChain> capturedChain = newCapture();
+
+    AllRequestFilter filter = mockControl.createMock(AllRequestFilter.class);
+    filter.init(config);
+    filter.doFilter(eq(req), eq(res), capture(capturedChain));
+    chain.doFilter(req, res);
+    filter.destroy();
+
+    ems.replayAll();
+
+    AllRequestFilter.FilterProxy filterProxy = getFilterProxy();
+    addFilter(filter);
+
+    filterProxy.init(config);
+    filterProxy.doFilter(req, res, chain);
+    capturedChain.getValue().doFilter(req, res);
+    filterProxy.destroy();
+
+    ems.verifyAll();
+  }
+
+  @Test
+  public void testTwoFiltersNoBubbling() throws Exception {
+    EasyMockSupport ems = new EasyMockSupport();
+
+    FilterConfig config = ems.createMock(FilterConfig.class);
+    HttpServletRequest req = ems.createMock(HttpServletRequest.class);
+    HttpServletResponse res = ems.createMock(HttpServletResponse.class);
+
+    IMocksControl mockControl = ems.createStrictControl();
+    FilterChain chain = mockControl.createMock(FilterChain.class);
+
+    AllRequestFilter filterA = mockControl.createMock(AllRequestFilter.class);
+
+    AllRequestFilter filterB = mockControl.createMock(AllRequestFilter.class);
+    filterA.init(config);
+    filterB.init(config);
+    filterA.doFilter(eq(req), eq(res), anyObject(FilterChain.class));
+    filterA.destroy();
+    filterB.destroy();
+
+    ems.replayAll();
+
+    AllRequestFilter.FilterProxy filterProxy = getFilterProxy();
+    addFilter(filterA);
+    addFilter(filterB);
+
+    filterProxy.init(config);
+    filterProxy.doFilter(req, res, chain);
+    filterProxy.destroy();
+
+    ems.verifyAll();
+  }
+
+  @Test
+  public void testTwoFiltersBubbling() throws Exception {
+    EasyMockSupport ems = new EasyMockSupport();
+
+    FilterConfig config = ems.createMock(FilterConfig.class);
+    HttpServletRequest req = ems.createMock(HttpServletRequest.class);
+    HttpServletResponse res = ems.createMock(HttpServletResponse.class);
+
+    IMocksControl mockControl = ems.createStrictControl();
+    FilterChain chain = mockControl.createMock(FilterChain.class);
+
+    Capture<FilterChain> capturedChainA = newCapture();
+    Capture<FilterChain> capturedChainB = newCapture();
+
+    AllRequestFilter filterA = mockControl.createMock(AllRequestFilter.class);
+    AllRequestFilter filterB = mockControl.createMock(AllRequestFilter.class);
+
+    filterA.init(config);
+    filterB.init(config);
+    filterA.doFilter(eq(req), eq(res), capture(capturedChainA));
+    filterB.doFilter(eq(req), eq(res), capture(capturedChainB));
+    chain.doFilter(req, res);
+    filterA.destroy();
+    filterB.destroy();
+
+    ems.replayAll();
+
+    AllRequestFilter.FilterProxy filterProxy = getFilterProxy();
+    addFilter(filterA);
+    addFilter(filterB);
+
+    filterProxy.init(config);
+    filterProxy.doFilter(req, res, chain);
+    capturedChainA.getValue().doFilter(req, res);
+    capturedChainB.getValue().doFilter(req, res);
+    filterProxy.destroy();
+
+    ems.verifyAll();
+  }
+
+  @Test
+  public void testPostponedLoading() throws Exception {
+    EasyMockSupport ems = new EasyMockSupport();
+
+    FilterConfig config = ems.createMock(FilterConfig.class);
+    HttpServletRequest req1 = ems.createMock("req1", HttpServletRequest.class);
+    HttpServletRequest req2 = ems.createMock("req2", HttpServletRequest.class);
+    HttpServletResponse res1 = ems.createMock("res1", HttpServletResponse.class);
+    HttpServletResponse res2 = ems.createMock("res2", HttpServletResponse.class);
+
+    IMocksControl mockControl = ems.createStrictControl();
+    FilterChain chain = mockControl.createMock("chain", FilterChain.class);
+
+    Capture<FilterChain> capturedChainA1 = newCapture();
+    Capture<FilterChain> capturedChainA2 = newCapture();
+    Capture<FilterChain> capturedChainB = newCapture();
+
+    AllRequestFilter filterA = mockControl.createMock("filterA", AllRequestFilter.class);
+    AllRequestFilter filterB = mockControl.createMock("filterB", AllRequestFilter.class);
+
+    filterA.init(config);
+    filterA.doFilter(eq(req1), eq(res1), capture(capturedChainA1));
+    chain.doFilter(req1, res1);
+
+    filterA.doFilter(eq(req2), eq(res2), capture(capturedChainA2));
+    filterB.init(config); // <-- This is crucial part. filterB got loaded
+    // after filterProxy's init finished. Nonetheless filterB gets initialized.
+    filterB.doFilter(eq(req2), eq(res2), capture(capturedChainB));
+    chain.doFilter(req2, res2);
+
+    filterA.destroy();
+    filterB.destroy();
+
+    ems.replayAll();
+
+    AllRequestFilter.FilterProxy filterProxy = getFilterProxy();
+    addFilter(filterA);
+
+    filterProxy.init(config);
+    filterProxy.doFilter(req1, res1, chain);
+    capturedChainA1.getValue().doFilter(req1, res1);
+
+    addFilter(filterB); // <-- Adds filter after filterProxy's init got called.
+    filterProxy.doFilter(req2, res2, chain);
+    capturedChainA2.getValue().doFilter(req2, res2);
+    capturedChainB.getValue().doFilter(req2, res2);
+
+    filterProxy.destroy();
+
+    ems.verifyAll();
+  }
+
+  @Test
+  public void testDynamicUnloading() throws Exception {
+    EasyMockSupport ems = new EasyMockSupport();
+
+    FilterConfig config = ems.createMock(FilterConfig.class);
+    HttpServletRequest req1 = ems.createMock("req1", HttpServletRequest.class);
+    HttpServletRequest req2 = ems.createMock("req2", HttpServletRequest.class);
+    HttpServletRequest req3 = ems.createMock("req3", HttpServletRequest.class);
+    HttpServletResponse res1 = ems.createMock("res1", HttpServletResponse.class);
+    HttpServletResponse res2 = ems.createMock("res2", HttpServletResponse.class);
+    HttpServletResponse res3 = ems.createMock("res3", HttpServletResponse.class);
+
+    Plugin plugin = ems.createMock(Plugin.class);
+
+    IMocksControl mockControl = ems.createStrictControl();
+    FilterChain chain = mockControl.createMock("chain", FilterChain.class);
+
+    Capture<FilterChain> capturedChainA1 = newCapture();
+    Capture<FilterChain> capturedChainB1 = newCapture();
+    Capture<FilterChain> capturedChainB2 = newCapture();
+
+    AllRequestFilter filterA = mockControl.createMock("filterA", AllRequestFilter.class);
+    AllRequestFilter filterB = mockControl.createMock("filterB", AllRequestFilter.class);
+
+    filterA.init(config);
+    filterB.init(config);
+
+    filterA.doFilter(eq(req1), eq(res1), capture(capturedChainA1));
+    filterB.doFilter(eq(req1), eq(res1), capture(capturedChainB1));
+    chain.doFilter(req1, res1);
+
+    filterA.destroy(); // Cleaning up of filterA after it got unloaded
+
+    filterB.doFilter(eq(req2), eq(res2), capture(capturedChainB2));
+    chain.doFilter(req2, res2);
+
+    filterB.destroy(); // Cleaning up of filterA after it got unloaded
+
+    chain.doFilter(req3, res3);
+
+    ems.replayAll();
+
+    AllRequestFilter.FilterProxy filterProxy = getFilterProxy();
+    ReloadableRegistrationHandle<AllRequestFilter> handleFilterA =
+        addFilter(filterA);
+    ReloadableRegistrationHandle<AllRequestFilter> handleFilterB =
+        addFilter(filterB);
+
+    filterProxy.init(config);
+
+    // Request #1 with filterA and filterB
+    filterProxy.doFilter(req1, res1, chain);
+    capturedChainA1.getValue().doFilter(req1, res1);
+    capturedChainB1.getValue().doFilter(req1, res1);
+
+    // Unloading filterA
+    handleFilterA.remove();
+    filterProxy.onStopPlugin(plugin);
+
+    // Request #1 only with filterB
+    filterProxy.doFilter(req2, res2, chain);
+    capturedChainA1.getValue().doFilter(req2, res2);
+
+    // Unloading filterB
+    handleFilterB.remove();
+    filterProxy.onStopPlugin(plugin);
+
+    // Request #1 with no additional filters
+    filterProxy.doFilter(req3, res3, chain);
+
+    filterProxy.destroy();
+
+    ems.verifyAll();
+  }
+}
diff --git a/gerrit-lucene/BUCK b/gerrit-lucene/BUCK
index a146774..8ba7479 100644
--- a/gerrit-lucene/BUCK
+++ b/gerrit-lucene/BUCK
@@ -11,7 +11,7 @@
     '//gerrit-server:server',
     '//lib:gwtorm',
     '//lib:guava',
-    '//lib/lucene:core',
+    '//lib/lucene:core-and-backward-codecs',
   ],
   visibility = ['PUBLIC'],
 )
@@ -34,8 +34,7 @@
     '//lib/jgit:jgit',
     '//lib/log:api',
     '//lib/lucene:analyzers-common',
-    '//lib/lucene:backward-codecs',
-    '//lib/lucene:core',
+    '//lib/lucene:core-and-backward-codecs',
     '//lib/lucene:misc',
   ],
   visibility = ['PUBLIC'],
diff --git a/gerrit-pgm/BUCK b/gerrit-pgm/BUCK
index b52e7af..80c37f7 100644
--- a/gerrit-pgm/BUCK
+++ b/gerrit-pgm/BUCK
@@ -100,6 +100,7 @@
     ':init-api',
     ':util',
     '//gerrit-cache-h2:cache-h2',
+    '//gerrit-gpg:gpg',
     '//gerrit-lucene:lucene',
     '//gerrit-oauth:oauth',
     '//gerrit-openid:openid',
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
index 0a0d000..da87a20 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
@@ -20,6 +20,7 @@
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.MoreObjects;
 import com.google.gerrit.common.ChangeHookRunner;
+import com.google.gerrit.gpg.GpgModule;
 import com.google.gerrit.httpd.AllRequestFilter;
 import com.google.gerrit.httpd.GerritOptions;
 import com.google.gerrit.httpd.GetUserFilter;
@@ -53,8 +54,6 @@
 import com.google.gerrit.server.config.GerritGlobalModule;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.RestCacheAdminModule;
-import com.google.gerrit.server.contact.ContactStoreModule;
-import com.google.gerrit.server.contact.HttpContactStoreConnection;
 import com.google.gerrit.server.git.ChangeCacheImplModule;
 import com.google.gerrit.server.git.GarbageCollectionModule;
 import com.google.gerrit.server.git.ReceiveCommitsExecutorModule;
@@ -145,6 +144,7 @@
   private final LifecycleManager manager = new LifecycleManager();
   private Injector dbInjector;
   private Injector cfgInjector;
+  private Config config;
   private Injector sysInjector;
   private Injector sshInjector;
   private Injector webInjector;
@@ -274,14 +274,15 @@
       dbInjector = createDbInjector(MULTI_USER);
     }
     cfgInjector = createCfgInjector();
+    config = cfgInjector.getInstance(
+        Key.get(Config.class, GerritServerConfig.class));
     sysInjector = createSysInjector();
     sysInjector.getInstance(PluginGuiceEnvironment.class)
       .setDbCfgInjector(dbInjector, cfgInjector);
     manager.add(dbInjector, cfgInjector, sysInjector);
 
     if (!consoleLog) {
-      manager.add(ErrorLogFile.start(getSitePath(),
-          cfgInjector.getInstance(Key.get(Config.class, GerritServerConfig.class))));
+      manager.add(ErrorLogFile.start(getSitePath(), config));
     }
 
     sshd &= !sshdOff();
@@ -302,8 +303,7 @@
   }
 
   private boolean sshdOff() {
-    Config cfg = cfgInjector.getInstance(Key.get(Config.class, GerritServerConfig.class));
-    return new SshAddressesModule().getListenAddresses(cfg).isEmpty();
+    return new SshAddressesModule().getListenAddresses(config).isEmpty();
   }
 
   private String myVersion() {
@@ -337,6 +337,7 @@
     modules.add(new SignedTokenEmailTokenVerifier.Module());
     modules.add(new PluginRestApiModule());
     modules.add(new RestCacheAdminModule());
+    modules.add(new GpgModule(config));
     modules.add(createIndexModule());
     if (MoreObjects.firstNonNull(httpd, true)) {
       modules.add(new CanonicalWebUrlModule() {
@@ -427,11 +428,9 @@
     modules.add(RequestContextFilter.module());
     modules.add(AllRequestFilter.module());
     modules.add(H2CacheBasedWebSession.module());
-    modules.add(HttpContactStoreConnection.module());
     modules.add(sysInjector.getInstance(GitOverHttpModule.class));
     modules.add(sysInjector.getInstance(WebModule.class));
     modules.add(new HttpPluginModule());
-    modules.add(new ContactStoreModule());
     if (sshd) {
       modules.add(sshInjector.getInstance(WebSshGlueModule.class));
     } else {
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Gsql.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Gsql.java
index a5ce908..8e09578 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Gsql.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Gsql.java
@@ -16,10 +16,10 @@
 
 import static com.google.gerrit.server.schema.DataSourceProvider.Context.SINGLE_USER;
 
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.lifecycle.LifecycleManager;
 import com.google.gerrit.pgm.util.RuntimeShutdown;
 import com.google.gerrit.pgm.util.SiteProgram;
-import com.google.gerrit.server.config.FactoryModule;
 import com.google.gerrit.sshd.commands.QueryShell;
 import com.google.gerrit.sshd.commands.QueryShell.Factory;
 import com.google.inject.Injector;
@@ -52,6 +52,7 @@
         try {
           System.in.close();
         } catch (IOException e) {
+          // Ignored
         }
         manager.stop();
       }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
index e2775c1..a77bcfa 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
@@ -170,6 +170,7 @@
     try {
       proc.getOutputStream().close();
     } catch (IOException e) {
+      // Ignored
     }
 
     IoUtil.copyWithThread(proc.getInputStream(), System.err);
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/LocalUsernamesToLowerCase.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/LocalUsernamesToLowerCase.java
index c3cf914..224c75c 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/LocalUsernamesToLowerCase.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/LocalUsernamesToLowerCase.java
@@ -63,7 +63,7 @@
     try (ReviewDb db = database.open()) {
       todo = db.accountExternalIds().all().toList();
       synchronized (monitor) {
-        monitor.beginTask("Converting local username", todo.size());
+        monitor.beginTask("Converting local usernames", todo.size());
       }
     }
 
@@ -116,14 +116,7 @@
   private class Worker extends Thread {
     @Override
     public void run() {
-      final ReviewDb db;
-      try {
-        db = database.open();
-      } catch (OrmException e) {
-        e.printStackTrace();
-        return;
-      }
-      try {
+      try (ReviewDb db = database.open()){
         for (;;) {
           final AccountExternalId extId = next();
           if (extId == null) {
@@ -134,8 +127,8 @@
             monitor.update(1);
           }
         }
-      } finally {
-        db.close();
+      } catch (OrmException e) {
+        e.printStackTrace();
       }
     }
   }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Rulec.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Rulec.java
index 9cf6892..7134f49 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Rulec.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Rulec.java
@@ -16,11 +16,11 @@
 
 import static com.google.gerrit.server.schema.DataSourceProvider.Context.SINGLE_USER;
 
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.lifecycle.LifecycleManager;
 import com.google.gerrit.pgm.rules.PrologCompiler;
 import com.google.gerrit.pgm.util.SiteProgram;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.config.FactoryModule;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.inject.Inject;
 import com.google.inject.Injector;
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/Browser.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/Browser.java
index 98bee3d..41cb87e 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/Browser.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/Browser.java
@@ -75,6 +75,7 @@
         try {
           Thread.sleep(100);
         } catch (InterruptedException ie) {
+          // Ignored
         }
         continue;
       }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitAuth.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitAuth.java
index 5c716ef..a3a3f27 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitAuth.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitAuth.java
@@ -17,6 +17,7 @@
 import static com.google.gerrit.pgm.init.api.InitUtil.dnOf;
 
 import com.google.gerrit.pgm.init.api.ConsoleUI;
+import com.google.gerrit.pgm.init.api.InitFlags;
 import com.google.gerrit.pgm.init.api.InitStep;
 import com.google.gerrit.pgm.init.api.Section;
 import com.google.gerrit.reviewdb.client.AuthType;
@@ -24,28 +25,53 @@
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
+import org.eclipse.jgit.lib.Config;
+
 /** Initialize the {@code auth} configuration section. */
 @Singleton
 class InitAuth implements InitStep {
+  private static final String RECEIVE = "receive";
+  private static final String ENABLE_SIGNED_PUSH = "enableSignedPush";
+
+  private final Config cfg;
   private final ConsoleUI ui;
   private final Section auth;
   private final Section ldap;
+  private final Section receive;
+  private final Libraries libraries;
 
   @Inject
-  InitAuth(final ConsoleUI ui, final Section.Factory sections) {
+  InitAuth(InitFlags flags,
+      ConsoleUI ui,
+      Libraries libraries,
+      Section.Factory sections) {
+    this.cfg = flags.cfg;
     this.ui = ui;
     this.auth = sections.get("auth", null);
     this.ldap = sections.get("ldap", null);
+    this.receive = sections.get(RECEIVE, null);
+    this.libraries = libraries;
   }
 
   @Override
   public void run() {
     ui.header("User Authentication");
 
-    final AuthType auth_type =
-        auth.select("Authentication method", "type", AuthType.OPENID);
+    initAuthType();
+    if (auth.getSecure("registerEmailPrivateKey") == null) {
+      auth.setSecure("registerEmailPrivateKey", SignedToken.generateRandomKey());
+    }
+    if (auth.getSecure("restTokenPrivateKey") == null) {
+      auth.setSecure("restTokenPrivateKey", SignedToken.generateRandomKey());
+    }
 
-    switch (auth_type) {
+    initSignedPush();
+  }
+
+  private void initAuthType() {
+    AuthType authType =
+        auth.select("Authentication method", "type", AuthType.OPENID);
+    switch (authType) {
       case HTTP:
       case HTTP_LDAP: {
         String hdr = auth.get("httpHeader");
@@ -69,7 +95,7 @@
         break;
     }
 
-    switch (auth_type) {
+    switch (authType) {
       case LDAP:
       case LDAP_BIND:
       case HTTP_LDAP: {
@@ -103,13 +129,15 @@
       case OPENID_SSO:
         break;
     }
+  }
 
-    if (auth.getSecure("registerEmailPrivateKey") == null) {
-      auth.setSecure("registerEmailPrivateKey", SignedToken.generateRandomKey());
-    }
-
-    if (auth.getSecure("restTokenPrivateKey") == null) {
-      auth.setSecure("restTokenPrivateKey", SignedToken.generateRandomKey());
+  private void initSignedPush() {
+    boolean def = cfg.getBoolean(RECEIVE, ENABLE_SIGNED_PUSH, false);
+    boolean enable = ui.yesno(def, "Enable signed push support");
+    receive.set("enableSignedPush", Boolean.toString(enable));
+    if (enable) {
+      libraries.bouncyCastleProvider.downloadRequired();
+      libraries.bouncyCastlePGP.downloadRequired();
     }
   }
 
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitModule.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitModule.java
index 8d08520..57a1a30 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitModule.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitModule.java
@@ -14,10 +14,10 @@
 
 package com.google.gerrit.pgm.init;
 
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.pgm.init.api.InitFlags;
 import com.google.gerrit.pgm.init.api.InitStep;
 import com.google.gerrit.pgm.init.api.Section;
-import com.google.gerrit.server.config.FactoryModule;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.inject.binder.LinkedBindingBuilder;
 import com.google.inject.internal.UniqueAnnotations;
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/Libraries.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/Libraries.java
index 869e1c4..8ae712d 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/Libraries.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/Libraries.java
@@ -37,6 +37,7 @@
 
   private final Provider<LibraryDownloader> downloadProvider;
 
+  /* final */LibraryDownloader bouncyCastlePGP;
   /* final */LibraryDownloader bouncyCastleProvider;
   /* final */LibraryDownloader bouncyCastleSSL;
   /* final */LibraryDownloader db2Driver;
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java
index 06d907a..aea438c 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/api/AllProjectsConfig.java
@@ -18,7 +18,6 @@
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.git.GroupList;
 import com.google.gerrit.server.git.ProjectConfig;
-import com.google.gerrit.server.git.ValidationError;
 import com.google.gerrit.server.git.VersionedMetaData;
 import com.google.inject.Inject;
 
@@ -102,15 +101,8 @@
   }
 
   private GroupList readGroupList() throws IOException {
-    ValidationError.Sink errors = new ValidationError.Sink() {
-      @Override
-      public void error(ValidationError error) {
-        log.error("Error parsing file " + GroupList.FILE_NAME + ": " + error.getMessage());
-      }
-    };
-    String text = readUTF8(GroupList.FILE_NAME);
-
-    return GroupList.parse(text, errors);
+    return GroupList.parse(readUTF8(GroupList.FILE_NAME),
+        GroupList.createLoggerSink(GroupList.FILE_NAME, log));
   }
 
   @Override
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchGitModule.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchGitModule.java
index 11ab073..be573e6 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchGitModule.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchGitModule.java
@@ -16,9 +16,9 @@
 
 import com.google.gerrit.common.ChangeHooks;
 import com.google.gerrit.common.DisabledChangeHooks;
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
 import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.server.config.FactoryModule;
 import com.google.gerrit.server.git.GitModule;
 import com.google.gerrit.server.git.validators.CommitValidationListener;
 import com.google.gerrit.server.git.validators.CommitValidators;
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java
index 60d4acc..5d09305 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java
@@ -17,6 +17,7 @@
 import static com.google.inject.Scopes.SINGLETON;
 
 import com.google.common.cache.Cache;
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.reviewdb.client.AccountGroup;
@@ -39,7 +40,6 @@
 import com.google.gerrit.server.config.CanonicalWebUrlProvider;
 import com.google.gerrit.server.config.DisableReverseDnsLookup;
 import com.google.gerrit.server.config.DisableReverseDnsLookupProvider;
-import com.google.gerrit.server.config.FactoryModule;
 import com.google.gerrit.server.config.GitReceivePackGroups;
 import com.google.gerrit.server.config.GitUploadPackGroups;
 import com.google.gerrit.server.git.MergeUtil;
diff --git a/gerrit-pgm/src/main/resources/com/google/gerrit/pgm/init/libraries.config b/gerrit-pgm/src/main/resources/com/google/gerrit/pgm/init/libraries.config
index 20dc4ce..a74da30 100644
--- a/gerrit-pgm/src/main/resources/com/google/gerrit/pgm/init/libraries.config
+++ b/gerrit-pgm/src/main/resources/com/google/gerrit/pgm/init/libraries.config
@@ -28,6 +28,14 @@
   needs = bouncyCastleProvider
   remove = bcpkix-.*[.]jar
 
+# Version should match lib/bouncycastle/BUCK
+[library "bouncyCastlePGP"]
+  name = Bouncy Castle Crypto OpenPGP v152
+  url = http://www.bouncycastle.org/download/bcpg-jdk15on-152.jar
+  sha1 = ff4665a4b5633ff6894209d5dd10b7e612291858
+  needs = bouncyCastleProvider
+  remove = bcpg-.*[.]jar
+
 [library "mysqlDriver"]
   name = MySQL Connector/J 5.1.21
   url = http://repo2.maven.org/maven2/mysql/mysql-connector-java/5.1.21/mysql-connector-java-5.1.21.jar
diff --git a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/FormatUtil.java b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/FormatUtil.java
index f52bbef..e5e8c62 100644
--- a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/FormatUtil.java
+++ b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/plugin/client/FormatUtil.java
@@ -22,7 +22,7 @@
 import java.util.Date;
 
 public class FormatUtil {
-  private final static AccountFormatter accountFormatter =
+  private static final AccountFormatter accountFormatter =
       new AccountFormatter(Plugin.get().getServerInfo().user()
           .anonymousCowardName());
 
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java
index 2750380..3e18bc8 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Account.java
@@ -182,9 +182,7 @@
   @Column(id = 4, notNull = false)
   protected String preferredEmail;
 
-  /** When did the user last give us contact information? Null if never. */
-  @Column(id = 5, notNull = false)
-  protected Timestamp contactFiledOn;
+  // DELETED: id = 5 (contactFiledOn)
 
   /** This user's preferences */
   @Column(id = 6, name = Column.NONE)
@@ -257,18 +255,6 @@
     generalPreferences = p;
   }
 
-  public boolean isContactFiled() {
-    return contactFiledOn != null;
-  }
-
-  public Timestamp getContactFiledOn() {
-    return contactFiledOn;
-  }
-
-  public void setContactFiled(Timestamp ts) {
-    contactFiledOn = ts;
-  }
-
   public boolean isActive() {
     return ! inactive;
   }
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/ContactInformation.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/ContactInformation.java
deleted file mode 100644
index 195387c..0000000
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/ContactInformation.java
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.reviewdb.client;
-
-import com.google.gwtorm.client.Column;
-
-/** Non-Internet contact details, such as a postal address and telephone. */
-public final class ContactInformation {
-  @Column(id = 1, length = Integer.MAX_VALUE, notNull = false)
-  protected String address;
-
-  @Column(id = 2, notNull = false, length = 40)
-  protected String country;
-
-  @Column(id = 3, notNull = false, length = 30)
-  protected String phoneNbr;
-
-  @Column(id = 4, notNull = false, length = 30)
-  protected String faxNbr;
-
-  public ContactInformation() {
-  }
-
-  public String getAddress() {
-    return address;
-  }
-
-  public void setAddress(final String a) {
-    address = a;
-  }
-
-  public String getCountry() {
-    return country;
-  }
-
-  public void setCountry(final String c) {
-    country = c;
-  }
-
-  public String getPhoneNumber() {
-    return phoneNbr;
-  }
-
-  public void setPhoneNumber(final String p) {
-    phoneNbr = p;
-  }
-
-  public String getFaxNumber() {
-    return faxNbr;
-  }
-
-  public void setFaxNumber(final String f) {
-    faxNbr = f;
-  }
-
-  public static boolean hasData(final ContactInformation contactInformation) {
-    if (contactInformation == null) {
-      return false;
-    }
-    return hasData(contactInformation.address)
-        || hasData(contactInformation.country)
-        || hasData(contactInformation.phoneNbr)
-        || hasData(contactInformation.faxNbr);
-  }
-
-  public static boolean hasAddress(final ContactInformation contactInformation) {
-    return contactInformation != null && hasData(contactInformation.address);
-  }
-
-  private static boolean hasData(final String s) {
-    return s != null && s.trim().length() > 0;
-  }
-}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/RefNames.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/RefNames.java
index 707664f..da66929 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/RefNames.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/RefNames.java
@@ -57,12 +57,6 @@
 
   public static final String EDIT_PREFIX = "edit-";
 
-  /**
-   * Special ref for GPG public keys used by {@link
-   * com.google.gerrit.server.git.gpg.SignedPushPreReceiveHook}.
-   */
-  public static final String REFS_GPG_KEYS = "refs/meta/gpg-keys";
-
   public static String fullName(String ref) {
     return ref.startsWith(REFS) ? ref : REFS_HEADS + ref;
   }
diff --git a/gerrit-server/BUCK b/gerrit-server/BUCK
index ac3e291..31a9b49 100644
--- a/gerrit-server/BUCK
+++ b/gerrit-server/BUCK
@@ -63,7 +63,7 @@
     '//lib/log:jsonevent-layout',
     '//lib/log:log4j',
     '//lib/lucene:analyzers-common',
-    '//lib/lucene:core',
+    '//lib/lucene:core-and-backward-codecs',
     '//lib/lucene:queryparser',
     '//lib/ow2:ow2-asm',
     '//lib/ow2:ow2-asm-tree',
@@ -72,9 +72,6 @@
   ],
   provided_deps = [
     '//lib:servlet-api-3_1',
-    '//lib/bouncycastle:bcprov',
-    '//lib/bouncycastle:bcpg',
-    '//lib/bouncycastle:bcpkix',
   ],
   visibility = ['PUBLIC'],
 )
@@ -88,7 +85,6 @@
 TESTUTIL = glob([
   'src/test/java/com/google/gerrit/testutil/**/*.java',
   'src/test/java/com/google/gerrit/server/project/Util.java',
-  'src/test/java/com/google/gerrit/server/git/gpg/TestKey.java',
   ])
 java_library(
   name = 'testutil',
@@ -98,14 +94,13 @@
     '//gerrit-common:server',
     '//gerrit-cache-h2:cache-h2',
     '//gerrit-extension-api:api',
+    '//gerrit-gpg:gpg',
     '//gerrit-lucene:lucene',
     '//gerrit-reviewdb:server',
     '//lib:gwtorm',
     '//lib:h2',
     '//lib:truth',
     '//lib/auto:auto-value',
-    '//lib/bouncycastle:bcpg',
-    '//lib/bouncycastle:bcprov',
     '//lib/guice:guice',
     '//lib/guice:guice-servlet',
     '//lib/jgit:jgit',
@@ -139,6 +134,7 @@
   deps = [
     ':server',
     '//gerrit-common:server',
+    '//gerrit-extension-api:api',
     '//lib:guava',
     '//lib:junit',
     '//lib:truth',
@@ -210,6 +206,7 @@
     '//gerrit-common:annotations',
     '//gerrit-common:server',
     '//gerrit-extension-api:api',
+    '//gerrit-gpg:gpg',
     '//gerrit-reviewdb:server',
     '//gerrit-server/src/main/prolog:common',
     '//lib:args4j',
@@ -217,9 +214,6 @@
     '//lib:guava',
     '//lib:gwtorm',
     '//lib:truth',
-    '//lib/bouncycastle:bcprov',
-    '//lib/bouncycastle:bcpg',
-    '//lib/bouncycastle:bcpkix',
     '//lib/guice:guice',
     '//lib/guice:guice-assistedinject',
     '//lib/jgit:jgit',
diff --git a/gerrit-server/src/main/java/com/google/gerrit/lifecycle/LifecycleModule.java b/gerrit-server/src/main/java/com/google/gerrit/lifecycle/LifecycleModule.java
index a22e6da..24df965 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/lifecycle/LifecycleModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/lifecycle/LifecycleModule.java
@@ -1,7 +1,7 @@
 package com.google.gerrit.lifecycle;
 
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.extensions.events.LifecycleListener;
-import com.google.gerrit.server.config.FactoryModule;
 import com.google.inject.Singleton;
 import com.google.inject.binder.LinkedBindingBuilder;
 import com.google.inject.internal.UniqueAnnotations;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/rules/PrologModule.java b/gerrit-server/src/main/java/com/google/gerrit/rules/PrologModule.java
index 74a3928..7ed048b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/rules/PrologModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/rules/PrologModule.java
@@ -14,8 +14,8 @@
 
 package com.google.gerrit.rules;
 
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.server.config.FactoryModule;
 
 public class PrologModule extends FactoryModule {
   @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/CmdLineParserModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/CmdLineParserModule.java
index 956a0d1..282d51e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/CmdLineParserModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/CmdLineParserModule.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server;
 
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.client.Change;
@@ -27,7 +28,6 @@
 import com.google.gerrit.server.args4j.ProjectControlHandler;
 import com.google.gerrit.server.args4j.SocketAddressHandler;
 import com.google.gerrit.server.args4j.TimestampHandler;
-import com.google.gerrit.server.config.FactoryModule;
 import com.google.gerrit.server.project.ProjectControl;
 import com.google.gerrit.util.cli.CmdLineParser;
 import com.google.gerrit.util.cli.OptionHandlerUtil;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/EnableSignedPush.java b/gerrit-server/src/main/java/com/google/gerrit/server/EnableSignedPush.java
new file mode 100644
index 0000000..13942a67
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/EnableSignedPush.java
@@ -0,0 +1,29 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.inject.BindingAnnotation;
+
+import java.lang.annotation.Retention;
+
+/**
+ * Marker on a boolean indicating whether signed push is enabled on the server.
+ */
+@Retention(RUNTIME)
+@BindingAnnotation
+public @interface EnableSignedPush {
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/GpgException.java b/gerrit-server/src/main/java/com/google/gerrit/server/GpgException.java
new file mode 100644
index 0000000..5ed27b5
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/GpgException.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server;
+
+/** Generic exception type for GPG-related exceptions. */
+public class GpgException extends Exception {
+  private static final long serialVersionUID = 1L;
+
+  public GpgException(String message) {
+    super(message);
+  }
+
+  public GpgException(Throwable cause) {
+    super(cause);
+  }
+
+  public GpgException(String message, Throwable cause) {
+    super(message, cause);
+  }
+}
+
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountResource.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountResource.java
index 7b5bead..75e5ae5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountResource.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountResource.java
@@ -22,8 +22,6 @@
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.inject.TypeLiteral;
 
-import org.bouncycastle.openpgp.PGPPublicKeyRing;
-
 public class AccountResource implements RestResource {
   public static final TypeLiteral<RestView<AccountResource>> ACCOUNT_KIND =
       new TypeLiteral<RestView<AccountResource>>() {};
@@ -37,9 +35,6 @@
   public static final TypeLiteral<RestView<SshKey>> SSH_KEY_KIND =
       new TypeLiteral<RestView<SshKey>>() {};
 
-  public static final TypeLiteral<RestView<GpgKey>> GPG_KEY_KIND =
-      new TypeLiteral<RestView<GpgKey>>() {};
-
   public static final TypeLiteral<RestView<StarredChange>> STARRED_CHANGE_KIND =
       new TypeLiteral<RestView<StarredChange>>() {};
 
@@ -101,19 +96,6 @@
     }
   }
 
-  public static class GpgKey extends AccountResource {
-    private final PGPPublicKeyRing keyRing;
-
-    public GpgKey(IdentifiedUser user, PGPPublicKeyRing keyRing) {
-      super(user);
-      this.keyRing = keyRing;
-    }
-
-    public PGPPublicKeyRing getKeyRing() {
-      return keyRing;
-    }
-  }
-
   public static class StarredChange extends AccountResource {
     private final ChangeResource change;
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateAccount.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateAccount.java
index 38fda4c..cf6c27e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateAccount.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateAccount.java
@@ -154,6 +154,7 @@
         try {
           db.accountExternalIds().delete(Collections.singleton(extUser));
         } catch (OrmException cleanupError) {
+          // Ignored
         }
         throw new UnprocessableEntityException(
             "email '" + input.email + "' already exists");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateEmail.java
index a0fadb5..5c4b9cd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateEmail.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateEmail.java
@@ -40,7 +40,7 @@
 import org.slf4j.LoggerFactory;
 
 public class CreateEmail implements RestModifyView<AccountResource, EmailInput> {
-  private final Logger log = LoggerFactory.getLogger(getClass());
+  private static final Logger log = LoggerFactory.getLogger(CreateEmail.class);
 
   public static interface Factory {
     CreateEmail create(String email);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetDetail.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetDetail.java
index 3698844..81c860e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetDetail.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetDetail.java
@@ -43,7 +43,6 @@
     Account a = rsrc.getUser().getAccount();
     AccountDetailInfo info = new AccountDetailInfo(a.getId().get());
     info.registeredOn = a.getRegisteredOn();
-    info.contactFiledOn = a.getContactFiledOn();
     try {
       directory.fillAccountInfo(Collections.singleton(info),
           EnumSet.allOf(FillOptions.class));
@@ -56,7 +55,6 @@
 
   public static class AccountDetailInfo extends AccountInfo {
     public Timestamp registeredOn;
-    public Timestamp contactFiledOn;
 
     public AccountDetailInfo(Integer id) {
       super(id);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java
index 137a8fe..7cf1e37 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java
@@ -17,13 +17,11 @@
 import static com.google.gerrit.server.account.AccountResource.ACCOUNT_KIND;
 import static com.google.gerrit.server.account.AccountResource.CAPABILITY_KIND;
 import static com.google.gerrit.server.account.AccountResource.EMAIL_KIND;
-import static com.google.gerrit.server.account.AccountResource.GPG_KEY_KIND;
 import static com.google.gerrit.server.account.AccountResource.SSH_KEY_KIND;
 import static com.google.gerrit.server.account.AccountResource.STARRED_CHANGE_KIND;
 
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.RestApiModule;
-import com.google.inject.assistedinject.FactoryModuleBuilder;
 
 public class Module extends RestApiModule {
   @Override
@@ -34,7 +32,6 @@
     DynamicMap.mapOf(binder(), ACCOUNT_KIND);
     DynamicMap.mapOf(binder(), CAPABILITY_KIND);
     DynamicMap.mapOf(binder(), EMAIL_KIND);
-    DynamicMap.mapOf(binder(), GPG_KEY_KIND);
     DynamicMap.mapOf(binder(), SSH_KEY_KIND);
     DynamicMap.mapOf(binder(), STARRED_CHANGE_KIND);
 
@@ -63,11 +60,6 @@
     get(SSH_KEY_KIND).to(GetSshKey.class);
     delete(SSH_KEY_KIND).to(DeleteSshKey.class);
 
-    child(ACCOUNT_KIND, "gpgkeys").to(GpgKeys.class);
-    post(ACCOUNT_KIND, "gpgkeys").to(PostGpgKeys.class);
-    get(GPG_KEY_KIND).to(GpgKeys.Get.class);
-    delete(GPG_KEY_KIND).to(DeleteGpgKey.class);
-
     get(ACCOUNT_KIND, "avatar").to(GetAvatar.class);
     get(ACCOUNT_KIND, "avatar.change.url").to(GetAvatarChangeUrl.class);
 
@@ -85,7 +77,7 @@
     delete(STARRED_CHANGE_KIND).to(StarredChanges.Delete.class);
     bind(StarredChanges.Create.class);
 
-    install(new FactoryModuleBuilder().build(CreateAccount.Factory.class));
-    install(new FactoryModuleBuilder().build(CreateEmail.Factory.class));
+    factory(CreateAccount.Factory.class);
+    factory(CreateEmail.Factory.class);
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/VersionedAccountQueries.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/VersionedAccountQueries.java
index b12e7ce..2ea6c53 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/VersionedAccountQueries.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/VersionedAccountQueries.java
@@ -17,7 +17,6 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.git.QueryList;
-import com.google.gerrit.server.git.ValidationError;
 import com.google.gerrit.server.git.VersionedMetaData;
 
 import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -53,14 +52,8 @@
 
   @Override
   protected void onLoad() throws IOException, ConfigInvalidException {
-    ValidationError.Sink errors = new ValidationError.Sink() {
-      @Override
-      public void error(ValidationError error) {
-        log.error("Error parsing file " + QueryList.FILE_NAME + ": " +
-            error.getMessage());
-      }
-    };
-    queryList = QueryList.parse(readUTF8(QueryList.FILE_NAME), errors);
+    queryList = QueryList.parse(readUTF8(QueryList.FILE_NAME),
+        QueryList.createLoggerSink(QueryList.FILE_NAME, log));
   }
 
   @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
index 85e519b..c6e4ad1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/AccountApiImpl.java
@@ -23,11 +23,10 @@
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
+import com.google.gerrit.server.GpgException;
 import com.google.gerrit.server.account.AccountLoader;
 import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.account.CreateEmail;
-import com.google.gerrit.server.account.GpgKeys;
-import com.google.gerrit.server.account.PostGpgKeys;
 import com.google.gerrit.server.account.StarredChanges;
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.change.ChangesCollection;
@@ -35,9 +34,6 @@
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
-import org.bouncycastle.openpgp.PGPException;
-
-import java.io.IOException;
 import java.util.List;
 import java.util.Map;
 
@@ -52,9 +48,7 @@
   private final StarredChanges.Create starredChangesCreate;
   private final StarredChanges.Delete starredChangesDelete;
   private final CreateEmail.Factory createEmailFactory;
-  private final PostGpgKeys postGpgKeys;
-  private final GpgKeys gpgKeys;
-  private final GpgKeyApiImpl.Factory gpgKeyApiFactory;
+  private final GpgApiAdapter gpgApiAdapter;
 
   @Inject
   AccountApiImpl(AccountLoader.Factory ailf,
@@ -62,9 +56,7 @@
       StarredChanges.Create starredChangesCreate,
       StarredChanges.Delete starredChangesDelete,
       CreateEmail.Factory createEmailFactory,
-      PostGpgKeys postGpgKeys,
-      GpgKeys gpgKeys,
-      GpgKeyApiImpl.Factory gpgKeyApiFactory,
+      GpgApiAdapter gpgApiAdapter,
       @Assisted AccountResource account) {
     this.account = account;
     this.accountLoaderFactory = ailf;
@@ -72,9 +64,7 @@
     this.starredChangesCreate = starredChangesCreate;
     this.starredChangesDelete = starredChangesDelete;
     this.createEmailFactory = createEmailFactory;
-    this.postGpgKeys = postGpgKeys;
-    this.gpgKeys = gpgKeys;
-    this.gpgKeyApiFactory = gpgKeyApiFactory;
+    this.gpgApiAdapter = gpgApiAdapter;
   }
 
   @Override
@@ -131,8 +121,8 @@
   @Override
   public Map<String, GpgKeyInfo> listGpgKeys() throws RestApiException {
     try {
-      return gpgKeys.list().apply(account);
-    } catch (OrmException | PGPException | IOException e) {
+      return gpgApiAdapter.listGpgKeys(account);
+    } catch (GpgException e) {
       throw new RestApiException("Cannot list GPG keys", e);
     }
   }
@@ -140,12 +130,9 @@
   @Override
   public Map<String, GpgKeyInfo> putGpgKeys(List<String> add,
       List<String> delete) throws RestApiException {
-    PostGpgKeys.Input in = new PostGpgKeys.Input();
-    in.add = add;
-    in.delete = delete;
     try {
-      return postGpgKeys.apply(account, in);
-    } catch (PGPException | OrmException | IOException e) {
+      return gpgApiAdapter.putGpgKeys(account, add, delete);
+    } catch (GpgException e) {
       throw new RestApiException("Cannot add GPG key", e);
     }
   }
@@ -153,9 +140,8 @@
   @Override
   public GpgKeyApi gpgKey(String id) throws RestApiException {
     try {
-      IdString idStr = IdString.fromDecoded(id);
-      return gpgKeyApiFactory.create(gpgKeys.parse(account, idStr));
-    } catch (PGPException | OrmException | IOException e) {
+      return gpgApiAdapter.gpgKey(account, IdString.fromDecoded(id));
+    } catch (GpgException e) {
       throw new RestApiException("Cannot get PGP key", e);
     }
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/GpgApiAdapter.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/GpgApiAdapter.java
new file mode 100644
index 0000000..9faa418
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/GpgApiAdapter.java
@@ -0,0 +1,34 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.api.accounts;
+
+import com.google.gerrit.extensions.api.accounts.GpgKeyApi;
+import com.google.gerrit.extensions.common.GpgKeyInfo;
+import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.server.GpgException;
+import com.google.gerrit.server.account.AccountResource;
+
+import java.util.List;
+import java.util.Map;
+
+public interface GpgApiAdapter {
+  Map<String, GpgKeyInfo> listGpgKeys(AccountResource account)
+      throws RestApiException, GpgException;
+  Map<String, GpgKeyInfo> putGpgKeys(AccountResource account, List<String> add,
+      List<String> delete) throws RestApiException, GpgException;
+  GpgKeyApi gpgKey(AccountResource account, IdString idStr)
+      throws RestApiException, GpgException;
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/Module.java
index a9dd9d6..935c4d7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/accounts/Module.java
@@ -15,7 +15,7 @@
 package com.google.gerrit.server.api.accounts;
 
 import com.google.gerrit.extensions.api.accounts.Accounts;
-import com.google.gerrit.server.config.FactoryModule;
+import com.google.gerrit.extensions.config.FactoryModule;
 
 public class Module extends FactoryModule {
   @Override
@@ -23,6 +23,5 @@
     bind(Accounts.class).to(AccountsImpl.class);
 
     factory(AccountApiImpl.Factory.class);
-    factory(GpgKeyApiImpl.Factory.class);
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/Module.java
index a37f8be..a5e584e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/changes/Module.java
@@ -15,7 +15,7 @@
 package com.google.gerrit.server.api.changes;
 
 import com.google.gerrit.extensions.api.changes.Changes;
-import com.google.gerrit.server.config.FactoryModule;
+import com.google.gerrit.extensions.config.FactoryModule;
 
 public class Module extends FactoryModule {
   @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/config/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/config/Module.java
index feddf54..b37aa1c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/config/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/config/Module.java
@@ -16,7 +16,7 @@
 
 import com.google.gerrit.extensions.api.config.Config;
 import com.google.gerrit.extensions.api.config.Server;
-import com.google.gerrit.server.config.FactoryModule;
+import com.google.gerrit.extensions.config.FactoryModule;
 
 public class Module extends FactoryModule {
   @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/Module.java
index dae08cd..7d7af4e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/Module.java
@@ -15,7 +15,7 @@
 package com.google.gerrit.server.api.groups;
 
 import com.google.gerrit.extensions.api.groups.Groups;
-import com.google.gerrit.server.config.FactoryModule;
+import com.google.gerrit.extensions.config.FactoryModule;
 
 public class Module extends FactoryModule {
   @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/Module.java
index 2e6b761..3113d07 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/Module.java
@@ -15,7 +15,7 @@
 package com.google.gerrit.server.api.projects;
 
 import com.google.gerrit.extensions.api.projects.Projects;
-import com.google.gerrit.server.config.FactoryModule;
+import com.google.gerrit.extensions.config.FactoryModule;
 
 public class Module extends FactoryModule {
   @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java
index 966c77a..e830cfc 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java
@@ -263,6 +263,7 @@
             recursivelyExpandGroups(groupDNs, schema, ctx, nextDN);
           }
         } catch (PartialResultException e) {
+          // Ignored
         }
       }
     }
@@ -298,6 +299,7 @@
                 dns.add((String) groups.next());
               }
             } catch (PartialResultException e) {
+              // Ignored
             }
           }
         } catch (NamingException e) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapQuery.java
index 1f68011..e9ae8f5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapQuery.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapQuery.java
@@ -75,6 +75,7 @@
           r.add(new Result(res.next()));
         }
       } catch (PartialResultException e) {
+        // Ignored
       }
       return r;
     } finally {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/cache/CacheModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/CacheModule.java
index b54cd88..9f51de7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/cache/CacheModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/cache/CacheModule.java
@@ -19,7 +19,7 @@
 import com.google.common.cache.LoadingCache;
 import com.google.common.cache.Weigher;
 import com.google.gerrit.extensions.annotations.Exports;
-import com.google.gerrit.server.config.FactoryModule;
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.inject.Key;
 import com.google.inject.Provider;
 import com.google.inject.Scopes;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java
index 8bee4a6..f3d1455 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java
@@ -310,17 +310,13 @@
       ChangeData.Factory changeDataFactory,
       ProjectCache projectCache,
       GitRepositoryManager repoManager) {
-    Repository repo = null;
     // TODO - dborowitz: add NEW_CHANGE type for default.
     ChangeKind kind = ChangeKind.REWORK;
     // Trivial case: if we're on the first patch, we don't need to open
     // the repository.
     if (patch.getId().get() > 1) {
-      try {
+      try (Repository repo = repoManager.openRepository(change.getProject())) {
         ProjectState projectState = projectCache.checkedGet(change.getProject());
-
-        repo = repoManager.openRepository(change.getProject());
-
         ChangeData cd = changeDataFactory.create(db, change);
         Collection<PatchSet> patchSetCollection = cd.patchSets();
         PatchSet priorPs = patch;
@@ -347,10 +343,6 @@
         // Do nothing; assume we have a complex change
         log.warn("Unable to get change kind for patchSet " + patch.getPatchSetId() +
             "of change " + change.getChangeId(), e);
-      } finally {
-        if (repo != null) {
-          repo.close();
-        }
       }
     }
     return kind;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPick.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPick.java
index 08117d1..ad5940e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPick.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPick.java
@@ -66,6 +66,7 @@
       throw new BadRequestException("destination must be non-empty");
     }
 
+    @SuppressWarnings("resource")
     ReviewDb db = dbProvider.get();
     if (!control.isVisible(db)) {
       throw new AuthException("Cherry pick not permitted");
@@ -87,10 +88,8 @@
       return json.create(ChangeJson.NO_OPTIONS).format(cherryPickedChangeId);
     } catch (InvalidChangeOperationException e) {
       throw new BadRequestException(e.getMessage());
-    } catch (MergeException e) {
+    } catch (MergeException | NoSuchChangeException e) {
       throw new ResourceConflictException(e.getMessage());
-    } catch (NoSuchChangeException e) {
-      throw new ResourceNotFoundException(e.getMessage());
     }
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/FileContentUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/FileContentUtil.java
index a8e1793..4336058 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/FileContentUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/FileContentUtil.java
@@ -83,13 +83,6 @@
         raw = null;
       }
 
-      BinaryResult result;
-      if (raw != null) {
-        result = BinaryResult.create(raw);
-      } else {
-        result = asBinaryResult(obj);
-      }
-
       String type;
       if (mode == org.eclipse.jgit.lib.FileMode.SYMLINK) {
         type = X_GIT_SYMLINK;
@@ -97,11 +90,16 @@
         type = registry.getMimeType(path, raw).toString();
         type = resolveContentType(project, path, FileMode.FILE, type);
       }
-      return result.setContentType(type).base64();
+
+      return asBinaryResult(raw, obj).setContentType(type).base64();
     }
   }
 
-  private static BinaryResult asBinaryResult(final ObjectLoader obj) {
+  private static BinaryResult asBinaryResult(byte[] raw,
+      final ObjectLoader obj) {
+    if (raw != null) {
+      return BinaryResult.create(raw);
+    }
     BinaryResult result = new BinaryResult() {
       @Override
       public void writeTo(OutputStream os) throws IOException {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
index f92b13d..ed2b835 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
@@ -27,7 +27,6 @@
 import com.google.gerrit.server.account.AccountLoader;
 import com.google.gerrit.server.change.Reviewed.DeleteReviewed;
 import com.google.gerrit.server.change.Reviewed.PutReviewed;
-import com.google.gerrit.server.config.FactoryModule;
 
 public class Module extends RestApiModule {
   @Override
@@ -119,17 +118,12 @@
     get(CHANGE_EDIT_KIND, "/").to(ChangeEdits.Get.class);
     get(CHANGE_EDIT_KIND, "meta").to(ChangeEdits.GetMeta.class);
 
-    install(new FactoryModule() {
-      @Override
-      protected void configure() {
-        factory(ReviewerResource.Factory.class);
-        factory(AccountLoader.Factory.class);
-        factory(EmailReviewComments.Factory.class);
-        factory(ChangeInserter.Factory.class);
-        factory(PatchSetInserter.Factory.class);
-        factory(ChangeEdits.Create.Factory.class);
-        factory(ChangeEdits.DeleteFile.Factory.class);
-      }
-    });
+    factory(ReviewerResource.Factory.class);
+    factory(AccountLoader.Factory.class);
+    factory(EmailReviewComments.Factory.class);
+    factory(ChangeInserter.Factory.class);
+    factory(PatchSetInserter.Factory.class);
+    factory(ChangeEdits.Create.Factory.class);
+    factory(ChangeEdits.DeleteFile.Factory.class);
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Rebase.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Rebase.java
index d4a7bfc..ece6752 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Rebase.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Rebase.java
@@ -114,6 +114,7 @@
       return change.getDest().get();
     }
 
+    @SuppressWarnings("resource")
     ReviewDb db = dbProvider.get();
     PatchSet basePatchSet = parseBase(base);
     if (basePatchSet == null) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java
index 2d7db67..c6048bb 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java
@@ -217,6 +217,7 @@
   private String problemsForSubmittingChangeset(
       ChangeSet cs, IdentifiedUser identifiedUser) {
     try {
+      @SuppressWarnings("resource")
       ReviewDb db = dbProvider.get();
       for (PatchSet.Id psId : cs.patchIds()) {
         ChangeControl changeControl = changeControlFactory
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
index a452561..cf1053d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -24,6 +24,7 @@
 import com.google.gerrit.extensions.config.DownloadCommand;
 import com.google.gerrit.extensions.config.DownloadScheme;
 import com.google.gerrit.extensions.config.ExternalIncludedIn;
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.extensions.events.GarbageCollectorListener;
 import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
 import com.google.gerrit.extensions.events.HeadUpdatedListener;
@@ -82,7 +83,6 @@
 import com.google.gerrit.server.git.ReceivePackInitializer;
 import com.google.gerrit.server.git.TagCache;
 import com.google.gerrit.server.git.TransferConfig;
-import com.google.gerrit.server.git.gpg.SignedPushModule;
 import com.google.gerrit.server.git.validators.CommitValidationListener;
 import com.google.gerrit.server.git.validators.CommitValidators;
 import com.google.gerrit.server.git.validators.MergeValidationListener;
@@ -180,7 +180,6 @@
     install(new NoteDbModule());
     install(new PrologModule());
     install(new SshAddressesModule());
-    install(new SignedPushModule());
     install(ThreadLocalRequestContext.module());
 
     bind(AccountResolver.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
index d481b44..5d88ec0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
@@ -16,6 +16,7 @@
 
 import static com.google.inject.Scopes.SINGLETON;
 
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.RequestCleanup;
 import com.google.gerrit.server.project.PerRequestProjectControlCache;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GetServerInfo.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GetServerInfo.java
index 2ebdadf..b75d775 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GetServerInfo.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GetServerInfo.java
@@ -29,12 +29,12 @@
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AuthType;
+import com.google.gerrit.server.EnableSignedPush;
 import com.google.gerrit.server.account.Realm;
 import com.google.gerrit.server.avatar.AvatarProvider;
 import com.google.gerrit.server.change.ArchiveFormat;
 import com.google.gerrit.server.change.GetArchive;
 import com.google.gerrit.server.change.Submit;
-import com.google.gerrit.server.git.gpg.SignedPushModule;
 import com.google.inject.Inject;
 
 import org.eclipse.jgit.lib.Config;
@@ -47,9 +47,9 @@
 import java.util.concurrent.TimeUnit;
 
 public class GetServerInfo implements RestReadView<ConfigResource> {
-  private final static String URL_ALIAS = "urlAlias";
-  private final static String KEY_MATCH = "match";
-  private final static String KEY_TOKEN = "token";
+  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 AuthConfig authConfig;
@@ -63,6 +63,7 @@
   private final String anonymousCowardName;
   private final GitwebConfig gitwebConfig;
   private final DynamicItem<AvatarProvider> avatar;
+  private final boolean enableSignedPush;
 
   @Inject
   public GetServerInfo(
@@ -77,7 +78,8 @@
       AllUsersName allUsersName,
       @AnonymousCowardName String anonymousCowardName,
       GitwebConfig gitwebConfig,
-      DynamicItem<AvatarProvider> avatar) {
+      DynamicItem<AvatarProvider> avatar,
+      @EnableSignedPush boolean enableSignedPush) {
     this.config = config;
     this.authConfig = authConfig;
     this.realm = realm;
@@ -90,6 +92,7 @@
     this.anonymousCowardName = anonymousCowardName;
     this.gitwebConfig = gitwebConfig;
     this.avatar = avatar;
+    this.enableSignedPush = enableSignedPush;
   }
 
   @Override
@@ -97,7 +100,6 @@
     ServerInfo info = new ServerInfo();
     info.auth = getAuthInfo(authConfig, realm);
     info.change = getChangeInfo(config);
-    info.contactStore = getContactStoreInfo();
     info.download =
         getDownloadInfo(downloadSchemes, downloadCommands, cloneCommands,
             archiveFormats);
@@ -111,7 +113,7 @@
     info.urlAliases = !urlAliases.isEmpty() ? urlAliases : null;
 
     info.user = getUserInfo(anonymousCowardName);
-    info.receive = getReceiveInfo(config);
+    info.receive = getReceiveInfo();
     return info;
   }
 
@@ -170,17 +172,6 @@
     return info;
   }
 
-  private ContactStoreInfo getContactStoreInfo() {
-    String url = config.getString("contactstore", null, "url");
-    if (url == null) {
-      return null;
-    }
-
-    ContactStoreInfo contactStore = new ContactStoreInfo();
-    contactStore.url = url;
-    return contactStore;
-  }
-
   private DownloadInfo getDownloadInfo(
       DynamicMap<DownloadScheme> downloadSchemes,
       DynamicMap<DownloadCommand> downloadCommands,
@@ -310,9 +301,9 @@
     return info;
   }
 
-  private ReceiveInfo getReceiveInfo(Config cfg) {
+  private ReceiveInfo getReceiveInfo() {
     ReceiveInfo info = new ReceiveInfo();
-    info.enableSignedPush = SignedPushModule.isEnabled(cfg);
+    info.enableSignedPush = enableSignedPush;
     return info;
   }
 
@@ -323,7 +314,6 @@
   public static class ServerInfo {
     public AuthInfo auth;
     public ChangeConfigInfo change;
-    public ContactStoreInfo contactStore;
     public DownloadInfo download;
     public GerritInfo gerrit;
     public GitwebInfo gitweb;
@@ -358,10 +348,6 @@
     public Boolean submitWholeTopic;
   }
 
-  public static class ContactStoreInfo {
-    public String url;
-  }
-
   public static class DownloadInfo {
     public Map<String, DownloadSchemeInfo> schemes;
     public List<String> archives;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GetSummary.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GetSummary.java
index 48fe57f..c99a3af 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GetSummary.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GetSummary.java
@@ -187,6 +187,7 @@
     try {
       jvmSummary.host = InetAddress.getLocalHost().getHostName();
     } catch (UnknownHostException e) {
+      // Ignored
     }
 
     jvmSummary.currentWorkingDirectory =
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/RequestScopedReviewDbProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/RequestScopedReviewDbProvider.java
index 4ada7b1..c2c002e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/RequestScopedReviewDbProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/RequestScopedReviewDbProvider.java
@@ -37,6 +37,7 @@
     this.cleanup = cleanup;
   }
 
+  @SuppressWarnings("resource")
   @Override
   public ReviewDb get() {
     if (db == null) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePaths.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePaths.java
index a6a3e53..e41fb30 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePaths.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePaths.java
@@ -50,7 +50,6 @@
 
   public final Path gerrit_config;
   public final Path secure_config;
-  public final Path contact_information_pub;
 
   public final Path ssl_keystore;
   public final Path ssh_key;
@@ -89,7 +88,6 @@
 
     gerrit_config = etc_dir.resolve("gerrit.config");
     secure_config = etc_dir.resolve("secure.config");
-    contact_information_pub = etc_dir.resolve("contact_information.pub");
 
     ssl_keystore = etc_dir.resolve("keystore");
     ssh_key = etc_dir.resolve("ssh_host_key");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/contact/ContactStore.java b/gerrit-server/src/main/java/com/google/gerrit/server/contact/ContactStore.java
deleted file mode 100644
index 0ab21f9..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/contact/ContactStore.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.contact;
-
-import com.google.gerrit.common.errors.ContactInformationStoreException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.ContactInformation;
-
-public interface ContactStore {
-  boolean isEnabled();
-
-  void store(Account account, ContactInformation info)
-      throws ContactInformationStoreException;
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/contact/ContactStoreConnection.java b/gerrit-server/src/main/java/com/google/gerrit/server/contact/ContactStoreConnection.java
deleted file mode 100644
index 24c9e66..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/contact/ContactStoreConnection.java
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (C) 2011 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.contact;
-
-import java.io.IOException;
-import java.net.URL;
-
-/** Single connection to a {@link ContactStore}. */
-public interface ContactStoreConnection {
-  public static interface Factory {
-    /**
-     * Open a new connection to a {@link ContactStore}.
-     *
-     * @param url contact store URL.
-     * @return a new connection to the store.
-     *
-     * @throws IOException the URL couldn't be opened.
-     */
-    ContactStoreConnection open(URL url) throws IOException;
-  }
-
-  /**
-   * Store a blob of contact data in the store.
-   *
-   * @param body protocol-specific body data.
-   *
-   * @throws IOException an error occurred storing the contact data.
-   */
-  public void store(byte[] body) throws IOException;
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/contact/ContactStoreModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/contact/ContactStoreModule.java
deleted file mode 100644
index 2d0f4f1..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/contact/ContactStoreModule.java
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.contact;
-
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.util.BouncyCastleUtil;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.AbstractModule;
-import com.google.inject.Provides;
-import com.google.inject.ProvisionException;
-
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.util.StringUtils;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.nio.file.Files;
-import java.nio.file.Path;
-
-/** Creates the {@link ContactStore} based on the configuration. */
-public class ContactStoreModule extends AbstractModule {
-  @Override
-  protected void configure() {
-  }
-
-  @Nullable
-  @Provides
-  public ContactStore provideContactStore(@GerritServerConfig final Config config,
-      final SitePaths site, final SchemaFactory<ReviewDb> schema,
-      final ContactStoreConnection.Factory connFactory) {
-    String url = config.getString("contactstore", null, "url");
-    if (StringUtils.isEmptyOrNull(url)) {
-      return new NoContactStore();
-    }
-
-    if (!BouncyCastleUtil.havePGP()) {
-      throw new ProvisionException("BouncyCastle PGP not installed; "
-          + " needed to encrypt contact information");
-    }
-
-    URL storeUrl;
-    try {
-      storeUrl = new URL(url);
-    } catch (MalformedURLException e) {
-      throw new ProvisionException("Invalid contactstore.url: " + url, e);
-    }
-
-    String storeAPPSEC = config.getString("contactstore", null, "appsec");
-    Path pubkey = site.contact_information_pub;
-    if (!Files.exists(pubkey)) {
-      throw new ProvisionException("PGP public key file \""
-          + pubkey.toAbsolutePath() + "\" not found");
-    }
-    return new EncryptedContactStore(storeUrl, storeAPPSEC, pubkey, schema,
-        connFactory);
-  }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java b/gerrit-server/src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java
deleted file mode 100644
index ad4652b..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java
+++ /dev/null
@@ -1,277 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.contact;
-
-import com.google.gerrit.common.TimeUtil;
-import com.google.gerrit.common.errors.ContactInformationStoreException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountExternalId;
-import com.google.gerrit.reviewdb.client.ContactInformation;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.UrlEncoded;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.ProvisionException;
-import com.google.inject.Singleton;
-
-import org.bouncycastle.bcpg.ArmoredOutputStream;
-import org.bouncycastle.openpgp.PGPCompressedData;
-import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
-import org.bouncycastle.openpgp.PGPEncryptedData;
-import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
-import org.bouncycastle.openpgp.PGPException;
-import org.bouncycastle.openpgp.PGPLiteralData;
-import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
-import org.bouncycastle.openpgp.PGPPublicKey;
-import org.bouncycastle.openpgp.PGPPublicKeyRing;
-import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
-import org.bouncycastle.openpgp.PGPUtil;
-import org.bouncycastle.openpgp.bc.BcPGPPublicKeyRingCollection;
-import org.bouncycastle.openpgp.operator.bc.BcPGPDataEncryptorBuilder;
-import org.bouncycastle.openpgp.operator.bc.BcPublicKeyKeyEncryptionMethodGenerator;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.URL;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.sql.Timestamp;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.TimeZone;
-
-/** Encrypts {@link ContactInformation} instances and saves them. */
-@Singleton
-class EncryptedContactStore implements ContactStore {
-  private static final Logger log =
-      LoggerFactory.getLogger(EncryptedContactStore.class);
-  private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
-
-  private final SchemaFactory<ReviewDb> schema;
-  private final PGPPublicKey dest;
-  private final SecureRandom prng;
-  private final URL storeUrl;
-  private final String storeAPPSEC;
-  private final ContactStoreConnection.Factory connFactory;
-
-  EncryptedContactStore(final URL storeUrl, final String storeAPPSEC,
-      final Path pubKey, final SchemaFactory<ReviewDb> schema,
-      final ContactStoreConnection.Factory connFactory) {
-    this.storeUrl = storeUrl;
-    this.storeAPPSEC = storeAPPSEC;
-    this.schema = schema;
-    this.dest = selectKey(readPubRing(pubKey));
-    this.connFactory = connFactory;
-
-    final String prngName = "SHA1PRNG";
-    try {
-      prng = SecureRandom.getInstance(prngName);
-    } catch (NoSuchAlgorithmException e) {
-      throw new ProvisionException("Cannot create " + prngName, e);
-    }
-
-    // Run a test encryption to verify the proper algorithms exist in
-    // our JVM and we are able to invoke them. This helps to identify
-    // a system configuration problem early at startup, rather than a
-    // lot later during runtime.
-    //
-    try {
-      encrypt("test", new Date(0), "test".getBytes("UTF-8"));
-    } catch (PGPException | IOException e) {
-      throw new ProvisionException("PGP encryption not available", e);
-    }
-  }
-
-  @Override
-  public boolean isEnabled() {
-    return true;
-  }
-
-  private static PGPPublicKeyRingCollection readPubRing(Path pub) {
-    try (InputStream fin = Files.newInputStream(pub);
-        InputStream in = PGPUtil.getDecoderStream(fin)) {
-        return new BcPGPPublicKeyRingCollection(in);
-    } catch (IOException | PGPException e) {
-      throw new ProvisionException("Cannot read " + pub, e);
-    }
-  }
-
-  private static PGPPublicKey selectKey(final PGPPublicKeyRingCollection rings) {
-    for (final Iterator<?> ri = rings.getKeyRings(); ri.hasNext();) {
-      final PGPPublicKeyRing currRing = (PGPPublicKeyRing) ri.next();
-      for (final Iterator<?> ki = currRing.getPublicKeys(); ki.hasNext();) {
-        final PGPPublicKey k = (PGPPublicKey) ki.next();
-        if (k.isEncryptionKey()) {
-          return k;
-        }
-      }
-    }
-    return null;
-  }
-
-  @Override
-  public void store(final Account account, final ContactInformation info)
-      throws ContactInformationStoreException {
-    try {
-      final byte[] plainText = format(account, info).getBytes("UTF-8");
-      final String fileName = "account-" + account.getId();
-      final Date fileDate = account.getContactFiledOn();
-      final byte[] encText = encrypt(fileName, fileDate, plainText);
-      final String encStr = new String(encText, "UTF-8");
-
-      final Timestamp filedOn = account.getContactFiledOn();
-      final UrlEncoded u = new UrlEncoded();
-      if (storeAPPSEC != null) {
-        u.put("APPSEC", storeAPPSEC);
-      }
-      if (account.getPreferredEmail() != null) {
-        u.put("email", account.getPreferredEmail());
-      }
-      if (filedOn != null) {
-        u.put("filed", String.valueOf(filedOn.getTime() / 1000L));
-      }
-      u.put("account_id", String.valueOf(account.getId().get()));
-      u.put("data", encStr);
-      connFactory.open(storeUrl).store(u.toString().getBytes("UTF-8"));
-    } catch (IOException | PGPException e) {
-      log.error("Cannot store encrypted contact information", e);
-      throw new ContactInformationStoreException(e);
-    }
-  }
-
-  private final PGPEncryptedDataGenerator cpk() {
-    final BcPGPDataEncryptorBuilder builder =
-        new BcPGPDataEncryptorBuilder(PGPEncryptedData.CAST5)
-            .setSecureRandom(prng);
-    PGPEncryptedDataGenerator cpk =
-        new PGPEncryptedDataGenerator(builder, true);
-    final BcPublicKeyKeyEncryptionMethodGenerator methodGenerator =
-        new BcPublicKeyKeyEncryptionMethodGenerator(dest);
-    cpk.addMethod(methodGenerator);
-    return cpk;
-  }
-
-  private byte[] encrypt(final String name, final Date date,
-      final byte[] rawText) throws PGPException,
-      IOException {
-    final byte[] zText = compress(name, date, rawText);
-
-    final ByteArrayOutputStream buf = new ByteArrayOutputStream();
-    try (ArmoredOutputStream aout = new ArmoredOutputStream(buf);
-        OutputStream cout = cpk().open(aout, zText.length)) {
-      cout.write(zText);
-    }
-
-    return buf.toByteArray();
-  }
-
-  private static byte[] compress(final String fileName, Date fileDate,
-      final byte[] plainText) throws IOException {
-    final ByteArrayOutputStream buf = new ByteArrayOutputStream();
-    final PGPCompressedDataGenerator comdg;
-    final int len = plainText.length;
-    if (fileDate == null) {
-      fileDate = PGPLiteralData.NOW;
-    }
-
-    comdg = new PGPCompressedDataGenerator(PGPCompressedData.ZIP);
-    try (OutputStream out =
-        new PGPLiteralDataGenerator().open(comdg.open(buf),
-            PGPLiteralData.BINARY, fileName, len, fileDate)) {
-      out.write(plainText);
-    } finally {
-      comdg.close(); // PGPCompressedDataGenerator doesn't implement Closable
-    }
-    return buf.toByteArray();
-  }
-
-  private String format(final Account account, final ContactInformation info)
-      throws ContactInformationStoreException {
-    Timestamp on = account.getContactFiledOn();
-    if (on == null) {
-      on = TimeUtil.nowTs();
-    }
-
-    final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
-    df.setTimeZone(UTC);
-
-    final StringBuilder b = new StringBuilder();
-    field(b, "Account-Id", account.getId().toString());
-    field(b, "Date", df.format(on) + " " + UTC.getID());
-    field(b, "Full-Name", account.getFullName());
-    field(b, "Preferred-Email", account.getPreferredEmail());
-
-    try (ReviewDb db = schema.open()) {
-      for (final AccountExternalId e : db.accountExternalIds().byAccount(
-          account.getId())) {
-        final StringBuilder oistr = new StringBuilder();
-        if (e.getEmailAddress() != null && e.getEmailAddress().length() > 0) {
-          if (oistr.length() > 0) {
-            oistr.append(' ');
-          }
-          oistr.append(e.getEmailAddress());
-        }
-        if (e.isScheme(AccountExternalId.SCHEME_MAILTO)) {
-          if (oistr.length() > 0) {
-            oistr.append(' ');
-          }
-          oistr.append('<');
-          oistr.append(e.getExternalId());
-          oistr.append('>');
-        }
-        field(b, "Identity", oistr.toString());
-      }
-    } catch (OrmException e) {
-      throw new ContactInformationStoreException(e);
-    }
-
-    field(b, "Address", info.getAddress());
-    field(b, "Country", info.getCountry());
-    field(b, "Phone-Number", info.getPhoneNumber());
-    field(b, "Fax-Number", info.getFaxNumber());
-    return b.toString();
-  }
-
-  private static void field(final StringBuilder b, final String name,
-      String value) {
-    if (value == null) {
-      return;
-    }
-    value = value.trim();
-    if (value.length() == 0) {
-      return;
-    }
-
-    b.append(name);
-    b.append(':');
-    if (value.indexOf('\n') == -1) {
-      b.append(' ');
-      b.append(value);
-    } else {
-      value = value.replaceAll("\r\n", "\n");
-      value = value.replaceAll("\n", "\n\t");
-      b.append("\n\t");
-      b.append(value);
-    }
-    b.append('\n');
-  }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/contact/HttpContactStoreConnection.java b/gerrit-server/src/main/java/com/google/gerrit/server/contact/HttpContactStoreConnection.java
deleted file mode 100644
index ac500de..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/contact/HttpContactStoreConnection.java
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-
-package com.google.gerrit.server.contact;
-
-import com.google.inject.AbstractModule;
-import com.google.inject.Inject;
-import com.google.inject.Module;
-import com.google.inject.assistedinject.Assisted;
-import com.google.inject.assistedinject.FactoryModuleBuilder;
-
-import org.eclipse.jgit.util.IO;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.net.URLConnection;
-
-/** {@link ContactStoreConnection} with an underlying {@literal @HttpURLConnection}. */
-public class HttpContactStoreConnection implements ContactStoreConnection {
-  public static Module module() {
-    return new AbstractModule() {
-      @Override
-      protected void configure() {
-        install(new FactoryModuleBuilder()
-            .implement(ContactStoreConnection.class, HttpContactStoreConnection.class)
-            .build(ContactStoreConnection.Factory.class));
-      }
-    };
-  }
-
-  private final HttpURLConnection conn;
-
-  @Inject
-  HttpContactStoreConnection(@Assisted final URL url) throws IOException {
-    final URLConnection urlConn = url.openConnection();
-    if (!(urlConn instanceof HttpURLConnection)) {
-      throw new IllegalArgumentException("Non-HTTP URL not supported: " + urlConn);
-    }
-    conn = (HttpURLConnection) urlConn;
-  }
-
-  @Override
-  public void store(final byte[] body) throws IOException {
-    conn.setRequestMethod("POST");
-    conn.setRequestProperty("Content-Type",
-        "application/x-www-form-urlencoded; charset=UTF-8");
-    conn.setDoOutput(true);
-    conn.setFixedLengthStreamingMode(body.length);
-    try (OutputStream out = conn.getOutputStream()) {
-      out.write(body);
-    }
-    if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
-      throw new IOException("Connection failed: " + conn.getResponseCode());
-    }
-    final byte[] dst = new byte[2];
-    try (InputStream in = conn.getInputStream()) {
-      IO.readFully(in, dst, 0, 2);
-    }
-    if (dst[0] != 'O' || dst[1] != 'K') {
-      throw new IOException("Store failed: " + dst[0] + dst[1]);
-    }
-  }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/contact/NoContactStore.java b/gerrit-server/src/main/java/com/google/gerrit/server/contact/NoContactStore.java
deleted file mode 100644
index 98001bb..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/contact/NoContactStore.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.contact;
-
-import com.google.gerrit.common.errors.ContactInformationStoreException;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.ContactInformation;
-
-public class NoContactStore implements ContactStore {
-  @Override
-  public boolean isEnabled() {
-    return false;
-  }
-
-  @Override
-  public void store(Account account, ContactInformation info)
-      throws ContactInformationStoreException {
-    throw new ContactInformationStoreException(new IllegalStateException(
-        "ContactStore not configured"));
-  }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
index 2b1e3dd..5aef5bd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
@@ -367,6 +367,7 @@
         patchSetAttribute.files.add(p);
       }
     } catch (PatchListNotAvailableException e) {
+      log.warn("Cannot get patch list", e);
     }
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/GitModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/GitModule.java
index 9ef7256..aa0fc55 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/GitModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/GitModule.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.server.git;
 
-import com.google.gerrit.server.config.FactoryModule;
+import com.google.gerrit.extensions.config.FactoryModule;
 
 /** Configures the Git support. */
 public class GitModule extends FactoryModule {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
index 52932c4..cb99688 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
@@ -97,7 +97,6 @@
 
   private static final String CONTRIBUTOR_AGREEMENT = "contributor-agreement";
   private static final String KEY_ACCEPTED = "accepted";
-  private static final String KEY_REQUIRE_CONTACT_INFORMATION = "requireContactInformation";
   private static final String KEY_AUTO_VERIFY = "autoVerify";
   private static final String KEY_AGREEMENT_URL = "agreementUrl";
 
@@ -455,8 +454,6 @@
     for (String name : rc.getSubsections(CONTRIBUTOR_AGREEMENT)) {
       ContributorAgreement ca = getContributorAgreement(name, true);
       ca.setDescription(rc.getString(CONTRIBUTOR_AGREEMENT, name, KEY_DESCRIPTION));
-      ca.setRequireContactInformation(
-          rc.getBoolean(CONTRIBUTOR_AGREEMENT, name, KEY_REQUIRE_CONTACT_INFORMATION, false));
       ca.setAgreementUrl(rc.getString(CONTRIBUTOR_AGREEMENT, name, KEY_AGREEMENT_URL));
       ca.setAccepted(loadPermissionRules(
           rc, CONTRIBUTOR_AGREEMENT, name, KEY_ACCEPTED, groupsByName, false));
@@ -883,7 +880,6 @@
       Config rc, Set<AccountGroup.UUID> keepGroups) {
     for (ContributorAgreement ca : sort(contributorAgreements.values())) {
       set(rc, CONTRIBUTOR_AGREEMENT, ca.getName(), KEY_DESCRIPTION, ca.getDescription());
-      set(rc, CONTRIBUTOR_AGREEMENT, ca.getName(), KEY_REQUIRE_CONTACT_INFORMATION, ca.isRequireContactInformation());
       set(rc, CONTRIBUTOR_AGREEMENT, ca.getName(), KEY_AGREEMENT_URL, ca.getAgreementUrl());
 
       if (ca.getAutoVerify() != null) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/SubmoduleOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/SubmoduleOp.java
index d83da3e..8fe8379 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/SubmoduleOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/SubmoduleOp.java
@@ -221,15 +221,10 @@
   private void updateGitlinks(ReviewDb db, Branch.NameKey subscriber,
       Collection<SubmoduleSubscription> updates) throws SubmoduleException {
     PersonIdent author = null;
-
-    Repository pdb = null;
-    RevWalk recRw = null;
-
     StringBuilder msgbuf = new StringBuilder("Updated git submodules\n\n");
-    try {
-      boolean sameAuthorForAll = true;
+    boolean sameAuthorForAll = true;
 
-      pdb = repoManager.openRepository(subscriber.getParentKey());
+    try (Repository pdb = repoManager.openRepository(subscriber.getParentKey())) {
       if (pdb.getRef(subscriber.get()) == null) {
         throw new SubmoduleException(
             "The branch was probably deleted from the subscriber repository");
@@ -343,19 +338,11 @@
         default:
           throw new IOException(rfu.getResult().name());
       }
-      recRw = new RevWalk(pdb);
       // Recursive call: update subscribers of the subscriber
       updateSuperProjects(db, Sets.newHashSet(subscriber));
     } catch (IOException e) {
       throw new SubmoduleException("Cannot update gitlinks for "
           + subscriber.get(), e);
-    } finally {
-      if (recRw != null) {
-        recRw.close();
-      }
-      if (pdb != null) {
-        pdb.close();
-      }
     }
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/TabFile.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/TabFile.java
index 74d8f2d..13d2b1e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/TabFile.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/TabFile.java
@@ -14,6 +14,8 @@
 
 package com.google.gerrit.server.git;
 
+import org.slf4j.Logger;
+
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.StringReader;
@@ -126,4 +128,9 @@
     }
     return r.toString();
   }
+
+  public static ValidationError.Sink createLoggerSink(String file, Logger log) {
+    return ValidationError.createLoggerSink("Error parsing file " + file + ": ",
+        log);
+  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/TagSet.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/TagSet.java
index a003235..61cafc0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/TagSet.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/TagSet.java
@@ -76,6 +76,7 @@
   }
 
   void prepare(TagMatcher m) {
+    @SuppressWarnings("resource")
     RevWalk rw = null;
     try {
       for (Ref currentRef : m.include) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ValidationError.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ValidationError.java
index ad84046..e6a8ae4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ValidationError.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ValidationError.java
@@ -14,6 +14,8 @@
 
 package com.google.gerrit.server.git;
 
+import org.slf4j.Logger;
+
 /** Indicates a problem with Git based data. */
 public class ValidationError {
   private final String message;
@@ -42,4 +44,13 @@
   public interface Sink {
     void error(ValidationError error);
   }
+
+  public static Sink createLoggerSink(final String message, final Logger log) {
+    return new ValidationError.Sink() {
+          @Override
+          public void error(ValidationError error) {
+            log.error(message + error.getMessage());
+          }
+        };
+  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupModule.java
index 7fb5f58..5939fd6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/GroupModule.java
@@ -16,13 +16,13 @@
 
 import static com.google.inject.Scopes.SINGLETON;
 
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.server.InternalUser;
 import com.google.gerrit.server.account.GroupBackend;
 import com.google.gerrit.server.account.IncludingGroupMembership;
 import com.google.gerrit.server.account.InternalGroupBackend;
 import com.google.gerrit.server.account.UniversalGroupBackend;
-import com.google.gerrit.server.config.FactoryModule;
 
 public class GroupModule extends FactoryModule {
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/Module.java
index 041e2fa..58b3ffb 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/Module.java
@@ -26,7 +26,6 @@
 import com.google.gerrit.server.group.AddMembers.UpdateMember;
 import com.google.gerrit.server.group.DeleteIncludedGroups.DeleteIncludedGroup;
 import com.google.gerrit.server.group.DeleteMembers.DeleteMember;
-import com.google.inject.assistedinject.FactoryModuleBuilder;
 
 public class Module extends RestApiModule {
   @Override
@@ -67,7 +66,7 @@
     put(INCLUDED_GROUP_KIND).to(UpdateIncludedGroup.class);
     delete(INCLUDED_GROUP_KIND).to(DeleteIncludedGroup.class);
 
-    install(new FactoryModuleBuilder().build(CreateGroup.Factory.class));
+    factory(CreateGroup.Factory.class);
 
     DynamicSet.bind(binder(), GroupMemberAuditListener.class).to(
         DbGroupMemberAuditListener.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java
index 9bb0816..8948ce3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java
@@ -109,11 +109,11 @@
       for (Account.Id who : changeData.reviewers().values()) {
         names.add(getNameEmailFor(who));
       }
-
       for (String name : names) {
         appendText("Gerrit-Reviewer: " + name + "\n");
       }
     } catch (OrmException e) {
+      log.warn("Cannot get change reviewers", e);
     }
     formatFooter();
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailModule.java
index f50f538..a125517 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailModule.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.server.mail;
 
-import com.google.gerrit.server.config.FactoryModule;
+import com.google.gerrit.extensions.config.FactoryModule;
 
 public class EmailModule extends FactoryModule {
   @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/SmtpEmailSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/SmtpEmailSender.java
index 9baada5..785093a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/SmtpEmailSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/SmtpEmailSender.java
@@ -278,6 +278,7 @@
         try {
           client.disconnect();
         } catch (IOException e2) {
+          //Ignored
         }
       }
       throw new EmailException(e.getMessage(), e);
@@ -286,6 +287,7 @@
         try {
           client.disconnect();
         } catch (IOException e2) {
+          // Ignored
         }
       }
       throw e;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/NoteDbModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/NoteDbModule.java
index 4193fe4..75d926b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/NoteDbModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/NoteDbModule.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.server.notedb;
 
-import com.google.gerrit.server.config.FactoryModule;
+import com.google.gerrit.extensions.config.FactoryModule;
 
 public class NoteDbModule extends FactoryModule {
   @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListLoader.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListLoader.java
index e22ea57..5cc8b87 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListLoader.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListLoader.java
@@ -391,17 +391,14 @@
         Map<String, ObjectId> resolved = new HashMap<>();
         for (Map.Entry<String, MergeResult<? extends Sequence>> entry : r.entrySet()) {
           MergeResult<? extends Sequence> p = entry.getValue();
-          TemporaryBuffer buf =
-              new TemporaryBuffer.LocalFile(null, 10 * 1024 * 1024);
-          try {
+          try (TemporaryBuffer buf =
+              new TemporaryBuffer.LocalFile(null, 10 * 1024 * 1024)) {
             fmt.formatMerge(buf, p, "BASE", oursName, theirsName, "UTF-8");
             buf.close();
 
             try (InputStream in = buf.openInputStream()) {
               resolved.put(entry.getKey(), ins.insert(Constants.OBJ_BLOB, buf.length(), in));
             }
-          } finally {
-            buf.destroy();
           }
         }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/InstallPlugin.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/InstallPlugin.java
index 83643ff..13f8098 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/InstallPlugin.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/InstallPlugin.java
@@ -59,20 +59,8 @@
       throw new MethodNotAllowedException("remote installation is disabled");
     }
     try {
-      InputStream in;
-      if (input.raw != null) {
-        in = input.raw.getInputStream();
-      } else {
-        try {
-          in = new URL(input.url).openStream();
-        } catch (IOException e) {
-          throw new BadRequestException(e.getMessage());
-        }
-      }
-      try {
+      try (InputStream in = openStream(input)) {
         loader.installPluginFromStream(name, in);
-      } finally {
-        in.close();
       }
     } catch (PluginInstallException e) {
       StringWriter buf = new StringWriter();
@@ -93,6 +81,18 @@
     return created ? Response.created(info) : Response.ok(info);
   }
 
+  private InputStream openStream(Input input)
+      throws IOException, BadRequestException {
+    if (input.raw != null) {
+      return input.raw.getInputStream();
+    }
+    try {
+      return new URL(input.url).openStream();
+    } catch (IOException e) {
+      throw new BadRequestException(e.getMessage());
+    }
+  }
+
   @RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
   static class Overwrite implements RestModifyView<PluginResource, Input> {
     private final PluginLoader loader;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginCleanerTask.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginCleanerTask.java
index c13c533..6c4c451 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginCleanerTask.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginCleanerTask.java
@@ -45,6 +45,7 @@
         Thread.sleep(50);
       }
     } catch (InterruptedException e) {
+      // Ignored
     }
 
     int left = loader.processPendingCleanups();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginScannerThread.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginScannerThread.java
index a484c5d..e6c3dbd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginScannerThread.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/PluginScannerThread.java
@@ -37,6 +37,7 @@
           return;
         }
       } catch (InterruptedException e) {
+        // Ignored
       }
       loader.rescan();
     }
@@ -47,6 +48,7 @@
     try {
       join();
     } catch (InterruptedException e) {
+      // Ignored
     }
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/AccessControlModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/AccessControlModule.java
index 7215d18..29ab220 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/AccessControlModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/AccessControlModule.java
@@ -16,8 +16,8 @@
 
 import static com.google.inject.Scopes.SINGLETON;
 
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.server.config.FactoryModule;
 import com.google.gerrit.server.config.GitReceivePackGroups;
 import com.google.gerrit.server.config.GitReceivePackGroupsProvider;
 import com.google.gerrit.server.config.GitUploadPackGroups;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfo.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfo.java
index 1801712..ae8fd53 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfo.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfo.java
@@ -31,11 +31,8 @@
 import com.google.gerrit.server.config.ProjectConfigEntry;
 import com.google.gerrit.server.extensions.webui.UiActions;
 import com.google.gerrit.server.git.TransferConfig;
-import com.google.gerrit.server.git.gpg.SignedPushModule;
 import com.google.inject.util.Providers;
 
-import org.eclipse.jgit.lib.Config;
-
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
@@ -58,7 +55,7 @@
   public Map<String, CommentLinkInfo> commentlinks;
   public ThemeInfo theme;
 
-  public ConfigInfo(Config gerritConfig,
+  public ConfigInfo(boolean serverEnableSignedPush,
       ProjectControl control,
       TransferConfig config,
       DynamicMap<ProjectConfigEntry> pluginConfigEntries,
@@ -112,7 +109,7 @@
     this.useContentMerge = useContentMerge;
     this.requireChangeId = requireChangeId;
     this.createNewChangeForAllNotInTarget = createNewChangeForAllNotInTarget;
-    if (SignedPushModule.isEnabled(gerritConfig)) {
+    if (serverEnableSignedPush) {
       this.enableSignedPush = enableSignedPush;
     }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetConfig.java
index 2ab10c9..469da93 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetConfig.java
@@ -17,19 +17,17 @@
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.extensions.restapi.RestView;
+import com.google.gerrit.server.EnableSignedPush;
 import com.google.gerrit.server.config.AllProjectsNameProvider;
-import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gerrit.server.config.ProjectConfigEntry;
 import com.google.gerrit.server.git.TransferConfig;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
-import org.eclipse.jgit.lib.Config;
-
 @Singleton
 public class GetConfig implements RestReadView<ProjectResource> {
-  private final Config gerritConfig;
+  private final boolean serverEnableSignedPush;
   private final TransferConfig config;
   private final DynamicMap<ProjectConfigEntry> pluginConfigEntries;
   private final PluginConfigFactory cfgFactory;
@@ -37,13 +35,13 @@
   private final DynamicMap<RestView<ProjectResource>> views;
 
   @Inject
-  public GetConfig(@GerritServerConfig Config gerritConfig,
+  public GetConfig(@EnableSignedPush boolean serverEnableSignedPush,
       TransferConfig config,
       DynamicMap<ProjectConfigEntry> pluginConfigEntries,
       PluginConfigFactory cfgFactory,
       AllProjectsNameProvider allProjects,
       DynamicMap<RestView<ProjectResource>> views) {
-    this.gerritConfig = gerritConfig;
+    this.serverEnableSignedPush = serverEnableSignedPush;
     this.config = config;
     this.pluginConfigEntries = pluginConfigEntries;
     this.allProjects = allProjects;
@@ -53,7 +51,7 @@
 
   @Override
   public ConfigInfo apply(ProjectResource resource) {
-    return new ConfigInfo(gerritConfig, resource.getControl(), config,
+    return new ConfigInfo(serverEnableSignedPush, resource.getControl(), config,
         pluginConfigEntries, cfgFactory, allProjects, views);
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/Module.java
index 430d8f5..3ad6f8f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/Module.java
@@ -24,7 +24,6 @@
 
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.RestApiModule;
-import com.google.inject.assistedinject.FactoryModuleBuilder;
 
 public class Module extends RestApiModule {
   @Override
@@ -65,7 +64,7 @@
     get(BRANCH_KIND).to(GetBranch.class);
     delete(BRANCH_KIND).to(DeleteBranch.class);
     post(PROJECT_KIND, "branches:delete").to(DeleteBranches.class);
-    install(new FactoryModuleBuilder().build(CreateBranch.Factory.class));
+    factory(CreateBranch.Factory.class);
     get(BRANCH_KIND, "reflog").to(GetReflog.class);
     child(BRANCH_KIND, "files").to(FilesCollection.class);
     get(FILE_KIND, "content").to(GetContent.class);
@@ -81,7 +80,7 @@
     get(DASHBOARD_KIND).to(GetDashboard.class);
     put(DASHBOARD_KIND).to(SetDashboard.class);
     delete(DASHBOARD_KIND).to(DeleteDashboard.class);
-    install(new FactoryModuleBuilder().build(CreateProject.Factory.class));
+    factory(CreateProject.Factory.class);
 
     get(PROJECT_KIND, "config").to(GetConfig.class);
     put(PROJECT_KIND, "config").to(PutConfig.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
index b782e3d..7f7dff6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
@@ -352,19 +352,10 @@
     }
     final IdentifiedUser iUser = (IdentifiedUser) user;
 
-    boolean hasContactInfo = !missing(iUser.getAccount().getFullName())
-        && !missing(iUser.getAccount().getPreferredEmail())
-        && iUser.getAccount().isContactFiled();
-
     List<AccountGroup.UUID> okGroupIds = Lists.newArrayList();
-    List<AccountGroup.UUID> missingInfoGroupIds = Lists.newArrayList();
     for (ContributorAgreement ca : contributorAgreements) {
       List<AccountGroup.UUID> groupIds;
-      if (hasContactInfo || !ca.isRequireContactInformation()) {
-        groupIds = okGroupIds;
-      } else {
-        groupIds = missingInfoGroupIds;
-      }
+      groupIds = okGroupIds;
 
       for (PermissionRule rule : ca.getAccepted()) {
         if ((rule.getAction() == Action.ALLOW) && (rule.getGroup() != null)
@@ -378,28 +369,6 @@
       return Capable.OK;
     }
 
-    if (iUser.getEffectiveGroups().containsAnyOf(missingInfoGroupIds)) {
-      final StringBuilder msg = new StringBuilder();
-      for (ContributorAgreement ca : contributorAgreements) {
-        if (ca.isRequireContactInformation()) {
-          msg.append(ca.getName());
-          break;
-        }
-      }
-      msg.append(" contributor agreement requires");
-      msg.append(" current contact information.\n");
-      if (canonicalWebUrl != null) {
-        msg.append("\nPlease review your contact information");
-        msg.append(":\n\n  ");
-        msg.append(canonicalWebUrl);
-        msg.append("#");
-        msg.append(PageLinks.SETTINGS_CONTACT);
-        msg.append("\n");
-      }
-      msg.append("\n");
-      return new Capable(msg.toString());
-    }
-
     final StringBuilder msg = new StringBuilder();
     msg.append(" A Contributor Agreement must be completed before uploading");
     if (canonicalWebUrl != null) {
@@ -415,10 +384,6 @@
     return new Capable(msg.toString());
   }
 
-  private static boolean missing(final String value) {
-    return value == null || value.trim().equals("");
-  }
-
   private boolean canPerformOnAnyRef(String permissionName) {
     for (SectionMatcher matcher : access()) {
       AccessSection section = matcher.section;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java
index 7a4fb6e..e529043 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java
@@ -31,9 +31,9 @@
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.EnableSignedPush;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.config.AllProjectsNameProvider;
-import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.PluginConfig;
 import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.gerrit.server.config.ProjectConfigEntry;
@@ -48,7 +48,6 @@
 
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.ObjectId;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -78,7 +77,7 @@
     public Map<String, Map<String, ConfigValue>> pluginConfigValues;
   }
 
-  private final Config gerritConfig;
+  private final boolean serverEnableSignedPush;
   private final Provider<MetaDataUpdate.User> metaDataUpdateFactory;
   private final ProjectCache projectCache;
   private final GitRepositoryManager gitMgr;
@@ -92,7 +91,7 @@
   private final ChangeHooks hooks;
 
   @Inject
-  PutConfig(@GerritServerConfig Config gerritConfig,
+  PutConfig(@EnableSignedPush boolean serverEnableSignedPush,
       Provider<MetaDataUpdate.User> metaDataUpdateFactory,
       ProjectCache projectCache,
       GitRepositoryManager gitMgr,
@@ -104,7 +103,7 @@
       DynamicMap<RestView<ProjectResource>> views,
       ChangeHooks hooks,
       Provider<CurrentUser> currentUser) {
-    this.gerritConfig = gerritConfig;
+    this.serverEnableSignedPush = serverEnableSignedPush;
     this.metaDataUpdateFactory = metaDataUpdateFactory;
     this.projectCache = projectCache;
     this.gitMgr = gitMgr;
@@ -214,8 +213,9 @@
       }
 
       ProjectState state = projectStateFactory.create(projectConfig);
-      return new ConfigInfo(gerritConfig, state.controlFor(currentUser.get()),
-          config, pluginConfigEntries, cfgFactory, allProjects, views);
+      return new ConfigInfo(serverEnableSignedPush,
+          state.controlFor(currentUser.get()), config, pluginConfigEntries,
+          cfgFactory, allProjects, views);
     } catch (ConfigInvalidException err) {
       throw new ResourceConflictException("Cannot read project " + projectName, err);
     } catch (IOException err) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommitPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommitPredicate.java
index a63f80c..983e8ef 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommitPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommitPredicate.java
@@ -14,25 +14,24 @@
 
 package com.google.gerrit.server.query.change;
 
+import static com.google.gerrit.server.index.ChangeField.COMMIT;
 import static com.google.gerrit.server.index.ChangeField.EXACT_COMMIT;
+import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH;
 
 import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.server.index.ChangeField;
 import com.google.gerrit.server.index.FieldDef;
 import com.google.gerrit.server.index.IndexPredicate;
 import com.google.gerrit.server.index.Schema;
 import com.google.gwtorm.server.OrmException;
 
-import org.eclipse.jgit.lib.Constants;
-
 class CommitPredicate extends IndexPredicate<ChangeData> {
   static FieldDef<ChangeData, ?> commitField(Schema<ChangeData> schema,
       String id) {
-    if (id.length() == Constants.OBJECT_ID_STRING_LENGTH
+    if (id.length() == OBJECT_ID_STRING_LENGTH
         && schema.hasField(EXACT_COMMIT)) {
       return EXACT_COMMIT;
     }
-    return ChangeField.COMMIT;
+    return COMMIT;
   }
 
   CommitPredicate(Schema<ChangeData> schema, String id) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsVisibleToPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsVisibleToPredicate.java
index 7f91699..dc7a579 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsVisibleToPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsVisibleToPredicate.java
@@ -65,6 +65,7 @@
         return true;
       }
     } catch (NoSuchChangeException e) {
+      // Ignored
     }
     return false;
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllProjectsCreator.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllProjectsCreator.java
index b142bb0..9b6e36a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllProjectsCreator.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllProjectsCreator.java
@@ -96,27 +96,19 @@
   }
 
   public void create() throws IOException, ConfigInvalidException {
-    Repository git = null;
-    try {
-      git = mgr.openRepository(allProjectsName);
+    try (Repository git = mgr.openRepository(allProjectsName)) {
       initAllProjects(git);
     } catch (RepositoryNotFoundException notFound) {
       // A repository may be missing if this project existed only to store
       // inheritable permissions. For example 'All-Projects'.
-      try {
-        git = mgr.createRepository(allProjectsName);
+      try (Repository git = mgr.createRepository(allProjectsName)) {
         initAllProjects(git);
-
         RefUpdate u = git.updateRef(Constants.HEAD);
         u.link(RefNames.REFS_CONFIG);
       } catch (RepositoryNotFoundException err) {
         String name = allProjectsName.get();
         throw new IOException("Cannot create repository " + name, err);
       }
-    } finally {
-      if (git != null) {
-        git.close();
-      }
     }
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllUsersCreator.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllUsersCreator.java
index 3fa7986..22a345a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllUsersCreator.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllUsersCreator.java
@@ -61,22 +61,15 @@
   }
 
   public void create() throws IOException, ConfigInvalidException {
-    Repository git = null;
-    try {
-      git = mgr.openRepository(allUsersName);
+    try (Repository git = mgr.openRepository(allUsersName)) {
       initAllUsers(git);
     } catch (RepositoryNotFoundException notFound) {
-      try {
-        git = mgr.createRepository(allUsersName);
+      try (Repository git = mgr.createRepository(allUsersName)) {
         initAllUsers(git);
       } catch (RepositoryNotFoundException err) {
         String name = allUsersName.get();
         throw new IOException("Cannot create repository " + name, err);
       }
-    } finally {
-      if (git != null) {
-        git.close();
-      }
     }
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/BaseDataSourceType.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/BaseDataSourceType.java
index e72c3e9..fd1502d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/BaseDataSourceType.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/BaseDataSourceType.java
@@ -46,15 +46,12 @@
     if (path == null) {
       return ScriptRunner.NOOP;
     }
-    InputStream in =  ReviewDb.class.getResourceAsStream(path);
-    if (in == null) {
-      throw new IllegalStateException("SQL script " + path + " not found");
-    }
     ScriptRunner runner;
-    try {
+    try (InputStream in = ReviewDb.class.getResourceAsStream(path)) {
+      if (in == null) {
+        throw new IllegalStateException("SQL script " + path + " not found");
+      }
       runner = new ScriptRunner(path, in);
-    } finally {
-      in.close();
     }
     return runner;
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/DatabaseModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/DatabaseModule.java
index c7832d4..199b3bc 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/DatabaseModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/DatabaseModule.java
@@ -16,8 +16,8 @@
 
 import static com.google.inject.Scopes.SINGLETON;
 
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.config.FactoryModule;
 import com.google.gwtorm.jdbc.Database;
 import com.google.gwtorm.server.SchemaFactory;
 import com.google.inject.TypeLiteral;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaModule.java
index 6faf148..3770b82 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaModule.java
@@ -16,6 +16,7 @@
 
 import static com.google.inject.Scopes.SINGLETON;
 
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.server.GerritPersonIdent;
 import com.google.gerrit.server.GerritPersonIdentProvider;
 import com.google.gerrit.server.config.AllProjectsName;
@@ -24,7 +25,6 @@
 import com.google.gerrit.server.config.AllUsersNameProvider;
 import com.google.gerrit.server.config.AnonymousCowardName;
 import com.google.gerrit.server.config.AnonymousCowardNameProvider;
-import com.google.gerrit.server.config.FactoryModule;
 
 import org.eclipse.jgit.lib.PersonIdent;
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
index 7918df4..769329d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
@@ -32,7 +32,7 @@
 /** A version of the database schema. */
 public abstract class SchemaVersion {
   /** The current schema version. */
-  public static final Class<Schema_110> C = Schema_110.class;
+  public static final Class<Schema_111> C = Schema_111.class;
 
   public static int getBinaryVersion() {
     return guessVersion(C);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_111.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_111.java
new file mode 100644
index 0000000..223fdb6
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_111.java
@@ -0,0 +1,25 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.schema;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+public class Schema_111 extends SchemaVersion {
+  @Inject
+  Schema_111(Provider<Schema_110> prior) {
+    super(prior);
+  }
+}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/git/ProjectConfigTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/git/ProjectConfigTest.java
index ba06770..4f32ba8 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/git/ProjectConfigTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/git/ProjectConfigTest.java
@@ -82,7 +82,6 @@
             + "  description = A simple description\n" //
             + "  accepted = group Developers\n" //
             + "  accepted = group Staff\n" //
-            + "  requireContactInformation = true\n" //
             + "  autoVerify = group Developers\n" //
             + "  agreementUrl = http://www.example.com/agree\n")) //
         ));
@@ -97,7 +96,6 @@
     assertThat(ca.getAccepted().get(0).getGroup()).isEqualTo(developers);
     assertThat(ca.getAccepted().get(1).getGroup().getName()).isEqualTo("Staff");
     assertThat(ca.getAutoVerify().getName()).isEqualTo("Developers");
-    assertThat(ca.isRequireContactInformation()).isTrue();
 
     AccessSection section = cfg.getAccessSection("refs/heads/*");
     assertThat(section).isNotNull();
@@ -184,7 +182,6 @@
             + "[contributor-agreement \"Individual\"]\n" //
             + "  description = A simple description\n" //
             + "  accepted = group Developers\n" //
-            + "  requireContactInformation = true\n" //
             + "  autoVerify = group Developers\n" //
             + "  agreementUrl = http://www.example.com/agree\n")) //
         ));
@@ -197,7 +194,6 @@
     Permission submit = section.getPermission(Permission.SUBMIT);
     submit.add(new PermissionRule(cfg.resolve(staff)));
     ContributorAgreement ca = cfg.getContributorAgreement("Individual");
-    ca.setRequireContactInformation(false);
     ca.setAccepted(Collections.singletonList(new PermissionRule(cfg.resolve(staff))));
     ca.setAutoVerify(null);
     ca.setDescription("A new description");
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
index 108c20f..8030f9f 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
@@ -21,6 +21,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.gerrit.common.TimeUtil;
 import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.CommentRange;
@@ -41,7 +42,6 @@
 import com.google.gerrit.server.config.AnonymousCowardNameProvider;
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.DisableReverseDnsLookup;
-import com.google.gerrit.server.config.FactoryModule;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
 import com.google.gerrit.server.git.GitModule;
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java b/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java
index 6c3a39a..a671523 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/project/Util.java
@@ -28,6 +28,7 @@
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.common.data.PermissionRange;
 import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.client.AccountProjectWatch;
 import com.google.gerrit.reviewdb.client.Change;
@@ -52,7 +53,6 @@
 import com.google.gerrit.server.config.CanonicalWebUrl;
 import com.google.gerrit.server.config.CanonicalWebUrlProvider;
 import com.google.gerrit.server.config.DisableReverseDnsLookup;
-import com.google.gerrit.server.config.FactoryModule;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.git.GitRepositoryManager;
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/AndPredicateTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/AndPredicateTest.java
index 1cef4b4..382610f 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/AndPredicateTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/AndPredicateTest.java
@@ -27,7 +27,7 @@
 import java.util.Collections;
 import java.util.List;
 
-public class AndPredicateTest {
+public class AndPredicateTest extends PredicateTest {
   private static final class TestPredicate extends OperatorPredicate<String> {
     private TestPredicate(String name, String value) {
       super(name, value);
@@ -64,22 +64,16 @@
     final TestPredicate b = f("author", "bob");
     final Predicate<String> n = and(a, b);
 
-    try {
-      n.getChildren().clear();
-    } catch (RuntimeException e) {
-    }
+    exception.expect(UnsupportedOperationException.class);
+    n.getChildren().clear();
     assertChildren("clear", n, of(a, b));
 
-    try {
-      n.getChildren().remove(0);
-    } catch (RuntimeException e) {
-    }
+    exception.expect(UnsupportedOperationException.class);
+    n.getChildren().remove(0);
     assertChildren("remove(0)", n, of(a, b));
 
-    try {
-      n.getChildren().iterator().remove();
-    } catch (RuntimeException e) {
-    }
+    exception.expect(UnsupportedOperationException.class);
+    n.getChildren().iterator().remove();
     assertChildren("remove(0)", n, of(a, b));
   }
 
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/NotPredicateTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/NotPredicateTest.java
index 9df906c..45a747c 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/NotPredicateTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/NotPredicateTest.java
@@ -27,7 +27,7 @@
 import java.util.Collections;
 import java.util.List;
 
-public class NotPredicateTest {
+public class NotPredicateTest extends PredicateTest {
   private static final class TestPredicate extends OperatorPredicate<String> {
     private TestPredicate(String name, String value) {
       super(name, value);
@@ -70,22 +70,16 @@
     final TestPredicate p = f("author", "bob");
     final Predicate<String> n = not(p);
 
-    try {
-      n.getChildren().clear();
-    } catch (RuntimeException e) {
-    }
+    exception.expect(UnsupportedOperationException.class);
+    n.getChildren().clear();
     assertOnlyChild("clear", p, n);
 
-    try {
-      n.getChildren().remove(0);
-    } catch (RuntimeException e) {
-    }
+    exception.expect(UnsupportedOperationException.class);
+    n.getChildren().remove(0);
     assertOnlyChild("remove(0)", p, n);
 
-    try {
-      n.getChildren().iterator().remove();
-    } catch (RuntimeException e) {
-    }
+    exception.expect(UnsupportedOperationException.class);
+    n.getChildren().iterator().remove();
     assertOnlyChild("remove(0)", p, n);
   }
 
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/OrPredicateTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/OrPredicateTest.java
index 01b8588..ee5e0b0 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/OrPredicateTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/OrPredicateTest.java
@@ -27,7 +27,7 @@
 import java.util.Collections;
 import java.util.List;
 
-public class OrPredicateTest {
+public class OrPredicateTest extends PredicateTest {
   private static final class TestPredicate extends OperatorPredicate<String> {
     private TestPredicate(String name, String value) {
       super(name, value);
@@ -64,22 +64,16 @@
     final TestPredicate b = f("author", "bob");
     final Predicate<String> n = or(a, b);
 
-    try {
-      n.getChildren().clear();
-    } catch (RuntimeException e) {
-    }
+    exception.expect(UnsupportedOperationException.class);
+    n.getChildren().clear();
     assertChildren("clear", n, of(a, b));
 
-    try {
-      n.getChildren().remove(0);
-    } catch (RuntimeException e) {
-    }
+    exception.expect(UnsupportedOperationException.class);
+    n.getChildren().remove(0);
     assertChildren("remove(0)", n, of(a, b));
 
-    try {
-      n.getChildren().iterator().remove();
-    } catch (RuntimeException e) {
-    }
+    exception.expect(UnsupportedOperationException.class);
+    n.getChildren().iterator().remove();
     assertChildren("remove(0)", n, of(a, b));
   }
 
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/PredicateTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/PredicateTest.java
new file mode 100644
index 0000000..865841e
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/PredicateTest.java
@@ -0,0 +1,23 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.query;
+
+import org.junit.Rule;
+import org.junit.rules.ExpectedException;
+
+public class PredicateTest {
+  @Rule
+  public ExpectedException exception = ExpectedException.none();
+}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java
index 7412b3f..a161405 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java
@@ -16,6 +16,7 @@
 
 import static org.junit.Assert.assertEquals;
 
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.lifecycle.LifecycleManager;
 import com.google.gerrit.reviewdb.client.SystemConfig;
 import com.google.gerrit.reviewdb.server.ReviewDb;
@@ -25,7 +26,6 @@
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.config.AnonymousCowardName;
 import com.google.gerrit.server.config.AnonymousCowardNameProvider;
-import com.google.gerrit.server.config.FactoryModule;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.git.GitRepositoryManager;
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java
index e594255..e5cd619 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java
@@ -20,6 +20,8 @@
 import com.google.common.util.concurrent.MoreExecutors;
 import com.google.gerrit.common.ChangeHooks;
 import com.google.gerrit.common.DisabledChangeHooks;
+import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.gerrit.gpg.GpgModule;
 import com.google.gerrit.reviewdb.client.AuthType;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.GerritPersonIdent;
@@ -33,7 +35,6 @@
 import com.google.gerrit.server.config.AnonymousCowardNameProvider;
 import com.google.gerrit.server.config.CanonicalWebUrlModule;
 import com.google.gerrit.server.config.CanonicalWebUrlProvider;
-import com.google.gerrit.server.config.FactoryModule;
 import com.google.gerrit.server.config.GerritGlobalModule;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.SitePath;
@@ -186,6 +187,7 @@
     install(new DefaultCacheFactory.Module());
     install(new FakeEmailSender.Module());
     install(new SignedTokenEmailTokenVerifier.Module());
+    install(new GpgModule(cfg));
 
     IndexType indexType = null;
     try {
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/TestChanges.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/TestChanges.java
index 675634e..69eecc3 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/testutil/TestChanges.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/TestChanges.java
@@ -18,6 +18,7 @@
 
 import com.google.common.collect.Ordering;
 import com.google.gerrit.common.TimeUtil;
+import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.Change;
@@ -28,7 +29,6 @@
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.config.AllUsersNameProvider;
-import com.google.gerrit.server.config.FactoryModule;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.notedb.ChangeDraftUpdate;
 import com.google.gerrit.server.notedb.ChangeNotes;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/BaseCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/BaseCommand.java
index 6d7ad0f..a84570b 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/BaseCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/BaseCommand.java
@@ -356,6 +356,7 @@
         err.write((f.getMessage() + "\n").getBytes(ENC));
         err.flush();
       } catch (IOException e2) {
+        // Ignored
       } catch (Throwable e2) {
         log.warn("Cannot send failure message to client", e2);
       }
@@ -366,6 +367,7 @@
         err.write("fatal: internal server error\n".getBytes(ENC));
         err.flush();
       } catch (IOException e2) {
+        // Ignored
       } catch (Throwable e2) {
         log.warn("Cannot send internal server error message to client", e2);
       }
@@ -450,10 +452,12 @@
           try {
             out.flush();
           } catch (Throwable e2) {
+            // Ignored
           }
           try {
             err.flush();
           } catch (Throwable e2) {
+            // Ignored
           }
           rc = handleError(e);
         } finally {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/Commands.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/Commands.java
index 1a5e62cc..e964819 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/Commands.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/Commands.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.sshd;
 
+import com.google.auto.value.AutoAnnotation;
 import com.google.inject.Key;
 
 import org.apache.sshd.server.Command;
@@ -47,35 +48,9 @@
   }
 
   /** Create a CommandName annotation for the supplied name. */
-  public static CommandName named(final String name) {
-    return new CommandName() {
-      @Override
-      public String value() {
-        return name;
-      }
-
-      @Override
-      public Class<? extends Annotation> annotationType() {
-        return CommandName.class;
-      }
-
-      @Override
-      public int hashCode() {
-        // This is specified in java.lang.Annotation.
-        return (127 * "value".hashCode()) ^ value().hashCode();
-      }
-
-      @Override
-      public boolean equals(final Object obj) {
-        return obj instanceof CommandName
-            && value().equals(((CommandName) obj).value());
-      }
-
-      @Override
-      public String toString() {
-        return "@" + CommandName.class.getName() + "(value=" + value() + ")";
-      }
-    };
+  @AutoAnnotation
+  public static CommandName named(final String value) {
+    return new AutoAnnotation_Commands_named(value);
   }
 
   /** Create a CommandName annotation for the supplied name. */
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/NoShell.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/NoShell.java
index a63abee..0207ede 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/NoShell.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/NoShell.java
@@ -187,6 +187,7 @@
         try {
           return new URL(url).getHost();
         } catch (MalformedURLException e) {
+          // Ignored
         }
       }
       return SystemReader.getInstance().getHostname();
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginInstallCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginInstallCommand.java
index 799686d..5d6621f 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginInstallCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginInstallCommand.java
@@ -107,6 +107,7 @@
       try {
         data.close();
       } catch (IOException err) {
+        // Ignored
       }
     }
   }
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
index bc820a4..1ca4c8c 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
@@ -361,6 +361,7 @@
     try {
       err.write(msg.getBytes(ENC));
     } catch (IOException e) {
+      // Ignored
     }
   }
 
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
index 8521058..e9043f7 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
@@ -270,6 +270,7 @@
     try {
       err.write((type + ": " + msg + "\n").getBytes(ENC));
     } catch (IOException e) {
+      // Ignored
     }
   }
 
diff --git a/gerrit-war/BUCK b/gerrit-war/BUCK
index 1f82849..27b1f4a 100644
--- a/gerrit-war/BUCK
+++ b/gerrit-war/BUCK
@@ -6,6 +6,7 @@
   deps = [
     '//gerrit-cache-h2:cache-h2',
     '//gerrit-extension-api:api',
+    '//gerrit-gpg:gpg',
     '//gerrit-httpd:httpd',
     '//gerrit-lucene:lucene',
     '//gerrit-oauth:oauth',
diff --git a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
index 50c822e..905d776 100644
--- a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
+++ b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
@@ -19,6 +19,7 @@
 
 import com.google.common.base.Splitter;
 import com.google.gerrit.common.ChangeHookRunner;
+import com.google.gerrit.gpg.GpgModule;
 import com.google.gerrit.httpd.auth.oauth.OAuthModule;
 import com.google.gerrit.httpd.auth.openid.OpenIdModule;
 import com.google.gerrit.httpd.plugins.HttpPluginModule;
@@ -38,8 +39,6 @@
 import com.google.gerrit.server.config.GerritServerConfigModule;
 import com.google.gerrit.server.config.RestCacheAdminModule;
 import com.google.gerrit.server.config.SitePath;
-import com.google.gerrit.server.contact.ContactStoreModule;
-import com.google.gerrit.server.contact.HttpContactStoreConnection;
 import com.google.gerrit.server.git.ChangeCacheImplModule;
 import com.google.gerrit.server.git.GarbageCollectionModule;
 import com.google.gerrit.server.git.LocalDiskRepositoryManager;
@@ -109,6 +108,7 @@
   private Path sitePath;
   private Injector dbInjector;
   private Injector cfgInjector;
+  private Config config;
   private Injector sysInjector;
   private Injector webInjector;
   private Injector sshInjector;
@@ -165,6 +165,8 @@
       }
 
       cfgInjector = createCfgInjector();
+      config = cfgInjector.getInstance(
+          Key.get(Config.class, GerritServerConfig.class));
       sysInjector = createSysInjector();
       if (!sshdOff()) {
         sshInjector = createSshInjector();
@@ -204,8 +206,7 @@
   }
 
   private boolean sshdOff() {
-    Config cfg = cfgInjector.getInstance(Key.get(Config.class, GerritServerConfig.class));
-    return new SshAddressesModule().getListenAddresses(cfg).isEmpty();
+    return new SshAddressesModule().getListenAddresses(config).isEmpty();
   }
 
   private Injector createDbInjector() {
@@ -296,6 +297,7 @@
     modules.add(new SignedTokenEmailTokenVerifier.Module());
     modules.add(new PluginRestApiModule());
     modules.add(new RestCacheAdminModule());
+    modules.add(new GpgModule(config));
     AbstractModule changeIndexModule;
     switch (IndexModule.getIndexType(cfgInjector)) {
       case LUCENE:
@@ -344,9 +346,7 @@
       modules.add(new NoSshModule());
     }
     modules.add(H2CacheBasedWebSession.module());
-    modules.add(HttpContactStoreConnection.module());
     modules.add(new HttpPluginModule());
-    modules.add(new ContactStoreModule());
 
     AuthConfig authConfig = cfgInjector.getInstance(AuthConfig.class);
     if (authConfig.getAuthType() == AuthType.OPENID) {
diff --git a/lib/asciidoctor/BUCK b/lib/asciidoctor/BUCK
index f8feb63..ad13313 100644
--- a/lib/asciidoctor/BUCK
+++ b/lib/asciidoctor/BUCK
@@ -36,7 +36,7 @@
     '//lib:args4j',
     '//lib:guava',
     '//lib/lucene:analyzers-common',
-    '//lib/lucene:core',
+    '//lib/lucene:core-and-backward-codecs',
   ],
   visibility = ['//tools/eclipse:classpath'],
 )
diff --git a/lib/auto/BUCK b/lib/auto/BUCK
index a596420..c688ee4 100644
--- a/lib/auto/BUCK
+++ b/lib/auto/BUCK
@@ -2,10 +2,10 @@
 
 maven_jar(
   name = 'auto-value',
-  id = 'com.google.auto.value:auto-value:1.0',
-  sha1 = '5d13e60f5d190003176ca6ba4a410fae2e3f6315',
+  id = 'com.google.auto.value:auto-value:1.1',
+  sha1 = 'f6951c141ea3e89c0f8b01da16834880a1ebf162',
   # Exclude un-relocated dependencies and replace with our own versions; see
-  # https://github.com/google/auto/blob/auto-value-1.0/value/pom.xml#L147
+  # https://github.com/google/auto/blob/auto-value-1.1/value/pom.xml#L151
   exclude = ['org/apache/*'],
   deps = ['//lib:velocity'],
   license = 'Apache2.0',
diff --git a/lib/lucene/BUCK b/lib/lucene/BUCK
index 6ab33d9..5e4a82f 100644
--- a/lib/lucene/BUCK
+++ b/lib/lucene/BUCK
@@ -2,8 +2,19 @@
 
 VERSION = '5.2.1'
 
+# core and backward-codecs both provide
+# META-INF/services/org.apache.lucene.codecs.Codec, so they must be merged.
+merge_maven_jars(
+  name = 'core-and-backward-codecs',
+  srcs = [
+    ':backward-codecs_jar',
+    ':core_jar',
+  ],
+  visibility = ['PUBLIC'],
+)
+
 maven_jar(
-  name = 'core',
+  name = 'core_jar',
   id = 'org.apache.lucene:lucene-core:' + VERSION,
   sha1 = 'a175590aa8b04e079eb1a136fd159f9163482ba4',
   license = 'Apache2.0',
@@ -11,6 +22,7 @@
     'META-INF/LICENSE.txt',
     'META-INF/NOTICE.txt',
   ],
+  visibility = [],
 )
 
 maven_jar(
@@ -18,7 +30,7 @@
   id = 'org.apache.lucene:lucene-analyzers-common:' + VERSION,
   sha1 = '33b7cc17d5a7c939af6fe3f67563f4709926d7f5',
   license = 'Apache2.0',
-  deps = [':core'],
+  deps = [':core-and-backward-codecs'],
   exclude = [
     'META-INF/LICENSE.txt',
     'META-INF/NOTICE.txt',
@@ -26,15 +38,16 @@
 )
 
 maven_jar(
-  name = 'backward-codecs',
+  name = 'backward-codecs_jar',
   id = 'org.apache.lucene:lucene-backward-codecs:' + VERSION,
   sha1 = '603d1f06b133449272799d698e5118db65e523ba',
   license = 'Apache2.0',
-  deps = [':core'],
+  deps = [':core_jar'],
   exclude = [
     'META-INF/LICENSE.txt',
     'META-INF/NOTICE.txt',
   ],
+  visibility = [],
 )
 
 maven_jar(
@@ -42,7 +55,7 @@
   id = 'org.apache.lucene:lucene-misc:' + VERSION,
   sha1 = 'be0a4f0ac06f0a2fa3689b4bf6cd1fe6847f9969',
   license = 'Apache2.0',
-  deps = [':core'],
+  deps = [':core-and-backward-codecs'],
   exclude = [
     'META-INF/LICENSE.txt',
     'META-INF/NOTICE.txt',
@@ -54,7 +67,7 @@
   id = 'org.apache.lucene:lucene-queryparser:' + VERSION,
   sha1 = '73be0a2d4ab3e6b574be1938bfb27f7f730f0ad9',
   license = 'Apache2.0',
-  deps = [':core'],
+  deps = [':core-and-backward-codecs'],
   exclude = [
     'META-INF/LICENSE.txt',
     'META-INF/NOTICE.txt',
diff --git a/lib/maven.defs b/lib/maven.defs
index 17f0e00..dd8097e 100644
--- a/lib/maven.defs
+++ b/lib/maven.defs
@@ -150,3 +150,30 @@
       visibility = visibility,
     )
 
+
+def merge_maven_jars(
+    name,
+    srcs,
+    visibility = []):
+
+  def cmd(jars):
+    return ('$(location //tools:merge_jars) $OUT '
+            + ' '.join(['$(location %s)' % j for j in jars]))
+
+  genrule(
+    name = '%s__merged_bin' % name,
+    cmd = cmd(['%s__download_bin' % s for s in srcs]),
+    out = '%s__merged.jar' % name,
+  )
+  genrule(
+    name = '%s__merged_src' % name,
+    cmd = cmd(['%s__download_src' % s for s in srcs]),
+    # tools/eclipse/project.py requires -src.jar suffix.
+    out = '%s__merged-src.jar' % name,
+  )
+  prebuilt_jar(
+    name = name,
+    binary_jar = ':%s__merged_bin' % name,
+    source_jar = ':%s__merged_src' % name,
+    visibility = visibility,
+  )
diff --git a/plugins/cookbook-plugin b/plugins/cookbook-plugin
index 7306135..b9d3ca8 160000
--- a/plugins/cookbook-plugin
+++ b/plugins/cookbook-plugin
@@ -1 +1 @@
-Subproject commit 730613516a733fa33f684cbe03fe22ecf811216e
+Subproject commit b9d3ca8a65030071e28be19296ba867ab424fbbf
diff --git a/plugins/replication b/plugins/replication
index acdedee..cc91e0c 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit acdedee5e90bba60cec8be3cdfd9ab3642f4f287
+Subproject commit cc91e0c2987a4606e19b10e320b14f6a0c911c06
diff --git a/plugins/reviewnotes b/plugins/reviewnotes
index d81e2d6..26f38c4 160000
--- a/plugins/reviewnotes
+++ b/plugins/reviewnotes
@@ -1 +1 @@
-Subproject commit d81e2d6d3edc27c7aceea47518cbb03fb5590f11
+Subproject commit 26f38c4514687c388472be19c9789eaa84b1d564
diff --git a/plugins/singleusergroup b/plugins/singleusergroup
index 082bf62..6fb0101 160000
--- a/plugins/singleusergroup
+++ b/plugins/singleusergroup
@@ -1 +1 @@
-Subproject commit 082bf62238d4d815636a329cc1ef4d86b36f982e
+Subproject commit 6fb010107a7dfdd6baff2b54e65fb74c933d6654
diff --git a/tools/BUCK b/tools/BUCK
index ee26062..0bdff3c 100644
--- a/tools/BUCK
+++ b/tools/BUCK
@@ -6,6 +6,12 @@
 )
 
 python_binary(
+  name = 'merge_jars',
+  main = 'merge_jars.py',
+  visibility = ['PUBLIC'],
+)
+
+python_binary(
   name = 'pack_war',
   main = 'pack_war.py',
   deps = [':util'],
diff --git a/tools/checkstyle.xml b/tools/checkstyle.xml
index fc94144..1bb40f7 100644
--- a/tools/checkstyle.xml
+++ b/tools/checkstyle.xml
@@ -104,4 +104,7 @@
     <property name="checkFormat" value="$1"/>
     <property name="influenceFormat" value="$2"/>
   </module>
+  <module name="SuppressionFilter">
+    <property name="file" value="${samedir}/checkstyle_suppressions.xml"/>
+  </module>
 </module>
diff --git a/tools/checkstyle_suppressions.xml b/tools/checkstyle_suppressions.xml
new file mode 100644
index 0000000..5f5d9ee
--- /dev/null
+++ b/tools/checkstyle_suppressions.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE suppressions PUBLIC
+  "-//Puppy Crawl//DTD Suppressions 1.1//EN"
+  "http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
+<suppressions>
+  <suppress files="[/\\].apt_generated[/\\]" checks=".*"/>
+</suppressions>
diff --git a/tools/eclipse/BUCK b/tools/eclipse/BUCK
index 865c9d7..57f3afb 100644
--- a/tools/eclipse/BUCK
+++ b/tools/eclipse/BUCK
@@ -4,6 +4,7 @@
   name = 'classpath',
   deps = LIBS + PGMLIBS + [
     '//gerrit-acceptance-tests:lib',
+    '//gerrit-gpg:gpg_tests',
     '//gerrit-gwtdebug:gwtdebug',
     '//gerrit-gwtui:ui_module',
     '//gerrit-gwtui:ui_tests',
diff --git a/tools/merge_jars.py b/tools/merge_jars.py
new file mode 100755
index 0000000..46016c0
--- /dev/null
+++ b/tools/merge_jars.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+# Copyright (C) 2015 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.
+
+from __future__ import print_function
+import collections
+import sys
+import zipfile
+
+
+if len(sys.argv) < 3:
+  print('usage: %s <out.zip> <in.zip>...' % sys.argv[0], file=sys.stderr)
+  exit(1)
+
+outfile = sys.argv[1]
+infiles = sys.argv[2:]
+seen = set()
+SERVICES = 'META-INF/services/'
+
+try:
+  with zipfile.ZipFile(outfile, 'w') as outzip:
+    services = collections.defaultdict(lambda: '')
+    for infile in infiles:
+      with zipfile.ZipFile(infile) as inzip:
+        for info in inzip.infolist():
+          n = info.filename
+          if n in seen:
+            continue
+          elif n.startswith(SERVICES):
+            # Concatenate all provider configuration files.
+            services[n] += inzip.read(n)
+            continue
+          outzip.writestr(info, inzip.read(n))
+          seen.add(n)
+
+    for n, v in services.iteritems():
+      outzip.writestr(n, v)
+except Exception as err:
+  exit('Failed to merge jars: %s' % err)