Add auto assign plugin.

Depends on gerrit-owners for parsing the OWNERS files and then will
automatically set the reviewers whenever a change is created or updated.

It will only add new reviewers, never removing old ones.
diff --git a/README.md b/README.md
index ecd9d35..b722578 100644
--- a/README.md
+++ b/README.md
@@ -43,3 +43,8 @@
   gerrit_owners:add_owner_approval(Approvers, Ds, A),
   S =.. [submit | A].
 ```
+
+## Auto assigner
+
+There is a second plugin, `gerrit-owners-autoassign` which depends on `gerrit-owners`. It will automatically assign
+all of the owners to review a change when it's created or updated.
diff --git a/gerrit-owners-autoassign/pom.xml b/gerrit-owners-autoassign/pom.xml
new file mode 100644
index 0000000..e23dbec
--- /dev/null
+++ b/gerrit-owners-autoassign/pom.xml
@@ -0,0 +1,97 @@
+<!--
+Copyright (c) 2013 VMware, Inc. All Rights Reserved.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.vmware.gerrit</groupId>
+    <artifactId>gerrit-owners-parent</artifactId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>gerrit-owners-autoassign</artifactId>
+  <packaging>jar</packaging>
+  <version>1.0-SNAPSHOT</version>
+  <name>gerrit-owners-autoassign</name>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <version>2.4</version>
+      </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>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-shade-plugin</artifactId>
+        <configuration>
+          <createDependencyReducedPom>false</createDependencyReducedPom>
+          <promoteTransitiveDependencies>true</promoteTransitiveDependencies>
+          <artifactSet>
+            <excludes>
+              <exclude>com.gerritforge:*</exclude>
+              <exclude>com.google.gerrit:*</exclude>
+              <exclude>org.bouncycastle:*</exclude>
+              <exclude>org.slf4j:*</exclude>
+              <exclude>com.google.guava:*</exclude>
+              <exclude>org.eclipse.jgit:*</exclude>
+              <exclude>wsdl4j:wsdl4j:*</exclude>
+            </excludes>
+          </artifactSet>
+          <transformers>
+            <transformer
+                implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+              <manifestEntries>
+                <Implementation-Vendor>VMware, Inc.</Implementation-Vendor>
+                <Implementation-URL>http://github.com/vadims/gerrit-owners</Implementation-URL>
+                <Implementation-Title>${Gerrit-ApiType} ${project.artifactId}</Implementation-Title>
+                <Implementation-Version>${project.version}</Implementation-Version>
+                <Gerrit-ApiType>${Gerrit-ApiType}</Gerrit-ApiType>
+                <Gerrit-ApiVersion>${project.version}</Gerrit-ApiVersion>
+              </manifestEntries>
+            </transformer>
+          </transformers>
+        </configuration>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>shade</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+    </plugins>
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>com.google.gerrit</groupId>
+      <artifactId>gerrit-${Gerrit-ApiType}-api</artifactId>
+      <version>${Gerrit-ApiVersion}</version>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>com.vmware.gerrit</groupId>
+      <artifactId>gerrit-owners</artifactId>
+      <version>1.0-SNAPSHOT</version>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/gerrit-owners-autoassign/src/main/java/com/vmware/gerrit/owners/autoassigner/GitRefListener.java b/gerrit-owners-autoassign/src/main/java/com/vmware/gerrit/owners/autoassigner/GitRefListener.java
new file mode 100644
index 0000000..fcda396
--- /dev/null
+++ b/gerrit-owners-autoassign/src/main/java/com/vmware/gerrit/owners/autoassigner/GitRefListener.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2013 VMware, Inc. All Rights Reserved.
+ */
+package com.vmware.gerrit.owners.autoassigner;
+
+import com.vmware.gerrit.owners.PathOwners;
+
+import com.google.gerrit.extensions.annotations.Listen;
+import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.account.AccountResolver;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.patch.PatchList;
+import com.google.gerrit.server.patch.PatchListCache;
+import com.google.gerrit.server.patch.PatchListKey;
+import com.google.gerrit.server.patch.PatchListNotAvailableException;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Provider;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Inject;
+import java.io.IOException;
+import java.util.List;
+
+import static com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace.IGNORE_NONE;
+
+@Listen
+public class GitRefListener implements GitReferenceUpdatedListener {
+  private static final Logger logger = LoggerFactory.getLogger(GitRefListener.class);
+
+  private static final String CHANGES_REF = "refs/changes/";
+
+  private final Provider<ReviewDb> db;
+
+  private final PatchListCache patchListCache;
+
+  private final GitRepositoryManager repositoryManager;
+
+  private final AccountResolver accountResolver;
+
+  private final ReviewerManager reviewerManager;
+
+  @Inject
+  public GitRefListener(Provider<ReviewDb> db,
+                        PatchListCache patchListCache,
+                        GitRepositoryManager repositoryManager,
+                        AccountResolver accountResolver,
+                        ReviewerManager reviewerManager) {
+    this.db = db;
+    this.patchListCache = patchListCache;
+    this.repositoryManager = repositoryManager;
+    this.accountResolver = accountResolver;
+    this.reviewerManager = reviewerManager;
+  }
+
+  @Override
+  public void onGitReferenceUpdated(Event event) {
+    String projectName = event.getProjectName();
+    Repository repository;
+    try {
+      repository = repositoryManager.openRepository(Project.NameKey.parse(projectName));
+      try {
+        processUpdates(repository, event.getUpdates());
+      } finally {
+        repository.close();
+      }
+    } catch (IOException e) {
+      logger.warn("Couldn't open repository: {}", projectName, e);
+    }
+  }
+
+  private void processUpdates(Repository repository, List<Update> updates) {
+    for (Update update : updates) {
+      if (update.getRefName().startsWith(CHANGES_REF)) {
+        Change.Id id = Change.Id.fromRef(update.getRefName());
+        try {
+          Change change = db.get().changes().get(id);
+          PatchList patchList = getPatchList(update, change);
+          if (patchList != null) {
+            PathOwners owners = new PathOwners(accountResolver, repository, patchList);
+            reviewerManager.addReviewers(change, owners.get().values());
+          }
+        } catch (OrmException e) {
+          logger.warn("Could not open change: {}", id, e);
+        } catch (ReviewerManagerException e) {
+          logger.warn("Could not add reviewers for change: {}", id, e);
+        }
+      }
+    }
+  }
+
+  private PatchList getPatchList(Update update, Change change) {
+    ObjectId newId = null;
+    if (update.getNewObjectId() != null) {
+      newId = ObjectId.fromString(update.getNewObjectId());
+    }
+
+    PatchListKey plKey = new PatchListKey(change.getProject(), null, newId, IGNORE_NONE);
+    try {
+      return patchListCache.get(plKey);
+    } catch (PatchListNotAvailableException e) {
+      logger.warn("Could not load patch list: {}", plKey, e);
+    }
+    return null;
+  }
+}
diff --git a/gerrit-owners-autoassign/src/main/java/com/vmware/gerrit/owners/autoassigner/ReviewerManager.java b/gerrit-owners-autoassign/src/main/java/com/vmware/gerrit/owners/autoassigner/ReviewerManager.java
new file mode 100644
index 0000000..8e547be
--- /dev/null
+++ b/gerrit-owners-autoassign/src/main/java/com/vmware/gerrit/owners/autoassigner/ReviewerManager.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2013 VMware, Inc. All Rights Reserved.
+ */
+package com.vmware.gerrit.owners.autoassigner;
+
+import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.PostReviewers;
+import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gwtorm.server.OrmException;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+import java.util.Collection;
+
+@Singleton
+public class ReviewerManager {
+
+  private final Provider<CurrentUser> currentUserProvider;
+
+  private final Provider<PostReviewers> postReviewersProvider;
+
+  private final ChangeControl.GenericFactory changeControlFactory;
+
+  @Inject
+  public ReviewerManager(Provider<CurrentUser> currentUserProvider,
+                         Provider<PostReviewers> postReviewersProvider,
+                         ChangeControl.GenericFactory changeControlFactory) {
+    this.currentUserProvider = currentUserProvider;
+    this.postReviewersProvider = postReviewersProvider;
+    this.changeControlFactory = changeControlFactory;
+  }
+
+  public void addReviewers(Change change, Collection<Account.Id> reviewers) throws ReviewerManagerException {
+    try {
+      PostReviewers postReviewers = postReviewersProvider.get();
+      ChangeControl changeControl = changeControlFactory.controlFor(change, currentUserProvider.get());
+      ChangeResource changeResource = new ChangeResource(changeControl);
+
+      // HACK(vspivak): Using PostReviewers is probably inefficient here, however it has all the hook/notification
+      // logic, so it's easier to call it then to mimic/copy the logic here.
+      for (Account.Id accountId : reviewers) {
+        PostReviewers.Input input = new PostReviewers.Input();
+        input.reviewer = accountId.toString();
+        postReviewers.apply(changeResource, input);
+      }
+    } catch (RestApiException e) {
+      throw new ReviewerManagerException(e);
+    } catch (NoSuchChangeException e) {
+      throw new ReviewerManagerException(e);
+    } catch (EmailException e) {
+      throw new ReviewerManagerException(e);
+    } catch (OrmException e) {
+      throw new ReviewerManagerException(e);
+    }
+  }
+}
diff --git a/gerrit-owners-autoassign/src/main/java/com/vmware/gerrit/owners/autoassigner/ReviewerManagerException.java b/gerrit-owners-autoassign/src/main/java/com/vmware/gerrit/owners/autoassigner/ReviewerManagerException.java
new file mode 100644
index 0000000..a5aecfe
--- /dev/null
+++ b/gerrit-owners-autoassign/src/main/java/com/vmware/gerrit/owners/autoassigner/ReviewerManagerException.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2013 VMware, Inc. All Rights Reserved.
+ */
+package com.vmware.gerrit.owners.autoassigner;
+
+public class ReviewerManagerException extends Exception {
+  public ReviewerManagerException(String message) {
+    super(message);
+  }
+
+  public ReviewerManagerException(String message, Throwable cause) {
+    super(message, cause);
+  }
+
+  public ReviewerManagerException(Throwable cause) {
+    super(cause);
+  }
+}
diff --git a/pom.xml b/pom.xml
index 525455e..367106a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,6 +29,7 @@
 
   <modules>
     <module>gerrit-owners</module>
+    <module>gerrit-owners-autoassign</module>
   </modules>
 
   <build>