Merge branch 'stable-2.14'

* stable-2.14:
  Use Strings.isNullOrEmpty instead of just null check
  Code cleanup: use Objects.equals instead of own eq method
  Clarify and fix sharedDirectory configuration
  Upgrade Bazlets and use released 2.14.1 API
  Add configuration to disable synchronization
  Fix log message
  Move ForwardedAwareEventBroker to forwarder package

Change-Id: If6216d3a2f367433b0cd1929a0f8193895e636d5
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 680a8ce..6c56a8c 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/Configuration.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/Configuration.java
@@ -22,9 +22,12 @@
 import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.server.config.ConfigUtil;
 import com.google.gerrit.server.config.PluginConfigFactory;
+import com.google.gerrit.server.config.SitePaths;
 import com.google.inject.Inject;
 import com.google.inject.ProvisionException;
 import com.google.inject.Singleton;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import org.eclipse.jgit.lib.Config;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -53,12 +56,18 @@
   // cache section
   static final String CACHE_SECTION = "cache";
 
+  // event section
+  static final String EVENT_SECTION = "event";
+
   // index section
   static final String INDEX_SECTION = "index";
 
-  // common parameters to cache and index section
+  // common parameters to cache and index sections
   static final String THREAD_POOL_SIZE_KEY = "threadPoolSize";
 
+  // common parameters to cache, event index and websession sections
+  static final String SYNCHRONIZE_KEY = "synchronize";
+
   // websession section
   static final String WEBSESSION_SECTION = "websession";
   static final String CLEANUP_INTERVAL_KEY = "cleanupInterval";
@@ -69,21 +78,25 @@
   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);
+  static final boolean DEFAULT_SYNCHRONIZE = true;
 
   private final Main main;
   private final PeerInfo peerInfo;
   private final Http http;
   private final Cache cache;
+  private final Event event;
   private final Index index;
   private final Websession websession;
 
   @Inject
-  Configuration(PluginConfigFactory pluginConfigFactory, @PluginName String pluginName) {
+  Configuration(
+      PluginConfigFactory pluginConfigFactory, @PluginName String pluginName, SitePaths site) {
     Config cfg = pluginConfigFactory.getGlobalPluginConfig(pluginName);
-    main = new Main(cfg);
+    main = new Main(site, cfg);
     peerInfo = new PeerInfo(cfg);
     http = new Http(cfg);
     cache = new Cache(cfg);
+    event = new Event(cfg);
     index = new Index(cfg);
     websession = new Websession(cfg);
   }
@@ -104,6 +117,10 @@
     return cache;
   }
 
+  public Event event() {
+    return event;
+  }
+
   public Index index() {
     return index;
   }
@@ -117,23 +134,38 @@
       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);
