Zookeeper module reads it's own configuration

Zookeeper configuration logic is removed from Configuration class
and Zookeeper module is responsible for reading it's own configuration.

Feature: Issue 10824
Change-Id: I35601e9621762fd663c7dc5d02242da73e9d4067
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/Configuration.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/Configuration.java
index d96ab95..3764013 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/Configuration.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/Configuration.java
@@ -14,7 +14,6 @@
 
 package com.googlesource.gerrit.plugins.multisite;
 
-import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Suppliers.memoize;
 import static com.google.common.base.Suppliers.ofInstance;
 
@@ -41,11 +40,6 @@
 import java.util.Map;
 import java.util.Properties;
 import java.util.UUID;
-import org.apache.commons.lang.StringUtils;
-import org.apache.curator.RetryPolicy;
-import org.apache.curator.framework.CuratorFramework;
-import org.apache.curator.framework.CuratorFrameworkFactory;
-import org.apache.curator.retry.BoundedExponentialBackoffRetry;
 import org.apache.kafka.common.serialization.StringSerializer;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.lib.Config;
@@ -66,7 +60,6 @@
 
   // common parameters to cache and index sections
   static final String THREAD_POOL_SIZE_KEY = "threadPoolSize";
-
   static final int DEFAULT_INDEX_MAX_TRIES = 2;
   static final int DEFAULT_INDEX_RETRY_INTERVAL = 30000;
   private static final int DEFAULT_POLLING_INTERVAL_MS = 1000;
@@ -85,8 +78,9 @@
   private final Supplier<Index> index;
   private final Supplier<KafkaSubscriber> subscriber;
   private final Supplier<Kafka> kafka;
-  private final Supplier<ZookeeperConfig> zookeeperConfig;
+  private final Supplier<SharedRefDatabase> sharedRefDb;
   private final Supplier<Collection<Message>> replicationConfigValidation;
+  private final Config multiSiteConfig;
 
   @Inject
   Configuration(SitePaths sitePaths) {
@@ -95,19 +89,24 @@
 
   @VisibleForTesting
   public Configuration(Config multiSiteConfig, Config replicationConfig) {
-    Supplier<Config> lazyCfg = lazyLoad(multiSiteConfig);
+    Supplier<Config> lazyMultiSiteCfg = lazyLoad(multiSiteConfig);
+    this.multiSiteConfig = multiSiteConfig;
     replicationConfigValidation = lazyValidateReplicatioConfig(replicationConfig);
-    kafka = memoize(() -> new Kafka(lazyCfg));
-    publisher = memoize(() -> new KafkaPublisher(lazyCfg));
-    subscriber = memoize(() -> new KafkaSubscriber(lazyCfg));
-    cache = memoize(() -> new Cache(lazyCfg));
-    event = memoize(() -> new Event(lazyCfg));
-    index = memoize(() -> new Index(lazyCfg));
-    zookeeperConfig = memoize(() -> new ZookeeperConfig(lazyCfg));
+    kafka = memoize(() -> new Kafka(lazyMultiSiteCfg));
+    publisher = memoize(() -> new KafkaPublisher(lazyMultiSiteCfg));
+    subscriber = memoize(() -> new KafkaSubscriber(lazyMultiSiteCfg));
+    cache = memoize(() -> new Cache(lazyMultiSiteCfg));
+    event = memoize(() -> new Event(lazyMultiSiteCfg));
+    index = memoize(() -> new Index(lazyMultiSiteCfg));
+    sharedRefDb = memoize(() -> new SharedRefDatabase(lazyMultiSiteCfg));
   }
 
-  public ZookeeperConfig getZookeeperConfig() {
-    return zookeeperConfig.get();
+  public Config getMultiSiteConfig() {
+    return multiSiteConfig;
+  }
+
+  public SharedRefDatabase getSharedRefDb() {
+    return sharedRefDb.get();
   }
 
   public Kafka getKafka() {
@@ -193,17 +192,6 @@
     }
   }
 
-  private static long getLong(
-      Supplier<Config> cfg, String section, String subSection, String name, long defaultValue) {
-    try {
-      return cfg.get().getLong(section, subSection, name, defaultValue);
-    } catch (IllegalArgumentException e) {
-      log.error("invalid value for {}; using default value {}", name, defaultValue);
-      log.debug("Failed to retrieve long value: {}", e.getMessage(), e);
-      return defaultValue;
-    }
-  }
-
   private static String getString(
       Supplier<Config> cfg, String section, String subsection, String name, String defaultValue) {
     String value = cfg.get().getString(section, subsection, name);
@@ -400,6 +388,37 @@
     }
   }
 
