Bind SharedRefDB as DynamicItem to allow other implementations

Decouple the multi-site engine from the shared ref-db implementation
by using a DynamicItem. The multi-site library would ship and bind
a NoOp impleentation, so that the whole functionality is preserved.

Inside the multi-site.jar there is a Zookeeper implementation embendded
which can be loaded by linking the multi-site.jar from the $GERRIT_SITE/lib
into the $GERRIT_SITE/plugins and loaded as a plugin.

Change-Id: I7f978b9ba4edf81bf5a170e2eb9a5829edabef2e
diff --git a/BUILD b/BUILD
index d6e7348..8ff8cd2 100644
--- a/BUILD
+++ b/BUILD
@@ -11,7 +11,7 @@
     srcs = glob(["src/main/java/**/*.java"]),
     manifest_entries = [
         "Gerrit-PluginName: multi-site",
-        "Gerrit-Module: com.googlesource.gerrit.plugins.multisite.Module",
+        "Gerrit-Module: com.googlesource.gerrit.plugins.multisite.PluginModule",
         "Implementation-Title: multi-site plugin",
         "Implementation-URL: https://review.gerrithub.io/admin/repos/GerritForge/plugins_multi-site",
     ],
diff --git a/README.md b/README.md
index 35503b8..01b3498 100644
--- a/README.md
+++ b/README.md
@@ -71,6 +71,8 @@
 
 Install the multi-site plugin into the `$GERRIT_SITE/lib` directory of all
 the Gerrit servers that are part of the multi-site cluster.
+Create a symbolic link from `$GERRIT_SITE/lib/multi-site.jar` into the
+`$GERRIT_SITE/plugins`.
 
 Add the multi-site module to `$GERRIT_SITE/etc/gerrit.config` as follows:
 
diff --git a/setup_local_env/setup.sh b/setup_local_env/setup.sh
index c42d465..23176fa 100755
--- a/setup_local_env/setup.sh
+++ b/setup_local_env/setup.sh
@@ -355,7 +355,7 @@
 	# Deploying TLS certificates
 	if [ "$HTTPS_ENABLED" = "true" ];then deploy_tls_certificates;fi
 
-	echo "Copy multi-site library"
+	echo "Copy multi-site library to lib directory"
 	cp -f $DEPLOYMENT_LOCATION/multi-site.jar $LOCATION_TEST_SITE_1/lib/multi-site.jar
 
 	echo "Copy websession-flatfile plugin"
@@ -374,6 +374,9 @@
 	ln -s $LOCATION_TEST_SITE_1/plugins/replication.jar $LOCATION_TEST_SITE_1/lib/replication.jar
 	ln -s $LOCATION_TEST_SITE_2/plugins/replication.jar $LOCATION_TEST_SITE_2/lib/replication.jar
 
+	echo "Link multi-site library to plugin directory"
+	ln -s $LOCATION_TEST_SITE_1/lib/multi-site.jar $LOCATION_TEST_SITE_1/plugins/multi-site.jar
+	ln -s $LOCATION_TEST_SITE_2/lib/multi-site.jar $LOCATION_TEST_SITE_2/plugins/multi-site.jar
 fi
 
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/Module.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/Module.java
index 156ce27..ad752d7 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/Module.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/Module.java
@@ -33,7 +33,6 @@
 import com.googlesource.gerrit.plugins.multisite.index.IndexModule;
 import com.googlesource.gerrit.plugins.multisite.kafka.router.KafkaForwardedEventRouterModule;
 import com.googlesource.gerrit.plugins.multisite.validation.ValidationModule;
-import com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.zookeeper.ZkValidationModule;
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
 import java.io.IOException;
@@ -124,8 +123,6 @@
         new ValidationModule(
             config, disableGitRepositoryValidation || !config.getSharedRefDb().isEnabled()));
 
