Merge branch 'stable-3.2'

* stable-3.2:
  Set forward context for events having a different instanceId

Change-Id: I23e02d50add39f48e99b2c944452e5d3694e3778
diff --git a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedAwareEventBroker.java b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedAwareEventBroker.java
index 700d5e1..fb651b7 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedAwareEventBroker.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedAwareEventBroker.java
@@ -24,6 +24,7 @@
 import com.google.gerrit.server.plugincontext.PluginSetContext;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.inject.Inject;
+import java.util.Objects;
 import javax.annotation.Nullable;
 
 class ForwardedAwareEventBroker extends EventBroker {
@@ -45,8 +46,19 @@
         gerritInstanceId);
   }
 
+  private boolean isProducedByLocalInstance(Event event) {
+    return Objects.equals(event.instanceId, gerritInstanceId);
+  }
+
   @Override
   protected void fireEventForUnrestrictedListeners(Event event) {
+    // An event should not be dispatched when it is "forwarded".
+    // Meaning, it was either produced somewhere else
+    if (!isProducedByLocalInstance(event)) {
+      Context.setForwardedEvent(true);
+    }
+    // or it was consumed by the high-availability rest endpoint and
+    // thus the context of its consumption has already been set to "forwarded".
     if (!Context.isForwardedEvent()) {
       super.fireEventForUnrestrictedListeners(event);
     }
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index 377626d..1440e0b 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -236,3 +236,23 @@
 
 ```healthcheck.enable```
 :   Whether to enable the health check endpoint. Defaults to 'true'.
+
+File 'gerrit.config'
+--------------------
+
+```gerrit.instanceId```
+:   Optional identifier for this Gerrit instance.
+[[docs](https://gerrit-documentation.storage.googleapis.com/Documentation/3.2.0/config-gerrit.html#gerrit.instanceId)].
+
+Whilst this is not, specifically, a high-availability plugin configuration, it plays
+an important role on which events are forwarded to peers.
+
+If `instanceId` is set, events produced by that Gerrit instance will contain its
+origin in the `instanceId` field, allowing to track where they are coming from.
+
+The high-availability plugin will check the `instanceId` value to decide whether
+an event should be forwarded: events that originated from different Gerrit instances
+will not be forwarded.
+
+When neither `gerrit.instanceId` nor `event.instanceId` are set, it is not possible
+to identify the origin of the event and thus the event is always forwarded.
diff --git a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedAwareEventBrokerTest.java b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedAwareEventBrokerTest.java
index ec5b1de..cfe0475 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedAwareEventBrokerTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/highavailability/forwarder/ForwardedAwareEventBrokerTest.java
@@ -30,7 +30,9 @@
 
   private EventListener listenerMock;
   private ForwardedAwareEventBroker broker;
-  private Event event = new TestEvent();
+  private ForwardedAwareEventBroker brokerWithGerritInstanceId;
+  private Event event;
+  private String gerritInstanceId = "gerrit-instance-id";
 
   @Before
   public void setUp() {
@@ -38,8 +40,11 @@
     listenerMock = mock(EventListener.class);
     DynamicSet<EventListener> set = DynamicSet.emptySet();
     set.add("high-availability", listenerMock);
+    event = new TestEvent();
     PluginSetContext<EventListener> listeners = new PluginSetContext<>(set, mockMetrics);
     broker = new ForwardedAwareEventBroker(null, listeners, null, null, null, null);
+    brokerWithGerritInstanceId =
+        new ForwardedAwareEventBroker(null, listeners, null, null, null, gerritInstanceId);
   }
 
   @Test
@@ -58,4 +63,43 @@
     }
     verifyZeroInteractions(listenerMock);
   }
+
+  @Test
+  public void shouldNotDispatchEventWhenEventInstanceIdIsDefinedButGerritInstanceIdIsNot() {
+    event.instanceId = gerritInstanceId;
+    try {
+      broker.fireEventForUnrestrictedListeners(event);
+    } finally {
+      Context.unsetForwardedEvent();
+    }
+    verifyZeroInteractions(listenerMock);
+  }
+
+  @Test
+  public void shouldNotDispatchEventWhenGerritInstanceIdIsDefinedButEventInstanceIdIsNot() {
+    try {
+      brokerWithGerritInstanceId.fireEventForUnrestrictedListeners(event);
+    } finally {
+      Context.unsetForwardedEvent();
+    }
+    verifyZeroInteractions(listenerMock);
+  }
+
+  @Test
+  public void shouldNotDispatchEventWhenInstanceIdsAreDifferent() {
+    event.instanceId = "some-other-gerrit-instance-id";
+    try {
+      brokerWithGerritInstanceId.fireEventForUnrestrictedListeners(event);
+    } finally {
+      Context.unsetForwardedEvent();
+    }
+    verifyZeroInteractions(listenerMock);
+  }
+
+  @Test
+  public void shouldDispatchEventWhenInstanceIdsAreEqual() {
+    event.instanceId = gerritInstanceId;
+    brokerWithGerritInstanceId.fireEventForUnrestrictedListeners(event);
+    verify(listenerMock).onEvent(event);
+  }
 }