+  public static class SharedRefDatabase {
+    public static final String SECTION = "ref-database";
+    public static final String SUBSECTION_ENFORCEMENT_RULES = "enforcementRules";
+
+    private final boolean enabled;
+    private final Multimap<EnforcePolicy, String> enforcementRules;
+
+    private SharedRefDatabase(Supplier<Config> cfg) {
+      enabled = getBoolean(cfg, SECTION, null, ENABLE_KEY, true);
+
+      enforcementRules = MultimapBuilder.hashKeys().arrayListValues().build();
+      for (EnforcePolicy policy : EnforcePolicy.values()) {
+        enforcementRules.putAll(
+            policy, getList(cfg, SECTION, SUBSECTION_ENFORCEMENT_RULES, policy.name()));
+      }
+    }
+
+    public boolean isEnabled() {
+      return enabled;
+    }
+
+    public Multimap<EnforcePolicy, String> getEnforcementRules() {
+      return enforcementRules;
+    }
+
+    private List<String> getList(
+        Supplier<Config> cfg, String section, String subsection, String name) {
+      return ImmutableList.copyOf(cfg.get().getStringList(section, subsection, name));
+    }
+  }
+
   /** Common parameters to cache, event, index */
   public abstract static class Forwarding {
     static final boolean DEFAULT_SYNCHRONIZE = true;
@@ -487,177 +506,6 @@
     }
   }
 
