Merge branch 'stable-2.13' into stable-2.14
* stable-2.13:
Fix log message
Move ForwardedAwareEventBroker to forwarder package
Change-Id: I6d9f76dff1d51474157657cbce50dd4a0b83fc08
diff --git a/.buckconfig b/.buckconfig
deleted file mode 100644
index 5c0ead0..0000000
--- a/.buckconfig
+++ /dev/null
@@ -1,16 +0,0 @@
-[alias]
- high-availability = //:high-availability
- plugin = //:high-availability
- src = //:high-availability-sources
-
-[java]
- jar_spool_mode = direct_to_jar
- src_roots = java, resources
-
-[project]
- ignore = .git, eclipse-out/
- parallel_parsing = true
-
-[cache]
- mode = dir
- dir = buck-out/cache
diff --git a/.gitignore b/.gitignore
index c27e17f..912f8a6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,11 @@
-/.buckd/
-/.buckversion
/.classpath
/.project
/.settings/
-/.watchmanconfig
-/buck-out/
-/bucklets
+/.primary_build_tool
+/bazel-bin
+/bazel-genfiles
+/bazel-out
+/bazel-reviewers
+/bazel-testlogs
+/bazel-high-availability
/eclipse-out/
diff --git a/BUCK b/BUCK
deleted file mode 100644
index a4e7846..0000000
--- a/BUCK
+++ /dev/null
@@ -1,82 +0,0 @@
-include_defs('//bucklets/gerrit_plugin.bucklet')
-include_defs('//bucklets/java_sources.bucklet')
-include_defs('//bucklets/maven_jar.bucklet')
-
-SOURCES = glob(['src/main/java/**/*.java'])
-RESOURCES = glob(['src/main/resources/**/*'])
-
-TEST_DEPS = GERRIT_PLUGIN_API + GERRIT_TESTS + [
- ':high-availability__plugin',
- ':mockito',
- ':wiremock',
-]
-
-gerrit_plugin(
- name = 'high-availability',
- srcs = SOURCES,
- resources = RESOURCES,
- manifest_entries = [
- 'Gerrit-PluginName: high-availability',
- 'Gerrit-ApiType: plugin',
- 'Gerrit-Module: com.ericsson.gerrit.plugins.highavailability.Module',
- 'Gerrit-HttpModule: com.ericsson.gerrit.plugins.highavailability.HttpModule',
- 'Gerrit-InitStep: com.ericsson.gerrit.plugins.highavailability.Setup',
- 'Implementation-Title: high-availability plugin',
- 'Implementation-URL: https://gerrit-review.googlesource.com/#/admin/projects/plugins/high-availability',
- 'Implementation-Vendor: Ericsson',
- ],
- provided_deps = GERRIT_TESTS,
-)
-
-java_sources(
- name = 'high-availability-sources',
- srcs = SOURCES + RESOURCES,
-)
-
-java_library(
- name = 'classpath',
- deps = TEST_DEPS,
-)
-
-java_test(
- name = 'high-availability_tests',
- srcs = glob(['src/test/java/**/*.java']),
- resources = glob(['src/test/resources/**/']),
- labels = ['high-availability'],
- deps = TEST_DEPS,
-)
-
-maven_jar(
- name = 'wiremock',
- id = 'com.github.tomakehurst:wiremock-standalone:2.5.1',
- sha1 = '9cda1bf1674c8de3a1116bae4d7ce0046a857d30',
- license = 'Apache2.0',
- attach_source = False,
-)
-
-maven_jar(
- name = 'mockito',
- id = 'org.mockito:mockito-core:2.7.21',
- sha1 = '23e9f7bfb9717e849a05b84c29ee3ac723f1a653',
- license = 'DO_NOT_DISTRIBUTE',
- deps = [
- ':byte-buddy',
- ':objenesis',
- ],
-)
-
-maven_jar(
- name = 'byte-buddy',
- id = 'net.bytebuddy:byte-buddy:1.6.11',
- sha1 = '8a8f9409e27f1d62c909c7eef2aa7b3a580b4901',
- license = 'DO_NOT_DISTRIBUTE',
- attach_source = False,
-)
-
-maven_jar(
- name = 'objenesis',
- id = 'org.objenesis:objenesis:2.5',
- sha1 = '612ecb799912ccf77cba9b3ed8c813da086076e9',
- license = 'DO_NOT_DISTRIBUTE',
- attach_source = False,
-)
diff --git a/BUILD b/BUILD
new file mode 100644
index 0000000..a9d055d
--- /dev/null
+++ b/BUILD
@@ -0,0 +1,45 @@
+load("//tools/bzl:junit.bzl", "junit_tests")
+load(
+ "//tools/bzl:plugin.bzl",
+ "gerrit_plugin",
+ "PLUGIN_DEPS",
+ "PLUGIN_TEST_DEPS",
+)
+
+gerrit_plugin(
+ name = "high-availability",
+ srcs = glob(["src/main/java/**/*.java"]),
+ manifest_entries = [
+ "Gerrit-PluginName: high-availability",
+ "Gerrit-Module: com.ericsson.gerrit.plugins.highavailability.Module",
+ "Gerrit-HttpModule: com.ericsson.gerrit.plugins.highavailability.HttpModule",
+ "Implementation-Title: high-availability plugin",
+ "Implementation-URL: https://gerrit-review.googlesource.com/#/admin/projects/plugins/high-availability",
+ ],
+ resources = glob(["src/main/resources/**/*"]),
+)
+
+junit_tests(
+ name = "high_availability_tests",
+ srcs = glob(["src/test/java/**/*.java"]),
+ resources = glob(["src/test/resources/**/*"]),
+ tags = [
+ "high-availability",
+ "local",
+ ],
+ deps = PLUGIN_DEPS + PLUGIN_TEST_DEPS + [
+ ":high-availability__plugin_test_deps",
+ ":high-availability__plugin",
+ ],
+)
+
+java_library(
+ name = "high-availability__plugin_test_deps",
+ visibility = ["//visibility:public"],
+ exports = [
+ "@byte-buddy//jar",
+ "@mockito//jar",
+ "@objenesis//jar",
+ "@wiremock//jar",
+ ],
+)
diff --git a/WORKSPACE b/WORKSPACE
new file mode 100644
index 0000000..50b3f72
--- /dev/null
+++ b/WORKSPACE
@@ -0,0 +1,51 @@
+workspace(name = "high_availability")
+load("//:bazlets.bzl", "load_bazlets")
+
+load_bazlets(
+ commit = "74b31c8fae3a92c6c3e46a046b57cf1d8a6549d4",
+ #local_path = "/home/ehugare/workspaces/bazlets",
+)
+
+#Snapshot Plugin API
+load(
+ "@com_googlesource_gerrit_bazlets//:gerrit_api_maven_local.bzl",
+ "gerrit_api_maven_local",
+)
+
+# Load snapshot Plugin API
+gerrit_api_maven_local()
+
+# Release Plugin API
+#load(
+# "@com_googlesource_gerrit_bazlets//:gerrit_api.bzl",
+# "gerrit_api",
+#)
+
+# Load release Plugin API
+#gerrit_api()
+
+load("@com_googlesource_gerrit_bazlets//tools:maven_jar.bzl", "maven_jar")
+
+maven_jar(
+ name = "wiremock",
+ artifact = "com.github.tomakehurst:wiremock-standalone:2.5.1",
+ sha1 = "9cda1bf1674c8de3a1116bae4d7ce0046a857d30",
+)
+
+maven_jar(
+ name = "mockito",
+ artifact = "org.mockito:mockito-core:2.7.21",
+ sha1 = "23e9f7bfb9717e849a05b84c29ee3ac723f1a653",
+)
+
+maven_jar(
+ name = "byte-buddy",
+ artifact = "net.bytebuddy:byte-buddy:1.6.11",
+ sha1 = "8a8f9409e27f1d62c909c7eef2aa7b3a580b4901",
+)
+
+maven_jar(
+ name = "objenesis",
+ artifact = "org.objenesis:objenesis:2.5",
+ sha1 = "612ecb799912ccf77cba9b3ed8c813da086076e9",
+)
diff --git a/bazlets.bzl b/bazlets.bzl
new file mode 100644
index 0000000..e14e488
--- /dev/null
+++ b/bazlets.bzl
@@ -0,0 +1,17 @@
+NAME = "com_googlesource_gerrit_bazlets"
+
+def load_bazlets(
+ commit,
+ local_path = None
+ ):
+ if not local_path:
+ native.git_repository(
+ name = NAME,
+ remote = "https://gerrit.googlesource.com/bazlets",
+ commit = commit,
+ )
+ else:
+ native.local_repository(
+ name = NAME,
+ path = local_path,
+ )
diff --git a/external_plugin_deps.bzl b/external_plugin_deps.bzl
new file mode 100644
index 0000000..029d997
--- /dev/null
+++ b/external_plugin_deps.bzl
@@ -0,0 +1,26 @@
+load("//tools/bzl:maven_jar.bzl", "maven_jar")
+
+def external_plugin_deps():
+ maven_jar(
+ name = "wiremock",
+ artifact = "com.github.tomakehurst:wiremock-standalone:2.5.1",
+ sha1 = "9cda1bf1674c8de3a1116bae4d7ce0046a857d30",
+ )
+
+ maven_jar(
+ name = "mockito",
+ artifact = "org.mockito:mockito-core:2.7.21",
+ sha1 = "23e9f7bfb9717e849a05b84c29ee3ac723f1a653",
+ )
+
+ maven_jar(
+ name = "byte-buddy",
+ artifact = "net.bytebuddy:byte-buddy:1.6.11",
+ sha1 = "8a8f9409e27f1d62c909c7eef2aa7b3a580b4901",
+ )
+
+ maven_jar(
+ name = "objenesis",
+ artifact = "org.objenesis:objenesis:2.5",
+ sha1 = "612ecb799912ccf77cba9b3ed8c813da086076e9",
+ )
diff --git a/lib/gerrit/BUCK b/lib/gerrit/BUCK
deleted file mode 100644
index b7a40b3..0000000
--- a/lib/gerrit/BUCK
+++ /dev/null
@@ -1,22 +0,0 @@
-include_defs('//bucklets/maven_jar.bucklet')
-
-VER = '2.13.8'
-REPO = MAVEN_CENTRAL
-
-maven_jar(
- name = 'acceptance-framework',
- id = 'com.google.gerrit:gerrit-acceptance-framework:' + VER,
- sha1 = 'b35d038d0727889837f0b9710a8a0442471ba8b6',
- license = 'Apache2.0',
- attach_source = False,
- repository = REPO,
-)
-
-maven_jar(
- name = 'plugin-api',
- id = 'com.google.gerrit:gerrit-plugin-api:' + VER,
- sha1 = 'd8137cc9b0cb34429959374ca44d5d2bcf0eff4b',
- license = 'Apache2.0',
- attach_source = False,
- repository = REPO,
-)
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/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 6d18be3..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);
}
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/resources/Documentation/build.md b/src/main/resources/Documentation/build.md
index 3babed2..b243edc 100644
--- a/src/main/resources/Documentation/build.md
+++ b/src/main/resources/Documentation/build.md
@@ -1,94 +1,91 @@
-Build
-=====
+# Build
-This plugin is built with Buck.
+This plugin can be built with Bazel, and two build modes are supported:
-Two build modes are supported: Standalone and in Gerrit tree. Standalone
-build mode is recommended, as this mode doesn't require local Gerrit
-tree to exist.
+* Standalone
+* In Gerrit tree
-Build standalone
-----------------
+Standalone build mode is recommended, as this mode doesn't require local Gerrit
+tree to exist. Moreover, there are some limitations and additional manual steps
+required when building in Gerrit tree mode (see corresponding sections).
-Clone bucklets library:
-
-```
- git clone https://gerrit.googlesource.com/bucklets
-
-```
-and link it to @PLUGIN@ directory:
-
-```
- cd @PLUGIN@ && ln -s ../bucklets .
-```
-
-Add link to the .buckversion file:
-
-```
- cd @PLUGIN@ && ln -s bucklets/buckversion .buckversion
-```
-
-Add link to the .watchmanconfig file:
-
-```
- cd @PLUGIN@ && ln -s bucklets/watchmanconfig .watchmanconfig
-```
+## Build standalone
To build the plugin, issue the following command:
```
- buck build plugin
+ bazel build @PLUGIN@
+```
+
+The output is created in
+
+```
+ bazel-genfiles/@PLUGIN@.jar
+```
+
+To package the plugin sources run:
+
+```
+ bazel build lib@PLUGIN@__plugin-src.jar
```
The output is created in:
```
- buck-out/gen/@PLUGIN@.jar
-```
-
-This project can be imported into the Eclipse IDE:
-
-```
- ./bucklets/tools/eclipse.py
+ bazel-bin/lib@PLUGIN@__plugin-src.jar
```
To execute the tests run:
```
- buck test
-```
-
-To build plugin sources run:
-
-```
- buck build src
-```
-
-The output is created in:
-
-```
- buck-out/gen/@PLUGIN@-sources.jar
-```
-
-Build in Gerrit tree
---------------------
-
-Clone or link this plugin to the plugins directory of Gerrit's source
-tree, and issue the command:
-
-```
- buck build plugins/@PLUGIN@
-```
-
-The output is created in:
-
-```
- buck-out/gen/plugins/@PLUGIN@/@PLUGIN@.jar
+ bazel test high_availability_tests
```
This project can be imported into the Eclipse IDE:
```
+ ./tools/eclipse.py
+```
+
+## Build in Gerrit tree
+
+Clone or link this plugin to the plugins directory of Gerrit's
+source tree. Put the external dependency Bazel build file into
+the Gerrit /plugins directory, replacing the existing empty one.
+
+```
+ cd gerrit/plugins
+ rm external_plugin_deps.bzl
+ ln -s @PLUGIN@/external_plugin_deps.bzl .
+```
+
+From Gerrit source tree issue the command:
+
+```
+ bazel build plugins/@PLUGIN@
+```
+
+Note that due to a [known issue in Bazel][bazelissue], if the plugin
+has previously been built in standalone mode, it is necessary to clean
+the workspace before building in-tree:
+
+```
+ cd plugins/@PLUGIN@
+ bazel clean --expunge
+```
+
+The output is created in
+
+```
+ bazel-genfiles/plugins/@PLUGIN@/@PLUGIN@.jar
+```
+
+This project can be imported into the Eclipse IDE:
+Add the plugin name to the `CUSTOM_PLUGINS` and to the
+`CUSTOM_PLUGINS_TEST_DEPS` set in Gerrit core in
+`tools/bzl/plugins.bzl`, and execute:
+
+```
./tools/eclipse/project.py
```
@@ -104,3 +101,4 @@
[Back to @PLUGIN@ documentation index][index]
[index]: index.html
+[bazelissue]: https://github.com/bazelbuild/bazel/issues/2797
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 11fc496..f3d6a9d 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
@@ -21,37 +21,48 @@
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.client.WireMock.verify;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
+import static org.junit.Assert.fail;
import com.github.tomakehurst.wiremock.http.Request;
import com.github.tomakehurst.wiremock.http.RequestListener;
import com.github.tomakehurst.wiremock.http.Response;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
-import com.google.common.base.Throwables;
-import com.google.gerrit.acceptance.GerritConfig;
-import com.google.gerrit.acceptance.GerritConfigs;
+import com.google.gerrit.acceptance.GlobalPluginConfig;
+import com.google.gerrit.acceptance.LightweightPluginDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
-import com.google.gerrit.acceptance.PluginDaemonTest;
+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;
import java.util.concurrent.TimeUnit;
import org.apache.http.HttpStatus;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
@NoHttpd
-@Ignore
-public class CacheEvictionIT extends PluginDaemonTest {
+@UseSsh
+@TestPlugin(
+ name = "high-availability",
+ sysModule = "com.ericsson.gerrit.plugins.highavailability.Module",
+ httpModule = "com.ericsson.gerrit.plugins.highavailability.HttpModule"
+)
+public class CacheEvictionIT extends LightweightPluginDaemonTest {
+ private static final int PORT = 18888;
+ private static final String URL = "http://localhost:" + PORT;
- @Rule public WireMockRule wireMockRule = new WireMockRule(options().port(18888), false);
+ @Rule public WireMockRule wireMockRule = new WireMockRule(options().port(PORT), false);
@Test
- @GerritConfigs({
- @GerritConfig(name = "plugin.high-availability.url", value = "http://localhost:18888"),
- @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);
@@ -63,7 +74,7 @@
try {
checkPoint.await();
} catch (InterruptedException | BrokenBarrierException e) {
- Throwables.propagateIfPossible(e);
+ fail();
}
}
}
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 3a736cb..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
@@ -50,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 =
@@ -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 e50d713..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,11 +161,19 @@
}
@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);
- assertThat(task.equals(task)).isTrue();
- assertThat(task.hashCode()).isEqualTo(task.hashCode());
+ IndexChangeTask sameTask = task;
+ assertThat(task.equals(sameTask)).isTrue();
+ assertThat(task.hashCode()).isEqualTo(sameTask.hashCode());
IndexChangeTask identicalTask = indexEventHandler.new IndexChangeTask(CHANGE_ID, false);
assertThat(task.equals(identicalTask)).isTrue();
@@ -159,8 +196,9 @@
public void testIndexAccountTaskHashCodeAndEquals() {
IndexAccountTask task = indexEventHandler.new IndexAccountTask(ACCOUNT_ID);
- assertThat(task.equals(task)).isTrue();
- assertThat(task.hashCode()).isEqualTo(task.hashCode());
+ IndexAccountTask sameTask = task;
+ assertThat(task.equals(sameTask)).isTrue();
+ assertThat(task.hashCode()).isEqualTo(sameTask.hashCode());
IndexAccountTask identicalTask = indexEventHandler.new IndexAccountTask(ACCOUNT_ID);
assertThat(task.equals(identicalTask)).isTrue();
@@ -174,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/tools/BUILD b/tools/BUILD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/BUILD
diff --git a/tools/bazel.rc b/tools/bazel.rc
new file mode 100644
index 0000000..4ed16cf
--- /dev/null
+++ b/tools/bazel.rc
@@ -0,0 +1,2 @@
+build --workspace_status_command=./tools/workspace-status.sh
+test --build_tests_only
diff --git a/tools/bzl/BUILD b/tools/bzl/BUILD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/bzl/BUILD
diff --git a/tools/bzl/classpath.bzl b/tools/bzl/classpath.bzl
new file mode 100644
index 0000000..dfcbe9c
--- /dev/null
+++ b/tools/bzl/classpath.bzl
@@ -0,0 +1,2 @@
+load("@com_googlesource_gerrit_bazlets//tools:classpath.bzl",
+ "classpath_collector")
diff --git a/tools/bzl/junit.bzl b/tools/bzl/junit.bzl
new file mode 100644
index 0000000..3af7e58
--- /dev/null
+++ b/tools/bzl/junit.bzl
@@ -0,0 +1,4 @@
+load(
+ "@com_googlesource_gerrit_bazlets//tools:junit.bzl",
+ "junit_tests",
+)
diff --git a/tools/bzl/plugin.bzl b/tools/bzl/plugin.bzl
new file mode 100644
index 0000000..a2e438f
--- /dev/null
+++ b/tools/bzl/plugin.bzl
@@ -0,0 +1,6 @@
+load(
+ "@com_googlesource_gerrit_bazlets//:gerrit_plugin.bzl",
+ "gerrit_plugin",
+ "PLUGIN_DEPS",
+ "PLUGIN_TEST_DEPS",
+)
diff --git a/tools/eclipse/BUILD b/tools/eclipse/BUILD
new file mode 100644
index 0000000..a70caed
--- /dev/null
+++ b/tools/eclipse/BUILD
@@ -0,0 +1,24 @@
+load("//tools/bzl:plugin.bzl", "PLUGIN_DEPS", "PLUGIN_TEST_DEPS")
+load("//tools/bzl:classpath.bzl", "classpath_collector")
+
+java_library(
+ name = "classpath",
+ runtime_deps = PLUGIN_DEPS + PLUGIN_TEST_DEPS + [
+ "@wiremock//jar",
+ "@mockito//jar",
+ "@byte-buddy//jar",
+ "@objenesis//jar",
+ "//:high-availability__plugin",
+ ],
+)
+
+classpath_collector(
+ name = "main_classpath_collect",
+ deps = PLUGIN_DEPS + PLUGIN_TEST_DEPS + [
+ "@wiremock//jar",
+ "@mockito//jar",
+ "@byte-buddy//jar",
+ "@objenesis//jar",
+ "//:high-availability__plugin",
+ ],
+)
diff --git a/tools/eclipse/project.py b/tools/eclipse/project.py
new file mode 100755
index 0000000..88ca4b0
--- /dev/null
+++ b/tools/eclipse/project.py
@@ -0,0 +1,179 @@
+#!/usr/bin/env python
+# Copyright (C) 2016 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.
+#
+
+from __future__ import print_function
+# TODO(davido): use Google style for importing instead:
+# import optparse
+#
+# optparse.OptionParser
+from optparse import OptionParser
+from os import environ, path, makedirs
+from subprocess import CalledProcessError, check_call, check_output
+from xml.dom import minidom
+import re
+import sys
+
+MAIN = '//tools/eclipse:classpath'
+JRE = '/'.join([
+ 'org.eclipse.jdt.launching.JRE_CONTAINER',
+ 'org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType',
+ 'JavaSE-1.8',
+])
+# Map of targets to corresponding classpath collector rules
+cp_targets = {
+ MAIN: '//tools/eclipse:main_classpath_collect',
+}
+
+ROOT = path.abspath(__file__)
+while not path.exists(path.join(ROOT, 'WORKSPACE')):
+ ROOT = path.dirname(ROOT)
+
+opts = OptionParser()
+opts.add_option('--name', help='name of the generated project',
+ action='store', default='sync-index', dest='project_name')
+args, _ = opts.parse_args()
+
+def retrieve_ext_location():
+ return check_output(['bazel', 'info', 'output_base']).strip()
+
+def gen_primary_build_tool():
+ bazel = check_output(['which', 'bazel']).strip()
+ with open(path.join(ROOT, ".primary_build_tool"), 'w') as fd:
+ fd.write("bazel=%s\n" % bazel)
+ fd.write("PATH=%s\n" % environ["PATH"])
+
+def _query_classpath(target):
+ deps = []
+ t = cp_targets[target]
+ try:
+ check_call(['bazel', 'build', t])
+ except CalledProcessError:
+ exit(1)
+ name = 'bazel-bin/tools/eclipse/' + t.split(':')[1] + '.runtime_classpath'
+ deps = [line.rstrip('\n') for line in open(name)]
+ return deps
+
+def gen_project(name='gerrit', root=ROOT):
+ p = path.join(root, '.project')
+ with open(p, 'w') as fd:
+ print("""\
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>%(name)s</name>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>\
+ """ % {"name": name}, file=fd)
+
+def gen_classpath(ext):
+ def make_classpath():
+ impl = minidom.getDOMImplementation()
+ return impl.createDocument(None, 'classpath', None)
+
+ def classpathentry(kind, path, src=None, out=None, exported=None):
+ e = doc.createElement('classpathentry')
+ e.setAttribute('kind', kind)
+ # TODO(davido): Remove this and other exclude BUILD files hack
+ # when this Bazel bug is fixed:
+ # https://github.com/bazelbuild/bazel/issues/1083
+ if kind == 'src':
+ e.setAttribute('excluding', '**/BUILD')
+ e.setAttribute('path', path)
+ if src:
+ e.setAttribute('sourcepath', src)
+ if out:
+ e.setAttribute('output', out)
+ if exported:
+ e.setAttribute('exported', 'true')
+ doc.documentElement.appendChild(e)
+
+ doc = make_classpath()
+ src = set()
+ lib = set()
+
+ # Classpath entries are absolute for cross-cell support
+ java_library = re.compile('bazel-out/local-fastbuild/bin/lib[^/]+[.]jar$')
+ srcs = re.compile('(.*/external/[^/]+)/jar/(.*)[.]jar')
+ for p in _query_classpath(MAIN):
+ m = java_library.match(p)
+ if m:
+ src.add(".")
+ else:
+ if p.startswith("external"):
+ p = path.join(ext, p)
+ lib.add(p)
+
+ for s in sorted(src):
+ out = None
+
+ if s.startswith('lib/'):
+ out = 'eclipse-out/lib'
+
+ p = path.join(s, 'java')
+ if path.exists(p):
+ classpathentry('src', p, out=out)
+ continue
+
+ for env in ['main', 'test']:
+ o = None
+ if out:
+ o = out + '/' + env
+ elif env == 'test':
+ o = 'eclipse-out/test'
+
+ for srctype in ['java', 'resources']:
+ p = path.join(s, 'src', env, srctype)
+ if path.exists(p):
+ classpathentry('src', p, out=o)
+
+ for libs in [lib]:
+ for j in sorted(libs):
+ s = None
+ m = srcs.match(j)
+ if m:
+ prefix = m.group(1)
+ suffix = m.group(2)
+ p = path.join(prefix, "src", "%s-src.jar" % suffix)
+ if path.exists(p):
+ s = p
+ classpathentry('lib', j, s)
+
+ classpathentry('con', JRE)
+ classpathentry('output', 'eclipse-out/classes')
+
+ p = path.join(ROOT, '.classpath')
+ with open(p, 'w') as fd:
+ doc.writexml(fd, addindent='\t', newl='\n', encoding='UTF-8')
+
+try:
+ ext_location = retrieve_ext_location()
+ gen_project(args.project_name)
+ gen_classpath(ext_location)
+ gen_primary_build_tool()
+
+ try:
+ check_call(['bazel', 'build', MAIN])
+ except CalledProcessError:
+ exit(1)
+except KeyboardInterrupt:
+ print('Interrupted by user', file=sys.stderr)
+ exit(1)
diff --git a/tools/workspace-status.sh b/tools/workspace-status.sh
new file mode 100755
index 0000000..c83d416
--- /dev/null
+++ b/tools/workspace-status.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+# This script will be run by bazel when the build process starts to
+# generate key-value information that represents the status of the
+# workspace. The output should be like
+#
+# KEY1 VALUE1
+# KEY2 VALUE2
+#
+# If the script exits with non-zero code, it's considered as a failure
+# and the output will be discarded.
+
+function rev() {
+ cd $1; git describe --always --match "v[0-9].*" --dirty
+}
+
+echo STABLE_BUILD_HIGH-AVAILABILITY_LABEL $(rev .)