+      log.debug("Failed to retrieve integer value: " + e.getMessage(), e);
+      return defaultValue;
+    }
+  }
+
+  private static boolean getBoolean(Config cfg, String section, String name, boolean defaultValue) {
+    try {
+      return cfg.getBoolean(section, name, defaultValue);
+    } catch (IllegalArgumentException e) {
+      log.error(String.format("invalid value for %s; using default value %s", name, defaultValue));
+      log.debug("Failed to retrieve boolean value: " + e.getMessage(), e);
       return defaultValue;
     }
   }
 
   public static class Main {
-    private final String sharedDirectory;
+    private final Path sharedDirectory;
 
-    private Main(Config cfg) {
-      sharedDirectory =
-          Strings.emptyToNull(cfg.getString(MAIN_SECTION, null, SHARED_DIRECTORY_KEY));
-      if (sharedDirectory == null) {
+    private Main(SitePaths site, Config cfg) {
+      String shared = Strings.emptyToNull(cfg.getString(MAIN_SECTION, null, SHARED_DIRECTORY_KEY));
+      if (shared == null) {
         throw new ProvisionException(SHARED_DIRECTORY_KEY + " must be configured");
       }
+      Path p = Paths.get(shared);
+      if (p.isAbsolute()) {
+        sharedDirectory = p;
+      } else {
+        sharedDirectory = site.resolve(shared);
+      }
     }
 
-    public String sharedDirectory() {
+    public Path sharedDirectory() {
       return sharedDirectory;
     }
   }
@@ -195,10 +227,24 @@
     }
   }
 
-  public static class Cache {
+  /** Common parameters to cache, event, index and websession */
+  public abstract static class Forwarding {
+    private final boolean synchronize;
+
+    private Forwarding(Config cfg, String section) {
+      synchronize = getBoolean(cfg, section, SYNCHRONIZE_KEY, DEFAULT_SYNCHRONIZE);
+    }
+
+    public boolean synchronize() {
+      return synchronize;
+    }
+  }
+
+  public static class Cache extends Forwarding {
     private final int threadPoolSize;
 
     private Cache(Config cfg) {
+      super(cfg, CACHE_SECTION);
       threadPoolSize = getInt(cfg, CACHE_SECTION, THREAD_POOL_SIZE_KEY, DEFAULT_THREAD_POOL_SIZE);
     }
 
@@ -207,10 +253,17 @@
     }
   }
 
-  public static class Index {
+  public static class Event extends Forwarding {
+    private Event(Config cfg) {
+      super(cfg, EVENT_SECTION);
+    }
+  }
+
+  public static class Index extends Forwarding {
     private final int threadPoolSize;
 
     private Index(Config cfg) {
+      super(cfg, INDEX_SECTION);
       threadPoolSize = getInt(cfg, INDEX_SECTION, THREAD_POOL_SIZE_KEY, DEFAULT_THREAD_POOL_SIZE);
     }
 
@@ -219,10 +272,11 @@
     }
   }
 
-  public static class Websession {
+  public static class Websession extends Forwarding {
     private final long cleanupInterval;
 
     private Websession(Config cfg) {
+      super(cfg, WEBSESSION_SECTION);
       this.cleanupInterval =
           ConfigUtil.getTimeUnit(
               Strings.nullToEmpty(cfg.getString(WEBSESSION_SECTION, null, CLEANUP_INTERVAL_KEY)),
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/HttpModule.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/HttpModule.java
index 1ff0344..57d1d91 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/HttpModule.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/HttpModule.java
@@ -17,11 +17,21 @@
 import com.ericsson.gerrit.plugins.highavailability.forwarder.rest.RestForwarderServletModule;
 import com.ericsson.gerrit.plugins.highavailability.websession.file.FileBasedWebsessionModule;
 import com.google.gerrit.httpd.plugins.HttpPluginModule;
+import com.google.inject.Inject;
 
 class HttpModule extends HttpPluginModule {
+  private final Configuration config;
+
+  @Inject
+  HttpModule(Configuration config) {
+    this.config = config;
+  }
+
   @Override
   protected void configureServlets() {
     install(new RestForwarderServletModule());
-    install(new FileBasedWebsessionModule());
+    if (config.websession().synchronize()) {
+      install(new FileBasedWebsessionModule());
+    }
   }
 }
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 ecef7cd..dc9ff62 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/Module.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/Module.java
@@ -16,35 +16,48 @@
 
 import com.ericsson.gerrit.plugins.highavailability.cache.CacheModule;
 import com.ericsson.gerrit.plugins.highavailability.event.EventModule;
+import com.ericsson.gerrit.plugins.highavailability.forwarder.ForwarderModule;
 import com.ericsson.gerrit.plugins.highavailability.forwarder.rest.RestForwarderModule;
 import com.ericsson.gerrit.plugins.highavailability.index.IndexModule;
 import com.ericsson.gerrit.plugins.highavailability.peers.PeerInfoModule;
 import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
 import com.google.inject.Provides;
-import com.google.inject.Scopes;
 import com.google.inject.Singleton;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.nio.file.Paths;
 
 class Module extends AbstractModule {
+  private final Configuration config;
+
+  @Inject
+  Module(Configuration config) {
+    this.config = config;
+  }
 
   @Override
   protected void configure() {
-    bind(Configuration.class).in(Scopes.SINGLETON);
+    install(new ForwarderModule());
     install(new RestForwarderModule());
-    install(new EventModule());
-    install(new IndexModule());
-    install(new CacheModule());
+
+    if (config.cache().synchronize()) {
+      install(new CacheModule());
+    }
+    if (config.event().synchronize()) {
+      install(new EventModule());
+    }
+    if (config.index().synchronize()) {
+      install(new IndexModule());
+    }
     install(new PeerInfoModule());
   }
 
   @Provides
   @Singleton
   @SharedDirectory
-  Path getSharedDirectory(Configuration cfg) throws IOException {
-    Path sharedDirectoryPath = Paths.get(cfg.main().sharedDirectory());
+  Path getSharedDirectory() throws IOException {
+    Path sharedDirectoryPath = config.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 ab9d0a4..dfb4887 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/Setup.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/Setup.java
@@ -24,6 +24,7 @@
 import com.google.gerrit.server.config.SitePaths;
 import com.google.inject.Inject;
 import java.nio.file.Path;
+import java.util.Objects;
 import org.eclipse.jgit.storage.file.FileBasedConfig;
 import org.eclipse.jgit.util.FS;
 
@@ -65,7 +66,7 @@
     ui.header("Main section");
     String sharedDir =
         promptAndSetString("Shared directory", MAIN_SECTION, SHARED_DIRECTORY_KEY, null);
-    if (sharedDir != null) {
+    if (!Strings.isNullOrEmpty(sharedDir)) {
       Path shared = site.site_path.resolve(sharedDir);
       FileUtil.mkdirsOrDie(shared, "cannot create " + shared);
     }
@@ -121,8 +122,8 @@
       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) {
+    if (!Objects.equals(oldValue, newValue)) {
+      if (!Strings.isNullOrEmpty(newValue)) {
         config.setString(section, null, name, newValue);
       } else {
         config.unset(section, name, name);
@@ -131,10 +132,6 @@
     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) {
     return Integer.toString(n);
   }
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/event/EventModule.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/event/EventModule.java
index 5de02fa..10789a3 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/event/EventModule.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/event/EventModule.java
@@ -14,9 +14,7 @@
 
 package com.ericsson.gerrit.plugins.highavailability.event;
 
-import com.google.gerrit.common.EventDispatcher;
 import com.google.gerrit.common.EventListener;
-import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.lifecycle.LifecycleModule;
 import java.util.concurrent.Executor;
@@ -28,6 +26,5 @@
     bind(Executor.class).annotatedWith(EventExecutor.class).toProvider(EventExecutorProvider.class);
     listener().to(EventExecutorProvider.class);
     DynamicSet.bind(binder(), EventListener.class).to(EventHandler.class);
-    DynamicItem.bind(binder(), EventDispatcher.class).to(ForwardedAwareEventBroker.class);
   }
 }
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/event/ForwardedAwareEventBroker.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedAwareEventBroker.java
similarity index 93%
rename from src/main/java/com/ericsson/gerrit/plugins/highavailability/event/ForwardedAwareEventBroker.java
rename to src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedAwareEventBroker.java
index 2f0c074..fa277c2 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/event/ForwardedAwareEventBroker.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedAwareEventBroker.java
@@ -12,9 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.ericsson.gerrit.plugins.highavailability.event;
+package com.ericsson.gerrit.plugins.highavailability.forwarder;
 
-import com.ericsson.gerrit.plugins.highavailability.forwarder.Context;
 import com.google.gerrit.common.EventBroker;
 import com.google.gerrit.common.EventListener;
 import com.google.gerrit.common.UserScopedEventListener;
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwarderModule.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwarderModule.java
new file mode 100644
index 0000000..5e2ab61
--- /dev/null
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwarderModule.java
@@ -0,0 +1,27 @@
+// Copyright (C) 2017 Ericsson
+//
+// 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.ericsson.gerrit.plugins.highavailability.forwarder;
+
+import com.google.gerrit.common.EventDispatcher;
+import com.google.gerrit.extensions.registration.DynamicItem;
+import com.google.inject.AbstractModule;
+
+public class ForwarderModule extends AbstractModule {
+
+  @Override
+  protected void configure() {
+    DynamicItem.bind(binder(), EventDispatcher.class).to(ForwardedAwareEventBroker.class);
+  }
+}
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index 3290f1b..ca10488 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -17,6 +17,10 @@
 
 main.sharedDirectory
 :   Path to a directory accessible from both master instances.
+    When given as a relative path, then it is resolved against the $SITE_PATH
+    or Gerrit server. For example, if $SITE_PATH is "/gerrit/root" and
+    sharedDirectory is given as "shared/dir" then the real path of the shared
+    directory is "/gerrit/root/shared/dir".
 
 peerInfo.url
 :   Specify the URL for the secondary (target) instance.
@@ -50,14 +54,30 @@
 :   The interval of time in milliseconds between the subsequent auto-retries.
     When not specified, the default value is set to 1000ms.
 
+cache.synchronize
+:   Whether to synchronize cache evictions.
+    Defaults to true.
+
 cache.threadPoolSize
 :   Maximum number of threads used to send cache evictions to the target instance.
     Defaults to 1.
 
+event.synchronize
+:   Whether to synchronize stream events.
+    Defaults to true.
+
+index.synchronize
+:   Whether to synchronize secondary indexes.
+    Defaults to true.
+
 index.threadPoolSize
 :   Maximum number of threads used to send index events to the target instance.
     Defaults to 1.
 
+websession.synchronize
+:   Whether to synchronize web sessions.
+    Defaults to true.
+
 websession.cleanupInterval
 :   Frequency for deleting expired web sessions. Values should use common time
     unit suffixes to express their setting:
@@ -69,4 +89,4 @@
 * 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
+Defaults to 24 hours.
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 31449e3..a0246f6 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/ConfigurationTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/ConfigurationTest.java
@@ -20,8 +20,10 @@
 import static com.ericsson.gerrit.plugins.highavailability.Configuration.DEFAULT_CLEANUP_INTERVAL_MS;
 import static com.ericsson.gerrit.plugins.highavailability.Configuration.DEFAULT_MAX_TRIES;
 import static com.ericsson.gerrit.plugins.highavailability.Configuration.DEFAULT_RETRY_INTERVAL;
+import static com.ericsson.gerrit.plugins.highavailability.Configuration.DEFAULT_SYNCHRONIZE;
 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.EVENT_SECTION;
 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;
@@ -31,16 +33,22 @@
 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.SYNCHRONIZE_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.junit.Assert.assertEquals;
 import static org.mockito.Mockito.when;
 
 import com.google.gerrit.server.config.PluginConfigFactory;
+import com.google.gerrit.server.config.SitePaths;
 import com.google.inject.ProvisionException;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import org.eclipse.jgit.lib.Config;
 import org.junit.Before;
 import org.junit.Test;
@@ -59,22 +67,27 @@
   private static final int RETRY_INTERVAL = 1000;
   private static final int THREAD_POOL_SIZE = 1;
   private static final String SHARED_DIRECTORY = "/some/directory";
+  private static final Path SHARED_DIR_PATH = Paths.get(SHARED_DIRECTORY);
+  private static final String RELATIVE_SHARED_DIRECTORY = "relative/dir";
+  private static final Path SITE_PATH = Paths.get("/site_path");
   private static final String ERROR_MESSAGE = "some error message";
 
   @Mock private PluginConfigFactory cfgFactoryMock;
   @Mock private Config configMock;
+  private SitePaths site;
   private Configuration configuration;
   private String pluginName = "high-availability";
 
   @Before
-  public void setUp() {
+  public void setUp() throws IOException {
     when(cfgFactoryMock.getGlobalPluginConfig(pluginName)).thenReturn(configMock);
     when(configMock.getString(MAIN_SECTION, null, SHARED_DIRECTORY_KEY))
         .thenReturn(SHARED_DIRECTORY);
+    site = new SitePaths(SITE_PATH);
   }
 
   private void initializeConfiguration() {
-    configuration = new Configuration(cfgFactoryMock, pluginName);
+    configuration = new Configuration(cfgFactoryMock, pluginName, site);
   }
 
   @Test
@@ -195,6 +208,24 @@
   }
 
   @Test
+  public void testGetIndexSynchronize() throws Exception {
+    when(configMock.getBoolean(INDEX_SECTION, SYNCHRONIZE_KEY, DEFAULT_SYNCHRONIZE))
+        .thenReturn(true);
+    initializeConfiguration();
+    assertThat(configuration.index().synchronize()).isTrue();
+
+    when(configMock.getBoolean(INDEX_SECTION, SYNCHRONIZE_KEY, DEFAULT_SYNCHRONIZE))
+        .thenReturn(false);
+    initializeConfiguration();
+    assertThat(configuration.index().synchronize()).isFalse();
+
+    when(configMock.getBoolean(INDEX_SECTION, SYNCHRONIZE_KEY, DEFAULT_SYNCHRONIZE))
+        .thenThrow(new IllegalArgumentException(ERROR_MESSAGE));
+    initializeConfiguration();
+    assertThat(configuration.index().synchronize()).isTrue();
+  }
+
+  @Test
   public void testGetCacheThreadPoolSize() throws Exception {
     initializeConfiguration();
     assertThat(configuration.cache().threadPoolSize()).isEqualTo(0);
@@ -211,9 +242,45 @@
   }
 
   @Test
+  public void testGetCacheSynchronize() throws Exception {
+    when(configMock.getBoolean(CACHE_SECTION, SYNCHRONIZE_KEY, DEFAULT_SYNCHRONIZE))
+        .thenReturn(true);
+    initializeConfiguration();
+    assertThat(configuration.cache().synchronize()).isTrue();
+
+    when(configMock.getBoolean(CACHE_SECTION, SYNCHRONIZE_KEY, DEFAULT_SYNCHRONIZE))
+        .thenReturn(false);
+    initializeConfiguration();
+    assertThat(configuration.cache().synchronize()).isFalse();
+
+    when(configMock.getBoolean(CACHE_SECTION, SYNCHRONIZE_KEY, DEFAULT_SYNCHRONIZE))
+        .thenThrow(new IllegalArgumentException(ERROR_MESSAGE));
+    initializeConfiguration();
+    assertThat(configuration.cache().synchronize()).isTrue();
+  }
+
+  @Test
+  public void testGetEventSynchronize() throws Exception {
+    when(configMock.getBoolean(EVENT_SECTION, SYNCHRONIZE_KEY, DEFAULT_SYNCHRONIZE))
+        .thenReturn(true);
+    initializeConfiguration();
+    assertThat(configuration.event().synchronize()).isTrue();
+
+    when(configMock.getBoolean(EVENT_SECTION, SYNCHRONIZE_KEY, DEFAULT_SYNCHRONIZE))
+        .thenReturn(false);
+    initializeConfiguration();
+    assertThat(configuration.event().synchronize()).isFalse();
+
+    when(configMock.getBoolean(EVENT_SECTION, SYNCHRONIZE_KEY, DEFAULT_SYNCHRONIZE))
+        .thenThrow(new IllegalArgumentException(ERROR_MESSAGE));
+    initializeConfiguration();
+    assertThat(configuration.event().synchronize()).isTrue();
+  }
+
+  @Test
   public void testGetSharedDirectory() throws Exception {
     initializeConfiguration();
-    assertThat(configuration.main().sharedDirectory()).isEqualTo(SHARED_DIRECTORY);
+    assertEquals(configuration.main().sharedDirectory(), SHARED_DIR_PATH);
   }
 
   @Test(expected = ProvisionException.class)
@@ -223,6 +290,16 @@
   }
 
   @Test
+  public void testRelativeSharedDir() {
+    when(configMock.getString(MAIN_SECTION, null, SHARED_DIRECTORY_KEY))
+        .thenReturn(RELATIVE_SHARED_DIRECTORY);
+    initializeConfiguration();
+
+    assertEquals(
+        configuration.main().sharedDirectory(), SITE_PATH.resolve(RELATIVE_SHARED_DIRECTORY));
+  }
+
+  @Test
   public void testGetCleanupInterval() throws Exception {
     initializeConfiguration();
     assertThat(configuration.websession().cleanupInterval()).isEqualTo(DEFAULT_CLEANUP_INTERVAL_MS);
@@ -232,4 +309,22 @@
     initializeConfiguration();
     assertThat(configuration.websession().cleanupInterval()).isEqualTo(SECONDS.toMillis(30));
   }
+
+  @Test
+  public void testGetWebsessionSynchronize() throws Exception {
+    when(configMock.getBoolean(WEBSESSION_SECTION, SYNCHRONIZE_KEY, DEFAULT_SYNCHRONIZE))
+        .thenReturn(true);
+    initializeConfiguration();
+    assertThat(configuration.websession().synchronize()).isTrue();
+
+    when(configMock.getBoolean(WEBSESSION_SECTION, SYNCHRONIZE_KEY, DEFAULT_SYNCHRONIZE))
+        .thenReturn(false);
+    initializeConfiguration();
+    assertThat(configuration.websession().synchronize()).isFalse();
+
+    when(configMock.getBoolean(WEBSESSION_SECTION, SYNCHRONIZE_KEY, DEFAULT_SYNCHRONIZE))
+        .thenThrow(new IllegalArgumentException(ERROR_MESSAGE));
+    initializeConfiguration();
+    assertThat(configuration.websession().synchronize()).isTrue();
+  }
 }
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 c577a01..c7760ba 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/ModuleTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/ModuleTest.java
@@ -41,24 +41,24 @@
 
   @Before
   public void setUp() {
-    module = new Module();
+    module = new Module(configMock);
   }
 
   @Test
   public void shouldCreateSharedDirectoryIfItDoesNotExist() throws Exception {
     File configuredDirectory = tempFolder.newFolder();
     assertThat(configuredDirectory.delete()).isTrue();
-    when(configMock.main().sharedDirectory()).thenReturn(configuredDirectory.getAbsolutePath());
+    when(configMock.main().sharedDirectory()).thenReturn(configuredDirectory.toPath());
 
-    Path sharedDirectory = module.getSharedDirectory(configMock);
+    Path sharedDirectory = module.getSharedDirectory();
     assertThat(sharedDirectory.toFile().exists()).isTrue();
   }
 
   @Test(expected = IOException.class)
   public void shouldThrowAnExceptionIfAnErrorOccurCreatingSharedDirectory() throws Exception {
     File configuredDirectory = tempFolder.newFile();
-    when(configMock.main().sharedDirectory()).thenReturn(configuredDirectory.getAbsolutePath());
+    when(configMock.main().sharedDirectory()).thenReturn(configuredDirectory.toPath());
 
-    module.getSharedDirectory(configMock);
+    module.getSharedDirectory();
   }
 }
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/event/ForwardedAwareEventBrokerTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedAwareEventBrokerTest.java
similarity index 92%
rename from src/test/java/com/ericsson/gerrit/plugins/highavailability/event/ForwardedAwareEventBrokerTest.java
rename to src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedAwareEventBrokerTest.java
index 1b2d345..1183b07 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/event/ForwardedAwareEventBrokerTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedAwareEventBrokerTest.java
@@ -12,13 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.ericsson.gerrit.plugins.highavailability.event;
+package com.ericsson.gerrit.plugins.highavailability.forwarder;
 
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
 
-import com.ericsson.gerrit.plugins.highavailability.forwarder.Context;
 import com.google.gerrit.common.EventListener;
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.server.events.Event;