-  public static class ZookeeperConfig {
-    public static final String SECTION = "ref-database";
-    public static final int defaultSessionTimeoutMs;
-    public static final int defaultConnectionTimeoutMs;
-    public static final String DEFAULT_ZK_CONNECT = "localhost:2181";
-    private final int DEFAULT_RETRY_POLICY_BASE_SLEEP_TIME_MS = 1000;
-    private final int DEFAULT_RETRY_POLICY_MAX_SLEEP_TIME_MS = 3000;
-    private final int DEFAULT_RETRY_POLICY_MAX_RETRIES = 3;
-    private final int DEFAULT_CAS_RETRY_POLICY_BASE_SLEEP_TIME_MS = 100;
-    private final int DEFAULT_CAS_RETRY_POLICY_MAX_SLEEP_TIME_MS = 300;
-    private final int DEFAULT_CAS_RETRY_POLICY_MAX_RETRIES = 3;
-    private final int DEFAULT_TRANSACTION_LOCK_TIMEOUT = 1000;
-
-    static {
-      CuratorFrameworkFactory.Builder b = CuratorFrameworkFactory.builder();
-      defaultSessionTimeoutMs = b.getSessionTimeoutMs();
-      defaultConnectionTimeoutMs = b.getConnectionTimeoutMs();
-    }
-
-    public static final String SUBSECTION = "zookeeper";
-    public static final String KEY_CONNECT_STRING = "connectString";
-    public static final String KEY_SESSION_TIMEOUT_MS = "sessionTimeoutMs";
-    public static final String KEY_CONNECTION_TIMEOUT_MS = "connectionTimeoutMs";
-    public static final String KEY_RETRY_POLICY_BASE_SLEEP_TIME_MS = "retryPolicyBaseSleepTimeMs";
-    public static final String KEY_RETRY_POLICY_MAX_SLEEP_TIME_MS = "retryPolicyMaxSleepTimeMs";
-    public static final String KEY_RETRY_POLICY_MAX_RETRIES = "retryPolicyMaxRetries";
-    public static final String KEY_LOCK_TIMEOUT_MS = "lockTimeoutMs";
-    public static final String KEY_ROOT_NODE = "rootNode";
-    public final String KEY_CAS_RETRY_POLICY_BASE_SLEEP_TIME_MS = "casRetryPolicyBaseSleepTimeMs";
-    public final String KEY_CAS_RETRY_POLICY_MAX_SLEEP_TIME_MS = "casRetryPolicyMaxSleepTimeMs";
-    public final String KEY_CAS_RETRY_POLICY_MAX_RETRIES = "casRetryPolicyMaxRetries";
-    public static final String KEY_MIGRATE = "migrate";
-    public final String TRANSACTION_LOCK_TIMEOUT_KEY = "transactionLockTimeoutMs";
-
-    public static final String SUBSECTION_ENFORCEMENT_RULES = "enforcementRules";
-
-    private final String connectionString;
-    private final String root;
-    private final int sessionTimeoutMs;
-    private final int connectionTimeoutMs;
-    private final int baseSleepTimeMs;
-    private final int maxSleepTimeMs;
-    private final int maxRetries;
-    private final int casBaseSleepTimeMs;
-    private final int casMaxSleepTimeMs;
-    private final int casMaxRetries;
-    private final boolean enabled;
-
-    private final Multimap<EnforcePolicy, String> enforcementRules;
-
-    private final Long transactionLockTimeOut;
-
-    private CuratorFramework build;
-
-    private ZookeeperConfig(Supplier<Config> cfg) {
-      connectionString =
-          getString(cfg, SECTION, SUBSECTION, KEY_CONNECT_STRING, DEFAULT_ZK_CONNECT);
-      root = getString(cfg, SECTION, SUBSECTION, KEY_ROOT_NODE, "gerrit/multi-site");
-      sessionTimeoutMs =
-          getInt(cfg, SECTION, SUBSECTION, KEY_SESSION_TIMEOUT_MS, defaultSessionTimeoutMs);
-      connectionTimeoutMs =
-          getInt(cfg, SECTION, SUBSECTION, KEY_CONNECTION_TIMEOUT_MS, defaultConnectionTimeoutMs);
-
-      baseSleepTimeMs =
-          getInt(
-              cfg,
-              SECTION,
-              SUBSECTION,
-              KEY_RETRY_POLICY_BASE_SLEEP_TIME_MS,
-              DEFAULT_RETRY_POLICY_BASE_SLEEP_TIME_MS);
-
-      maxSleepTimeMs =
-          getInt(
-              cfg,
-              SECTION,
-              SUBSECTION,
-              KEY_RETRY_POLICY_MAX_SLEEP_TIME_MS,
-              DEFAULT_RETRY_POLICY_MAX_SLEEP_TIME_MS);
-
-      maxRetries =
-          getInt(
-              cfg,
-              SECTION,
-              SUBSECTION,
-              KEY_RETRY_POLICY_MAX_RETRIES,
-              DEFAULT_RETRY_POLICY_MAX_RETRIES);
-
-      casBaseSleepTimeMs =
-          getInt(
-              cfg,
-              SECTION,
-              SUBSECTION,
-              KEY_CAS_RETRY_POLICY_BASE_SLEEP_TIME_MS,
-              DEFAULT_CAS_RETRY_POLICY_BASE_SLEEP_TIME_MS);
-
-      casMaxSleepTimeMs =
-          getInt(
-              cfg,
-              SECTION,
-              SUBSECTION,
-              KEY_CAS_RETRY_POLICY_MAX_SLEEP_TIME_MS,
-              DEFAULT_CAS_RETRY_POLICY_MAX_SLEEP_TIME_MS);
-
-      casMaxRetries =
-          getInt(
-              cfg,
-              SECTION,
-              SUBSECTION,
-              KEY_CAS_RETRY_POLICY_MAX_RETRIES,
-              DEFAULT_CAS_RETRY_POLICY_MAX_RETRIES);
-
-      transactionLockTimeOut =
-          getLong(
-              cfg,
-              SECTION,
-              SUBSECTION,
-              TRANSACTION_LOCK_TIMEOUT_KEY,
-              DEFAULT_TRANSACTION_LOCK_TIMEOUT);
-
-      checkArgument(StringUtils.isNotEmpty(connectionString), "zookeeper.%s contains no servers");
-
-      enabled = Configuration.getBoolean(cfg, SECTION, null, ENABLE_KEY, true);
-
-      enforcementRules = MultimapBuilder.hashKeys().arrayListValues().build();
-      for (EnforcePolicy policy : EnforcePolicy.values()) {
-        enforcementRules.putAll(
-            policy,
-            Configuration.getList(cfg, SECTION, SUBSECTION_ENFORCEMENT_RULES, policy.name()));
-      }
-    }
-
-    public CuratorFramework buildCurator() {
-      if (build == null) {
-        this.build =
-            CuratorFrameworkFactory.builder()
-                .connectString(connectionString)
-                .sessionTimeoutMs(sessionTimeoutMs)
-                .connectionTimeoutMs(connectionTimeoutMs)
-                .retryPolicy(
-                    new BoundedExponentialBackoffRetry(baseSleepTimeMs, maxSleepTimeMs, maxRetries))
-                .namespace(root)
-                .build();
-        this.build.start();
-      }
-
-      return this.build;
-    }
-
-    public Long getZkInterProcessLockTimeOut() {
-      return transactionLockTimeOut;
-    }
-
-    public RetryPolicy buildCasRetryPolicy() {
-      return new BoundedExponentialBackoffRetry(
-          casBaseSleepTimeMs, casMaxSleepTimeMs, casMaxRetries);
-    }
-
-    public boolean isEnabled() {
-      return enabled;
-    }
-
-    public Multimap<EnforcePolicy, String> getEnforcementRules() {
-      return enforcementRules;
-    }
-  }
-
-  static List<String> getList(
-      Supplier<Config> cfg, String section, String subsection, String name) {
-    return ImmutableList.copyOf(cfg.get().getStringList(section, subsection, name));
-  }
-
   static boolean getBoolean(
       Supplier<Config> cfg, String section, String subsection, String name, boolean defaultValue) {
     try {
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 cab11a8..6bc7027 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/Module.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/Module.java
@@ -110,7 +110,7 @@
 
     install(
         new ValidationModule(
-            config, disableGitRepositoryValidation || !config.getZookeeperConfig().isEnabled()));
+            config, disableGitRepositoryValidation || !config.getSharedRefDb().isEnabled()));
 
     install(new ZkValidationModule(config));
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/ZookeeperConfig.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/ZookeeperConfig.java
new file mode 100644
index 0000000..35471fa
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/ZookeeperConfig.java
@@ -0,0 +1,247 @@
+// 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;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Suppliers.memoize;
+import static com.google.common.base.Suppliers.ofInstance;
+
+import com.google.common.base.Strings;
+import com.google.common.base.Supplier;
+import com.google.gerrit.server.config.SitePaths;
+import java.io.IOException;
+import org.apache.commons.lang.StringUtils;
+import org.apache.curator.RetryPolicy;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.BoundedExponentialBackoffRetry;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.FS;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ZookeeperConfig {
+  private static final Logger log = LoggerFactory.getLogger(ZookeeperConfig.class);
+  public static final int defaultSessionTimeoutMs;
+  public static final int defaultConnectionTimeoutMs;
+  public static final String DEFAULT_ZK_CONNECT = "localhost:2181";
+  private final int DEFAULT_RETRY_POLICY_BASE_SLEEP_TIME_MS = 1000;
+  private final int DEFAULT_RETRY_POLICY_MAX_SLEEP_TIME_MS = 3000;
+  private final int DEFAULT_RETRY_POLICY_MAX_RETRIES = 3;
+  private final int DEFAULT_CAS_RETRY_POLICY_BASE_SLEEP_TIME_MS = 100;
+  private final int DEFAULT_CAS_RETRY_POLICY_MAX_SLEEP_TIME_MS = 300;
+  private final int DEFAULT_CAS_RETRY_POLICY_MAX_RETRIES = 3;
+  private final int DEFAULT_TRANSACTION_LOCK_TIMEOUT = 1000;
+
+  static {
+    CuratorFrameworkFactory.Builder b = CuratorFrameworkFactory.builder();
+    defaultSessionTimeoutMs = b.getSessionTimeoutMs();
+    defaultConnectionTimeoutMs = b.getConnectionTimeoutMs();
+  }
+
+  public static final String SUBSECTION = "zookeeper";
+  public static final String KEY_CONNECT_STRING = "connectString";
+  public static final String KEY_SESSION_TIMEOUT_MS = "sessionTimeoutMs";
+  public static final String KEY_CONNECTION_TIMEOUT_MS = "connectionTimeoutMs";
+  public static final String KEY_RETRY_POLICY_BASE_SLEEP_TIME_MS = "retryPolicyBaseSleepTimeMs";
+  public static final String KEY_RETRY_POLICY_MAX_SLEEP_TIME_MS = "retryPolicyMaxSleepTimeMs";
+  public static final String KEY_RETRY_POLICY_MAX_RETRIES = "retryPolicyMaxRetries";
+  public static final String KEY_ROOT_NODE = "rootNode";
+  public final String KEY_CAS_RETRY_POLICY_BASE_SLEEP_TIME_MS = "casRetryPolicyBaseSleepTimeMs";
+  public final String KEY_CAS_RETRY_POLICY_MAX_SLEEP_TIME_MS = "casRetryPolicyMaxSleepTimeMs";
+  public final String KEY_CAS_RETRY_POLICY_MAX_RETRIES = "casRetryPolicyMaxRetries";
+  public final String TRANSACTION_LOCK_TIMEOUT_KEY = "transactionLockTimeoutMs";
+
+  private final String connectionString;
+  private final String root;
+  private final int sessionTimeoutMs;
+  private final int connectionTimeoutMs;
+  private final int baseSleepTimeMs;
+  private final int maxSleepTimeMs;
+  private final int maxRetries;
+  private final int casBaseSleepTimeMs;
+  private final int casMaxSleepTimeMs;
+  private final int casMaxRetries;
+
+  public static final String SECTION = "ref-database";
+  private final Long transactionLockTimeOut;
+
+  private CuratorFramework build;
+
+  public ZookeeperConfig(Config zkCfg) {
+    Supplier<Config> lazyZkConfig = lazyLoad(zkCfg);
+    connectionString =
+        getString(lazyZkConfig, SECTION, SUBSECTION, KEY_CONNECT_STRING, DEFAULT_ZK_CONNECT);
+    root = getString(lazyZkConfig, SECTION, SUBSECTION, KEY_ROOT_NODE, "gerrit/multi-site");
+    sessionTimeoutMs =
+        getInt(lazyZkConfig, SECTION, SUBSECTION, KEY_SESSION_TIMEOUT_MS, defaultSessionTimeoutMs);
+    connectionTimeoutMs =
+        getInt(
+            lazyZkConfig,
+            SECTION,
+            SUBSECTION,
+            KEY_CONNECTION_TIMEOUT_MS,
+            defaultConnectionTimeoutMs);
+
+    baseSleepTimeMs =
+        getInt(
+            lazyZkConfig,
+            SECTION,
+            SUBSECTION,
+            KEY_RETRY_POLICY_BASE_SLEEP_TIME_MS,
+            DEFAULT_RETRY_POLICY_BASE_SLEEP_TIME_MS);
+
+    maxSleepTimeMs =
+        getInt(
+            lazyZkConfig,
+            SECTION,
+            SUBSECTION,
+            KEY_RETRY_POLICY_MAX_SLEEP_TIME_MS,
+            DEFAULT_RETRY_POLICY_MAX_SLEEP_TIME_MS);
+
+    maxRetries =
+        getInt(
+            lazyZkConfig,
+            SECTION,
+            SUBSECTION,
+            KEY_RETRY_POLICY_MAX_RETRIES,
+            DEFAULT_RETRY_POLICY_MAX_RETRIES);
+
+    casBaseSleepTimeMs =
+        getInt(
+            lazyZkConfig,
+            SECTION,
+            SUBSECTION,
+            KEY_CAS_RETRY_POLICY_BASE_SLEEP_TIME_MS,
+            DEFAULT_CAS_RETRY_POLICY_BASE_SLEEP_TIME_MS);
+
+    casMaxSleepTimeMs =
+        getInt(
+            lazyZkConfig,
+            SECTION,
+            SUBSECTION,
+            KEY_CAS_RETRY_POLICY_MAX_SLEEP_TIME_MS,
+            DEFAULT_CAS_RETRY_POLICY_MAX_SLEEP_TIME_MS);
+
+    casMaxRetries =
+        getInt(
+            lazyZkConfig,
+            SECTION,
+            SUBSECTION,
+            KEY_CAS_RETRY_POLICY_MAX_RETRIES,
+            DEFAULT_CAS_RETRY_POLICY_MAX_RETRIES);
+
+    transactionLockTimeOut =
+        getLong(
+            lazyZkConfig,
+            SECTION,
+            SUBSECTION,
+            TRANSACTION_LOCK_TIMEOUT_KEY,
+            DEFAULT_TRANSACTION_LOCK_TIMEOUT);
+
+    checkArgument(StringUtils.isNotEmpty(connectionString), "zookeeper.%s contains no servers");
+  }
+
+  public CuratorFramework buildCurator() {
+    if (build == null) {
+      this.build =
+          CuratorFrameworkFactory.builder()
+              .connectString(connectionString)
+              .sessionTimeoutMs(sessionTimeoutMs)
+              .connectionTimeoutMs(connectionTimeoutMs)
+              .retryPolicy(
+                  new BoundedExponentialBackoffRetry(baseSleepTimeMs, maxSleepTimeMs, maxRetries))
+              .namespace(root)
+              .build();
+      this.build.start();
+    }
+
+    return this.build;
+  }
+
+  public Long getZkInterProcessLockTimeOut() {
+    return transactionLockTimeOut;
+  }
+
+  public RetryPolicy buildCasRetryPolicy() {
+    return new BoundedExponentialBackoffRetry(casBaseSleepTimeMs, casMaxSleepTimeMs, casMaxRetries);
+  }
+
+  private static FileBasedConfig getConfigFile(SitePaths sitePaths, String configFileName) {
+    return new FileBasedConfig(sitePaths.etc_dir.resolve(configFileName).toFile(), FS.DETECTED);
+  }
+
+  private long getLong(
+      Supplier<Config> cfg, String section, String subSection, String name, long defaultValue) {
+    try {
+      return cfg.get().getLong(section, subSection, name, defaultValue);
+    } catch (IllegalArgumentException e) {
+      log.error("invalid value for {}; using default value {}", name, defaultValue);
+      log.debug("Failed to retrieve long value: {}", e.getMessage(), e);
+      return defaultValue;
+    }
+  }
+
+  private int getInt(
+      Supplier<Config> cfg, String section, String subSection, String name, int defaultValue) {
+    try {
+      return cfg.get().getInt(section, subSection, name, defaultValue);
+    } catch (IllegalArgumentException e) {
+      log.error("invalid value for {}; using default value {}", name, defaultValue);
+      log.debug("Failed to retrieve integer value: {}", e.getMessage(), e);
+      return defaultValue;
+    }
+  }
+
+  private Supplier<Config> lazyLoad(Config config) {
+    if (config instanceof FileBasedConfig) {
+      return memoize(
+          () -> {
+            FileBasedConfig fileConfig = (FileBasedConfig) config;
+            String fileConfigFileName = fileConfig.getFile().getPath();
+            try {
+              log.info("Loading configuration from {}", fileConfigFileName);
+              fileConfig.load();
+            } catch (IOException | ConfigInvalidException e) {
+              log.error("Unable to load configuration from " + fileConfigFileName, e);
+            }
+            return fileConfig;
+          });
+    }
+    return ofInstance(config);
+  }
+
+  private boolean getBoolean(
+      Supplier<Config> cfg, String section, String subsection, String name, boolean defaultValue) {
+    try {
+      return cfg.get().getBoolean(section, subsection, name, defaultValue);
+    } catch (IllegalArgumentException e) {
+      log.error("invalid value for {}; using default value {}", name, defaultValue);
+      log.debug("Failed to retrieve boolean value: {}", e.getMessage(), e);
+      return defaultValue;
+    }
+  }
+
+  private String getString(
+      Supplier<Config> cfg, String section, String subsection, String name, String defaultValue) {
+    String value = cfg.get().getString(section, subsection, name);
+    if (!Strings.isNullOrEmpty(value)) {
+      return value;
+    }
+    return defaultValue;
+  }
+}
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 11b7e85..1dfc3f9 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
@@ -15,6 +15,8 @@
 package com.googlesource.gerrit.plugins.multisite.validation;
 
 import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.gerrit.extensions.events.ProjectDeletedListener;
