Merge branch 'stable-2.14'
* stable-2.14:
CacheEvictionIT: Re-enable and adapt to use @GlobalPluginConfig
Migrate plugin config to own configuration file
Disallow DELETE when index does not support delete
Factor common code out of Index*RestApiServlet classes
Forward group indexing event
Index{Account,Change}RestApiServlet: Use UTF_8 constant
Revert "Ignore flaky CacheEvictionIT"
Ignore flaky CacheEvictionIT
IndexAccountRestApiServlet: Remove unnecessary semicolon
Change-Id: If56525eae31484d17cf59b2eb0ff324b7e8524be
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/Forwarder.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/Forwarder.java
index 99dff1d..651f609 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/Forwarder.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/Forwarder.java
@@ -44,6 +44,14 @@
boolean deleteChangeFromIndex(int changeId);
/**
+ * Forward a group indexing event to the other master.
+ *
+ * @param uuid the group to index.
+ * @return true if successful, otherwise false.
+ */
+ boolean indexGroup(String uuid);
+
+ /**
* Forward a stream event to the other master.
*
* @param event the event to forward.
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/AbstractIndexRestApiServlet.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/AbstractIndexRestApiServlet.java
new file mode 100644
index 0000000..fca807f
--- /dev/null
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/AbstractIndexRestApiServlet.java
@@ -0,0 +1,130 @@
+// Copyright (C) 2017 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.ericsson.gerrit.plugins.highavailability.forwarder.rest;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static javax.servlet.http.HttpServletResponse.SC_CONFLICT;
+import static javax.servlet.http.HttpServletResponse.SC_METHOD_NOT_ALLOWED;
+import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
+import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
+
+import com.ericsson.gerrit.plugins.highavailability.forwarder.Context;
+import com.google.gwtorm.server.OrmException;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractIndexRestApiServlet<T> extends HttpServlet {
+ private static final long serialVersionUID = -1L;
+ private static final Logger logger = LoggerFactory.getLogger(AbstractIndexRestApiServlet.class);
+ private final Map<T, AtomicInteger> idLocks = new HashMap<>();
+ private final String type;
+ private final boolean allowDelete;
+
+ enum Operation {
+ INDEX,
+ DELETE
+ }
+
+ abstract T parse(String id);
+
+ abstract void index(T id, Operation operation) throws IOException, OrmException;
+
+ AbstractIndexRestApiServlet(String type, boolean allowDelete) {
+ this.type = type;
+ this.allowDelete = allowDelete;
+ }
+
+ AbstractIndexRestApiServlet(String type) {
+ this(type, false);
+ }
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse rsp)
+ throws IOException, ServletException {
+ process(req, rsp, Operation.INDEX);
+ }
+
+ @Override
+ protected void doDelete(HttpServletRequest req, HttpServletResponse rsp)
+ throws IOException, ServletException {
+ if (!allowDelete) {
+ sendError(rsp, SC_METHOD_NOT_ALLOWED, String.format("cannot delete %s from index", type));
+ } else {
+ process(req, rsp, Operation.DELETE);
+ }
+ }
+
+ private void process(HttpServletRequest req, HttpServletResponse rsp, Operation operation) {
+ rsp.setContentType("text/plain");
+ rsp.setCharacterEncoding(UTF_8.name());
+ String path = req.getPathInfo();
+ T id = parse(path.substring(path.lastIndexOf('/') + 1));
+ try {
+ Context.setForwardedEvent(true);
+ AtomicInteger idLock = getAndIncrementIdLock(id);
+ synchronized (idLock) {
+ index(id, operation);
+ }
+ if (idLock.decrementAndGet() == 0) {
+ removeIdLock(id);
+ }
+ rsp.setStatus(SC_NO_CONTENT);
+ } catch (IOException e) {
+ sendError(rsp, SC_CONFLICT, e.getMessage());
+ logger.error(String.format("Unable to update %s index", type), e);
+ } catch (OrmException e) {
+ String msg = String.format("Error trying to find %s \n", type);
+ sendError(rsp, SC_NOT_FOUND, msg);
+ logger.debug(msg, e);
+ } finally {
+ Context.unsetForwardedEvent();
+ }
+ }
+
+ private AtomicInteger getAndIncrementIdLock(T id) {
+ synchronized (idLocks) {
+ AtomicInteger lock = idLocks.get(id);
+ if (lock == null) {
+ lock = new AtomicInteger(1);
+ idLocks.put(id, lock);
+ } else {
+ lock.incrementAndGet();
+ }
+ return lock;
+ }
+ }
+
+ private void removeIdLock(T id) {
+ synchronized (idLocks) {
+ idLocks.remove(id);
+ }
+ }
+
+ private void sendError(HttpServletResponse rsp, int statusCode, String message) {
+ try {
+ rsp.sendError(statusCode, message);
+ } catch (IOException e) {
+ logger.error("Failed to send error messsage: " + e.getMessage(), e);
+ }
+ }
+}
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/IndexAccountRestApiServlet.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexAccountRestApiServlet.java
index b743db9..94c9a4f 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexAccountRestApiServlet.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexAccountRestApiServlet.java
@@ -14,93 +14,35 @@
package com.ericsson.gerrit.plugins.highavailability.forwarder.rest;
-import static javax.servlet.http.HttpServletResponse.SC_CONFLICT;
-import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
-
-import com.ericsson.gerrit.plugins.highavailability.forwarder.Context;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.index.account.AccountIndexer;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
-class IndexAccountRestApiServlet extends HttpServlet {
+class IndexAccountRestApiServlet extends AbstractIndexRestApiServlet<Account.Id> {
private static final long serialVersionUID = -1L;
private static final Logger logger = LoggerFactory.getLogger(IndexAccountRestApiServlet.class);
- private static final Map<Account.Id, AtomicInteger> accountIdLocks = new HashMap<>();
private final AccountIndexer indexer;
@Inject
IndexAccountRestApiServlet(AccountIndexer indexer) {
+ super("account");
this.indexer = indexer;
}
@Override
- protected void doPost(HttpServletRequest req, HttpServletResponse rsp)
- throws IOException, ServletException {
- rsp.setContentType("text/plain");
- rsp.setCharacterEncoding("UTF-8");
- String path = req.getPathInfo();
- String accountId = path.substring(path.lastIndexOf('/') + 1);
- Account.Id id = Account.Id.parse(accountId);
- try {
- Context.setForwardedEvent(true);
- index(id);
- rsp.setStatus(SC_NO_CONTENT);
- } catch (IOException e) {
- sendError(rsp, SC_CONFLICT, e.getMessage());
- logger.error("Unable to update account index", e);
- } finally {
- Context.unsetForwardedEvent();
- }
+ Account.Id parse(String id) {
+ return Account.Id.parse(id);
}
- private static void sendError(HttpServletResponse rsp, int statusCode, String message) {
- try {
- rsp.sendError(statusCode, message);
- } catch (IOException e) {
- logger.error("Failed to send error messsage: " + e.getMessage(), e);
- }
- }
-
- private void index(Account.Id id) throws IOException {
- AtomicInteger accountIdLock = getAndIncrementAccountIdLock(id);
- synchronized (accountIdLock) {
- indexer.index(id);
- logger.debug("Account {} successfully indexed", id);
- }
- if (accountIdLock.decrementAndGet() == 0) {
- removeAccountIdLock(id);
- }
- }
-
- private AtomicInteger getAndIncrementAccountIdLock(Account.Id id) {
- synchronized (accountIdLocks) {
- AtomicInteger accountIdLock = accountIdLocks.get(id);
- if (accountIdLock == null) {
- accountIdLock = new AtomicInteger(1);
- accountIdLocks.put(id, accountIdLock);
- } else {
- accountIdLock.incrementAndGet();
- }
- return accountIdLock;
- }
- }
-
- private void removeAccountIdLock(Account.Id id) {
- synchronized (accountIdLocks) {
- accountIdLocks.remove(id);
- }
+ @Override
+ void index(Account.Id id, Operation operation) throws IOException {
+ indexer.index(id);
+ logger.debug("Account {} successfully indexed", id);
}
}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexChangeRestApiServlet.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexChangeRestApiServlet.java
index 75ca59b..f8a3c42 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexChangeRestApiServlet.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexChangeRestApiServlet.java
@@ -14,11 +14,6 @@
package com.ericsson.gerrit.plugins.highavailability.forwarder.rest;
-import static javax.servlet.http.HttpServletResponse.SC_CONFLICT;
-import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
-import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
-
-import com.ericsson.gerrit.plugins.highavailability.forwarder.Context;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.index.change.ChangeIndexer;
@@ -27,77 +22,33 @@
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
-class IndexChangeRestApiServlet extends HttpServlet {
+class IndexChangeRestApiServlet extends AbstractIndexRestApiServlet<Change.Id> {
private static final long serialVersionUID = -1L;
private static final Logger logger = LoggerFactory.getLogger(IndexChangeRestApiServlet.class);
- private static final Map<Change.Id, AtomicInteger> changeIdLocks = new HashMap<>();
private final ChangeIndexer indexer;
private final SchemaFactory<ReviewDb> schemaFactory;
@Inject
IndexChangeRestApiServlet(ChangeIndexer indexer, SchemaFactory<ReviewDb> schemaFactory) {
+ super("change", true);
this.indexer = indexer;
this.schemaFactory = schemaFactory;
}
@Override
- protected void doPost(HttpServletRequest req, HttpServletResponse rsp)
- throws IOException, ServletException {
- process(req, rsp, "index");
+ Change.Id parse(String id) {
+ return Change.Id.parse(id);
}
@Override
- protected void doDelete(HttpServletRequest req, HttpServletResponse rsp)
- throws IOException, ServletException {
- process(req, rsp, "delete");
- }
-
- private void process(HttpServletRequest req, HttpServletResponse rsp, String operation) {
- rsp.setContentType("text/plain");
- rsp.setCharacterEncoding("UTF-8");
- String path = req.getPathInfo();
- String changeId = path.substring(path.lastIndexOf('/') + 1);
- Change.Id id = Change.Id.parse(changeId);
- try {
- Context.setForwardedEvent(true);
- index(id, operation);
- rsp.setStatus(SC_NO_CONTENT);
- } catch (IOException e) {
- sendError(rsp, SC_CONFLICT, e.getMessage());
- logger.error("Unable to update change index", e);
- } catch (OrmException e) {
- String msg = "Error trying to find a change \n";
- sendError(rsp, SC_NOT_FOUND, msg);
- logger.debug(msg, e);
- } finally {
- Context.unsetForwardedEvent();
- }
- }
-
- private static void sendError(HttpServletResponse rsp, int statusCode, String message) {
- try {
- rsp.sendError(statusCode, message);
- } catch (IOException e) {
- logger.error("Failed to send error messsage: " + e.getMessage(), e);
- }
- }
-
- private void index(Change.Id id, String operation) throws IOException, OrmException {
- AtomicInteger changeIdLock = getAndIncrementChangeIdLock(id);
- synchronized (changeIdLock) {
- if ("index".equals(operation)) {
+ void index(Change.Id id, Operation operation) throws IOException, OrmException {
+ switch (operation) {
+ case INDEX:
try (ReviewDb db = schemaFactory.open()) {
Change change = db.changes().get(id);
if (change == null) {
@@ -107,33 +58,11 @@
indexer.index(db, change);
}
logger.debug("Change {} successfully indexed", id);
- }
- if ("delete".equals(operation)) {
+ break;
+ case DELETE:
indexer.delete(id);
logger.debug("Change {} successfully deleted from index", id);
- }
- }
- if (changeIdLock.decrementAndGet() == 0) {
- removeChangeIdLock(id);
- }
- }
-
- private AtomicInteger getAndIncrementChangeIdLock(Change.Id id) {
- synchronized (changeIdLocks) {
- AtomicInteger changeIdLock = changeIdLocks.get(id);
- if (changeIdLock == null) {
- changeIdLock = new AtomicInteger(1);
- changeIdLocks.put(id, changeIdLock);
- } else {
- changeIdLock.incrementAndGet();
- }
- return changeIdLock;
- }
- }
-
- private void removeChangeIdLock(Change.Id id) {
- synchronized (changeIdLocks) {
- changeIdLocks.remove(id);
+ break;
}
}
}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexGroupRestApiServlet.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexGroupRestApiServlet.java
new file mode 100644
index 0000000..0fcb0ca
--- /dev/null
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexGroupRestApiServlet.java
@@ -0,0 +1,48 @@
+// 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.rest;
+
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.server.index.group.GroupIndexer;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.io.IOException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+class IndexGroupRestApiServlet extends AbstractIndexRestApiServlet<AccountGroup.UUID> {
+ private static final long serialVersionUID = -1L;
+ private static final Logger logger = LoggerFactory.getLogger(IndexGroupRestApiServlet.class);
+
+ private final GroupIndexer indexer;
+
+ @Inject
+ IndexGroupRestApiServlet(GroupIndexer indexer) {
+ super("group");
+ this.indexer = indexer;
+ }
+
+ @Override
+ AccountGroup.UUID parse(String id) {
+ return AccountGroup.UUID.parse(id);
+ }
+
+ @Override
+ void index(AccountGroup.UUID uuid, Operation operation) throws IOException {
+ indexer.index(uuid);
+ logger.debug("Group {} successfully indexed", uuid);
+ }
+}
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 49326cc..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
@@ -74,6 +74,16 @@
}.execute();
}
+ @Override
+ public boolean indexGroup(final String uuid) {
+ return new Request("index group " + uuid) {
+ @Override
+ HttpResult send() throws IOException {
+ return httpSession.post(Joiner.on("/").join(pluginRelativePath, "index/group", uuid));
+ }
+ }.execute();
+ }
+
private String buildIndexEndpoint(int changeId) {
return Joiner.on("/").join(pluginRelativePath, "index/change", changeId);
}
@@ -123,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/forwarder/rest/RestForwarderServletModule.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarderServletModule.java
index bd093ae..d5027d1 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarderServletModule.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/RestForwarderServletModule.java
@@ -21,6 +21,7 @@
protected void configureServlets() {
serveRegex("/index/account/\\d+$").with(IndexAccountRestApiServlet.class);
serveRegex("/index/change/\\d+$").with(IndexChangeRestApiServlet.class);
+ serveRegex("/index/group/\\w+$").with(IndexGroupRestApiServlet.class);
serve("/event").with(EventRestApiServlet.class);
serve("/cache/*").with(CacheRestApiServlet.class);
}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandler.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandler.java
index b08052a..525c7ed 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandler.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandler.java
@@ -20,13 +20,15 @@
import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gerrit.extensions.events.AccountIndexedListener;
import com.google.gerrit.extensions.events.ChangeIndexedListener;
+import com.google.gerrit.extensions.events.GroupIndexedListener;
import com.google.inject.Inject;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
-class IndexEventHandler implements ChangeIndexedListener, AccountIndexedListener {
+class IndexEventHandler
+ implements ChangeIndexedListener, AccountIndexedListener, GroupIndexedListener {
private final Executor executor;
private final Forwarder forwarder;
private final String pluginName;
@@ -61,6 +63,16 @@
executeIndexChangeTask(id, true);
}
+ @Override
+ public void onGroupIndexed(String groupUUID) {
+ if (!Context.isForwardedEvent()) {
+ IndexGroupTask task = new IndexGroupTask(groupUUID);
+ if (queuedTasks.add(task)) {
+ executor.execute(task);
+ }
+ }
+ }
+
private void executeIndexChangeTask(int id, boolean deleted) {
if (!Context.isForwardedEvent()) {
IndexChangeTask task = new IndexChangeTask(id, deleted);
@@ -71,12 +83,6 @@
}
abstract class IndexTask implements Runnable {
- protected int id;
-
- IndexTask(int id) {
- this.id = id;
- }
-
@Override
public void run() {
queuedTasks.remove(this);
@@ -88,24 +94,25 @@
class IndexChangeTask extends IndexTask {
private boolean deleted;
+ private int changeId;
IndexChangeTask(int changeId, boolean deleted) {
- super(changeId);
+ this.changeId = changeId;
this.deleted = deleted;
}
@Override
public void execute() {
if (deleted) {
- forwarder.deleteChangeFromIndex(id);
+ forwarder.deleteChangeFromIndex(changeId);
} else {
- forwarder.indexChange(id);
+ forwarder.indexChange(changeId);
}
}
@Override
public int hashCode() {
- return Objects.hashCode(IndexChangeTask.class, id, deleted);
+ return Objects.hashCode(IndexChangeTask.class, changeId, deleted);
}
@Override
@@ -114,29 +121,30 @@
return false;
}
IndexChangeTask other = (IndexChangeTask) obj;
- return id == other.id && deleted == other.deleted;
+ return changeId == other.changeId && deleted == other.deleted;
}
@Override
public String toString() {
- return String.format("[%s] Index change %s in target instance", pluginName, id);
+ return String.format("[%s] Index change %s in target instance", pluginName, changeId);
}
}
class IndexAccountTask extends IndexTask {
+ private int accountId;
IndexAccountTask(int accountId) {
- super(accountId);
+ this.accountId = accountId;
}
@Override
public void execute() {
- forwarder.indexAccount(id);
+ forwarder.indexAccount(accountId);
}
@Override
public int hashCode() {
- return Objects.hashCode(IndexAccountTask.class, id);
+ return Objects.hashCode(accountId);
}
@Override
@@ -145,12 +153,44 @@
return false;
}
IndexAccountTask other = (IndexAccountTask) obj;
- return id == other.id;
+ return accountId == other.accountId;
}
@Override
public String toString() {
- return String.format("[%s] Index account %s in target instance", pluginName, id);
+ return String.format("[%s] Index account %s in target instance", pluginName, accountId);
+ }
+ }
+
+ class IndexGroupTask extends IndexTask {
+ private String groupUUID;
+
+ IndexGroupTask(String groupUUID) {
+ this.groupUUID = groupUUID;
+ }
+
+ @Override
+ public void execute() {
+ forwarder.indexGroup(groupUUID);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(IndexGroupTask.class, groupUUID);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof IndexGroupTask)) {
+ return false;
+ }
+ IndexGroupTask other = (IndexGroupTask) obj;
+ return groupUUID == other.groupUUID;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("[%s] Index group %s in target instance", pluginName, groupUUID);
}
}
}
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/CacheEvictionIT.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/cache/CacheEvictionIT.java
index 3ed3367..ac71b7b 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/cache/CacheEvictionIT.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/cache/CacheEvictionIT.java
@@ -27,10 +27,11 @@
import com.github.tomakehurst.wiremock.http.RequestListener;
import com.github.tomakehurst.wiremock.http.Response;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
-import com.google.gerrit.acceptance.GerritConfig;
+import com.google.gerrit.acceptance.GlobalPluginConfig;
import com.google.gerrit.acceptance.LightweightPluginDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.TestPlugin;
+import com.google.gerrit.acceptance.UseLocalDisk;
import com.google.gerrit.acceptance.UseSsh;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
@@ -53,10 +54,19 @@
@Rule public WireMockRule wireMockRule = new WireMockRule(options().port(PORT), false);
@Test
- @GerritConfig(name = "plugin.high-availability.url", value = URL)
- @GerritConfig(name = "plugin.high-availability.user", value = "admin")
- @GerritConfig(name = "plugin.high-availability.cacheThreadPoolSize", value = "10")
- @GerritConfig(name = "plugin.high-availability.sharedDirectory", value = "directory")
+ @UseLocalDisk
+ @GlobalPluginConfig(pluginName = "high-availability", name = "peerInfo.url", value = URL)
+ @GlobalPluginConfig(pluginName = "high-availability", name = "http.user", value = "admin")
+ @GlobalPluginConfig(
+ pluginName = "high-availability",
+ name = "cache.threadPoolSize",
+ value = "10"
+ )
+ @GlobalPluginConfig(
+ pluginName = "high-availability",
+ name = "main.sharedDirectory",
+ value = "directory"
+ )
public void flushAndSendPost() throws Exception {
final String flushRequest = "/plugins/high-availability/cache/" + Constants.PROJECT_LIST;
final CyclicBarrier checkPoint = new CyclicBarrier(2);
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/IndexAccountRestApiServletTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexAccountRestApiServletTest.java
index cc80fbb..9893c0a 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexAccountRestApiServletTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexAccountRestApiServletTest.java
@@ -15,6 +15,7 @@
package com.ericsson.gerrit.plugins.highavailability.forwarder.rest;
import static javax.servlet.http.HttpServletResponse.SC_CONFLICT;
+import static javax.servlet.http.HttpServletResponse.SC_METHOD_NOT_ALLOWED;
import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.times;
@@ -66,6 +67,12 @@
}
@Test
+ public void cannotDeleteAccount() throws Exception {
+ servlet.doDelete(req, rsp);
+ verify(rsp).sendError(SC_METHOD_NOT_ALLOWED, "cannot delete account from index");
+ }
+
+ @Test
public void indexerThrowsIOExceptionTryingToIndexAccount() throws Exception {
doThrow(new IOException("io-error")).when(indexer).index(id);
servlet.doPost(req, rsp);
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexChangeRestApiServletTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexChangeRestApiServletTest.java
index 0c791b8..af6c31a 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexChangeRestApiServletTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexChangeRestApiServletTest.java
@@ -95,7 +95,7 @@
public void schemaThrowsExceptionWhenLookingUpForChange() throws Exception {
setupPostMocks(CHANGE_EXISTS, THROW_ORM_EXCEPTION);
indexRestApiServlet.doPost(req, rsp);
- verify(rsp).sendError(SC_NOT_FOUND, "Error trying to find a change \n");
+ verify(rsp).sendError(SC_NOT_FOUND, "Error trying to find change \n");
}
@Test
@@ -123,10 +123,10 @@
public void sendErrorThrowsIOException() throws Exception {
doThrow(new IOException("someError"))
.when(rsp)
- .sendError(SC_NOT_FOUND, "Error trying to find a change \n");
+ .sendError(SC_NOT_FOUND, "Error trying to find change \n");
setupPostMocks(CHANGE_EXISTS, THROW_ORM_EXCEPTION);
indexRestApiServlet.doPost(req, rsp);
- verify(rsp).sendError(SC_NOT_FOUND, "Error trying to find a change \n");
+ verify(rsp).sendError(SC_NOT_FOUND, "Error trying to find change \n");
verifyZeroInteractions(indexer);
}
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexGroupRestApiServletTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexGroupRestApiServletTest.java
new file mode 100644
index 0000000..0994b9b
--- /dev/null
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/rest/IndexGroupRestApiServletTest.java
@@ -0,0 +1,89 @@
+// 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.rest;
+
+import static javax.servlet.http.HttpServletResponse.SC_CONFLICT;
+import static javax.servlet.http.HttpServletResponse.SC_METHOD_NOT_ALLOWED;
+import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.server.index.group.GroupIndexer;
+import com.google.gwtorm.client.KeyUtil;
+import com.google.gwtorm.server.StandardKeyEncoder;
+import java.io.IOException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class IndexGroupRestApiServletTest {
+ private static final String UUID = "we235jdf92nfj2351";
+
+ @Mock private GroupIndexer indexer;
+ @Mock private HttpServletRequest req;
+ @Mock private HttpServletResponse rsp;
+
+ private AccountGroup.UUID uuid;
+ private IndexGroupRestApiServlet servlet;
+
+ @BeforeClass
+ public static void setup() {
+ KeyUtil.setEncoderImpl(new StandardKeyEncoder());
+ }
+
+ @Before
+ public void setUpMocks() {
+ servlet = new IndexGroupRestApiServlet(indexer);
+ uuid = AccountGroup.UUID.parse(UUID);
+ when(req.getPathInfo()).thenReturn("/index/group/" + UUID);
+ }
+
+ @Test
+ public void groupIsIndexed() throws Exception {
+ servlet.doPost(req, rsp);
+ verify(indexer, times(1)).index(uuid);
+ verify(rsp).setStatus(SC_NO_CONTENT);
+ }
+
+ @Test
+ public void cannotDeleteGroup() throws Exception {
+ servlet.doDelete(req, rsp);
+ verify(rsp).sendError(SC_METHOD_NOT_ALLOWED, "cannot delete group from index");
+ }
+
+ @Test
+ public void indexerThrowsIOExceptionTryingToIndexGroup() throws Exception {
+ doThrow(new IOException("io-error")).when(indexer).index(uuid);
+ servlet.doPost(req, rsp);
+ verify(rsp).sendError(SC_CONFLICT, "io-error");
+ }
+
+ @Test
+ public void sendErrorThrowsIOException() throws Exception {
+ doThrow(new IOException("io-error")).when(indexer).index(uuid);
+ doThrow(new IOException("someError")).when(rsp).sendError(SC_CONFLICT, "io-error");
+ servlet.doPost(req, rsp);
+ verify(rsp).sendError(SC_CONFLICT, "io-error");
+ }
+}
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 ce4d56b..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";
@@ -49,6 +50,9 @@
private static final int ACCOUNT_NUMBER = 2;
private static final String INDEX_ACCOUNT_ENDPOINT =
Joiner.on("/").join("/plugins", PLUGIN_NAME, "index/account", ACCOUNT_NUMBER);
+ private static final String UUID = "we235jdf92nfj2351";
+ private static final String INDEX_GROUP_ENDPOINT =
+ Joiner.on("/").join("/plugins", PLUGIN_NAME, "index/group", UUID);
//Event
private static final String EVENT_ENDPOINT =
@@ -58,7 +62,6 @@
private RestForwarder forwarder;
private HttpSession httpSessionMock;
- private Configuration configurationMock;
@BeforeClass
public static void setup() {
@@ -68,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
@@ -95,6 +98,25 @@
}
@Test
+ public void testIndexGroupOK() throws Exception {
+ when(httpSessionMock.post(INDEX_GROUP_ENDPOINT))
+ .thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
+ assertThat(forwarder.indexGroup(UUID)).isTrue();
+ }
+
+ @Test
+ public void testIndexGroupFailed() throws Exception {
+ when(httpSessionMock.post(INDEX_GROUP_ENDPOINT)).thenReturn(new HttpResult(FAILED, EMPTY_MSG));
+ assertThat(forwarder.indexGroup(UUID)).isFalse();
+ }
+
+ @Test
+ public void testIndexGroupThrowsException() throws Exception {
+ doThrow(new IOException()).when(httpSessionMock).post(INDEX_GROUP_ENDPOINT);
+ assertThat(forwarder.indexGroup(UUID)).isFalse();
+ }
+
+ @Test
public void testIndexChangeOK() throws Exception {
when(httpSessionMock.post(INDEX_CHANGE_ENDPOINT))
.thenReturn(new HttpResult(SUCCESSFUL, EMPTY_MSG));
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandlerTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandlerTest.java
index 75cb675..e34b902 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandlerTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/index/IndexEventHandlerTest.java
@@ -24,8 +24,10 @@
import com.ericsson.gerrit.plugins.highavailability.forwarder.Forwarder;
import com.ericsson.gerrit.plugins.highavailability.index.IndexEventHandler.IndexAccountTask;
import com.ericsson.gerrit.plugins.highavailability.index.IndexEventHandler.IndexChangeTask;
+import com.ericsson.gerrit.plugins.highavailability.index.IndexEventHandler.IndexGroupTask;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.git.WorkQueue.Executor;
import com.google.gwtorm.client.KeyUtil;
@@ -42,11 +44,13 @@
private static final String PLUGIN_NAME = "high-availability";
private static final int CHANGE_ID = 1;
private static final int ACCOUNT_ID = 2;
+ private static final String UUID = "3";
private IndexEventHandler indexEventHandler;
@Mock private Forwarder forwarder;
private Change.Id changeId;
private Account.Id accountId;
+ private AccountGroup.UUID accountGroupUUID;
@BeforeClass
public static void setUp() {
@@ -57,6 +61,7 @@
public void setUpMocks() {
changeId = Change.Id.parse(Integer.toString(CHANGE_ID));
accountId = Account.Id.parse(Integer.toString(ACCOUNT_ID));
+ accountGroupUUID = AccountGroup.UUID.parse(UUID);
indexEventHandler =
new IndexEventHandler(MoreExecutors.directExecutor(), PLUGIN_NAME, forwarder);
}
@@ -80,6 +85,12 @@
}
@Test
+ public void shouldIndexInRemoteOnGroupIndexedEvent() throws Exception {
+ indexEventHandler.onGroupIndexed(accountGroupUUID.get());
+ verify(forwarder).indexGroup(UUID);
+ }
+
+ @Test
public void shouldNotCallRemoteWhenChangeEventIsForwarded() throws Exception {
Context.setForwardedEvent(true);
indexEventHandler.onChangeIndexed(changeId.get());
@@ -98,6 +109,15 @@
}
@Test
+ public void shouldNotCallRemoteWhenGroupEventIsForwarded() throws Exception {
+ Context.setForwardedEvent(true);
+ indexEventHandler.onGroupIndexed(accountGroupUUID.get());
+ indexEventHandler.onGroupIndexed(accountGroupUUID.get());
+ Context.unsetForwardedEvent();
+ verifyZeroInteractions(forwarder);
+ }
+
+ @Test
public void duplicateChangeEventOfAQueuedEventShouldGetDiscarded() {
Executor poolMock = mock(Executor.class);
indexEventHandler = new IndexEventHandler(poolMock, PLUGIN_NAME, forwarder);
@@ -116,6 +136,15 @@
}
@Test
+ public void duplicateGroupEventOfAQueuedEventShouldGetDiscarded() {
+ Executor poolMock = mock(Executor.class);
+ indexEventHandler = new IndexEventHandler(poolMock, PLUGIN_NAME, forwarder);
+ indexEventHandler.onGroupIndexed(accountGroupUUID.get());
+ indexEventHandler.onGroupIndexed(accountGroupUUID.get());
+ verify(poolMock, times(1)).execute(indexEventHandler.new IndexGroupTask(UUID));
+ }
+
+ @Test
public void testIndexChangeTaskToString() throws Exception {
IndexChangeTask task = indexEventHandler.new IndexChangeTask(CHANGE_ID, false);
assertThat(task.toString())
@@ -132,6 +161,13 @@
}
@Test
+ public void testIndexGroupTaskToString() throws Exception {
+ IndexGroupTask task = indexEventHandler.new IndexGroupTask(UUID);
+ assertThat(task.toString())
+ .isEqualTo(String.format("[%s] Index group %s in target instance", PLUGIN_NAME, UUID));
+ }
+
+ @Test
public void testIndexChangeTaskHashCodeAndEquals() {
IndexChangeTask task = indexEventHandler.new IndexChangeTask(CHANGE_ID, false);
@@ -176,4 +212,25 @@
assertThat(task.equals(differentAccountIdTask)).isFalse();
assertThat(task.hashCode()).isNotEqualTo(differentAccountIdTask.hashCode());
}
+
+ @Test
+ public void testIndexGroupTaskHashCodeAndEquals() {
+ IndexGroupTask task = indexEventHandler.new IndexGroupTask(UUID);
+
+ IndexGroupTask sameTask = task;
+ assertThat(task.equals(sameTask)).isTrue();
+ assertThat(task.hashCode()).isEqualTo(sameTask.hashCode());
+
+ IndexGroupTask identicalTask = indexEventHandler.new IndexGroupTask(UUID);
+ assertThat(task.equals(identicalTask)).isTrue();
+ assertThat(task.hashCode()).isEqualTo(identicalTask.hashCode());
+
+ assertThat(task.equals(null)).isFalse();
+ assertThat(task.equals("test")).isFalse();
+ assertThat(task.hashCode()).isNotEqualTo("test".hashCode());
+
+ IndexGroupTask differentGroupIdTask = indexEventHandler.new IndexGroupTask("123");
+ assertThat(task.equals(differentGroupIdTask)).isFalse();
+ assertThat(task.hashCode()).isNotEqualTo(differentGroupIdTask.hashCode());
+ }
}
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);
}