Allow to configure a code owners backend

We want to support multiple syntaxes for code owner config files. Each
syntax is implemented in a seperate CodeOwnersBackend. This change adds
a global plugin configuration that allows administrators to configure
which CodeOwnersBackend should be used.

Signed-off-by: Edwin Kempin <ekempin@google.com>
Change-Id: Ifa68b9a094035da019dd07822d49e247568af530
diff --git a/java/com/google/gerrit/plugins/codeowners/CodeOwnersPluginConfiguration.java b/java/com/google/gerrit/plugins/codeowners/CodeOwnersPluginConfiguration.java
new file mode 100644
index 0000000..b11e2d5
--- /dev/null
+++ b/java/com/google/gerrit/plugins/codeowners/CodeOwnersPluginConfiguration.java
@@ -0,0 +1,77 @@
+// Copyright (C) 2020 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.plugins.codeowners;
+
+import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.plugins.codeowners.backend.CodeOwnersBackend;
+import com.google.gerrit.plugins.codeowners.backend.findowners.FindOwnersBackend;
+import com.google.gerrit.server.config.PluginConfigFactory;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.util.Optional;
+
+/**
+ * The configuration of the code-owners plugin.
+ *
+ * <p>The configuration of the code-owners plugin is stored globally in the {@code gerrit.config}
+ * file in the {@code plugin.code-owners} subsection.
+ */
+@Singleton
+public class CodeOwnersPluginConfiguration {
+  private static final String KEY_BACKEND = "backend";
+
+  private final String pluginName;
+  private final DynamicMap<CodeOwnersBackend> codeOwnersBackends;
+
+  /** The name of the configured code owners backend. */
+  private final String backendName;
+
+  @Inject
+  CodeOwnersPluginConfiguration(
+      @PluginName String pluginName,
+      PluginConfigFactory pluginConfigFactory,
+      DynamicMap<CodeOwnersBackend> codeOwnersBackends) {
+    this.pluginName = pluginName;
+    this.codeOwnersBackends = codeOwnersBackends;
+
+    this.backendName =
+        pluginConfigFactory
+            .getFromGerritConfig(pluginName)
+            .getString(KEY_BACKEND, FindOwnersBackend.ID);
+  }
+
+  /**
+   * Returns the configured {@link CodeOwnersBackend}.
+   *
+   * @return the {@link CodeOwnersBackend} that should be used
+   */
+  public CodeOwnersBackend getBackend() {
+    return lookupBackend(backendName)
+        .orElseThrow(
+            () ->
+                new IllegalStateException(
+                    String.format(
+                        "Code owner backend '%s' that is configured in gerrit.config"
+                            + " (parameter plugin.%s.%s) not found",
+                        backendName, pluginName, KEY_BACKEND)));
+  }
+
+  private Optional<CodeOwnersBackend> lookupBackend(String backendName) {
+    // We must use "gerrit" as plugin name since DynamicMapProvider#get() hard-codes "gerrit" as
+    // plugin name.
+    return Optional.ofNullable(codeOwnersBackends.get("gerrit", backendName));
+  }
+}
diff --git a/java/com/google/gerrit/plugins/codeowners/Module.java b/java/com/google/gerrit/plugins/codeowners/Module.java
index d87a514..cf82ccd 100644
--- a/java/com/google/gerrit/plugins/codeowners/Module.java
+++ b/java/com/google/gerrit/plugins/codeowners/Module.java
@@ -15,11 +15,12 @@
 package com.google.gerrit.plugins.codeowners;
 
 import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.gerrit.plugins.codeowners.backend.BackendModule;
 
 /** Guice module that registers the extensions of the code-owners plugin. */
 public class Module extends FactoryModule {
   @Override
   protected void configure() {
-    // TODO register extensions
+    install(new BackendModule());
   }
 }
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/BackendModule.java b/java/com/google/gerrit/plugins/codeowners/backend/BackendModule.java
new file mode 100644
index 0000000..83fd3be
--- /dev/null
+++ b/java/com/google/gerrit/plugins/codeowners/backend/BackendModule.java
@@ -0,0 +1,31 @@
+// Copyright (C) 2020 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.plugins.codeowners.backend;
+
+import com.google.gerrit.extensions.annotations.Exports;
+import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.plugins.codeowners.backend.findowners.FindOwnersBackend;
+
+/** Guice module to bind code owner backends. */
+public class BackendModule extends FactoryModule {
+  @Override
+  protected void configure() {
+    DynamicMap.mapOf(binder(), CodeOwnersBackend.class);
+    bind(CodeOwnersBackend.class)
+        .annotatedWith(Exports.named(FindOwnersBackend.ID))
+        .to(FindOwnersBackend.class);
+  }
+}
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/findowners/FindOwnersBackend.java b/java/com/google/gerrit/plugins/codeowners/backend/findowners/FindOwnersBackend.java
index 125fa3d..6532a0f 100644
--- a/java/com/google/gerrit/plugins/codeowners/backend/findowners/FindOwnersBackend.java
+++ b/java/com/google/gerrit/plugins/codeowners/backend/findowners/FindOwnersBackend.java
@@ -36,6 +36,9 @@
 
 @Singleton
 public class FindOwnersBackend implements CodeOwnersBackend {
+  /** The ID of this code owner backend. */
+  public static final String ID = "find-owners";
+
   private final CodeOwnerConfigFile.Factory codeOwnerConfigFileFactory;
   private final GitRepositoryManager repoManager;
   private final PersonIdent serverIdent;
diff --git a/javatests/com/google/gerrit/plugins/codeowners/BUILD b/javatests/com/google/gerrit/plugins/codeowners/BUILD
new file mode 100644
index 0000000..c12ec57
--- /dev/null
+++ b/javatests/com/google/gerrit/plugins/codeowners/BUILD
@@ -0,0 +1,11 @@
+load("//javatests/com/google/gerrit/acceptance:tests.bzl", "acceptance_tests")
+acceptance_tests(
+    srcs = glob(["*Test.java"]),
+    group = "codeowners",
+    deps = [
+        "//plugins/code-owners:code-owners__plugin",
+        "//plugins/code-owners/java/com/google/gerrit/plugins/codeowners/acceptance",
+        "//plugins/code-owners/java/com/google/gerrit/plugins/codeowners/testing",
+    ],
+)
+
diff --git a/javatests/com/google/gerrit/plugins/codeowners/CodeOwnersPluginConfigurationTest.java b/javatests/com/google/gerrit/plugins/codeowners/CodeOwnersPluginConfigurationTest.java
new file mode 100644
index 0000000..72f1072
--- /dev/null
+++ b/javatests/com/google/gerrit/plugins/codeowners/CodeOwnersPluginConfigurationTest.java
@@ -0,0 +1,99 @@
+// Copyright (C) 2020 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.plugins.codeowners;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+
+import com.google.gerrit.acceptance.config.GerritConfig;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.registration.PrivateInternals_DynamicMapImpl;
+import com.google.gerrit.extensions.registration.RegistrationHandle;
+import com.google.gerrit.plugins.codeowners.acceptance.AbstractCodeOwnersTest;
+import com.google.gerrit.plugins.codeowners.backend.CodeOwnerConfig;
+import com.google.gerrit.plugins.codeowners.backend.CodeOwnerConfigUpdate;
+import com.google.gerrit.plugins.codeowners.backend.CodeOwnersBackend;
+import com.google.gerrit.plugins.codeowners.backend.findowners.FindOwnersBackend;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.inject.Key;
+import com.google.inject.util.Providers;
+import java.util.Optional;
+import org.junit.Before;
+import org.junit.Test;
+
+/** Tests for {@link CodeOwnersPluginConfiguration}. */
+public class CodeOwnersPluginConfigurationTest extends AbstractCodeOwnersTest {
+  private CodeOwnersPluginConfiguration codeOwnersPluginConfiguration;
+  private DynamicMap<CodeOwnersBackend> codeOwnersBackends;
+
+  @Before
+  public void setUpCodeOwnersPlugin() throws Exception {
+    codeOwnersPluginConfiguration =
+        plugin.getSysInjector().getInstance(CodeOwnersPluginConfiguration.class);
+    codeOwnersBackends =
+        plugin.getSysInjector().getInstance(new Key<DynamicMap<CodeOwnersBackend>>() {});
+  }
+
+  @Test
+  public void getDefaultBackendWhenNoBackendIsConfigured() throws Exception {
+    assertThat(codeOwnersPluginConfiguration.getBackend()).isInstanceOf(FindOwnersBackend.class);
+  }
+
+  @Test
+  @GerritConfig(name = "plugin.code-owners.backend", value = TestCodeOwnersBackend.ID)
+  public void getConfiguredBackend() throws Exception {
+    try (AutoCloseable registration = registerTestBackend()) {
+      assertThat(codeOwnersPluginConfiguration.getBackend())
+          .isInstanceOf(TestCodeOwnersBackend.class);
+    }
+  }
+
+  @Test
+  @GerritConfig(name = "plugin.code-owners.backend", value = "non-existing-backend")
+  public void cannotGetBackendIfNonExistingBackendIsConfigured() throws Exception {
+    IllegalStateException exception =
+        assertThrows(IllegalStateException.class, () -> codeOwnersPluginConfiguration.getBackend());
+    assertThat(exception)
+        .hasMessageThat()
+        .isEqualTo(
+            "Code owner backend 'non-existing-backend' that is configured in gerrit.config"
+                + " (parameter plugin.code-owners.backend) not found");
+  }
+
+  private AutoCloseable registerTestBackend() {
+    RegistrationHandle registrationHandle =
+        ((PrivateInternals_DynamicMapImpl<CodeOwnersBackend>) codeOwnersBackends)
+            .put("gerrit", TestCodeOwnersBackend.ID, Providers.of(new TestCodeOwnersBackend()));
+    return () -> registrationHandle.remove();
+  }
+
+  private static class TestCodeOwnersBackend implements CodeOwnersBackend {
+    static final String ID = "test-backend";
+
+    @Override
+    public Optional<CodeOwnerConfig> getCodeOwnerConfig(CodeOwnerConfig.Key codeOwnerConfigKey) {
+      throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    public Optional<CodeOwnerConfig> upsertCodeOwnerConfig(
+        CodeOwnerConfig.Key codeOwnerConfigKey,
+        CodeOwnerConfigUpdate codeOwnerConfigUpdate,
+        @Nullable IdentifiedUser currentUser) {
+      throw new UnsupportedOperationException("not implemented");
+    }
+  }
+}
diff --git a/resources/Documentation/config.md b/resources/Documentation/config.md
new file mode 100644
index 0000000..0ff10d6
--- /dev/null
+++ b/resources/Documentation/config.md
@@ -0,0 +1,20 @@
+# @PLUGIN@ - Configuration
+
+The configuration of the @PLUGIN@ plugin is stored in the `gerrit.config` file
+in the `plugin.@PLUGIN@` subsection.
+
+# <a id="globalConfiguration">Global configuration in gerrit.config</a>
+
+<a id="pluginCodeOwnersBackend">plugin.@PLUGIN@.backend</a>
+:       The code owners backend that should be used.
+
+        The following code owner backends are supported:
+
+        * `find-owners`: Code owners backend that supports the syntax of the
+        [find-owners](https://gerrit-review.googlesource.com/admin/repos/plugins/find-owners)
+        plugin.
+
+        By default `find-owners`.
+
+Part of [Gerrit Code Review](../../../Documentation/index.html)
+