+import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.inject.Scopes;
 import com.googlesource.gerrit.plugins.multisite.Configuration;
@@ -43,12 +45,13 @@
     if (!disableGitRepositoryValidation) {
       bind(GitRepositoryManager.class).to(MultiSiteGitRepositoryManager.class);
     }
-    if (cfg.getZookeeperConfig().getEnforcementRules().isEmpty()) {
+    if (cfg.getSharedRefDb().getEnforcementRules().isEmpty()) {
       bind(SharedRefEnforcement.class).to(DefaultSharedRefEnforcement.class).in(Scopes.SINGLETON);
     } else {
       bind(SharedRefEnforcement.class)
           .to(CustomSharedRefEnforcementByProject.class)
           .in(Scopes.SINGLETON);
     }
+    DynamicSet.bind(binder(), ProjectDeletedListener.class).to(ProjectDeletedSharedDbCleanup.class);
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/CustomSharedRefEnforcementByProject.java b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/CustomSharedRefEnforcementByProject.java
index 7a806a8..77a0c0b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/CustomSharedRefEnforcementByProject.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/CustomSharedRefEnforcementByProject.java
@@ -41,7 +41,7 @@
     Map<String, Map<String, EnforcePolicy>> enforcementMap = new HashMap<>();
 
     for (Map.Entry<EnforcePolicy, String> enforcementEntry :
-        config.getZookeeperConfig().getEnforcementRules().entries()) {
+        config.getSharedRefDb().getEnforcementRules().entries()) {
       parseEnforcementEntry(enforcementMap, enforcementEntry);
     }
 
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 3e8f75a..1f41b1f 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,34 +14,28 @@
 
 package com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.zookeeper;
 
-import com.google.gerrit.extensions.events.ProjectDeletedListener;
-import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.inject.AbstractModule;
 import com.googlesource.gerrit.plugins.multisite.Configuration;
-import com.googlesource.gerrit.plugins.multisite.validation.ProjectDeletedSharedDbCleanup;
+import com.googlesource.gerrit.plugins.multisite.ZookeeperConfig;
 import com.googlesource.gerrit.plugins.multisite.validation.ZkConnectionConfig;
 import com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.SharedRefDatabase;
 import org.apache.curator.framework.CuratorFramework;
 
 public class ZkValidationModule extends AbstractModule {
 
-  private Configuration cfg;
+  private ZookeeperConfig cfg;
 
   public ZkValidationModule(Configuration cfg) {
-    this.cfg = cfg;
+    this.cfg = new ZookeeperConfig(cfg.getMultiSiteConfig());
   }
 
   @Override
   protected void configure() {
     bind(SharedRefDatabase.class).to(ZkSharedRefDatabase.class);
-    bind(CuratorFramework.class).toInstance(cfg.getZookeeperConfig().buildCurator());
+    bind(CuratorFramework.class).toInstance(cfg.buildCurator());
 
     bind(ZkConnectionConfig.class)
         .toInstance(
-            new ZkConnectionConfig(
-                cfg.getZookeeperConfig().buildCasRetryPolicy(),
-                cfg.getZookeeperConfig().getZkInterProcessLockTimeOut()));
-
-    DynamicSet.bind(binder(), ProjectDeletedListener.class).to(ProjectDeletedSharedDbCleanup.class);
+            new ZkConnectionConfig(cfg.buildCasRetryPolicy(), cfg.getZkInterProcessLockTimeOut()));
   }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/zookeeper/CustomSharedRefEnforcementByProjectTest.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/zookeeper/CustomSharedRefEnforcementByProjectTest.java
index e4d7861..d63436c 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/zookeeper/CustomSharedRefEnforcementByProjectTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/zookeeper/CustomSharedRefEnforcementByProjectTest.java
@@ -18,7 +18,7 @@
 import static com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.SharedRefDatabase.newRef;
 
 import com.googlesource.gerrit.plugins.multisite.Configuration;
-import com.googlesource.gerrit.plugins.multisite.Configuration.ZookeeperConfig;
+import com.googlesource.gerrit.plugins.multisite.Configuration.SharedRefDatabase;
 import com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.CustomSharedRefEnforcementByProject;
 import com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.SharedRefEnforcement;
 import com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.SharedRefEnforcement.EnforcePolicy;
@@ -34,22 +34,22 @@
 
   @Before
   public void setUp() {
-    Config multiSiteConfig = new Config();
-    multiSiteConfig.setStringList(
-        ZookeeperConfig.SECTION,
-        ZookeeperConfig.SUBSECTION_ENFORCEMENT_RULES,
+    Config sharedRefDbConfig = new Config();
+    sharedRefDbConfig.setStringList(
+        SharedRefDatabase.SECTION,
+        SharedRefDatabase.SUBSECTION_ENFORCEMENT_RULES,
         EnforcePolicy.DESIRED.name(),
         Arrays.asList(
             "ProjectOne",
             "ProjectTwo:refs/heads/master/test",
             "ProjectTwo:refs/heads/master/test2"));
-    multiSiteConfig.setString(
-        ZookeeperConfig.SECTION,
-        ZookeeperConfig.SUBSECTION_ENFORCEMENT_RULES,
+    sharedRefDbConfig.setString(
+        SharedRefDatabase.SECTION,
+        SharedRefDatabase.SUBSECTION_ENFORCEMENT_RULES,
         EnforcePolicy.IGNORED.name(),
         ":refs/heads/master/test");
 
-    refEnforcement = newCustomRefEnforcement(multiSiteConfig);
+    refEnforcement = newCustomRefEnforcement(sharedRefDbConfig);
   }
 
   @Test
@@ -138,18 +138,18 @@
 
   private SharedRefEnforcement newCustomRefEnforcementWithValue(
       EnforcePolicy policy, String... projectAndRefs) {
-    Config multiSiteConfig = new Config();
-    multiSiteConfig.setStringList(
-        ZookeeperConfig.SECTION,
-        ZookeeperConfig.SUBSECTION_ENFORCEMENT_RULES,
+    Config sharedRefDbConfiguration = new Config();
+    sharedRefDbConfiguration.setStringList(
+        SharedRefDatabase.SECTION,
+        SharedRefDatabase.SUBSECTION_ENFORCEMENT_RULES,
         policy.name(),
         Arrays.asList(projectAndRefs));
-    return newCustomRefEnforcement(multiSiteConfig);
+    return newCustomRefEnforcement(sharedRefDbConfiguration);
   }
 
-  private SharedRefEnforcement newCustomRefEnforcement(Config multiSiteConfig) {
+  private SharedRefEnforcement newCustomRefEnforcement(Config sharedRefDbConfig) {
     return new CustomSharedRefEnforcementByProject(
-        new Configuration(multiSiteConfig, new Config()));
+        new Configuration(sharedRefDbConfig, new Config()));
   }
 
   @Override
diff --git a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/zookeeper/ZookeeperTestContainerSupport.java b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/zookeeper/ZookeeperTestContainerSupport.java
index 70f6428..3237e3a 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/zookeeper/ZookeeperTestContainerSupport.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/multisite/validation/dfsrefdb/zookeeper/ZookeeperTestContainerSupport.java
@@ -17,7 +17,7 @@
 import static com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.zookeeper.ZkSharedRefDatabase.pathFor;
 import static com.googlesource.gerrit.plugins.multisite.validation.dfsrefdb.zookeeper.ZkSharedRefDatabase.writeObjectId;
 
-import com.googlesource.gerrit.plugins.multisite.Configuration;
+import com.googlesource.gerrit.plugins.multisite.ZookeeperConfig;
 import org.apache.curator.framework.CuratorFramework;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.ObjectId;
@@ -38,7 +38,7 @@
   }
 
   private ZookeeperContainer container;
-  private Configuration configuration;
+  private ZookeeperConfig configuration;
   private CuratorFramework curator;
 
   public CuratorFramework getCurator() {
@@ -49,32 +49,23 @@
     return container;
   }
 
-  public Configuration getConfig() {
-    return configuration;
-  }
-
   @SuppressWarnings("resource")
   public ZookeeperTestContainerSupport(boolean migrationMode) {
     container = new ZookeeperContainer().withExposedPorts(2181).waitingFor(Wait.forListeningPort());
     container.start();
     Integer zkHostPort = container.getMappedPort(2181);
-    Config splitBrainconfig = new Config();
+    Config sharedRefDbConfig = new Config();
     String connectString = container.getContainerIpAddress() + ":" + zkHostPort;
-    splitBrainconfig.setBoolean("ref-database", null, "enabled", true);
-    splitBrainconfig.setString("ref-database", "zookeeper", "connectString", connectString);
-    splitBrainconfig.setString(
+    sharedRefDbConfig.setBoolean("ref-database", null, "enabled", true);
+    sharedRefDbConfig.setString("ref-database", "zookeeper", "connectString", connectString);
+    sharedRefDbConfig.setString(
         "ref-database",
-        Configuration.ZookeeperConfig.SUBSECTION,
-        Configuration.ZookeeperConfig.KEY_CONNECT_STRING,
+        ZookeeperConfig.SUBSECTION,
+        ZookeeperConfig.KEY_CONNECT_STRING,
         connectString);
-    splitBrainconfig.setBoolean(
-        "ref-database",
-        Configuration.ZookeeperConfig.SUBSECTION,
-        Configuration.ZookeeperConfig.KEY_MIGRATE,
-        migrationMode);
 
-    configuration = new Configuration(splitBrainconfig, new Config());
-    this.curator = configuration.getZookeeperConfig().buildCurator();
+    configuration = new ZookeeperConfig(sharedRefDbConfig);
+    this.curator = configuration.buildCurator();
   }
 
   public void cleanup() {