Merge branch 'stable-2.13' into stable-2.14

* stable-2.13:
  Migrate plugin config to own configuration file

Annotate CacheEvictionIT as @Ignore again since it needs to be
adapted to the new plugin configuration. This will be done in a
follow-up commit.

Change-Id: I84618d0fe6c1ef8784b225d3f60b1b41560870f5
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/Configuration.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/Configuration.java
index cd56982..680a8ce 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/Configuration.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/Configuration.java
@@ -21,11 +21,11 @@
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.server.config.ConfigUtil;
-import com.google.gerrit.server.config.PluginConfig;
 import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.inject.Inject;
 import com.google.inject.ProvisionException;
 import com.google.inject.Singleton;
+import org.eclipse.jgit.lib.Config;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -33,62 +33,88 @@
 public class Configuration {
   private static final Logger log = LoggerFactory.getLogger(Configuration.class);
 
+  // main section
+  static final String MAIN_SECTION = "main";
   static final String SHARED_DIRECTORY_KEY = "sharedDirectory";
+
+  // peerInfo section
+  static final String PEER_INFO_SECTION = "peerInfo";
   static final String URL_KEY = "url";
+
+  // http section
+  static final String HTTP_SECTION = "http";
   static final String USER_KEY = "user";
   static final String PASSWORD_KEY = "password";
   static final String CONNECTION_TIMEOUT_KEY = "connectionTimeout";
   static final String SOCKET_TIMEOUT_KEY = "socketTimeout";
   static final String MAX_TRIES_KEY = "maxTries";
   static final String RETRY_INTERVAL_KEY = "retryInterval";
-  static final String INDEX_THREAD_POOL_SIZE_KEY = "indexThreadPoolSize";
-  static final String CACHE_THREAD_POOL_SIZE_KEY = "cacheThreadPoolSize";
+
+  // cache section
+  static final String CACHE_SECTION = "cache";
+
+  // index section
+  static final String INDEX_SECTION = "index";
+
+  // common parameters to cache and index section
+  static final String THREAD_POOL_SIZE_KEY = "threadPoolSize";
+
+  // websession section
+  static final String WEBSESSION_SECTION = "websession";
   static final String CLEANUP_INTERVAL_KEY = "cleanupInterval";
 
   static final int DEFAULT_TIMEOUT_MS = 5000;
   static final int DEFAULT_MAX_TRIES = 5;
   static final int DEFAULT_RETRY_INTERVAL = 1000;
   static final int DEFAULT_THREAD_POOL_SIZE = 1;
+  static final String DEFAULT_CLEANUP_INTERVAL = "24 hours";
   static final long DEFAULT_CLEANUP_INTERVAL_MS = HOURS.toMillis(24);
 
-  private final String url;
-  private final String user;
-  private final String password;
-  private final int connectionTimeout;
-  private final int socketTimeout;
-  private final int maxTries;
-  private final int retryInterval;
-  private final int indexThreadPoolSize;
-  private final int cacheThreadPoolSize;
-  private final String sharedDirectory;
-  private final long cleanupInterval;
+  private final Main main;
+  private final PeerInfo peerInfo;
+  private final Http http;
+  private final Cache cache;
+  private final Index index;
+  private final Websession websession;
 
   @Inject
-  Configuration(PluginConfigFactory config, @PluginName String pluginName) {
-    PluginConfig cfg = config.getFromGerritConfig(pluginName, true);
-    url = Strings.nullToEmpty(cfg.getString(URL_KEY));
-    user = Strings.nullToEmpty(cfg.getString(USER_KEY));
-    password = Strings.nullToEmpty(cfg.getString(PASSWORD_KEY));
-    connectionTimeout = getInt(cfg, CONNECTION_TIMEOUT_KEY, DEFAULT_TIMEOUT_MS);
-    socketTimeout = getInt(cfg, SOCKET_TIMEOUT_KEY, DEFAULT_TIMEOUT_MS);
-    maxTries = getInt(cfg, MAX_TRIES_KEY, DEFAULT_MAX_TRIES);
-    retryInterval = getInt(cfg, RETRY_INTERVAL_KEY, DEFAULT_RETRY_INTERVAL);
-    indexThreadPoolSize = getInt(cfg, INDEX_THREAD_POOL_SIZE_KEY, DEFAULT_THREAD_POOL_SIZE);
-    cacheThreadPoolSize = getInt(cfg, CACHE_THREAD_POOL_SIZE_KEY, DEFAULT_THREAD_POOL_SIZE);
-    sharedDirectory = Strings.emptyToNull(cfg.getString(SHARED_DIRECTORY_KEY));
-    if (sharedDirectory == null) {
-      throw new ProvisionException(SHARED_DIRECTORY_KEY + " must be configured");
-    }
-    cleanupInterval =
-        ConfigUtil.getTimeUnit(
-            Strings.nullToEmpty(cfg.getString(CLEANUP_INTERVAL_KEY)),
-            DEFAULT_CLEANUP_INTERVAL_MS,
-            MILLISECONDS);
+  Configuration(PluginConfigFactory pluginConfigFactory, @PluginName String pluginName) {
+    Config cfg = pluginConfigFactory.getGlobalPluginConfig(pluginName);
+    main = new Main(cfg);
+    peerInfo = new PeerInfo(cfg);
+    http = new Http(cfg);
+    cache = new Cache(cfg);
+    index = new Index(cfg);
+    websession = new Websession(cfg);
   }
 
-  private int getInt(PluginConfig cfg, String name, int defaultValue) {
+  public Main main() {
+    return main;
+  }
+
+  public PeerInfo peerInfo() {
+    return peerInfo;
+  }
+
+  public Http http() {
+    return http;
+  }
+
+  public Cache cache() {
+    return cache;
+  }
+
+  public Index index() {
+    return index;
+  }
+
+  public Websession websession() {
+    return websession;
+  }
+
+  private static int getInt(Config cfg, String section, String name, int defaultValue) {
     try {
-      return cfg.getInt(name, defaultValue);
+      return cfg.getInt(section, name, defaultValue);
     } catch (IllegalArgumentException e) {
       log.error(String.format("invalid value for %s; using default value %d", name, defaultValue));
       log.debug("Failed retrieve integer value: " + e.getMessage(), e);
@@ -96,47 +122,116 @@
     }
   }
 
-  public int getConnectionTimeout() {
-    return connectionTimeout;
+  public static class Main {
+    private final String sharedDirectory;
+
+    private Main(Config cfg) {
+      sharedDirectory =
+          Strings.emptyToNull(cfg.getString(MAIN_SECTION, null, SHARED_DIRECTORY_KEY));
+      if (sharedDirectory == null) {
+        throw new ProvisionException(SHARED_DIRECTORY_KEY + " must be configured");
+      }
+    }
+
+    public String sharedDirectory() {
+      return sharedDirectory;
+    }
   }
 
-  public int getMaxTries() {
-    return maxTries;
+  public static class PeerInfo {
+    private final String url;
+
+    private PeerInfo(Config cfg) {
+      url =
+          CharMatcher.is('/')
+              .trimTrailingFrom(
+                  Strings.nullToEmpty(cfg.getString(PEER_INFO_SECTION, null, URL_KEY)));
+    }
+
+    public String url() {
+      return url;
+    }
   }
 
-  public int getRetryInterval() {
-    return retryInterval;
+  public static class Http {
+    private final String user;
+    private final String password;
+    private final int connectionTimeout;
+    private final int socketTimeout;
+    private final int maxTries;
+    private final int retryInterval;
+
+    private Http(Config cfg) {
+      user = Strings.nullToEmpty(cfg.getString(HTTP_SECTION, null, USER_KEY));
+      password = Strings.nullToEmpty(cfg.getString(HTTP_SECTION, null, PASSWORD_KEY));
+      connectionTimeout = getInt(cfg, HTTP_SECTION, CONNECTION_TIMEOUT_KEY, DEFAULT_TIMEOUT_MS);
+      socketTimeout = getInt(cfg, HTTP_SECTION, SOCKET_TIMEOUT_KEY, DEFAULT_TIMEOUT_MS);
+      maxTries = getInt(cfg, HTTP_SECTION, MAX_TRIES_KEY, DEFAULT_MAX_TRIES);
+      retryInterval = getInt(cfg, HTTP_SECTION, RETRY_INTERVAL_KEY, DEFAULT_RETRY_INTERVAL);
+    }
+
+    public String user() {
+      return user;
+    }
+
+    public String password() {
+      return password;
+    }
+
+    public int connectionTimeout() {
+      return connectionTimeout;
+    }
+
+    public int socketTimeout() {
+      return socketTimeout;
+    }
+
+    public int maxTries() {
+      return maxTries;
+    }
+
+    public int retryInterval() {
+      return retryInterval;
+    }
   }
 
-  public int getSocketTimeout() {
-    return socketTimeout;
+  public static class Cache {
+    private final int threadPoolSize;
+
+    private Cache(Config cfg) {
+      threadPoolSize = getInt(cfg, CACHE_SECTION, THREAD_POOL_SIZE_KEY, DEFAULT_THREAD_POOL_SIZE);
+    }
+
+    public int threadPoolSize() {
+      return threadPoolSize;
+    }
   }
 
-  public String getUrl() {
-    return CharMatcher.is('/').trimTrailingFrom(url);
+  public static class Index {
+    private final int threadPoolSize;
+
+    private Index(Config cfg) {
+      threadPoolSize = getInt(cfg, INDEX_SECTION, THREAD_POOL_SIZE_KEY, DEFAULT_THREAD_POOL_SIZE);
+    }
+
+    public int threadPoolSize() {
+      return threadPoolSize;
+    }
   }
 
-  public String getUser() {
-    return user;
-  }
+  public static class Websession {
+    private final long cleanupInterval;
 
-  public String getPassword() {
-    return password;
-  }
+    private Websession(Config cfg) {
+      this.cleanupInterval =
+          ConfigUtil.getTimeUnit(
+              Strings.nullToEmpty(cfg.getString(WEBSESSION_SECTION, null, CLEANUP_INTERVAL_KEY)),
+              DEFAULT_CLEANUP_INTERVAL_MS,
+              MILLISECONDS);
+    }
 
-  public int getIndexThreadPoolSize() {
-    return indexThreadPoolSize;
-  }
-
-  public int getCacheThreadPoolSize() {
-    return cacheThreadPoolSize;
-  }
-
-  public String getSharedDirectory() {
-    return sharedDirectory;
-  }
-
-  public Long getCleanupInterval() {
-    return cleanupInterval;
+    public long cleanupInterval() {
+      return cleanupInterval;
+    }
   }
 }
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/Module.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/Module.java
index b1276ba..ecef7cd 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/Module.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/Module.java
@@ -44,7 +44,7 @@
   @Singleton
   @SharedDirectory
   Path getSharedDirectory(Configuration cfg) throws IOException {
-    Path sharedDirectoryPath = Paths.get(cfg.getSharedDirectory());
+    Path sharedDirectoryPath = Paths.get(cfg.main().sharedDirectory());
     Files.createDirectories(sharedDirectoryPath);
     return sharedDirectoryPath;
   }
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/Setup.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/Setup.java
index 82f52d7..ab9d0a4 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/Setup.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/Setup.java
@@ -16,27 +16,27 @@
 
 import static com.ericsson.gerrit.plugins.highavailability.Configuration.*;
 
+import com.google.common.base.Strings;
 import com.google.gerrit.common.FileUtil;
 import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.pgm.init.api.ConsoleUI;
 import com.google.gerrit.pgm.init.api.InitStep;
-import com.google.gerrit.pgm.init.api.Section;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.inject.Inject;
 import java.nio.file.Path;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.FS;
 
 public class Setup implements InitStep {
 
   private final ConsoleUI ui;
-  private final Section mySection;
   private final String pluginName;
   private final SitePaths site;
+  private FileBasedConfig config;
 
   @Inject
-  public Setup(
-      ConsoleUI ui, Section.Factory sections, @PluginName String pluginName, SitePaths site) {
+  public Setup(ConsoleUI ui, @PluginName String pluginName, SitePaths site) {
     this.ui = ui;
-    this.mySection = sections.get("plugin", pluginName);
     this.pluginName = pluginName;
     this.site = site;
   }
@@ -48,44 +48,91 @@
 
     if (ui.yesno(true, "Configure %s", pluginName)) {
       ui.header("Configuring %s", pluginName);
-      configureSharedDir();
-      configurePeer();
-      configureTimeouts();
-      configureRetry();
-      configureThreadPools();
+      Path pluginConfigFile = site.etc_dir.resolve(pluginName + ".config");
+      config = new FileBasedConfig(pluginConfigFile.toFile(), FS.DETECTED);
+      config.load();
+      configureMainSection();
+      configurePeerInfoSection();
+      configureHttp();
+      configureCacheSection();
+      configureIndexSection();
+      configureWebsessiosSection();
+      config.save();
     }
   }
 
-  private void configureSharedDir() {
-    String sharedDir = mySection.string("Shared directory", SHARED_DIRECTORY_KEY, null);
+  private void configureMainSection() {
+    ui.header("Main section");
+    String sharedDir =
+        promptAndSetString("Shared directory", MAIN_SECTION, SHARED_DIRECTORY_KEY, null);
     if (sharedDir != null) {
       Path shared = site.site_path.resolve(sharedDir);
       FileUtil.mkdirsOrDie(shared, "cannot create " + shared);
     }
   }
 
-  private void configurePeer() {
-    mySection.string("Peer URL", URL_KEY, null);
-    mySection.string("User", USER_KEY, null);
-    mySection.string("Password", PASSWORD_KEY, null);
+  private void configurePeerInfoSection() {
+    ui.header("PeerInfo section");
+    promptAndSetString("Peer URL", PEER_INFO_SECTION, URL_KEY, null);
   }
 
-  private void configureTimeouts() {
-    mySection.string("Connection timeout [ms]", CONNECTION_TIMEOUT_KEY, str(DEFAULT_TIMEOUT_MS));
-    mySection.string("Socket timeout [ms]", SOCKET_TIMEOUT_KEY, str(DEFAULT_TIMEOUT_MS));
+  private void configureHttp() {
+    ui.header("Http section");
+    promptAndSetString("User", HTTP_SECTION, USER_KEY, null);
+    promptAndSetString("Password", HTTP_SECTION, PASSWORD_KEY, null);
+    promptAndSetString(
+        "Max number of tries to forward to remote peer",
+        HTTP_SECTION,
+        MAX_TRIES_KEY,
+        str(DEFAULT_MAX_TRIES));
+    promptAndSetString(
+        "Retry interval [ms]", HTTP_SECTION, RETRY_INTERVAL_KEY, str(DEFAULT_RETRY_INTERVAL));
+    promptAndSetString(
+        "Connection timeout [ms]", HTTP_SECTION, CONNECTION_TIMEOUT_KEY, str(DEFAULT_TIMEOUT_MS));
+    promptAndSetString(
+        "Socket timeout [ms]", HTTP_SECTION, SOCKET_TIMEOUT_KEY, str(DEFAULT_TIMEOUT_MS));
   }
 
-  private void configureRetry() {
-    mySection.string(
-        "Max number of tries to forward to remote peer", MAX_TRIES_KEY, str(DEFAULT_MAX_TRIES));
-    mySection.string("Retry interval [ms]", RETRY_INTERVAL_KEY, str(DEFAULT_RETRY_INTERVAL));
+  private void configureCacheSection() {
+    ui.header("Cache section");
+    promptAndSetString(
+        "Cache thread pool size",
+        CACHE_SECTION,
+        THREAD_POOL_SIZE_KEY,
+        str(DEFAULT_THREAD_POOL_SIZE));
   }
 
-  private void configureThreadPools() {
-    mySection.string(
-        "Index thread pool size", INDEX_THREAD_POOL_SIZE_KEY, str(DEFAULT_THREAD_POOL_SIZE));
-    mySection.string(
-        "Cache thread pool size", CACHE_THREAD_POOL_SIZE_KEY, str(DEFAULT_THREAD_POOL_SIZE));
+  private void configureIndexSection() {
+    ui.header("Index section");
+    promptAndSetString(
+        "Index thread pool size",
+        INDEX_SECTION,
+        THREAD_POOL_SIZE_KEY,
+        str(DEFAULT_THREAD_POOL_SIZE));
+  }
+
+  private void configureWebsessiosSection() {
+    ui.header("Websession section");
+    promptAndSetString(
+        "Cleanup interval", WEBSESSION_SECTION, CLEANUP_INTERVAL_KEY, DEFAULT_CLEANUP_INTERVAL);
+  }
+
+  private String promptAndSetString(
+      String title, String section, String name, String defaultValue) {
+    String oldValue = Strings.emptyToNull(config.getString(section, null, name));
+    String newValue = ui.readString(oldValue != null ? oldValue : defaultValue, title);
+    if (!eq(oldValue, newValue)) {
+      if (newValue != null) {
+        config.setString(section, null, name, newValue);
+      } else {
+        config.unset(section, name, name);
+      }
+    }
+    return newValue;
+  }
+
+  private static boolean eq(String a, String b) {
+    return (a == null && b == null) || (a != null && a.equals(b));
   }
 
   private static String str(int n) {
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/cache/CacheExecutorProvider.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/cache/CacheExecutorProvider.java
index dd40155..aed590d 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/cache/CacheExecutorProvider.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/cache/CacheExecutorProvider.java
@@ -25,6 +25,6 @@
 
   @Inject
   CacheExecutorProvider(WorkQueue workQueue, Configuration config) {
-    super(workQueue, config.getCacheThreadPoolSize(), "Forward-cache-eviction-event");
+    super(workQueue, config.cache().threadPoolSize(), "Forward-cache-eviction-event");
   }
 }
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpClientProvider.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpClientProvider.java
index e68875f..bd3077f 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpClientProvider.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpClientProvider.java
@@ -70,9 +70,9 @@
 
   private RequestConfig customRequestConfig() {
     return RequestConfig.custom()
-        .setConnectTimeout(cfg.getConnectionTimeout())
-        .setSocketTimeout(cfg.getSocketTimeout())
-        .setConnectionRequestTimeout(cfg.getConnectionTimeout())
+        .setConnectTimeout(cfg.http().connectionTimeout())
+        .setSocketTimeout(cfg.http().socketTimeout())
+        .setConnectionRequestTimeout(cfg.http().connectionTimeout())
         .build();
   }
 
@@ -109,7 +109,7 @@
   private BasicCredentialsProvider buildCredentials() {
     BasicCredentialsProvider creds = new BasicCredentialsProvider();
     creds.setCredentials(
-        AuthScope.ANY, new UsernamePasswordCredentials(cfg.getUser(), cfg.getPassword()));
+        AuthScope.ANY, new UsernamePasswordCredentials(cfg.http().user(), cfg.http().password()));
     return creds;
   }
 
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarder.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarder.java
index 7fea822..18c8645 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarder.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarder.java
@@ -133,14 +133,14 @@
             log.error("Failed to {}", name, e);
             return false;
           }
-          if (execCnt >= cfg.getMaxTries()) {
-            log.error("Failed to {}, after {} tries", name, cfg.getMaxTries());
+          if (execCnt >= cfg.http().maxTries()) {
+            log.error("Failed to {}, after {} tries", name, cfg.http().maxTries());
             return false;
           }
 
           logRetry(e);
           try {
-            Thread.sleep(cfg.getRetryInterval());
+            Thread.sleep(cfg.http().retryInterval());
           } catch (InterruptedException ie) {
             log.error("{} was interrupted, giving up", name, ie);
             Thread.currentThread().interrupt();
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexExecutorProvider.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexExecutorProvider.java
index efe323e..7efcc3f 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexExecutorProvider.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexExecutorProvider.java
@@ -25,6 +25,6 @@
 
   @Inject
   IndexExecutorProvider(WorkQueue workQueue, Configuration config) {
-    super(workQueue, config.getIndexThreadPoolSize(), "Forward-index-event");
+    super(workQueue, config.index().threadPoolSize(), "Forward-index-event");
   }
 }
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/PluginConfigPeerInfoProvider.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/PluginConfigPeerInfoProvider.java
index 74e148b..a78b397 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/PluginConfigPeerInfoProvider.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/peers/PluginConfigPeerInfoProvider.java
@@ -25,7 +25,7 @@
 
   @Inject
   PluginConfigPeerInfoProvider(Configuration cfg) {
-    peerInfo = Optional.of(new PeerInfo(cfg.getUrl()));
+    peerInfo = Optional.of(new PeerInfo(cfg.peerInfo().url()));
   }
 
   @Override
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/websession/file/FileBasedWebSessionCacheCleaner.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/websession/file/FileBasedWebSessionCacheCleaner.java
index f6b5542..68ffd17 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/websession/file/FileBasedWebSessionCacheCleaner.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/websession/file/FileBasedWebSessionCacheCleaner.java
@@ -41,7 +41,7 @@
       WorkQueue queue, Provider<CleanupTask> cleanupTaskProvider, Configuration config) {
     this.queue = queue;
     this.cleanupTaskProvider = cleanupTaskProvider;
-    this.cleanupIntervalMillis = config.getCleanupInterval();
+    this.cleanupIntervalMillis = config.websession().cleanupInterval();
   }
 
   @Override
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index f894822..3290f1b 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -2,57 +2,71 @@
 =========================
 
 The @PLUGIN@ plugin must be installed in both instances and the following fields
-should be specified in the corresponding Gerrit configuration file:
+should be specified in `$site_path/etc/@PLUGIN@.config` file:
 
-File 'gerrit.config'
+File '@PLUGIN@.config'
 --------------------
 
-[plugin "@PLUGIN@"]
+[main]
+:  sharedDirectory = /directory/accessible/from/both/instances
+[peerInfo]
 :  url = target_instance_url
+[http]
 :  user = username
 :  password = password
-:  sharedDirectory = /directory/accessible/from/both/instances
 
-plugin.@PLUGIN@.url
+main.sharedDirectory
+:   Path to a directory accessible from both master instances.
+
+peerInfo.url
 :   Specify the URL for the secondary (target) instance.
 
-plugin.@PLUGIN@.user
+http.user
 :   Username to connect to the secondary (target) instance.
 
-plugin.@PLUGIN@.password
-:   Password to connect to the secondary (target) instance. This value can
-     also be defined in secure.config.
-
-plugin.@PLUGIN@.sharedDirectory
-:   Path to a directory accessible from both master instances.
+http.password
+:   Password to connect to the secondary (target) instance.
 
 @PLUGIN@ plugin uses REST API calls to keep the target instance in-sync. It
 is possible to customize the parameters of the underlying http client doing these
 calls by specifying the following fields:
 
-@PLUGIN@.connectionTimeout
+http.connectionTimeout
 :   Maximum interval of time in milliseconds the plugin waits for a connection
     to the target instance. When not specified, the default value is set to 5000ms.
 
-@PLUGIN@.socketTimeout
+http.socketTimeout
 :   Maximum interval of time in milliseconds the plugin waits for a response from the
     target instance once the connection has been established. When not specified,
     the default value is set to 5000ms.
 
-@PLUGIN@.maxTries
+http.maxTries
 :   Maximum number of times the plugin should attempt when calling a REST API in
     the target instance. Setting this value to 0 will disable retries. When not
     specified, the default value is 5. After this number of failed tries, an
     error is logged.
 
-@PLUGIN@.retryInterval
+http.retryInterval
 :   The interval of time in milliseconds between the subsequent auto-retries.
     When not specified, the default value is set to 1000ms.
 
-@PLUGIN@.indexThreadPoolSize
+cache.threadPoolSize
+:   Maximum number of threads used to send cache evictions to the target instance.
+    Defaults to 1.
+
+index.threadPoolSize
 :   Maximum number of threads used to send index events to the target instance.
     Defaults to 1.
 
-@PLUGIN@.cacheThreadPoolSize
-:   Maximum number of threads used to send cache evictions to the target instance.
-    Defaults to 1.
\ No newline at end of file
+websession.cleanupInterval
+:   Frequency for deleting expired web sessions. Values should use common time
+    unit suffixes to express their setting:
+* s, sec, second, seconds
+* m, min, minute, minutes
+* h, hr, hour, hours
+* d, day, days
+* w, week, weeks (`1 week` is treated as `7 days`)
+* mon, month, months (`1 month` is treated as `30 days`)
+* y, year, years (`1 year` is treated as `365 days`)
+If a time unit suffix is not specified, `hours` is assumed.
+Defaults to 24 hours.
\ No newline at end of file
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/ConfigurationTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/ConfigurationTest.java
index a828fb2..31449e3 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/ConfigurationTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/ConfigurationTest.java
@@ -14,7 +14,7 @@
 
 package com.ericsson.gerrit.plugins.highavailability;
 
-import static com.ericsson.gerrit.plugins.highavailability.Configuration.CACHE_THREAD_POOL_SIZE_KEY;
+import static com.ericsson.gerrit.plugins.highavailability.Configuration.CACHE_SECTION;
 import static com.ericsson.gerrit.plugins.highavailability.Configuration.CLEANUP_INTERVAL_KEY;
 import static com.ericsson.gerrit.plugins.highavailability.Configuration.CONNECTION_TIMEOUT_KEY;
 import static com.ericsson.gerrit.plugins.highavailability.Configuration.DEFAULT_CLEANUP_INTERVAL_MS;
@@ -22,21 +22,26 @@
 import static com.ericsson.gerrit.plugins.highavailability.Configuration.DEFAULT_RETRY_INTERVAL;
 import static com.ericsson.gerrit.plugins.highavailability.Configuration.DEFAULT_THREAD_POOL_SIZE;
 import static com.ericsson.gerrit.plugins.highavailability.Configuration.DEFAULT_TIMEOUT_MS;
-import static com.ericsson.gerrit.plugins.highavailability.Configuration.INDEX_THREAD_POOL_SIZE_KEY;
+import static com.ericsson.gerrit.plugins.highavailability.Configuration.HTTP_SECTION;
+import static com.ericsson.gerrit.plugins.highavailability.Configuration.INDEX_SECTION;
+import static com.ericsson.gerrit.plugins.highavailability.Configuration.MAIN_SECTION;
 import static com.ericsson.gerrit.plugins.highavailability.Configuration.MAX_TRIES_KEY;
 import static com.ericsson.gerrit.plugins.highavailability.Configuration.PASSWORD_KEY;
+import static com.ericsson.gerrit.plugins.highavailability.Configuration.PEER_INFO_SECTION;
 import static com.ericsson.gerrit.plugins.highavailability.Configuration.RETRY_INTERVAL_KEY;
 import static com.ericsson.gerrit.plugins.highavailability.Configuration.SHARED_DIRECTORY_KEY;
 import static com.ericsson.gerrit.plugins.highavailability.Configuration.SOCKET_TIMEOUT_KEY;
+import static com.ericsson.gerrit.plugins.highavailability.Configuration.THREAD_POOL_SIZE_KEY;
 import static com.ericsson.gerrit.plugins.highavailability.Configuration.URL_KEY;
 import static com.ericsson.gerrit.plugins.highavailability.Configuration.USER_KEY;
+import static com.ericsson.gerrit.plugins.highavailability.Configuration.WEBSESSION_SECTION;
 import static com.google.common.truth.Truth.assertThat;
 import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.mockito.Mockito.when;
 
-import com.google.gerrit.server.config.PluginConfig;
 import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.inject.ProvisionException;
+import org.eclipse.jgit.lib.Config;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -57,14 +62,15 @@
   private static final String ERROR_MESSAGE = "some error message";
 
   @Mock private PluginConfigFactory cfgFactoryMock;
-  @Mock private PluginConfig configMock;
+  @Mock private Config configMock;
   private Configuration configuration;
   private String pluginName = "high-availability";
 
   @Before
   public void setUp() {
-    when(cfgFactoryMock.getFromGerritConfig(pluginName, true)).thenReturn(configMock);
-    when(configMock.getString(SHARED_DIRECTORY_KEY)).thenReturn(SHARED_DIRECTORY);
+    when(cfgFactoryMock.getGlobalPluginConfig(pluginName)).thenReturn(configMock);
+    when(configMock.getString(MAIN_SECTION, null, SHARED_DIRECTORY_KEY))
+        .thenReturn(SHARED_DIRECTORY);
   }
 
   private void initializeConfiguration() {
@@ -74,152 +80,156 @@
   @Test
   public void testGetUrl() throws Exception {
     initializeConfiguration();
-    assertThat(configuration.getUrl()).isEqualTo(EMPTY);
+    assertThat(configuration.peerInfo().url()).isEqualTo(EMPTY);
 
-    when(configMock.getString(URL_KEY)).thenReturn(URL);
+    when(configMock.getString(PEER_INFO_SECTION, null, URL_KEY)).thenReturn(URL);
     initializeConfiguration();
-    assertThat(configuration.getUrl()).isEqualTo(URL);
+    assertThat(configuration.peerInfo().url()).isEqualTo(URL);
   }
 
   @Test
   public void testGetUrlIsDroppingTrailingSlash() throws Exception {
-    when(configMock.getString("url")).thenReturn(URL + "/");
+    when(configMock.getString(PEER_INFO_SECTION, null, URL_KEY)).thenReturn(URL + "/");
     initializeConfiguration();
     assertThat(configuration).isNotNull();
-    assertThat(configuration.getUrl()).isEqualTo(URL);
+    assertThat(configuration.peerInfo().url()).isEqualTo(URL);
   }
 
   @Test
   public void testGetUser() throws Exception {
     initializeConfiguration();
-    assertThat(configuration.getUser()).isEqualTo(EMPTY);
+    assertThat(configuration.http().user()).isEqualTo(EMPTY);
 
-    when(configMock.getString(USER_KEY)).thenReturn(USER);
+    when(configMock.getString(HTTP_SECTION, null, USER_KEY)).thenReturn(USER);
     initializeConfiguration();
-    assertThat(configuration.getUser()).isEqualTo(USER);
+    assertThat(configuration.http().user()).isEqualTo(USER);
   }
 
   @Test
   public void testGetPassword() throws Exception {
     initializeConfiguration();
-    assertThat(configuration.getPassword()).isEqualTo(EMPTY);
+    assertThat(configuration.http().password()).isEqualTo(EMPTY);
 
-    when(configMock.getString(PASSWORD_KEY)).thenReturn(PASS);
+    when(configMock.getString(HTTP_SECTION, null, PASSWORD_KEY)).thenReturn(PASS);
     initializeConfiguration();
-    assertThat(configuration.getPassword()).isEqualTo(PASS);
+    assertThat(configuration.http().password()).isEqualTo(PASS);
   }
 
   @Test
   public void testGetConnectionTimeout() throws Exception {
     initializeConfiguration();
-    assertThat(configuration.getConnectionTimeout()).isEqualTo(0);
+    assertThat(configuration.http().connectionTimeout()).isEqualTo(0);
 
-    when(configMock.getInt(CONNECTION_TIMEOUT_KEY, DEFAULT_TIMEOUT_MS)).thenReturn(TIMEOUT);
+    when(configMock.getInt(HTTP_SECTION, CONNECTION_TIMEOUT_KEY, DEFAULT_TIMEOUT_MS))
+        .thenReturn(TIMEOUT);
     initializeConfiguration();
-    assertThat(configuration.getConnectionTimeout()).isEqualTo(TIMEOUT);
+    assertThat(configuration.http().connectionTimeout()).isEqualTo(TIMEOUT);
 
-    when(configMock.getInt(CONNECTION_TIMEOUT_KEY, DEFAULT_TIMEOUT_MS))
+    when(configMock.getInt(HTTP_SECTION, CONNECTION_TIMEOUT_KEY, DEFAULT_TIMEOUT_MS))
         .thenThrow(new IllegalArgumentException(ERROR_MESSAGE));
     initializeConfiguration();
-    assertThat(configuration.getConnectionTimeout()).isEqualTo(DEFAULT_TIMEOUT_MS);
+    assertThat(configuration.http().connectionTimeout()).isEqualTo(DEFAULT_TIMEOUT_MS);
   }
 
   @Test
   public void testGetSocketTimeout() throws Exception {
     initializeConfiguration();
-    assertThat(configuration.getSocketTimeout()).isEqualTo(0);
+    assertThat(configuration.http().socketTimeout()).isEqualTo(0);
 
-    when(configMock.getInt(SOCKET_TIMEOUT_KEY, DEFAULT_TIMEOUT_MS)).thenReturn(TIMEOUT);
+    when(configMock.getInt(HTTP_SECTION, SOCKET_TIMEOUT_KEY, DEFAULT_TIMEOUT_MS))
+        .thenReturn(TIMEOUT);
     initializeConfiguration();
-    assertThat(configuration.getSocketTimeout()).isEqualTo(TIMEOUT);
+    assertThat(configuration.http().socketTimeout()).isEqualTo(TIMEOUT);
 
-    when(configMock.getInt(SOCKET_TIMEOUT_KEY, DEFAULT_TIMEOUT_MS))
+    when(configMock.getInt(HTTP_SECTION, SOCKET_TIMEOUT_KEY, DEFAULT_TIMEOUT_MS))
         .thenThrow(new IllegalArgumentException(ERROR_MESSAGE));
     initializeConfiguration();
-    assertThat(configuration.getSocketTimeout()).isEqualTo(DEFAULT_TIMEOUT_MS);
+    assertThat(configuration.http().socketTimeout()).isEqualTo(DEFAULT_TIMEOUT_MS);
   }
 
   @Test
   public void testGetMaxTries() throws Exception {
     initializeConfiguration();
-    assertThat(configuration.getMaxTries()).isEqualTo(0);
+    assertThat(configuration.http().maxTries()).isEqualTo(0);
 
-    when(configMock.getInt(MAX_TRIES_KEY, DEFAULT_MAX_TRIES)).thenReturn(MAX_TRIES);
+    when(configMock.getInt(HTTP_SECTION, MAX_TRIES_KEY, DEFAULT_MAX_TRIES)).thenReturn(MAX_TRIES);
     initializeConfiguration();
-    assertThat(configuration.getMaxTries()).isEqualTo(MAX_TRIES);
+    assertThat(configuration.http().maxTries()).isEqualTo(MAX_TRIES);
 
-    when(configMock.getInt(MAX_TRIES_KEY, DEFAULT_MAX_TRIES))
+    when(configMock.getInt(HTTP_SECTION, MAX_TRIES_KEY, DEFAULT_MAX_TRIES))
         .thenThrow(new IllegalArgumentException(ERROR_MESSAGE));
     initializeConfiguration();
-    assertThat(configuration.getMaxTries()).isEqualTo(DEFAULT_MAX_TRIES);
+    assertThat(configuration.http().maxTries()).isEqualTo(DEFAULT_MAX_TRIES);
   }
 
   @Test
   public void testGetRetryInterval() throws Exception {
     initializeConfiguration();
-    assertThat(configuration.getRetryInterval()).isEqualTo(0);
+    assertThat(configuration.http().retryInterval()).isEqualTo(0);
 
-    when(configMock.getInt(RETRY_INTERVAL_KEY, DEFAULT_RETRY_INTERVAL)).thenReturn(RETRY_INTERVAL);
+    when(configMock.getInt(HTTP_SECTION, RETRY_INTERVAL_KEY, DEFAULT_RETRY_INTERVAL))
+        .thenReturn(RETRY_INTERVAL);
     initializeConfiguration();
-    assertThat(configuration.getRetryInterval()).isEqualTo(RETRY_INTERVAL);
+    assertThat(configuration.http().retryInterval()).isEqualTo(RETRY_INTERVAL);
 
-    when(configMock.getInt(RETRY_INTERVAL_KEY, DEFAULT_RETRY_INTERVAL))
+    when(configMock.getInt(HTTP_SECTION, RETRY_INTERVAL_KEY, DEFAULT_RETRY_INTERVAL))
         .thenThrow(new IllegalArgumentException(ERROR_MESSAGE));
     initializeConfiguration();
-    assertThat(configuration.getRetryInterval()).isEqualTo(DEFAULT_RETRY_INTERVAL);
+    assertThat(configuration.http().retryInterval()).isEqualTo(DEFAULT_RETRY_INTERVAL);
   }
 
   @Test
   public void testGetIndexThreadPoolSize() throws Exception {
     initializeConfiguration();
-    assertThat(configuration.getIndexThreadPoolSize()).isEqualTo(0);
+    assertThat(configuration.index().threadPoolSize()).isEqualTo(0);
 
-    when(configMock.getInt(INDEX_THREAD_POOL_SIZE_KEY, DEFAULT_THREAD_POOL_SIZE))
+    when(configMock.getInt(INDEX_SECTION, THREAD_POOL_SIZE_KEY, DEFAULT_THREAD_POOL_SIZE))
         .thenReturn(THREAD_POOL_SIZE);
     initializeConfiguration();
-    assertThat(configuration.getIndexThreadPoolSize()).isEqualTo(THREAD_POOL_SIZE);
+    assertThat(configuration.index().threadPoolSize()).isEqualTo(THREAD_POOL_SIZE);
 
-    when(configMock.getInt(INDEX_THREAD_POOL_SIZE_KEY, DEFAULT_THREAD_POOL_SIZE))
+    when(configMock.getInt(INDEX_SECTION, THREAD_POOL_SIZE_KEY, DEFAULT_THREAD_POOL_SIZE))
         .thenThrow(new IllegalArgumentException(ERROR_MESSAGE));
     initializeConfiguration();
-    assertThat(configuration.getIndexThreadPoolSize()).isEqualTo(DEFAULT_THREAD_POOL_SIZE);
+    assertThat(configuration.index().threadPoolSize()).isEqualTo(DEFAULT_THREAD_POOL_SIZE);
   }
 
   @Test
   public void testGetCacheThreadPoolSize() throws Exception {
     initializeConfiguration();
-    assertThat(configuration.getCacheThreadPoolSize()).isEqualTo(0);
+    assertThat(configuration.cache().threadPoolSize()).isEqualTo(0);
 
-    when(configMock.getInt(CACHE_THREAD_POOL_SIZE_KEY, DEFAULT_THREAD_POOL_SIZE))
+    when(configMock.getInt(CACHE_SECTION, THREAD_POOL_SIZE_KEY, DEFAULT_THREAD_POOL_SIZE))
         .thenReturn(THREAD_POOL_SIZE);
     initializeConfiguration();
-    assertThat(configuration.getCacheThreadPoolSize()).isEqualTo(THREAD_POOL_SIZE);
+    assertThat(configuration.cache().threadPoolSize()).isEqualTo(THREAD_POOL_SIZE);
 
-    when(configMock.getInt(CACHE_THREAD_POOL_SIZE_KEY, DEFAULT_THREAD_POOL_SIZE))
+    when(configMock.getInt(CACHE_SECTION, THREAD_POOL_SIZE_KEY, DEFAULT_THREAD_POOL_SIZE))
         .thenThrow(new IllegalArgumentException(ERROR_MESSAGE));
     initializeConfiguration();
-    assertThat(configuration.getCacheThreadPoolSize()).isEqualTo(DEFAULT_THREAD_POOL_SIZE);
+    assertThat(configuration.cache().threadPoolSize()).isEqualTo(DEFAULT_THREAD_POOL_SIZE);
   }
 
   @Test
   public void testGetSharedDirectory() throws Exception {
     initializeConfiguration();
-    assertThat(configuration.getSharedDirectory()).isEqualTo(SHARED_DIRECTORY);
+    assertThat(configuration.main().sharedDirectory()).isEqualTo(SHARED_DIRECTORY);
   }
 
   @Test(expected = ProvisionException.class)
   public void shouldThrowExceptionIfSharedDirectoryNotConfigured() throws Exception {
-    when(configMock.getString(SHARED_DIRECTORY_KEY)).thenReturn(null);
+    when(configMock.getString(MAIN_SECTION, null, SHARED_DIRECTORY_KEY)).thenReturn(null);
     initializeConfiguration();
   }
 
   @Test
   public void testGetCleanupInterval() throws Exception {
     initializeConfiguration();
-    assertThat(configuration.getCleanupInterval()).isEqualTo(DEFAULT_CLEANUP_INTERVAL_MS);
+    assertThat(configuration.websession().cleanupInterval()).isEqualTo(DEFAULT_CLEANUP_INTERVAL_MS);
 
-    when(configMock.getString(CLEANUP_INTERVAL_KEY)).thenReturn("30 seconds");
+    when(configMock.getString(WEBSESSION_SECTION, null, CLEANUP_INTERVAL_KEY))
+        .thenReturn("30 seconds");
     initializeConfiguration();
-    assertThat(configuration.getCleanupInterval()).isEqualTo(SECONDS.toMillis(30));
+    assertThat(configuration.websession().cleanupInterval()).isEqualTo(SECONDS.toMillis(30));
   }
 }
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/ModuleTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/ModuleTest.java
index 8af8c51..c577a01 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/ModuleTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/ModuleTest.java
@@ -25,13 +25,15 @@
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
+import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 
 @RunWith(MockitoJUnitRunner.class)
 public class ModuleTest {
 
-  @Mock private Configuration config;
+  @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+  private Configuration configMock;
 
   @Rule public TemporaryFolder tempFolder = new TemporaryFolder();
 
@@ -46,17 +48,17 @@
   public void shouldCreateSharedDirectoryIfItDoesNotExist() throws Exception {
     File configuredDirectory = tempFolder.newFolder();
     assertThat(configuredDirectory.delete()).isTrue();
-    when(config.getSharedDirectory()).thenReturn(configuredDirectory.getAbsolutePath());
+    when(configMock.main().sharedDirectory()).thenReturn(configuredDirectory.getAbsolutePath());
 
-    Path sharedDirectory = module.getSharedDirectory(config);
+    Path sharedDirectory = module.getSharedDirectory(configMock);
     assertThat(sharedDirectory.toFile().exists()).isTrue();
   }
 
   @Test(expected = IOException.class)
   public void shouldThrowAnExceptionIfAnErrorOccurCreatingSharedDirectory() throws Exception {
     File configuredDirectory = tempFolder.newFile();
-    when(config.getSharedDirectory()).thenReturn(configuredDirectory.getAbsolutePath());
+    when(configMock.main().sharedDirectory()).thenReturn(configuredDirectory.getAbsolutePath());
 
-    module.getSharedDirectory(config);
+    module.getSharedDirectory(configMock);
   }
 }
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/cache/CacheExecutorProviderTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/cache/CacheExecutorProviderTest.java
index 08a1b19..fbc11e0 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/cache/CacheExecutorProviderTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/cache/CacheExecutorProviderTest.java
@@ -24,6 +24,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 
@@ -31,14 +32,15 @@
 public class CacheExecutorProviderTest {
 
   @Mock private WorkQueue.Executor executorMock;
+
   private CacheExecutorProvider cacheExecutorProvider;
 
   @Before
   public void setUp() throws Exception {
     WorkQueue workQueueMock = mock(WorkQueue.class);
     when(workQueueMock.createQueue(4, "Forward-cache-eviction-event")).thenReturn(executorMock);
-    Configuration configMock = mock(Configuration.class);
-    when(configMock.getCacheThreadPoolSize()).thenReturn(4);
+    Configuration configMock = mock(Configuration.class, Answers.RETURNS_DEEP_STUBS);
+    when(configMock.cache().threadPoolSize()).thenReturn(4);
 
     cacheExecutorProvider = new CacheExecutorProvider(workQueueMock, configMock);
   }
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpClientProviderTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpClientProviderTest.java
index ac14ce4..a2aec9d 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpClientProviderTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpClientProviderTest.java
@@ -26,6 +26,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 
@@ -34,14 +35,15 @@
   private static final int TIME_INTERVAL = 1000;
   private static final String EMPTY = "";
 
-  @Mock private Configuration config;
+  @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+  private Configuration configMock;
 
   @Before
   public void setUp() throws Exception {
-    when(config.getUser()).thenReturn(EMPTY);
-    when(config.getPassword()).thenReturn(EMPTY);
-    when(config.getConnectionTimeout()).thenReturn(TIME_INTERVAL);
-    when(config.getSocketTimeout()).thenReturn(TIME_INTERVAL);
+    when(configMock.http().user()).thenReturn(EMPTY);
+    when(configMock.http().password()).thenReturn(EMPTY);
+    when(configMock.http().connectionTimeout()).thenReturn(TIME_INTERVAL);
+    when(configMock.http().socketTimeout()).thenReturn(TIME_INTERVAL);
   }
 
   @Test
@@ -58,7 +60,7 @@
   class TestModule extends AbstractModule {
     @Override
     protected void configure() {
-      bind(Configuration.class).toInstance(config);
+      bind(Configuration.class).toInstance(configMock);
       bind(CloseableHttpClient.class).toProvider(HttpClientProvider.class).in(Scopes.SINGLETON);
     }
   }
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpSessionTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpSessionTest.java
index 19a50df..d5e27b1 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpSessionTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/HttpSessionTest.java
@@ -41,6 +41,7 @@
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.mockito.Answers;
 
 public class HttpSessionTest {
   private static final int MAX_TRIES = 3;
@@ -63,23 +64,24 @@
 
   @Rule public WireMockRule wireMockRule = new WireMockRule(0);
 
-  private Configuration cfg;
+  private Configuration configMock;
 
   @Before
   public void setUp() throws Exception {
     String url = "http://localhost:" + wireMockRule.port();
-    cfg = mock(Configuration.class);
-    when(cfg.getUser()).thenReturn("user");
-    when(cfg.getPassword()).thenReturn("pass");
-    when(cfg.getMaxTries()).thenReturn(MAX_TRIES);
-    when(cfg.getConnectionTimeout()).thenReturn(TIMEOUT);
-    when(cfg.getSocketTimeout()).thenReturn(TIMEOUT);
-    when(cfg.getRetryInterval()).thenReturn(RETRY_INTERVAL);
+    configMock = mock(Configuration.class, Answers.RETURNS_DEEP_STUBS);
+    when(configMock.http().user()).thenReturn("user");
+    when(configMock.http().password()).thenReturn("pass");
+    when(configMock.http().maxTries()).thenReturn(MAX_TRIES);
+    when(configMock.http().connectionTimeout()).thenReturn(TIMEOUT);
+    when(configMock.http().socketTimeout()).thenReturn(TIMEOUT);
+    when(configMock.http().retryInterval()).thenReturn(RETRY_INTERVAL);
 
     PeerInfo peerInfo = mock(PeerInfo.class);
     when(peerInfo.getDirectUrl()).thenReturn(url);
     httpSession =
-        new HttpSession(new HttpClientProvider(cfg).get(), Providers.of(Optional.of(peerInfo)));
+        new HttpSession(
+            new HttpClientProvider(configMock).get(), Providers.of(Optional.of(peerInfo)));
   }
 
   @Test
@@ -184,7 +186,7 @@
   public void testNoRequestWhenPeerInfoUnknown() throws IOException {
     httpSession =
         new HttpSession(
-            new HttpClientProvider(cfg).get(), Providers.of(Optional.<PeerInfo>absent()));
+            new HttpClientProvider(configMock).get(), Providers.of(Optional.<PeerInfo>absent()));
     try {
       httpSession.post(ENDPOINT);
       fail("Expected PeerInfoNotAvailableException");
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarderTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarderTest.java
index a3172f8..e19f6f2 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarderTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarderTest.java
@@ -35,6 +35,7 @@
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.mockito.Answers;
 
 public class RestForwarderTest {
   private static final String PLUGIN_NAME = "high-availability";
@@ -61,7 +62,6 @@
 
   private RestForwarder forwarder;
   private HttpSession httpSessionMock;
-  private Configuration configurationMock;
 
   @BeforeClass
   public static void setup() {
@@ -71,10 +71,10 @@
   @Before
   public void setUp() {
     httpSessionMock = mock(HttpSession.class);
-    configurationMock = mock(Configuration.class);
-    when(configurationMock.getMaxTries()).thenReturn(3);
-    when(configurationMock.getRetryInterval()).thenReturn(10);
-    forwarder = new RestForwarder(httpSessionMock, PLUGIN_NAME, configurationMock);
+    Configuration configMock = mock(Configuration.class, Answers.RETURNS_DEEP_STUBS);
+    when(configMock.http().maxTries()).thenReturn(3);
+    when(configMock.http().retryInterval()).thenReturn(10);
+    forwarder = new RestForwarder(httpSessionMock, PLUGIN_NAME, configMock);
   }
 
   @Test
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/IndexExecutorProviderTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/IndexExecutorProviderTest.java
index 66b84f1..cc4ba99 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/IndexExecutorProviderTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/IndexExecutorProviderTest.java
@@ -24,6 +24,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 
@@ -37,8 +38,8 @@
     executorMock = mock(WorkQueue.Executor.class);
     WorkQueue workQueueMock = mock(WorkQueue.class);
     when(workQueueMock.createQueue(4, "Forward-index-event")).thenReturn(executorMock);
-    Configuration configMock = mock(Configuration.class);
-    when(configMock.getIndexThreadPoolSize()).thenReturn(4);
+    Configuration configMock = mock(Configuration.class, Answers.RETURNS_DEEP_STUBS);
+    when(configMock.index().threadPoolSize()).thenReturn(4);
     indexExecutorProvider = new IndexExecutorProvider(workQueueMock, configMock);
   }
 
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/websession/file/FileBasedWebSessionCacheCleanerTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/websession/file/FileBasedWebSessionCacheCleanerTest.java
index 61f6aa2..04c73d4 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/websession/file/FileBasedWebSessionCacheCleanerTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/websession/file/FileBasedWebSessionCacheCleanerTest.java
@@ -33,6 +33,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 
@@ -46,7 +47,9 @@
   @Mock private ScheduledFuture<?> scheduledFutureMock;
   @Mock private WorkQueue workQueueMock;
   @Mock private Provider<CleanupTask> cleanupTaskProviderMock;
-  @Mock private Configuration configMock;
+
+  @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+  private Configuration configMock;
 
   private FileBasedWebSessionCacheCleaner cleaner;
 
@@ -57,7 +60,7 @@
     doReturn(scheduledFutureMock)
         .when(executorMock)
         .scheduleAtFixedRate(isA(CleanupTask.class), anyLong(), anyLong(), isA(TimeUnit.class));
-    when(configMock.getCleanupInterval()).thenReturn(CLEANUP_INTERVAL);
+    when(configMock.websession().cleanupInterval()).thenReturn(CLEANUP_INTERVAL);
     cleaner =
         new FileBasedWebSessionCacheCleaner(workQueueMock, cleanupTaskProviderMock, configMock);
   }