initial revision

Change-Id: I8a62cfe06069af8d8946f579239f84b3484d4788
diff --git a/.buckconfig b/.buckconfig
new file mode 100644
index 0000000..4077128
--- /dev/null
+++ b/.buckconfig
@@ -0,0 +1,11 @@
+[alias]
+  plugin = //:reviewers
+
+[buildfile]
+  includes = //lib/build.defs
+
+[java]
+  src_roots = java, resources
+
+[project]
+  ignore = .git
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..85b5abe
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+/target
+/.classpath
+/.project
+/.settings
+/.buckconfig.local
+/.buckd
+/buck-cache
+/buck-out
+/local.properties
+*.pyc
diff --git a/BUCK b/BUCK
new file mode 100644
index 0000000..572baad
--- /dev/null
+++ b/BUCK
@@ -0,0 +1,18 @@
+API_VERSION = '2.9-SNAPSHOT'
+REPO = MAVEN_LOCAL
+
+gerrit_plugin(
+  name = 'reviewers',
+  srcs = glob(['src/main/java/**/*.java']),
+  resources = glob(['src/main/resources/**/*']),
+  manifest_entries = [
+    'Gerrit-PluginName: reviewers',
+    'Gerrit-Module: com.googlesource.gerrit.plugins.reviewers.Module',
+  ]
+)
+
+maven_jar(
+  name = 'plugin-lib',
+  id = 'com.google.gerrit:gerrit-plugin-api:' + API_VERSION,
+  repository = REPO,
+)
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..11069ed
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+                              Apache License
+                        Version 2.0, January 2004
+                     http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+   "License" shall mean the terms and conditions for use, reproduction,
+   and distribution as defined by Sections 1 through 9 of this document.
+
+   "Licensor" shall mean the copyright owner or entity authorized by
+   the copyright owner that is granting the License.
+
+   "Legal Entity" shall mean the union of the acting entity and all
+   other entities that control, are controlled by, or are under common
+   control with that entity. For the purposes of this definition,
+   "control" means (i) the power, direct or indirect, to cause the
+   direction or management of such entity, whether by contract or
+   otherwise, or (ii) ownership of fifty percent (50%) or more of the
+   outstanding shares, or (iii) beneficial ownership of such entity.
+
+   "You" (or "Your") shall mean an individual or Legal Entity
+   exercising permissions granted by this License.
+
+   "Source" form shall mean the preferred form for making modifications,
+   including but not limited to software source code, documentation
+   source, and configuration files.
+
+   "Object" form shall mean any form resulting from mechanical
+   transformation or translation of a Source form, including but
+   not limited to compiled object code, generated documentation,
+   and conversions to other media types.
+
+   "Work" shall mean the work of authorship, whether in Source or
+   Object form, made available under the License, as indicated by a
+   copyright notice that is included in or attached to the work
+   (an example is provided in the Appendix below).
+
+   "Derivative Works" shall mean any work, whether in Source or Object
+   form, that is based on (or derived from) the Work and for which the
+   editorial revisions, annotations, elaborations, or other modifications
+   represent, as a whole, an original work of authorship. For the purposes
+   of this License, Derivative Works shall not include works that remain
+   separable from, or merely link (or bind by name) to the interfaces of,
+   the Work and Derivative Works thereof.
+
+   "Contribution" shall mean any work of authorship, including
+   the original version of the Work and any modifications or additions
+   to that Work or Derivative Works thereof, that is intentionally
+   submitted to Licensor for inclusion in the Work by the copyright owner
+   or by an individual or Legal Entity authorized to submit on behalf of
+   the copyright owner. For the purposes of this definition, "submitted"
+   means any form of electronic, verbal, or written communication sent
+   to the Licensor or its representatives, including but not limited to
+   communication on electronic mailing lists, source code control systems,
+   and issue tracking systems that are managed by, or on behalf of, the
+   Licensor for the purpose of discussing and improving the Work, but
+   excluding communication that is conspicuously marked or otherwise
+   designated in writing by the copyright owner as "Not a Contribution."
+
+   "Contributor" shall mean Licensor and any individual or Legal Entity
+   on behalf of whom a Contribution has been received by Licensor and
+   subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   copyright license to reproduce, prepare Derivative Works of,
+   publicly display, publicly perform, sublicense, and distribute the
+   Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   (except as stated in this section) patent license to make, have made,
+   use, offer to sell, sell, import, and otherwise transfer the Work,
+   where such license applies only to those patent claims licensable
+   by such Contributor that are necessarily infringed by their
+   Contribution(s) alone or by combination of their Contribution(s)
+   with the Work to which such Contribution(s) was submitted. If You
+   institute patent litigation against any entity (including a
+   cross-claim or counterclaim in a lawsuit) alleging that the Work
+   or a Contribution incorporated within the Work constitutes direct
+   or contributory patent infringement, then any patent licenses
+   granted to You under this License for that Work shall terminate
+   as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+   Work or Derivative Works thereof in any medium, with or without
+   modifications, and in Source or Object form, provided that You
+   meet the following conditions:
+
+   (a) You must give any other recipients of the Work or
+       Derivative Works a copy of this License; and
+
+   (b) You must cause any modified files to carry prominent notices
+       stating that You changed the files; and
+
+   (c) You must retain, in the Source form of any Derivative Works
+       that You distribute, all copyright, patent, trademark, and
+       attribution notices from the Source form of the Work,
+       excluding those notices that do not pertain to any part of
+       the Derivative Works; and
+
+   (d) If the Work includes a "NOTICE" text file as part of its
+       distribution, then any Derivative Works that You distribute must
+       include a readable copy of the attribution notices contained
+       within such NOTICE file, excluding those notices that do not
+       pertain to any part of the Derivative Works, in at least one
+       of the following places: within a NOTICE text file distributed
+       as part of the Derivative Works; within the Source form or
+       documentation, if provided along with the Derivative Works; or,
+       within a display generated by the Derivative Works, if and
+       wherever such third-party notices normally appear. The contents
+       of the NOTICE file are for informational purposes only and
+       do not modify the License. You may add Your own attribution
+       notices within Derivative Works that You distribute, alongside
+       or as an addendum to the NOTICE text from the Work, provided
+       that such additional attribution notices cannot be construed
+       as modifying the License.
+
+   You may add Your own copyright statement to Your modifications and
+   may provide additional or different license terms and conditions
+   for use, reproduction, or distribution of Your modifications, or
+   for any such Derivative Works as a whole, provided Your use,
+   reproduction, and distribution of the Work otherwise complies with
+   the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+   any Contribution intentionally submitted for inclusion in the Work
+   by You to the Licensor shall be under the terms and conditions of
+   this License, without any additional terms or conditions.
+   Notwithstanding the above, nothing herein shall supersede or modify
+   the terms of any separate license agreement you may have executed
+   with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+   names, trademarks, service marks, or product names of the Licensor,
+   except as required for reasonable and customary use in describing the
+   origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+   agreed to in writing, Licensor provides the Work (and each
+   Contributor provides its Contributions) on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+   implied, including, without limitation, any warranties or conditions
+   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+   PARTICULAR PURPOSE. You are solely responsible for determining the
+   appropriateness of using or redistributing the Work and assume any
+   risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+   whether in tort (including negligence), contract, or otherwise,
+   unless required by applicable law (such as deliberate and grossly
+   negligent acts) or agreed to in writing, shall any Contributor be
+   liable to You for damages, including any direct, indirect, special,
+   incidental, or consequential damages of any character arising as a
+   result of this License or out of the use or inability to use the
+   Work (including but not limited to damages for loss of goodwill,
+   work stoppage, computer failure or malfunction, or any and all
+   other commercial damages or losses), even if such Contributor
+   has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+   the Work or Derivative Works thereof, You may choose to offer,
+   and charge a fee for, acceptance of support, warranty, indemnity,
+   or other liability obligations and/or rights consistent with this
+   License. However, in accepting such obligations, You may act only
+   on Your own behalf and on Your sole responsibility, not on behalf
+   of any other Contributor, and only if You agree to indemnify,
+   defend, and hold each Contributor harmless for any liability
+   incurred by, or claims asserted against, such Contributor by reason
+   of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+   To apply the Apache License to your work, attach the following
+   boilerplate notice, with the fields enclosed by brackets "[]"
+   replaced with your own identifying information. (Don't include
+   the brackets!)  The text should be enclosed in the appropriate
+   comment syntax for the file format. We also recommend that a
+   file or class name and description of purpose be included on the
+   same "printed page" as the copyright notice for easier
+   identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..d3827e7
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+1.0
diff --git a/lib/build.defs b/lib/build.defs
new file mode 100644
index 0000000..08418d0
--- /dev/null
+++ b/lib/build.defs
@@ -0,0 +1,76 @@
+include_defs('//lib/maven.defs')
+
+def java_library2(
+    name,
+    srcs = [],
+    resources = [],
+    deps = [],
+    compile_deps = [],
+    visibility = []):
+  c = name + '__compile'
+  t = name + '__link'
+  j = 'lib__%s__output/%s.jar' % (c, c)
+  o = 'lib__%s__output/%s.jar' % (name, name)
+  java_library(
+    name = c,
+    srcs = srcs,
+    resources = resources,
+    deps = deps + compile_deps,
+    visibility = visibility,
+  )
+  genrule(
+    name = t,
+    cmd = 'mkdir -p $(dirname $OUT);ln -s $SRCS $OUT',
+    srcs = [genfile(j)],
+    deps = [':' + c],
+    out = o,
+  )
+  prebuilt_jar(
+    name = name,
+    binary_jar = genfile(o),
+    deps = deps + [':' + t],
+    visibility = visibility,
+  )
+
+def gerrit_plugin(
+    name,
+    deps = [],
+    srcs = [],
+    resources = [],
+    manifest_file = None,
+    manifest_entries = [],
+    type = 'plugin',
+    visibility = ['PUBLIC']):
+  mf_cmd = 'v=$(cat VERSION);'
+  if manifest_file:
+    mf_src = [manifest_file]
+    mf_cmd += 'sed "s:@VERSION@:$v:g" $SRCS >$OUT'
+  else:
+    mf_src = []
+    mf_cmd += 'echo "Manifest-Version: 1.0" >$OUT;'
+    mf_cmd += 'echo "Gerrit-ApiType: %s" >>$OUT;' % type
+    mf_cmd += 'echo "Implementation-Version: $v" >>$OUT'
+    for line in manifest_entries:
+      mf_cmd += ';echo "%s" >> $OUT' % line
+  genrule(
+    name = name + '__manifest',
+    cmd = mf_cmd,
+    srcs = mf_src,
+    out = 'MANIFEST.MF',
+  )
+  java_library2(
+    name = name + '__plugin',
+    srcs = srcs,
+    resources = resources,
+    deps = deps,
+    compile_deps = ['//:%s-lib' % type],
+  )
+  java_binary(
+    name = name,
+    manifest_file = genfile('MANIFEST.MF'),
+    deps = [
+      ':%s__plugin' % name,
+      ':%s__manifest' % name,
+    ],
+    visibility = visibility,
+  )
diff --git a/lib/maven.defs b/lib/maven.defs
new file mode 100644
index 0000000..4ec5030
--- /dev/null
+++ b/lib/maven.defs
@@ -0,0 +1,61 @@
+GERRIT = 'GERRIT:'
+MAVEN_LOCAL = 'MAVEN_LOCAL:'
+
+def maven_jar(
+    name,
+    id,
+    deps = [],
+    sha1 = '',
+    bin_sha1 = '',
+    src_sha1 = '',
+    repository = GERRIT,
+    attach_source = True,
+    visibility = ['PUBLIC']):
+  from os import path
+
+  parts = id.split(':')
+  if len(parts) != 3:
+    raise NameError('expected id="groupId:artifactId:version"')
+  group, artifact, version = parts
+
+  file_version = version
+
+  jar = path.join(name, artifact.lower() + '-' + file_version)
+  url = '/'.join([
+    repository,
+    group.replace('.', '/'), artifact, version,
+    artifact + '-' + file_version])
+
+  binjar = jar + '.jar'
+  binurl = url + '.jar'
+
+  srcjar = jar + '-src.jar'
+  srcurl = url + '-sources.jar'
+
+  cmd = ['$(exe //tools:download_file)', '-o', '$OUT', '-u', binurl]
+  if sha1:
+    cmd.extend(['-v', sha1])
+  elif bin_sha1:
+    cmd.extend(['-v', bin_sha1])
+
+  genrule(
+    name = name + '__download_bin',
+    cmd = ' '.join(cmd),
+    deps = ['//tools:download_file'],
+    out = binjar,
+  )
+
+  srcjar = None
+  genrule(
+    name = name + '__download_src',
+    cmd = ':>$OUT',
+    out = '__' + name + '__no_src',
+  )
+
+  prebuilt_jar(
+    name = name,
+    deps = deps + [':' + name + '__download_bin'],
+    binary_jar = genfile(binjar),
+    source_jar = genfile(srcjar) if srcjar else None,
+    visibility = visibility,
+  )
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..bfbfeea
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2013 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>com.googlesource.gerrit.plugins.reviewers</groupId>
+  <artifactId>reviewers</artifactId>
+  <packaging>jar</packaging>
+  <version>2.9-SNAPSHOT</version>
+  <name>reviewers</name>
+
+  <properties>
+    <Gerrit-ApiType>plugin</Gerrit-ApiType>
+    <Gerrit-ApiVersion>${project.version}</Gerrit-ApiVersion>
+  </properties>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <version>2.4</version>
+        <configuration>
+          <archive>
+            <manifestEntries>
+              <Gerrit-PluginName>reviewers</Gerrit-PluginName>
+              <Gerrit-Module>com.googlesource.gerrit.plugins.reviewers.Module</Gerrit-Module>
+              <Implementation-Vendor>Gerrit Code Review</Implementation-Vendor>
+              <Implementation-URL>http://code.google.com/p/gerrit/</Implementation-URL>
+
+              <Implementation-Title>${Gerrit-ApiType} ${project.artifactId}</Implementation-Title>
+              <Implementation-Version>${project.version}</Implementation-Version>
+
+              <Gerrit-ApiType>${Gerrit-ApiType}</Gerrit-ApiType>
+              <Gerrit-ApiVersion>${Gerrit-ApiVersion}</Gerrit-ApiVersion>
+            </manifestEntries>
+          </archive>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <version>2.3.2</version>
+        <configuration>
+          <source>1.6</source>
+          <target>1.6</target>
+          <encoding>UTF-8</encoding>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>com.google.gerrit</groupId>
+      <artifactId>gerrit-${Gerrit-ApiType}-api</artifactId>
+      <version>${Gerrit-ApiVersion}</version>
+      <scope>provided</scope>
+    </dependency>
+  </dependencies>
+
+  <repositories>
+    <repository>
+      <id>gerrit-api-repository</id>
+      <url>https://gerrit-api.commondatastorage.googleapis.com/snapshot/</url>
+    </repository>
+  </repositories>
+  <description>Add default reviewers with different strategies</description>
+</project>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/ChangeEventListener.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/ChangeEventListener.java
new file mode 100644
index 0000000..6345fd3
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/ChangeEventListener.java
@@ -0,0 +1,235 @@
+// Copyright (C) 2013 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.googlesource.gerrit.plugins.reviewers;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Set;
+
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import com.google.gerrit.common.ChangeListener;
+import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountByEmailCache;
+import com.google.gerrit.server.account.AccountResolver;
+import com.google.gerrit.server.account.GroupMembers;
+import com.google.gerrit.server.config.PluginConfigFactory;
+import com.google.gerrit.server.events.ChangeEvent;
+import com.google.gerrit.server.events.PatchSetCreatedEvent;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.WorkQueue;
+import com.google.gerrit.server.group.GroupsCollection;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.util.RequestContext;
+import com.google.gerrit.server.util.ThreadLocalRequestContext;
+import com.google.gwtorm.server.OrmException;
+import com.google.gwtorm.server.SchemaFactory;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
+
+class ChangeEventListener implements ChangeListener {
+
+  private static final Logger log = LoggerFactory
+      .getLogger(ChangeEventListener.class);
+
+  private final AccountResolver accountResolver;
+  private final AccountByEmailCache byEmailCache;
+  private final Provider<GroupsCollection> groupsCollection;
+  private final GroupMembers.Factory groupMembersFactory;
+  private final DefaultReviewers.Factory reviewersFactory;
+  private final GitRepositoryManager repoManager;
+  private final WorkQueue workQueue;
+  private final IdentifiedUser.GenericFactory identifiedUserFactory;
+  private final ThreadLocalRequestContext tl;
+  private final SchemaFactory<ReviewDb> schemaFactory;
+  private final PluginConfigFactory cfg;
+  private final String pluginName;
+  private ReviewDb db;
+
+  @Inject
+  ChangeEventListener(
+      final AccountResolver accountResolver,
+      final AccountByEmailCache byEmailCache,
+      final Provider<GroupsCollection> groupsCollection,
+      final GroupMembers.Factory groupMembersFactory,
+      final DefaultReviewers.Factory reviewersFactory,
+      final GitRepositoryManager repoManager,
+      final WorkQueue workQueue,
+      final IdentifiedUser.GenericFactory identifiedUserFactory,
+      final ThreadLocalRequestContext tl,
+      final SchemaFactory<ReviewDb> schemaFactory,
+      final PluginConfigFactory cfg,
+      final @PluginName String pluginName) {
+    this.accountResolver = accountResolver;
+    this.byEmailCache = byEmailCache;
+    this.groupsCollection = groupsCollection;
+    this.groupMembersFactory = groupMembersFactory;
+    this.reviewersFactory = reviewersFactory;
+    this.repoManager = repoManager;
+    this.workQueue = workQueue;
+    this.identifiedUserFactory = identifiedUserFactory;
+    this.tl = tl;
+    this.schemaFactory = schemaFactory;
+    this.cfg = cfg;
+    this.pluginName = pluginName;
+  }
+
+  @Override
+  public void onChangeEvent(ChangeEvent event) {
+    if (!(event instanceof PatchSetCreatedEvent)) {
+      return;
+    }
+    PatchSetCreatedEvent e = (PatchSetCreatedEvent) event;
+    Project.NameKey projectName = new Project.NameKey(e.change.project);
+    Set<Account> reviewers;
+    try {
+      reviewers = reviewers(cfg
+          .getFromProjectConfigWithInheritance(projectName, pluginName)
+          .getStringList("reviewer"), projectName, e.uploader.email);
+    } catch (NoSuchProjectException x) {
+      log.error(x.getMessage(), x);
+      return;
+    }
+
+    if (reviewers.isEmpty()) {
+      return;
+    }
+
+    Repository git;
+    try {
+      git = repoManager.openRepository(projectName);
+    } catch (RepositoryNotFoundException x) {
+      log.error(x.getMessage(), x);
+      return;
+    } catch (IOException x) {
+      log.error(x.getMessage(), x);
+      return;
+    }
+
+    final ReviewDb reviewDb;
+    final RevWalk rw = new RevWalk(git);
+
+    try {
+      reviewDb = schemaFactory.open();
+      try {
+        Change.Id changeId = new Change.Id(Integer.parseInt(e.change.number));
+        PatchSet.Id psId = new PatchSet.Id(changeId,
+            Integer.parseInt(e.patchSet.number));
+        PatchSet ps = reviewDb.patchSets().get(psId);
+        if (ps == null) {
+          log.warn("Patch set " + psId.get() + " not found.");
+          return;
+        }
+
+        final Change change = reviewDb.changes().get(psId.getParentKey());
+        if (change == null) {
+          log.warn("Change " + changeId.get() + " not found.");
+          return;
+        }
+
+        final Runnable task = reviewersFactory.create(change, reviewers);
+
+        workQueue.getDefaultQueue().submit(new Runnable() {
+          public void run() {
+            RequestContext old = tl.setContext(new RequestContext() {
+
+              @Override
+              public CurrentUser getCurrentUser() {
+                return identifiedUserFactory.create(change.getOwner());
+              }
+
+              @Override
+              public Provider<ReviewDb> getReviewDbProvider() {
+                return new Provider<ReviewDb>() {
+                  @Override
+                  public ReviewDb get() {
+                    if (db == null) {
+                      try {
+                        db = schemaFactory.open();
+                      } catch (OrmException e) {
+                        throw new ProvisionException("Cannot open ReviewDb", e);
+                      }
+                    }
+                    return db;
+                  }
+                };
+              }
+            });
+            try {
+              task.run();
+            } finally {
+              tl.setContext(old);
+              if (db != null) {
+                db.close();
+                db = null;
+              }
+            }
+          }
+        });
+      } catch (OrmException x) {
+        log.error(x.getMessage(), x);
+      } finally {
+        reviewDb.close();
+      }
+    } catch (OrmException x) {
+      log.error(x.getMessage(), x);
+    } finally {
+      rw.release();
+      git.close();
+    }
+  }
+
+  private Set<Account> reviewers(String[] list, Project.NameKey p,
+      String uploaderEMail) {
+    if (list == null || list.length == 0) {
+      return Collections.emptySet();
+    }
+    Set<Account> reviewers = Sets.newHashSetWithExpectedSize(list.length);
+    GroupMembers groupMembers = null;
+    for (String r : list) {
+      try {
+        Account account = accountResolver.find(r);
+        if (account != null) {
+          reviewers.add(account);
+          continue;
+        }
+        if (groupMembers == null) {
+          groupMembers =
+              groupMembersFactory.create(identifiedUserFactory.create(Iterables
+                  .getOnlyElement(byEmailCache.get(uploaderEMail))));
+        }
+        reviewers.addAll(groupMembers.listAccounts(groupsCollection.get()
+            .parseInternal(r).getGroupUUID(), p));
+      } catch (Exception e) {
+        log.warn("Cannot resolve reviewer: " + r, e);
+      }
+    }
+    return reviewers;
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/DefaultReviewers.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/DefaultReviewers.java
new file mode 100644
index 0000000..1968a62
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/DefaultReviewers.java
@@ -0,0 +1,87 @@
+// Copyright (C) 2013 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.googlesource.gerrit.plugins.reviewers;
+
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.PostReviewers;
+import com.google.gerrit.server.project.ChangeControl;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.assistedinject.Assisted;
+
+public class DefaultReviewers implements Runnable {
+  private static final Logger log = LoggerFactory
+      .getLogger(DefaultReviewers.class);
+
+  private final Change change;
+  private final Set<Account> reviewers;
+  private final Provider<PostReviewers> reviewersProvider;
+  private final IdentifiedUser.GenericFactory identifiedUserFactory;
+  private final ChangeControl.GenericFactory changeControlFactory;
+
+  public interface Factory {
+    DefaultReviewers create(Change change,
+        Set<Account> reviewers);
+  }
+
+  @Inject
+  public DefaultReviewers(
+      ChangeControl.GenericFactory changeControlFactory,
+      Provider<PostReviewers> reviewersProvider,
+      IdentifiedUser.GenericFactory identifiedUserFactory,
+      @Assisted Change change, @Assisted Set<Account> reviewers) {
+    this.change = change;
+    this.reviewers = reviewers;
+    this.reviewersProvider = reviewersProvider;
+    this.identifiedUserFactory = identifiedUserFactory;
+    this.changeControlFactory = changeControlFactory;
+  }
+
+  @Override
+  public void run() {
+    addReviewers(reviewers, change);
+  }
+
+  /**
+   * Append the reviewers to change#{@link Change}
+   *
+   * @param topReviewers Set of reviewers proposed
+   * @param change {@link Change} to add the reviewers to
+   */
+  private void addReviewers(Set<Account> reviewers, Change change) {
+    try {
+      ChangeControl changeControl =
+          changeControlFactory.controlFor(change,
+              identifiedUserFactory.create(change.getOwner()));
+      ChangeResource changeResource = new ChangeResource(changeControl);
+      PostReviewers post = reviewersProvider.get();
+      for (Account account : reviewers) {
+        PostReviewers.Input input = new PostReviewers.Input();
+        input.reviewer = account.getId().toString();
+        post.apply(changeResource, input);
+      }
+    } catch (Exception ex) {
+      log.error("Couldn't add reviewers to the change", ex);
+    }
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/reviewers/Module.java b/src/main/java/com/googlesource/gerrit/plugins/reviewers/Module.java
new file mode 100644
index 0000000..3b4b803
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/reviewers/Module.java
@@ -0,0 +1,28 @@
+// Copyright (C) 2013 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.googlesource.gerrit.plugins.reviewers;
+
+import com.google.gerrit.common.ChangeListener;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.server.config.FactoryModule;
+
+public class Module extends FactoryModule {
+  @Override
+  protected void configure() {
+    DynamicSet.bind(binder(), ChangeListener.class).to(
+        ChangeEventListener.class);
+    factory(DefaultReviewers.Factory.class);
+  }
+}
diff --git a/src/main/resources/Documentation/about.md b/src/main/resources/Documentation/about.md
new file mode 100644
index 0000000..96d54b5
--- /dev/null
+++ b/src/main/resources/Documentation/about.md
@@ -0,0 +1,9 @@
+A plugin that allows adding default reviewers to a change.
+
+The configuration for adding reviewers to submitted changes can be
+[configured per project](config.html).
+
+SEE ALSO
+--------
+
+* [reviewers-by-blame plugin](https://gerrit-review.googlesource.com/#/admin/projects/plugins/reviewers-by-blame)
diff --git a/src/main/resources/Documentation/build.md b/src/main/resources/Documentation/build.md
new file mode 100644
index 0000000..997d702
--- /dev/null
+++ b/src/main/resources/Documentation/build.md
@@ -0,0 +1,43 @@
+Build
+=====
+
+This plugin can be built with Buck or Maven.
+
+Buck
+----
+
+Two modes of operations of building with Buck are supported: build in Gerrit
+tree and outside of the Gerrit tree.
+
+To build in Gerrit tree clone the plugin under plugins directory and run
+
+```
+  $>buck build plugins/reviewers:reviewers
+```
+
+from the Gerrit base directory. To build the plugin standalone (outside of
+the Gerrit tree), run
+
+```
+  $>buck build plugin
+```
+
+Maven
+-----
+
+To build with Maven, run
+
+```
+mvn clean package
+```
+
+Prerequisites
+-------------
+
+Only Gerrit in tree mode doesn't need gerrit-plugin-api dependency. For
+other build modes gerrit-plugin-api must be fetched from remote or local
+Maven repository.
+
+How to obtain the Gerrit Plugin API is described in the [Gerrit
+documentation](../../../Documentation/dev-buck.html#_extension_and_plugin_api_jar_files).
+
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
new file mode 100644
index 0000000..152f88d
--- /dev/null
+++ b/src/main/resources/Documentation/config.md
@@ -0,0 +1,20 @@
+Configuration
+=============
+
+The configuration of the @PLUGIN@ plugin is done on project level in
+the `project.config` file of the project. Missing values are inherited
+from the parent projects. This means a global default configuration can
+be done in the `project.config` file of the `All-Projects` root project.
+Other projects can then override the configuration in their own
+`project.config` file.
+
+```
+  [plugin "reviewers"]
+    reviewer = john.doe@example.com
+    reviewer = jane.doe@example.com
+    reviewer = QAGroup
+```
+
+plugin.reviewers.reviewer
+:	An account (email or full user name) or a group name. Multiple
+	`reviewer` occurrences are allowed.
diff --git a/tools/BUCK b/tools/BUCK
new file mode 100644
index 0000000..1b1175c
--- /dev/null
+++ b/tools/BUCK
@@ -0,0 +1,5 @@
+python_binary(
+  name = 'download_file',
+  main = 'download_file.py',
+  visibility = ['PUBLIC'],
+)
diff --git a/tools/download_file.py b/tools/download_file.py
new file mode 100755
index 0000000..8d76a40
--- /dev/null
+++ b/tools/download_file.py
@@ -0,0 +1,208 @@
+#!/usr/bin/python
+# Copyright (C) 2013 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
+
+from hashlib import sha1
+from optparse import OptionParser
+from os import link, makedirs, path, remove
+import shutil
+from subprocess import check_call, CalledProcessError
+from sys import stderr
+from zipfile import ZipFile, BadZipfile, LargeZipFile
+
+REPO_ROOTS = {
+  'GERRIT': 'http://gerrit-maven.storage.googleapis.com',
+  'ECLIPSE': 'https://repo.eclipse.org/content/groups/releases',
+  'MAVEN_CENTRAL': 'http://repo1.maven.org/maven2',
+  'MAVEN_LOCAL': 'file://' + path.expanduser('~/.m2/repository'),
+}
+
+GERRIT_HOME = path.expanduser('~/.gerritcodereview')
+CACHE_DIR = path.join(GERRIT_HOME, 'buck-cache')
+LOCAL_PROPERTIES = 'local.properties'
+
+
+def hashfile(p):
+  d = sha1()
+  with open(p, 'rb') as f:
+    while True:
+      b = f.read(8192)
+      if not b:
+        break
+      d.update(b)
+  return d.hexdigest()
+
+def safe_mkdirs(d):
+  if path.isdir(d):
+    return
+  try:
+    makedirs(d)
+  except OSError as err:
+    if not path.isdir(d):
+      raise err
+
+def download_properties(root_dir):
+  """ Get the download properties.
+
+  First tries to find the properties file in the given root directory,
+  and if not found there, tries in the Gerrit settings folder in the
+  user's home directory.
+
+  Returns a set of download properties, which may be empty.
+
+  """
+  p = {}
+  local_prop = path.join(root_dir, LOCAL_PROPERTIES)
+  if not path.isfile(local_prop):
+    local_prop = path.join(GERRIT_HOME, LOCAL_PROPERTIES)
+  if path.isfile(local_prop):
+    try:
+      with open(local_prop) as fd:
+        for line in fd:
+          if line.startswith('download.'):
+            d = [e.strip() for e in line.split('=', 1)]
+            name, url = d[0], d[1]
+            p[name[len('download.'):]] = url
+    except OSError:
+      pass
+  return p
+
+def cache_entry(args):
+  if args.v:
+    h = args.v
+  else:
+    h = sha1(args.u).hexdigest()
+  name = '%s-%s' % (path.basename(args.o), h)
+  return path.join(CACHE_DIR, name)
+
+def resolve_url(url, redirects):
+  s = url.find(':')
+  if s < 0:
+    return url
+  scheme, rest = url[:s], url[s+1:]
+  if scheme not in REPO_ROOTS:
+    return url
+  if scheme in redirects:
+    root = redirects[scheme]
+  else:
+    root = REPO_ROOTS[scheme]
+  root = root.rstrip('/')
+  rest = rest.lstrip('/')
+  return '/'.join([root, rest])
+
+opts = OptionParser()
+opts.add_option('-o', help='local output file')
+opts.add_option('-u', help='URL to download')
+opts.add_option('-v', help='expected content SHA-1')
+opts.add_option('-x', action='append', help='file to delete from ZIP')
+opts.add_option('--exclude_java_sources', action='store_true')
+opts.add_option('--unsign', action='store_true')
+args, _ = opts.parse_args()
+
+root_dir = args.o
+while root_dir:
+  root_dir, n = path.split(root_dir)
+  if n == 'buck-out':
+    break
+
+redirects = download_properties(root_dir)
+cache_ent = cache_entry(args)
+src_url = resolve_url(args.u, redirects)
+
+if not path.exists(cache_ent):
+  try:
+    safe_mkdirs(path.dirname(cache_ent))
+  except OSError as err:
+    print('error creating directory %s: %s' %
+          (path.dirname(cache_ent), err), file=stderr)
+    exit(1)
+
+  print('Download %s' % src_url, file=stderr)
+  try:
+    check_call(['curl', '--proxy-anyauth', '-sfo', cache_ent, src_url])
+  except OSError as err:
+    print('could not invoke curl: %s\nis curl installed?' % err, file=stderr)
+    exit(1)
+  except CalledProcessError as err:
+    print('error using curl: %s' % err, file=stderr)
+    exit(1)
+
+if args.v:
+  have = hashfile(cache_ent)
+  if args.v != have:
+    print((
+      '%s:\n' +
+      'expected %s\n' +
+      'received %s\n') % (src_url, args.v, have), file=stderr)
+    try:
+      remove(cache_ent)
+    except OSError as err:
+      if path.exists(cache_ent):
+        print('error removing %s: %s' % (cache_ent, err), file=stderr)
+    exit(1)
+
+exclude = []
+if args.x:
+  exclude += args.x
+if args.exclude_java_sources:
+  try:
+    zf = ZipFile(cache_ent, 'r')
+    try:
+      for n in zf.namelist():
+        if n.endswith('.java'):
+          exclude.append(n)
+    finally:
+      zf.close()
+  except (BadZipfile, LargeZipFile) as err:
+    print('error opening %s: %s'  % (cache_ent, err), file=stderr)
+    exit(1)
+
+if args.unsign:
+  try:
+    zf = ZipFile(cache_ent, 'r')
+    try:
+      for n in zf.namelist():
+        if (n.endswith('.RSA')
+            or n.endswith('.SF')
+            or n.endswith('.LIST')):
+          exclude.append(n)
+    finally:
+      zf.close()
+  except (BadZipfile, LargeZipFile) as err:
+    print('error opening %s: %s'  % (cache_ent, err), file=stderr)
+    exit(1)
+
+safe_mkdirs(path.dirname(args.o))
+if exclude:
+  try:
+    shutil.copyfile(cache_ent, args.o)
+  except (shutil.Error, IOError) as err:
+    print('error copying to %s: %s' % (args.o, err), file=stderr)
+    exit(1)
+  try:
+    check_call(['zip', '-d', args.o] + exclude)
+  except CalledProcessError as err:
+    print('error removing files from zip: %s' % err, file=stderr)
+    exit(1)
+else:
+  try:
+    link(cache_ent, args.o)
+  except OSError as err:
+    try:
+      shutil.copyfile(cache_ent, args.o)
+    except (shutil.Error, IOError) as err:
+      print('error copying to %s: %s' % (args.o, err), file=stderr)
+      exit(1)