-    install(new ZkValidationModule(config));
-
     bind(Gson.class)
         .annotatedWith(BrokerGson.class)
         .toProvider(GsonProvider.class)
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/PluginModule.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/PluginModule.java
new file mode 100644
index 0000000..ff8bd13
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/PluginModule.java
@@ -0,0 +1,41 @@
+// 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.googlesource.gerrit.plugins.multisite;
+
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.lifecycle.LifecycleModule;
+import com.google.inject.Inject;
+import com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.zookeeper.ZkValidationModule;
+
+public class PluginModule extends LifecycleModule {
+  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+  private Configuration config;
+  private ZkValidationModule zkValidationModule;
+
+  @Inject
+  public PluginModule(Configuration config, ZkValidationModule zkValidationModule) {
+    this.config = config;
+    this.zkValidationModule = zkValidationModule;
+  }
+
+  @Override
+  protected void configure() {
+    if (config.getSharedRefDb().isEnabled()) {
+      logger.atInfo().log("Shared ref-db engine: Zookeeper");
+      install(zkValidationModule);
+    }
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/SharedRefDatabaseWrapper.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/SharedRefDatabaseWrapper.java
index 7d8ae22..bbda6e2 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/SharedRefDatabaseWrapper.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/SharedRefDatabaseWrapper.java
@@ -14,6 +14,7 @@
 
 package com.googlesource.gerrit.plugins.multisite;
 
+import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.SharedLockException;
 import com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.SharedRefDatabase;
@@ -23,25 +24,25 @@
 
 public class SharedRefDatabaseWrapper implements SharedRefDatabase {
 
-  private final SharedRefDatabase sharedRefDb;
+  private final DynamicItem<SharedRefDatabase> sharedRefDbDynamicItem;
   private final SharedRefLogger sharedRefLogger;
 
   @Inject
   public SharedRefDatabaseWrapper(
-      SharedRefDatabase sharedRefDatabase, SharedRefLogger sharedRefLogger) {
-    this.sharedRefDb = sharedRefDatabase;
+      DynamicItem<SharedRefDatabase> sharedRefDbDynamicItem, SharedRefLogger sharedRefLogger) {
+    this.sharedRefDbDynamicItem = sharedRefDbDynamicItem;
     this.sharedRefLogger = sharedRefLogger;
   }
 
   @Override
   public boolean isUpToDate(String project, Ref ref) throws SharedLockException {
-    return sharedRefDb.isUpToDate(project, ref);
+    return sharedRefDbDynamicItem.get().isUpToDate(project, ref);
   }
 
   @Override
   public boolean compareAndPut(String project, Ref currRef, ObjectId newRefValue)
       throws IOException {
-    boolean succeeded = sharedRefDb.compareAndPut(project, currRef, newRefValue);
+    boolean succeeded = sharedRefDbDynamicItem.get().compareAndPut(project, currRef, newRefValue);
     if (succeeded) {
       sharedRefLogger.logRefUpdate(project, currRef, newRefValue);
     }
@@ -50,19 +51,19 @@
 
   @Override
   public AutoCloseable lockRef(String project, String refName) throws SharedLockException {
-    AutoCloseable locker = sharedRefDb.lockRef(project, refName);
+    AutoCloseable locker = sharedRefDbDynamicItem.get().lockRef(project, refName);
     sharedRefLogger.logLockAcquisition(project, refName);
     return locker;
   }
 
   @Override
   public boolean exists(String project, String refName) {
-    return sharedRefDb.exists(project, refName);
+    return sharedRefDbDynamicItem.get().exists(project, refName);
   }
 
   @Override
   public void removeProject(String project) throws IOException {
-    sharedRefDb.removeProject(project);
+    sharedRefDbDynamicItem.get().removeProject(project);
     sharedRefLogger.logProjectDelete(project);
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ValidationModule.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ValidationModule.java
index 86943f4..c3b3571 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ValidationModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/ValidationModule.java
@@ -14,6 +14,7 @@
 
 package com.googlesource.gerrit.plugins.multisite.validation;
 
+import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.extensions.events.ProjectDeletedListener;
 import com.google.gerrit.extensions.registration.DynamicItem;
@@ -26,11 +27,15 @@
 import com.googlesource.gerrit.plugins.multisite.SharedRefLogger;
 import com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.CustomSharedRefEnforcementByProject;
 import com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.DefaultSharedRefEnforcement;
+import com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.NoopSharedRefDatabase;
+import com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.SharedRefDatabase;
 import com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.SharedRefEnforcement;
 import com.googlesource.gerrit.plugins.replication.ReplicationExtensionPointModule;
 import com.googlesource.gerrit.plugins.replication.ReplicationPushFilter;
 
 public class ValidationModule extends FactoryModule {
+  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
   private final Configuration cfg;
   private final boolean disableGitRepositoryValidation;
 
@@ -43,6 +48,10 @@
   protected void configure() {
     install(new ReplicationExtensionPointModule());
 
+    DynamicItem.itemOf(binder(), SharedRefDatabase.class);
+    DynamicItem.bind(binder(), SharedRefDatabase.class).to(NoopSharedRefDatabase.class);
+    logger.atInfo().log("Shared ref-db engine: none");
+
     bind(SharedRefLogger.class).to(Log4jSharedRefLogger.class);
     factory(LockWrapper.Factory.class);
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/NoopSharedRefDatabase.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/NoopSharedRefDatabase.java
new file mode 100644
index 0000000..e40688f
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/NoopSharedRefDatabase.java
@@ -0,0 +1,43 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb;
+
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+
+public class NoopSharedRefDatabase implements SharedRefDatabase {
+  @Override
+  public boolean isUpToDate(String project, Ref ref) {
+    return true;
+  }
+
+  @Override
+  public boolean compareAndPut(String project, Ref currRef, ObjectId newRefValue) {
+    return true;
+  }
+
+  @Override
+  public AutoCloseable lockRef(String project, String refName) {
+    return () -> {};
+  }
+
+  @Override
+  public boolean exists(String project, String refName) {
+    return false;
+  }
+
+  @Override
+  public void removeProject(String project) {}
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/zookeeper/ZkValidationModule.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/zookeeper/ZkValidationModule.java
index 1f41b1f..cc407a0 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/zookeeper/ZkValidationModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/zookeeper/ZkValidationModule.java
@@ -14,7 +14,9 @@
 
 package com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.zookeeper;
 
+import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.multisite.Configuration;
 import com.googlesource.gerrit.plugins.multisite.ZookeeperConfig;
 import com.googlesource.gerrit.plugins.multisite.validation.ZkConnectionConfig;
@@ -25,13 +27,14 @@
 
   private ZookeeperConfig cfg;
 
+  @Inject
   public ZkValidationModule(Configuration cfg) {
     this.cfg = new ZookeeperConfig(cfg.getMultiSiteConfig());
   }
 
   @Override
   protected void configure() {
-    bind(SharedRefDatabase.class).to(ZkSharedRefDatabase.class);
+    DynamicItem.bind(binder(), SharedRefDatabase.class).to(ZkSharedRefDatabase.class);
     bind(CuratorFramework.class).toInstance(cfg.buildCurator());
 
     bind(ZkConnectionConfig.class)
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/BatchRefUpdateValidatorTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/BatchRefUpdateValidatorTest.java
index 661a480..5c18e4f 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/BatchRefUpdateValidatorTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/BatchRefUpdateValidatorTest.java
@@ -18,6 +18,7 @@
 import static junit.framework.TestCase.assertFalse;
 import static org.eclipse.jgit.transport.ReceiveCommand.Type.UPDATE;
 
+import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.metrics.DisabledMetricMaker;
 import com.googlesource.gerrit.plugins.multisite.SharedRefDatabaseWrapper;
 import com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.DefaultSharedRefEnforcement;
@@ -81,11 +82,13 @@
 
     zkSharedRefDatabase =
         new SharedRefDatabaseWrapper(
-            new ZkSharedRefDatabase(
-                zookeeperContainer.getCurator(),
-                new ZkConnectionConfig(
-                    new RetryNTimes(NUMBER_OF_RETRIES, SLEEP_BETWEEN_RETRIES_MS),
-                    TRANSACTION_LOCK_TIMEOUT)),
+            DynamicItem.itemOf(
+                SharedRefDatabase.class,
+                new ZkSharedRefDatabase(
+                    zookeeperContainer.getCurator(),
+                    new ZkConnectionConfig(
+                        new RetryNTimes(NUMBER_OF_RETRIES, SLEEP_BETWEEN_RETRIES_MS),
+                        TRANSACTION_LOCK_TIMEOUT))),
             new DisabledSharedRefLogger());
   }
 
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/zookeeper/ZkSharedRefDatabaseIT.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/zookeeper/ZkSharedRefDatabaseIT.java
index 317f130..e2bdfbc 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/zookeeper/ZkSharedRefDatabaseIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/zookeeper/ZkSharedRefDatabaseIT.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertTrue;
 
 import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.metrics.DisabledMetricMaker;
 import com.googlesource.gerrit.plugins.multisite.SharedRefDatabaseWrapper;
 import com.googlesource.gerrit.plugins.multisite.validation.BatchRefUpdateValidator;
@@ -28,6 +29,7 @@
 import com.googlesource.gerrit.plugins.multisite.validation.ValidationMetrics;
 import com.googlesource.gerrit.plugins.multisite.validation.ZkConnectionConfig;
 import com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.DefaultSharedRefEnforcement;
+import com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.SharedRefDatabase;
 import com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.SharedRefEnforcement;
 import org.apache.curator.retry.RetryNTimes;
 import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
@@ -62,11 +64,13 @@
     zookeeperContainer = new ZookeeperTestContainerSupport(false);
     zkSharedRefDatabase =
         new SharedRefDatabaseWrapper(
-            new ZkSharedRefDatabase(
-                zookeeperContainer.getCurator(),
-                new ZkConnectionConfig(
-                    new RetryNTimes(NUMBER_OF_RETRIES, SLEEP_BETWEEN_RETRIES_MS),
-                    TRANSACTION_LOCK_TIMEOUT)),
+            DynamicItem.itemOf(
+                SharedRefDatabase.class,
+                new ZkSharedRefDatabase(
+                    zookeeperContainer.getCurator(),
+                    new ZkConnectionConfig(
+                        new RetryNTimes(NUMBER_OF_RETRIES, SLEEP_BETWEEN_RETRIES_MS),
+                        TRANSACTION_LOCK_TIMEOUT))),
             new DisabledSharedRefLogger());
   }
 
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/zookeeper/ZkSharedRefDatabaseTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/zookeeper/ZkSharedRefDatabaseTest.java
index a786180..bff41f1 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/zookeeper/ZkSharedRefDatabaseTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/zookeeper/ZkSharedRefDatabaseTest.java
@@ -19,6 +19,7 @@
 
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.events.ProjectDeletedListener;
+import com.google.gerrit.extensions.registration.DynamicItem;
 import com.googlesource.gerrit.plugins.multisite.SharedRefDatabaseWrapper;
 import com.googlesource.gerrit.plugins.multisite.validation.DisabledSharedRefLogger;
 import com.googlesource.gerrit.plugins.multisite.validation.ProjectDeletedSharedDbCleanup;
@@ -55,11 +56,13 @@
 
     zkSharedRefDatabase =
         new SharedRefDatabaseWrapper(
-            new ZkSharedRefDatabase(
-                zookeeperContainer.getCurator(),
-                new ZkConnectionConfig(
-                    new RetryNTimes(NUMBER_OF_RETRIES, SLEEP_BETWEEN_RETRIES_MS),
-                    TRANSACTION_LOCK_TIMEOUT)),
+            DynamicItem.itemOf(
+                SharedRefDatabase.class,
+                new ZkSharedRefDatabase(
+                    zookeeperContainer.getCurator(),
+                    new ZkConnectionConfig(
+                        new RetryNTimes(NUMBER_OF_RETRIES, SLEEP_BETWEEN_RETRIES_MS),
+                        TRANSACTION_LOCK_TIMEOUT))),
             new DisabledSharedRefLogger());
 
     mockValidationMetrics = mock(ValidationMetrics.class);