Support connection to multiple its-servers

So far, its-base derived plugins were able to connect only to a single
server whose configuration was defined in gerrit_site/etc/gerrit.config.
In enterprise environments, it is not unusual to have multiple instances
of the same ITS serving different clients. In case of its-* plugins, the
only way to achieve the integration of different clients with Gerrit was
to deploy multiple copies of the its plugin with different names.

This change is an attempt to bring a more flexible way of supporting the
described use case, i.e., multiple instances of the same ITS. Given the
information about the project name is handled by its-base and not passed
directly to the derivative plugins, a new interface, 'ItsFacadeFactory',
facilitates the tunnelling of information to and from its-base.

The child its-plugins can implement both single or multiple servers as
per requirements.

In the first case, child its-plugins need to bind the 'ItsFacadeFactory'
interface to the 'SingleItsServer' implementation and no further code
change is required.

In the multiple servers case, child its-plugins need to implement the
'ItsFacade' interface in such a way that it captures the project name
passed by its-base to the its-child plugin and the latter will send back
the 'ItsFacade' object to its-base. An example of such mechanism is in
the its-jira plugin.

Change-Id: I4d3a1710caa0817faf610f191c4d798a9a43d5ff
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/its/ItsFacadeFactory.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/its/ItsFacadeFactory.java
new file mode 100644
index 0000000..4e2f035
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/its/ItsFacadeFactory.java
@@ -0,0 +1,25 @@
+// Copyright (C) 2018 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.googlesource.gerrit.plugins.its.base.its;
+
+import com.google.gerrit.reviewdb.client.Project;
+
+/* An interface to get server information from child its-plugin embedded in the ItsFacade implementation */
+public interface ItsFacadeFactory {
+
+  /* Returns the object of type ItsFacade containing server info extracted from project.config if configured
+   * or the default server configured in gerrit.config if project name is empty or null*/
+  ItsFacade getFacade(Project.NameKey project);
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/its/SingleItsServer.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/its/SingleItsServer.java
new file mode 100644
index 0000000..08bfe62
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/its/SingleItsServer.java
@@ -0,0 +1,35 @@
+// Copyright (C) 2018 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.googlesource.gerrit.plugins.its.base.its;
+
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.inject.Inject;
+
+/* An ItsServer implementation that should be bound
+ * for backward compatibility */
+public class SingleItsServer implements ItsFacadeFactory {
+
+  private final ItsFacade its;
+
+  @Inject
+  public SingleItsServer(ItsFacade its) {
+    this.its = its;
+  }
+
+  @Override
+  public ItsFacade getFacade(Project.NameKey project) {
+    return its;
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/validation/ItsValidateComment.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/validation/ItsValidateComment.java
index d45d0a6..f02050b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/validation/ItsValidateComment.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/validation/ItsValidateComment.java
@@ -16,6 +16,7 @@
 
 import com.google.common.collect.Lists;
 import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.events.CommitReceivedEvent;
 import com.google.gerrit.server.git.validators.CommitValidationException;
 import com.google.gerrit.server.git.validators.CommitValidationListener;
@@ -23,6 +24,7 @@
 import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.its.base.its.ItsConfig;
 import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
+import com.googlesource.gerrit.plugins.its.base.its.ItsFacadeFactory;
 import com.googlesource.gerrit.plugins.its.base.util.IssueExtractor;
 import java.io.IOException;
 import java.util.Collections;
@@ -41,9 +43,11 @@
 
   @Inject private ItsConfig itsConfig;
 
+  @Inject private ItsFacadeFactory itsFacadeFactory;
+
   @Inject private IssueExtractor issueExtractor;
 
-  private List<CommitValidationMessage> validCommit(RevCommit commit)
+  private List<CommitValidationMessage> validCommit(Project.NameKey project, RevCommit commit)
       throws CommitValidationException {
     List<CommitValidationMessage> ret = Lists.newArrayList();
     ItsAssociationPolicy associationPolicy = itsConfig.getItsAssociationPolicy();
@@ -57,6 +61,7 @@
         String details = null;
         if (issueIds.length > 0) {
           List<String> nonExistingIssueIds = Lists.newArrayList();
+          client = itsFacadeFactory.getFacade(project);
           for (String issueId : issueIds) {
             boolean exists = false;
             try {
@@ -135,10 +140,11 @@
   @Override
   public List<CommitValidationMessage> onCommitReceived(CommitReceivedEvent receiveEvent)
       throws CommitValidationException {
-    ItsConfig.setCurrentProjectName(receiveEvent.getProjectNameKey());
+    Project.NameKey projectName = receiveEvent.getProjectNameKey();
+    ItsConfig.setCurrentProjectName(projectName);
 
-    if (itsConfig.isEnabled(receiveEvent.getProjectNameKey(), receiveEvent.getRefName())) {
-      return validCommit(receiveEvent.commit);
+    if (itsConfig.isEnabled(projectName, receiveEvent.getRefName())) {
+      return validCommit(projectName, receiveEvent.commit);
     }
 
     return Collections.emptyList();
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/Action.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/Action.java
index 10f362a..0f46ce6 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/Action.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/Action.java
@@ -14,6 +14,7 @@
 
 package com.googlesource.gerrit.plugins.its.base.workflow;
 
+import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
 import java.io.IOException;
 import java.util.Map;
 
@@ -23,10 +24,12 @@
   /**
    * Execute this action.
    *
+   * @param its The facade interface to execute actions.
    * @param issue The issue to execute on.
    * @param actionRequest The request to execute.
    * @param properties The properties for the execution.
    */
-  void execute(String issue, ActionRequest actionRequest, Map<String, String> properties)
+  void execute(
+      ItsFacade its, String issue, ActionRequest actionRequest, Map<String, String> properties)
       throws IOException;
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionExecutor.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionExecutor.java
index 726efac..26ae4e7 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionExecutor.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionExecutor.java
@@ -14,8 +14,10 @@
 
 package com.googlesource.gerrit.plugins.its.base.workflow;
 
+import com.google.gerrit.reviewdb.client.Project;
 import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
+import com.googlesource.gerrit.plugins.its.base.its.ItsFacadeFactory;
 import java.io.IOException;
 import java.util.Map;
 import org.slf4j.Logger;
@@ -25,7 +27,7 @@
 public class ActionExecutor {
   private static final Logger log = LoggerFactory.getLogger(ActionExecutor.class);
 
-  private final ItsFacade its;
+  private final ItsFacadeFactory itsFactory;
   private final AddComment.Factory addCommentFactory;
   private final AddStandardComment.Factory addStandardCommentFactory;
   private final AddSoyComment.Factory addSoyCommentFactory;
@@ -33,12 +35,12 @@
 
   @Inject
   public ActionExecutor(
-      ItsFacade its,
+      ItsFacadeFactory itsFactory,
       AddComment.Factory addCommentFactory,
       AddStandardComment.Factory addStandardCommentFactory,
       AddSoyComment.Factory addSoyCommentFactory,
       LogEvent.Factory logEventFactory) {
-    this.its = its;
+    this.itsFactory = itsFactory;
     this.addCommentFactory = addCommentFactory;
     this.addStandardCommentFactory = addStandardCommentFactory;
     this.addSoyCommentFactory = addSoyCommentFactory;
@@ -61,12 +63,13 @@
   }
 
   private void execute(String issue, ActionRequest actionRequest, Map<String, String> properties) {
+    ItsFacade its = itsFactory.getFacade(new Project.NameKey(properties.get("project")));
     try {
       Action action = getAction(actionRequest.getName());
       if (action == null) {
         its.performAction(issue, actionRequest.getUnparsed());
       } else {
-        action.execute(issue, actionRequest, properties);
+        action.execute(its, issue, actionRequest, properties);
       }
     } catch (IOException e) {
       log.error("Error while executing action " + actionRequest, e);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddComment.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddComment.java
index 3f376f2..82d1701 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddComment.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddComment.java
@@ -15,7 +15,6 @@
 package com.googlesource.gerrit.plugins.its.base.workflow;
 
 import com.google.common.base.Strings;
-import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
 import java.io.IOException;
 import java.util.Map;
@@ -30,15 +29,9 @@
     AddComment create();
   }
 
-  private final ItsFacade its;
-
-  @Inject
-  public AddComment(ItsFacade its) {
-    this.its = its;
-  }
-
   @Override
-  public void execute(String issue, ActionRequest actionRequest, Map<String, String> properties)
+  public void execute(
+      ItsFacade its, String issue, ActionRequest actionRequest, Map<String, String> properties)
       throws IOException {
     String comment = String.join(" ", actionRequest.getParameters());
     if (!Strings.isNullOrEmpty(comment)) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddSoyComment.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddSoyComment.java
index f1c1962..035a6da 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddSoyComment.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddSoyComment.java
@@ -46,14 +46,12 @@
     AddSoyComment create();
   }
 
-  private final ItsFacade its;
   private final Path templateDir;
   protected HashMap<String, Object> soyContext;
 
   @Inject
-  public AddSoyComment(@ItsPath Path itsPath, ItsFacade its) {
+  public AddSoyComment(@ItsPath Path itsPath) {
     this.templateDir = itsPath.resolve("templates");
-    this.its = its;
   }
 
   private String soyTemplate(
@@ -87,7 +85,8 @@
   }
 
   @Override
-  public void execute(String issue, ActionRequest actionRequest, Map<String, String> properties)
+  public void execute(
+      ItsFacade its, String issue, ActionRequest actionRequest, Map<String, String> properties)
       throws IOException {
     String comment = buildComment(actionRequest, properties);
     if (!Strings.isNullOrEmpty(comment)) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddStandardComment.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddStandardComment.java
index 95b9c5e..c2b95a6 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddStandardComment.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/AddStandardComment.java
@@ -15,7 +15,6 @@
 package com.googlesource.gerrit.plugins.its.base.workflow;
 
 import com.google.common.base.Strings;
-import com.google.inject.Inject;
 import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
 import java.io.IOException;
 import java.util.Map;
@@ -30,13 +29,6 @@
     AddStandardComment create();
   }
 
-  private final ItsFacade its;
-
-  @Inject
-  public AddStandardComment(ItsFacade its) {
-    this.its = its;
-  }
-
   private String getCommentChangeEvent(String action, String prefix, Map<String, String> map) {
     String ret = "";
     String changeNumber = Strings.nullToEmpty(map.get("changeNumber"));
@@ -74,7 +66,8 @@
   }
 
   @Override
-  public void execute(String issue, ActionRequest actionRequest, Map<String, String> properties)
+  public void execute(
+      ItsFacade its, String issue, ActionRequest actionRequest, Map<String, String> properties)
       throws IOException {
     String comment = buildComment(properties);
     if (!Strings.isNullOrEmpty(comment)) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/LogEvent.java b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/LogEvent.java
index 5df562e..1f12f19 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/LogEvent.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/its/base/workflow/LogEvent.java
@@ -15,6 +15,7 @@
 package com.googlesource.gerrit.plugins.its.base.workflow;
 
 import com.google.inject.Inject;
+import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
 import java.io.IOException;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -75,7 +76,8 @@
   }
 
   @Override
-  public void execute(String issue, ActionRequest actionRequest, Map<String, String> properties)
+  public void execute(
+      ItsFacade its, String issue, ActionRequest actionRequest, Map<String, String> properties)
       throws IOException {
     Level level = Level.fromString(actionRequest.getParameter(1));
     for (Entry<String, String> property : properties.entrySet()) {
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/validation/ItsValidateCommentTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/validation/ItsValidateCommentTest.java
index 14744c4..d247a7c 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/its/base/validation/ItsValidateCommentTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/validation/ItsValidateCommentTest.java
@@ -26,6 +26,7 @@
 import com.google.inject.Injector;
 import com.googlesource.gerrit.plugins.its.base.its.ItsConfig;
 import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
+import com.googlesource.gerrit.plugins.its.base.its.ItsFacadeFactory;
 import com.googlesource.gerrit.plugins.its.base.testutil.LoggingMockingTestCase;
 import com.googlesource.gerrit.plugins.its.base.util.IssueExtractor;
 import java.io.IOException;
@@ -45,6 +46,7 @@
   private IssueExtractor issueExtractor;
   private ItsFacade itsFacade;
   private ItsConfig itsConfig;
+  private ItsFacadeFactory itsFacadeFactory;
 
   private Project project = new Project(new Project.NameKey("myProject"));
 
@@ -157,6 +159,7 @@
     expect(commit.getId()).andReturn(commit).anyTimes();
     expect(commit.getName()).andReturn("TestCommit").anyTimes();
     expect(issueExtractor.getIssueIds("bug#4711")).andReturn(new String[] {"4711"}).atLeastOnce();
+    expect(itsFacadeFactory.getFacade(project.getNameKey())).andReturn(itsFacade);
     expect(itsFacade.exists("4711")).andReturn(true).atLeastOnce();
 
     replayMocks();
@@ -180,6 +183,7 @@
     expect(commit.getId()).andReturn(commit).anyTimes();
     expect(commit.getName()).andReturn("TestCommit").anyTimes();
     expect(issueExtractor.getIssueIds("bug#4711")).andReturn(new String[] {"4711"}).atLeastOnce();
+    expect(itsFacadeFactory.getFacade(project.getNameKey())).andReturn(itsFacade);
     expect(itsFacade.exists("4711")).andReturn(true).atLeastOnce();
 
     replayMocks();
@@ -204,6 +208,7 @@
     expect(commit.getId()).andReturn(commit).anyTimes();
     expect(commit.getName()).andReturn("TestCommit").anyTimes();
     expect(issueExtractor.getIssueIds("bug#4711")).andReturn(new String[] {"4711"}).atLeastOnce();
+    expect(itsFacadeFactory.getFacade(project.getNameKey())).andReturn(itsFacade);
     expect(itsFacade.exists("4711")).andReturn(false).atLeastOnce();
 
     replayMocks();
@@ -232,6 +237,7 @@
     expect(commit.getId()).andReturn(commit).anyTimes();
     expect(commit.getName()).andReturn("TestCommit").anyTimes();
     expect(issueExtractor.getIssueIds("bug#4711")).andReturn(new String[] {"4711"}).atLeastOnce();
+    expect(itsFacadeFactory.getFacade(project.getNameKey())).andReturn(itsFacade);
     expect(itsFacade.exists("4711")).andReturn(false).atLeastOnce();
 
     replayMocks();
@@ -262,6 +268,7 @@
     expect(issueExtractor.getIssueIds("bug#4711, bug#42"))
         .andReturn(new String[] {"4711", "42"})
         .atLeastOnce();
+    expect(itsFacadeFactory.getFacade(project.getNameKey())).andReturn(itsFacade).anyTimes();
     expect(itsFacade.exists("4711")).andReturn(true).atLeastOnce();
     expect(itsFacade.exists("42")).andReturn(true).atLeastOnce();
 
@@ -288,6 +295,7 @@
     expect(issueExtractor.getIssueIds("bug#4711, bug#42"))
         .andReturn(new String[] {"4711", "42"})
         .atLeastOnce();
+    expect(itsFacadeFactory.getFacade(project.getNameKey())).andReturn(itsFacade).anyTimes();
     expect(itsFacade.exists("4711")).andReturn(true).atLeastOnce();
     expect(itsFacade.exists("42")).andReturn(true).atLeastOnce();
 
@@ -315,9 +323,9 @@
     expect(issueExtractor.getIssueIds("bug#4711, bug#42"))
         .andReturn(new String[] {"4711", "42"})
         .atLeastOnce();
+    expect(itsFacadeFactory.getFacade(project.getNameKey())).andReturn(itsFacade).anyTimes();
     expect(itsFacade.exists("4711")).andReturn(false).atLeastOnce();
     expect(itsFacade.exists("42")).andReturn(true).atLeastOnce();
-
     replayMocks();
 
     ret = ivc.onCommitReceived(event);
@@ -349,6 +357,7 @@
     expect(issueExtractor.getIssueIds("bug#4711, bug#42"))
         .andReturn(new String[] {"4711", "42"})
         .atLeastOnce();
+    expect(itsFacadeFactory.getFacade(project.getNameKey())).andReturn(itsFacade).anyTimes();
     expect(itsFacade.exists("4711")).andReturn(false).atLeastOnce();
     expect(itsFacade.exists("42")).andReturn(true).atLeastOnce();
 
@@ -381,6 +390,7 @@
     expect(issueExtractor.getIssueIds("bug#4711, bug#42"))
         .andReturn(new String[] {"4711", "42"})
         .atLeastOnce();
+    expect(itsFacadeFactory.getFacade(project.getNameKey())).andReturn(itsFacade).anyTimes();
     expect(itsFacade.exists("4711")).andReturn(false).atLeastOnce();
     expect(itsFacade.exists("42")).andReturn(false).atLeastOnce();
 
@@ -415,8 +425,9 @@
     expect(issueExtractor.getIssueIds("bug#4711, bug#42"))
         .andReturn(new String[] {"4711", "42"})
         .atLeastOnce();
+    expect(itsFacadeFactory.getFacade(project.getNameKey())).andReturn(itsFacade).anyTimes();
     expect(itsFacade.exists("4711")).andReturn(false).atLeastOnce();
-    expect(itsFacade.exists("42")).andReturn(false).atLeastOnce();
+    expect(itsFacade.exists("42")).andReturn(true).atLeastOnce();
 
     replayMocks();
 
@@ -447,6 +458,7 @@
     expect(issueExtractor.getIssueIds("bug#4711, bug#42"))
         .andReturn(new String[] {"4711", "42"})
         .atLeastOnce();
+    expect(itsFacadeFactory.getFacade(project.getNameKey())).andReturn(itsFacade).anyTimes();
     expect(itsFacade.exists("4711")).andThrow(new IOException("InjectedEx1")).atLeastOnce();
     expect(itsFacade.exists("42")).andReturn(false).atLeastOnce();
 
@@ -534,6 +546,9 @@
 
       itsConfig = createMock(ItsConfig.class);
       bind(ItsConfig.class).toInstance(itsConfig);
+
+      itsFacadeFactory = createMock(ItsFacadeFactory.class);
+      bind(ItsFacadeFactory.class).toInstance(itsFacadeFactory);
     }
   }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionControllerTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionControllerTest.java
index 4f8b4ef..8c8a7de 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionControllerTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionControllerTest.java
@@ -54,7 +54,7 @@
     actionController.onEvent(event);
   }
 
-  public void testNoActionsOrNotIssues() {
+  public void testNoActionsOrNoIssues() {
     ActionController actionController = createActionController();
 
     ChangeEvent event = createMock(ChangeEvent.class);
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionExecutorTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionExecutorTest.java
index be0c33a..69c9be9 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionExecutorTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/ActionExecutorTest.java
@@ -19,9 +19,11 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.gerrit.reviewdb.client.Project;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
+import com.googlesource.gerrit.plugins.its.base.its.ItsFacadeFactory;
 import com.googlesource.gerrit.plugins.its.base.testutil.LoggingMockingTestCase;
 import java.io.IOException;
 import java.util.Map;
@@ -31,17 +33,21 @@
   private Injector injector;
 
   private ItsFacade its;
+  private ItsFacadeFactory itsFacadeFactory;
   private AddComment.Factory addCommentFactory;
   private AddStandardComment.Factory addStandardCommentFactory;
   private AddSoyComment.Factory addSoyCommentFactory;
   private LogEvent.Factory logEventFactory;
 
-  private Map<String, String> properties = ImmutableMap.of("issue", "4711");
+  private Map<String, String> properties =
+      ImmutableMap.of("issue", "4711", "project", "testProject");
 
   public void testExecuteItem() throws IOException {
     ActionRequest actionRequest = createMock(ActionRequest.class);
     expect(actionRequest.getName()).andReturn("unparsed");
     expect(actionRequest.getUnparsed()).andReturn("unparsed action 1");
+    expect(itsFacadeFactory.getFacade(new Project.NameKey(properties.get("project"))))
+        .andReturn(its);
 
     Set<ActionRequest> actionRequests = ImmutableSet.of(actionRequest);
 
@@ -57,6 +63,8 @@
     ActionRequest actionRequest = createMock(ActionRequest.class);
     expect(actionRequest.getName()).andReturn("unparsed");
     expect(actionRequest.getUnparsed()).andReturn("unparsed action 1");
+    expect(itsFacadeFactory.getFacade(new Project.NameKey(properties.get("project"))))
+        .andReturn(its);
 
     Set<ActionRequest> actionRequests = ImmutableSet.of(actionRequest);
 
@@ -79,6 +87,9 @@
     ActionRequest actionRequest2 = createMock(ActionRequest.class);
     expect(actionRequest2.getName()).andReturn("unparsed");
     expect(actionRequest2.getUnparsed()).andReturn("unparsed action 2");
+    expect(itsFacadeFactory.getFacade(new Project.NameKey(properties.get("project"))))
+        .andReturn(its)
+        .anyTimes();
 
     Set<ActionRequest> actionRequests = ImmutableSet.of(actionRequest1, actionRequest2);
 
@@ -103,6 +114,9 @@
     ActionRequest actionRequest3 = createMock(ActionRequest.class);
     expect(actionRequest3.getName()).andReturn("unparsed");
     expect(actionRequest3.getUnparsed()).andReturn("unparsed action 3");
+    expect(itsFacadeFactory.getFacade(new Project.NameKey(properties.get("project"))))
+        .andReturn(its)
+        .anyTimes();
 
     Set<ActionRequest> actionRequests =
         ImmutableSet.of(actionRequest1, actionRequest2, actionRequest3);
@@ -130,8 +144,10 @@
 
     AddComment addComment = createMock(AddComment.class);
     expect(addCommentFactory.create()).andReturn(addComment);
+    expect(itsFacadeFactory.getFacade(new Project.NameKey(properties.get("project"))))
+        .andReturn(its);
 
-    addComment.execute("4711", actionRequest, properties);
+    addComment.execute(its, "4711", actionRequest, properties);
 
     replayMocks();
 
@@ -147,8 +163,10 @@
 
     AddSoyComment addSoyComment = createMock(AddSoyComment.class);
     expect(addSoyCommentFactory.create()).andReturn(addSoyComment);
+    expect(itsFacadeFactory.getFacade(new Project.NameKey(properties.get("project"))))
+        .andReturn(its);
 
-    addSoyComment.execute("4711", actionRequest, properties);
+    addSoyComment.execute(its, "4711", actionRequest, properties);
 
     replayMocks();
 
@@ -164,8 +182,10 @@
 
     AddStandardComment addStandardComment = createMock(AddStandardComment.class);
     expect(addStandardCommentFactory.create()).andReturn(addStandardComment);
+    expect(itsFacadeFactory.getFacade(new Project.NameKey(properties.get("project"))))
+        .andReturn(its);
 
-    addStandardComment.execute("4711", actionRequest, properties);
+    addStandardComment.execute(its, "4711", actionRequest, properties);
 
     replayMocks();
 
@@ -181,8 +201,10 @@
 
     LogEvent logEvent = createMock(LogEvent.class);
     expect(logEventFactory.create()).andReturn(logEvent);
+    expect(itsFacadeFactory.getFacade(new Project.NameKey(properties.get("project"))))
+        .andReturn(its);
 
-    logEvent.execute("4711", actionRequest, properties);
+    logEvent.execute(its, "4711", actionRequest, properties);
 
     replayMocks();
 
@@ -217,6 +239,9 @@
 
       logEventFactory = createMock(LogEvent.Factory.class);
       bind(LogEvent.Factory.class).toInstance(logEventFactory);
+
+      itsFacadeFactory = createMock(ItsFacadeFactory.class);
+      bind(ItsFacadeFactory.class).toInstance(itsFacadeFactory);
     }
   }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/AddCommentTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/AddCommentTest.java
index e11f8f2..149dfca 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/AddCommentTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/AddCommentTest.java
@@ -35,7 +35,7 @@
     replayMocks();
 
     AddComment addComment = createAddComment();
-    addComment.execute("4711", actionRequest, ImmutableMap.of());
+    addComment.execute(null, "4711", actionRequest, ImmutableMap.of());
   }
 
   public void testPlain() throws IOException {
@@ -47,7 +47,7 @@
     replayMocks();
 
     AddComment addComment = createAddComment();
-    addComment.execute("4711", actionRequest, ImmutableMap.of());
+    addComment.execute(its, "4711", actionRequest, ImmutableMap.of());
   }
 
   private AddComment createAddComment() {
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/AddStandardCommentTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/AddStandardCommentTest.java
index 8b21c1d..044e6a1 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/AddStandardCommentTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/AddStandardCommentTest.java
@@ -36,7 +36,7 @@
     replayMocks();
 
     Action action = injector.getInstance(AddStandardComment.class);
-    action.execute("42", actionRequest, properties);
+    action.execute(its, "42", actionRequest, properties);
   }
 
   public void testChangeMergedFull() throws IOException {
@@ -60,7 +60,7 @@
     replayMocks();
 
     Action action = injector.getInstance(AddStandardComment.class);
-    action.execute("176", actionRequest, properties);
+    action.execute(its, "176", actionRequest, properties);
   }
 
   public void testChangeAbandonedPlain() throws IOException {
@@ -72,7 +72,7 @@
     replayMocks();
 
     Action action = injector.getInstance(AddStandardComment.class);
-    action.execute("42", actionRequest, properties);
+    action.execute(its, "42", actionRequest, properties);
   }
 
   public void testChangeAbandonedFull() throws IOException {
@@ -100,7 +100,7 @@
     replayMocks();
 
     Action action = injector.getInstance(AddStandardComment.class);
-    action.execute("176", actionRequest, properties);
+    action.execute(its, "176", actionRequest, properties);
   }
 
   public void testChangeRestoredPlain() throws IOException {
@@ -112,7 +112,7 @@
     replayMocks();
 
     Action action = injector.getInstance(AddStandardComment.class);
-    action.execute("42", actionRequest, properties);
+    action.execute(its, "42", actionRequest, properties);
   }
 
   public void testChangeRestoredFull() throws IOException {
@@ -140,7 +140,7 @@
     replayMocks();
 
     Action action = injector.getInstance(AddStandardComment.class);
-    action.execute("176", actionRequest, properties);
+    action.execute(its, "176", actionRequest, properties);
   }
 
   public void testPatchSetCreatedPlain() throws IOException {
@@ -152,7 +152,7 @@
     replayMocks();
 
     Action action = injector.getInstance(AddStandardComment.class);
-    action.execute("42", actionRequest, properties);
+    action.execute(its, "42", actionRequest, properties);
   }
 
   public void testPatchSetCreatedFull() throws IOException {
@@ -177,7 +177,7 @@
     replayMocks();
 
     Action action = injector.getInstance(AddStandardComment.class);
-    action.execute("176", actionRequest, properties);
+    action.execute(its, "176", actionRequest, properties);
   }
 
   @Override
diff --git a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/LogEventTest.java b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/LogEventTest.java
index c97c592..0a26db8 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/LogEventTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/its/base/workflow/LogEventTest.java
@@ -19,6 +19,7 @@
 import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
+import com.googlesource.gerrit.plugins.its.base.its.ItsFacade;
 import com.googlesource.gerrit.plugins.its.base.testutil.LoggingMockingTestCase;
 import java.io.IOException;
 import java.util.Map;
@@ -26,6 +27,7 @@
 
 public class LogEventTest extends LoggingMockingTestCase {
   private Injector injector;
+  private ItsFacade its;
 
   public void testNull() throws IOException {
     ActionRequest actionRequest = createMock(ActionRequest.class);
@@ -34,7 +36,7 @@
     replayMocks();
 
     LogEvent logEvent = createLogEvent();
-    logEvent.execute("4711", actionRequest, ImmutableMap.of());
+    logEvent.execute(null, "4711", actionRequest, ImmutableMap.of());
   }
 
   public void testEmpty() throws IOException {
@@ -44,7 +46,7 @@
     replayMocks();
 
     LogEvent logEvent = createLogEvent();
-    logEvent.execute("4711", actionRequest, ImmutableMap.of());
+    logEvent.execute(null, "4711", actionRequest, ImmutableMap.of());
   }
 
   public void testLevelDefault() throws IOException {
@@ -52,10 +54,11 @@
     expect(actionRequest.getParameter(1)).andReturn("");
 
     Map<String, String> properties = ImmutableMap.of("KeyA", "ValueA");
+
     replayMocks();
 
     LogEvent logEvent = createLogEvent();
-    logEvent.execute("4711", actionRequest, properties);
+    logEvent.execute(its, "4711", actionRequest, properties);
 
     assertLogMessageContains("KeyA = ValueA", Level.INFO);
   }
@@ -65,10 +68,11 @@
     expect(actionRequest.getParameter(1)).andReturn("error");
 
     Map<String, String> properties = ImmutableMap.of("KeyA", "ValueA");
+
     replayMocks();
 
     LogEvent logEvent = createLogEvent();
-    logEvent.execute("4711", actionRequest, properties);
+    logEvent.execute(its, "4711", actionRequest, properties);
 
     assertLogMessageContains("KeyA = ValueA", Level.ERROR);
   }
@@ -78,10 +82,11 @@
     expect(actionRequest.getParameter(1)).andReturn("warn");
 
     Map<String, String> properties = ImmutableMap.of("KeyA", "ValueA");
+
     replayMocks();
 
     LogEvent logEvent = createLogEvent();
-    logEvent.execute("4711", actionRequest, properties);
+    logEvent.execute(its, "4711", actionRequest, properties);
 
     assertLogMessageContains("KeyA = ValueA", Level.WARN);
   }
@@ -91,10 +96,11 @@
     expect(actionRequest.getParameter(1)).andReturn("info");
 
     Map<String, String> properties = ImmutableMap.of("KeyA", "ValueA");
+
     replayMocks();
 
     LogEvent logEvent = createLogEvent();
-    logEvent.execute("4711", actionRequest, properties);
+    logEvent.execute(its, "4711", actionRequest, properties);
 
     assertLogMessageContains("KeyA = ValueA", Level.INFO);
   }
@@ -104,10 +110,11 @@
     expect(actionRequest.getParameter(1)).andReturn("debug");
 
     Map<String, String> properties = ImmutableMap.of("KeyA", "ValueA");
+
     replayMocks();
 
     LogEvent logEvent = createLogEvent();
-    logEvent.execute("4711", actionRequest, properties);
+    logEvent.execute(its, "4711", actionRequest, properties);
 
     assertLogMessageContains("KeyA = ValueA", Level.DEBUG);
   }
@@ -122,10 +129,11 @@
             .put("KeyB", "ValueB")
             .put("KeyC", "ValueC")
             .build();
+
     replayMocks();
 
     LogEvent logEvent = createLogEvent();
-    logEvent.execute("4711", actionRequest, properties);
+    logEvent.execute(its, "4711", actionRequest, properties);
 
     assertLogMessageContains("KeyA = ValueA", Level.INFO);
     assertLogMessageContains("KeyB = ValueB", Level.INFO);
@@ -144,6 +152,8 @@
 
   private class TestModule extends FactoryModule {
     @Override
-    protected void configure() {}
+    protected void configure() {
+      its = createMock(ItsFacade.class);
+    }
   }
 }