Rename EiffelEventParser and EiffelEventParserIf

When an interface was extracted from EiffelEventParser in a previous
commit the implementing class kept the name EiffelEventParser to make
the diff more reviewable. In this change we rename the interface
`EiffelEventParser` and the implementation `EiffelEventParserImpl`
to align with the inofficial code-standard in Gerrit.

Solves: Jira GER-1715
Change-Id: I2ad5a139f0e6b983b615414450a9bc1a75a1128f
diff --git a/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/Module.java b/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/Module.java
index 9f53bee..9bdc0c4 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/Module.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/Module.java
@@ -40,7 +40,7 @@
 import com.googlesource.gerrit.plugins.eventseiffel.mapping.EiffelEventMapper;
 import com.googlesource.gerrit.plugins.eventseiffel.mq.RabbitMqPublisher;
 import com.googlesource.gerrit.plugins.eventseiffel.parsing.EiffelEventParser;
-import com.googlesource.gerrit.plugins.eventseiffel.parsing.EiffelEventParserIf;
+import com.googlesource.gerrit.plugins.eventseiffel.parsing.EiffelEventParserImpl;
 import com.googlesource.gerrit.plugins.eventseiffel.parsing.EiffelEventParsingExecutor;
 import com.googlesource.gerrit.plugins.eventseiffel.parsing.EiffelEventParsingQueue;
 import com.googlesource.gerrit.plugins.eventseiffel.rest.RestModule;
@@ -100,7 +100,7 @@
         .in(Scopes.SINGLETON);
     bind(EiffelEventParsingExecutor.Scheduled.class).in(Scopes.SINGLETON);
     bind(EiffelEventParsingExecutor.class).to(EiffelEventParsingExecutor.Scheduled.class);
-    bind(EiffelEventParserIf.class).to(EiffelEventParser.class);
+    bind(EiffelEventParser.class).to(EiffelEventParserImpl.class);
     bind(EiffelEventParsingQueue.class).in(Scopes.SINGLETON);
     bind(EiffelEventHubImpl.class).in(Scopes.SINGLETON);
     bind(EiffelEventHub.class).to(EiffelEventHubImpl.class);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParser.java b/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParser.java
index df82291..6c0ad07 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParser.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParser.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2021 The Android Open Source Project
+// Copyright (C) 2022 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.
@@ -11,419 +11,64 @@
 // 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.eventseiffel.parsing;
 
-import static com.googlesource.gerrit.plugins.eventseiffel.eiffel.dto.EiffelEventType.SCC;
-
-import com.github.rholder.retry.RetryException;
-import com.github.rholder.retry.Retryer;
-import com.github.rholder.retry.RetryerBuilder;
-import com.github.rholder.retry.StopStrategies;
-import com.github.rholder.retry.WaitStrategies;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.Lists;
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.entities.Project;
-import com.google.gerrit.entities.RefNames;
-import com.google.gerrit.exceptions.NoSuchEntityException;
 import com.google.gerrit.extensions.common.AccountInfo;
-import com.google.gerrit.extensions.common.CommitInfo;
 import com.google.gerrit.extensions.events.RevisionCreatedListener.Event;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.inject.Inject;
-import com.googlesource.gerrit.plugins.eventseiffel.EiffelEventHub;
-import com.googlesource.gerrit.plugins.eventseiffel.cache.EiffelEventIdLookupException;
-import com.googlesource.gerrit.plugins.eventseiffel.eiffel.CompositionDefinedEventKey;
-import com.googlesource.gerrit.plugins.eventseiffel.eiffel.EventKey;
 import com.googlesource.gerrit.plugins.eventseiffel.eiffel.SourceChangeEventKey;
-import com.googlesource.gerrit.plugins.eventseiffel.eiffel.dto.EiffelEvent;
-import com.googlesource.gerrit.plugins.eventseiffel.mapping.EiffelEventMapper;
-import com.googlesource.gerrit.plugins.eventseiffel.parsing.UnprocessedCommitsWalker.EventCreate;
-import com.googlesource.gerrit.plugins.eventseiffel.parsing.UnprocessedCommitsWalker.ScsWalker;
-import java.io.IOException;
-import java.util.List;
-import java.util.Optional;
-import java.util.UUID;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.errors.IncorrectObjectTypeException;
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
 
-/** Creates and pushes missing Eiffel events to the Eiffel event queue. */
-public class EiffelEventParser implements EiffelEventParserIf {
-  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+public interface EiffelEventParser {
 
-  private static final int NBR_RETRIES = 3;
+  void createAndScheduleSccFromEvent(Event event);
 
-  private final EiffelEventHub eventHub;
-  private final GitRepositoryManager repoManager;
-  private final UnprocessedCommitsWalker.Factory walkerFactory;
-  private final EiffelEventMapper mapper;
-
-  @Inject
-  public EiffelEventParser(
-      EiffelEventHub eventQueue,
-      GitRepositoryManager repoManager,
-      EiffelEventMapper mapper,
-      UnprocessedCommitsWalker.Factory walkerFactory) {
-    this.eventHub = eventQueue;
-    this.repoManager = repoManager;
-    this.walkerFactory = walkerFactory;
-    this.mapper = mapper;
-  }
-
-  /* (non-Javadoc)
-   * @see com.googlesource.gerrit.plugins.eventseiffel.parsing.EiffelEventParserIf#createAndScheduleSccFromEvent(com.google.gerrit.extensions.events.RevisionCreatedListener.Event)
+  /**
+   * Creates SCC events for all commits reachable from branchRef. I.e. create SCCs for already
+   * merged commit that are missing SCCs.
+   *
+   * @param repoName
+   * @param branchRef
    */
-  @Override
-  public void createAndScheduleSccFromEvent(Event event) {
-    CommitInfo commit = event.getRevision().commit;
-    SourceChangeEventKey scc =
-        SourceChangeEventKey.sccKey(
-            event.getChange().project, event.getChange().branch, commit.commit);
-    try {
-      if (eventHub.getExistingId(scc).isPresent()) {
-        logger.atWarning().log(
-            "Event %s already pushed for %d/%d",
-            scc, event.getChange()._number, event.getRevision()._number);
-        return;
-      }
-      List<UUID> parentUuids = Lists.newArrayList();
-      for (CommitInfo parent : commit.parents) {
-        Optional<UUID> parentUuid = eventHub.getExistingId(scc.copy(parent.commit));
-        if (parentUuid.isPresent()) {
-          parentUuids.add(parentUuid.get());
-        }
-      }
+  void createAndScheduleSccFromBranch(String repoName, String branchRef);
 
-      /* Eiffel events have been scheduled or published for all parents. */
-      if (parentUuids.size() == commit.parents.size()) {
-        pushToHub(mapper.toScc(event, parentUuids));
-      } else {
-        createAndScheduleMissingSccs(scc);
-      }
-    } catch (IOException
-        | ConfigInvalidException
-        | NoSuchEntityException
-        | EiffelEventIdLookupException
-        | InterruptedException e) {
-      logger.atSevere().withCause(e).log(
-          "Event creation failed for: %s, %s, %s to SCC.",
-          event.getChange().project, event.getChange().branch, event.getRevision().commit.commit);
-    }
-  }
-
-  /* (non-Javadoc)
-   * @see com.googlesource.gerrit.plugins.eventseiffel.parsing.EiffelEventParserIf#createAndScheduleSccFromBranch(java.lang.String, java.lang.String)
+  /**
+   * Creates SCC events for all commits reachable from commit.
+   *
+   * @param repoName
+   * @param targetBranch
+   * @param commit
    */
-  @Override
-  public void createAndScheduleSccFromBranch(String repoName, String branchRef) {
-    ObjectId tip = getTipOf(repoName, branchRef);
-    if (tip == null) {
-      return;
-    }
-    createAndScheduleSccFromCommit(repoName, branchRef, tip.getName());
-  }
+  void createAndScheduleSccFromCommit(String repoName, String targetBranch, String commit);
 
-  /* (non-Javadoc)
-   * @see com.googlesource.gerrit.plugins.eventseiffel.parsing.EiffelEventParserIf#createAndScheduleSccFromCommit(java.lang.String, java.lang.String, java.lang.String)
+  /**
+   * Creates missing SCS events for repoName, branch.
+   *
+   * @param repoName
+   * @param branch
    */
-  @Override
-  public void createAndScheduleSccFromCommit(String repoName, String branchRef, String commit) {
+  void createAndScheduleMissingScssFromBranch(String repoName, String branch);
 
-    SourceChangeEventKey scc = SourceChangeEventKey.sccKey(repoName, branchRef, commit);
-    try {
-      createAndScheduleMissingSccs(scc);
-    } catch (IOException
-        | EiffelEventIdLookupException
-        | NoSuchEntityException
-        | ConfigInvalidException
-        | InterruptedException e) {
-      logger.atSevere().withCause(e).log("Event creation failed for: %s", scc);
-    }
-  }
-
-  /* (non-Javadoc)
-   * @see com.googlesource.gerrit.plugins.eventseiffel.parsing.EiffelEventParserIf#createAndScheduleMissingScssFromBranch(java.lang.String, java.lang.String)
+  /**
+   * Creates missing SCS events for a submit transaction. submitter and submittedAt is set for all
+   * events created within the submit transaction, i.e. not reachable from commitSha1TransactionEnd.
+   *
+   * @param scs
+   * @param commitSha1TransactionEnd
+   * @param submitter
+   * @param submittedAt
    */
-  @Override
-  public void createAndScheduleMissingScssFromBranch(String repoName, String branchRef) {
-    ObjectId tip = getTipOf(repoName, branchRef);
-    if (tip == null) {
-      return;
-    }
-    SourceChangeEventKey scs = SourceChangeEventKey.scsKey(repoName, branchRef, tip.getName());
-    createAndScheduleMissingScss(scs, null, null, null);
-  }
-
-  /* (non-Javadoc)
-   * @see com.googlesource.gerrit.plugins.eventseiffel.parsing.EiffelEventParserIf#createAndScheduleMissingScss(com.googlesource.gerrit.plugins.eventseiffel.eiffel.SourceChangeEventKey, java.lang.String, com.google.gerrit.extensions.common.AccountInfo, java.lang.Long)
-   */
-  @Override
-  public void createAndScheduleMissingScss(
+  void createAndScheduleMissingScss(
       SourceChangeEventKey scs,
       String commitSha1TransactionEnd,
       AccountInfo submitter,
-      Long submittedAt) {
-    SourceChangeEventKey currentScs = scs;
-    SourceChangeEventKey scc = scs.copy(SCC);
-    try {
-      try (ScsWalker scsFinder =
-          walkerFactory.scsWalker(scs, commitSha1TransactionEnd, submitter, submittedAt)) {
+      Long submittedAt);
 
-        if (eventHub.getExistingId(scc).isEmpty()) {
-          /* One or several SCC events are missing, create them first */
-          try (UnprocessedCommitsWalker sccFinder = scsFinder.sccWalker()) {
-            createAndScheduleMissingSccs(scc, sccFinder);
-          }
-        }
-
-        if (!scsFinder.hasNext()) {
-          logger.atInfo().log("All events were already published for %s", scs);
-        } else {
-          logger.atFine().log("Start publishing events for: %s", scs);
-        }
-        while (scsFinder.hasNext()) {
-          EventCreate create = scsFinder.next();
-          currentScs = create.key;
-          Optional<UUID> sccId = eventHub.getExistingId(create.key.copy(SCC));
-          if (sccId.isEmpty()) {
-            throw new EiffelEventIdLookupException(
-                "Unable to find SCC event id: %s", create.key.copy(SCC));
-          }
-          List<UUID> scsParentEventIds = Lists.newArrayList();
-          for (RevCommit parent : create.commit.getParents()) {
-            Optional<UUID> parentUuid = eventHub.getExistingId(create.key.copy(parent.getName()));
-            if (parentUuid.isPresent()) {
-              scsParentEventIds.add(parentUuid.get());
-            } else {
-              exceptionForMissingParent(create, parent);
-            }
-          }
-          pushToHub(
-              mapper.toScs(
-                  create.commit,
-                  create.key.repo(),
-                  create.key.branch(),
-                  create.submitter,
-                  create.submittedAt,
-                  scsParentEventIds,
-                  sccId.get()));
-        }
-      }
-      logger.atFine().log("Done publishing events for: %s", scs);
-    } catch (IOException
-        | EiffelEventIdLookupException
-        | InterruptedException
-        | ConfigInvalidException
-        | NoSuchEntityException e) {
-      logger.atSevere().withCause(e).log("Failed to create Eiffel event(s) for %s.", currentScs);
-    }
-  }
-
-  /* (non-Javadoc)
-   * @see com.googlesource.gerrit.plugins.eventseiffel.parsing.EiffelEventParserIf#createAndScheduleArtc(java.lang.String, java.lang.String, java.lang.Long, boolean)
+  /**
+   * Creates missing ARTC for a tag, together with CD and (if missing) SCS for referenced commit.7
+   *
+   * @param projectName
+   * @param tagName
+   * @param creationTime
+   * @param force
    */
-  @Override
-  public void createAndScheduleArtc(
-      String repoName, String tagName, Long creationTime, boolean force) {
-    try {
-      CompositionDefinedEventKey cd =
-          CompositionDefinedEventKey.create(mapper.tagCompositionName(repoName), tagName);
-      Optional<UUID> oldCdId = eventHub.getExistingId(cd);
-      if (oldCdId.isEmpty() || force) {
-        createAndScheduleCd(repoName, tagName, creationTime, force);
-        Optional<UUID> cdId = eventHub.getExistingId(cd);
-        if (cdId.isPresent() && !cdId.equals(oldCdId)) {
-          pushToHub(mapper.toArtc(repoName, tagName, creationTime, cdId.get()), force);
-          if (oldCdId.isPresent()) {
-            logger.atInfo().log(
-                "Event Artc has been forcibly created for: %s, %s", repoName, tagName);
-          } else {
-            logger.atFine().log("Event Artc has been created for: %s, %s", repoName, tagName);
-          }
-        }
-      } else {
-        /* Artc event has already been created */
-        logger.atWarning().log(
-            "Event Artc has already been created for: %s, %s", repoName, tagName);
-      }
-    } catch (EiffelEventIdLookupException | InterruptedException e) {
-      logger.atSevere().withCause(e).log(
-          "Event creation failed for: %s, %s to Artc", repoName, tagName);
-    }
-  }
-
-  private void createAndScheduleCd(
-      String projectName, String tagName, Long creationTime, boolean force) {
-    SourceChangeEventKey scs = null;
-    Optional<UUID> scsId = Optional.empty();
-
-    try {
-      String commitId = peelTag(projectName, tagName);
-      if (commitId == null) {
-        return;
-      }
-      scs = SourceChangeEventKey.scsKey(projectName, RefNames.REFS_HEADS + "master", commitId);
-      scsId = eventHub.getExistingId(scs);
-
-      if (scsId.isEmpty()) {
-        Retryer<Optional<UUID>> retryer =
-            RetryerBuilder.<Optional<UUID>>newBuilder()
-                .retryIfResult(Optional::isEmpty)
-                .withWaitStrategy(WaitStrategies.fixedWait(10, TimeUnit.SECONDS))
-                .withStopStrategy(StopStrategies.stopAfterAttempt(2))
-                .build();
-        try {
-          scsId = retryer.call(() -> findSourceChangeEventKey(projectName, commitId));
-        } catch (RetryException | ExecutionException e) {
-          logger.atSevere().withCause(e).log(
-              "Failed to find SCS for %s in %s", commitId, projectName);
-          return;
-        }
-      }
-      if (scsId.isEmpty()) {
-        logger.atSevere().log("Could not find SCS for: %s in %s", commitId, projectName);
-        return;
-      }
-      pushToHub(mapper.toCd(projectName, tagName, creationTime, scsId.get()), force);
-    } catch (EiffelEventIdLookupException | InterruptedException e) {
-      logger.atSevere().withCause(e).log(
-          "Event creation failed for: %s",
-          CompositionDefinedEventKey.create(mapper.tagCompositionName(projectName), tagName));
-    }
-  }
-
-  private String peelTag(String projectName, String tagName) {
-    try (Repository repo = repoManager.openRepository(Project.nameKey(projectName))) {
-      Ref tagRef = repo.getRefDatabase().exactRef(Constants.R_TAGS + tagName);
-      if (tagRef != null) {
-        ObjectId peeled = repo.getRefDatabase().peel(tagRef).getPeeledObjectId();
-        return peeled != null ? peeled.getName() : tagRef.getObjectId().getName();
-      }
-      logger.atSevere().log("Cannot find tag: %s:%s", projectName, tagName);
-    } catch (IOException e) {
-      logger.atSevere().withCause(e).log("Unable to peel tag: %s:%s", projectName, tagName);
-    }
-    return null;
-  }
-
-  private Optional<UUID> findSourceChangeEventKey(String projectName, String commitId)
-      throws EiffelEventIdLookupException {
-    List<String> branches;
-    try (Repository repo = repoManager.openRepository(Project.nameKey(projectName))) {
-      branches =
-          repo.getRefDatabase().getRefsByPrefix(RefNames.REFS_HEADS).stream()
-              .map(Ref::getName)
-              .collect(Collectors.toList());
-    } catch (IOException ioe) {
-      logger.atSevere().withCause(ioe).log("Unable to find branches of %s.", projectName);
-      return Optional.empty();
-    }
-    return eventHub.getScsForCommit(projectName, commitId, branches);
-  }
-
-  private void pushToHub(EiffelEvent toPush) throws InterruptedException {
-    pushToHub(toPush, false);
-  }
-
-  private void pushToHub(EiffelEvent toPush, boolean force) throws InterruptedException {
-    int failureCount = 0;
-    EventKey key = EventKey.fromEvent(toPush);
-    while (true) {
-      try {
-        eventHub.push(toPush, force);
-        logger.atFine().log("Successfully pushed %s to EventHub", key);
-        return;
-      } catch (InterruptedException e) {
-        if (!eventHub.isOpen()) {
-          logger.atInfo().log("EventHub is closed, aborting.");
-          throw e;
-        }
-        failureCount++;
-        if (failureCount < NBR_RETRIES) {
-          logger.atWarning().withCause(e).log(
-              "Interrupted while pushing %s to EventHub, attempt %d/%d",
-              key, failureCount, NBR_RETRIES);
-        } else {
-          throw e;
-        }
-      }
-    }
-  }
-
-  @VisibleForTesting
-  void createAndScheduleMissingSccs(SourceChangeEventKey scc)
-      throws MissingObjectException, IncorrectObjectTypeException, IOException,
-          EiffelEventIdLookupException, RepositoryNotFoundException, NoSuchEntityException,
-          ConfigInvalidException, InterruptedException {
-    logger.atFine().log("Start publishing events for: %s", scc);
-    try (UnprocessedCommitsWalker commitFinder = walkerFactory.sccWalker(scc)) {
-      createAndScheduleMissingSccs(scc, commitFinder);
-    }
-    logger.atFine().log("Done publishing events for: %s", scc);
-  }
-
-  /* Callers are responsible for closing commitFinder. */
-  private void createAndScheduleMissingSccs(
-      SourceChangeEventKey scc, UnprocessedCommitsWalker commitFinder)
-      throws MissingObjectException, EiffelEventIdLookupException, IOException,
-          NoSuchEntityException, ConfigInvalidException, InterruptedException {
-    if (!commitFinder.hasNext()) {
-      logger.atInfo().log("All events were already published for %s", scc);
-    } else {
-      logger.atFine().log("Start publishing events for: %s", scc);
-    }
-    while (commitFinder.hasNext()) {
-      EventCreate job = commitFinder.next();
-      logger.atFine().log("Processing event-creation for: %s", job.key);
-      List<UUID> parentIds = Lists.newArrayList();
-      for (RevCommit parent : job.commit.getParents()) {
-        SourceChangeEventKey parentKey = scc.copy(parent.getName());
-        Optional<UUID> parentId = eventHub.getExistingId(parentKey);
-        if (parentId.isPresent()) {
-          parentIds.add(parentId.get());
-        } else {
-          exceptionForMissingParent(job, parent);
-        }
-      }
-      try {
-        pushToHub(mapper.toScc(job.commit, job.key.repo(), job.key.branch(), parentIds));
-      } catch (InterruptedException e) {
-        logger.atSevere().log("Interrupted while pushing %s to EventHub.", job.key);
-        throw e;
-      }
-    }
-  }
-
-  private ObjectId getTipOf(String repoName, String branch) {
-    try (Repository repo = repoManager.openRepository(Project.nameKey(repoName))) {
-      Ref branchRef = repo.exactRef(branch);
-      if (branchRef == null) {
-        logger.atWarning().log("Could not find ref: %s in project: %s", branch, repoName);
-        return null;
-      }
-      return branchRef.getTarget().getObjectId();
-    } catch (IOException ioe) {
-      logger.atSevere().withCause(ioe).log("Unable to identify tip of (%s:%s).", repoName, branch);
-      return null;
-    }
-  }
-
-  private void exceptionForMissingParent(EventCreate create, RevCommit parent)
-      throws NoSuchEntityException {
-    throw new NoSuchEntityException(
-        String.format(
-            "Unable to lookup parent (%s) event UUID for %s even though it should exist.",
-            parent.abbreviate(7).name(), create.key));
-  }
+  void createAndScheduleArtc(String projectName, String tagName, Long creationTime, boolean force);
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParserIf.java b/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParserIf.java
deleted file mode 100644
index 94aa921..0000000
--- a/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParserIf.java
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (C) 2022 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.eventseiffel.parsing;
-
-import com.google.gerrit.extensions.common.AccountInfo;
-import com.google.gerrit.extensions.events.RevisionCreatedListener.Event;
-import com.googlesource.gerrit.plugins.eventseiffel.eiffel.SourceChangeEventKey;
-
-public interface EiffelEventParserIf {
-
-  void createAndScheduleSccFromEvent(Event event);
-
-  /**
-   * Creates SCC events for all commits reachable from branchRef. I.e. create SCCs for already
-   * merged commit that are missing SCCs.
-   *
-   * @param repoName - Name of the repository where the branch exists.
-   * @param branchRef - Creates missing events for all commits reachable from branch.
-   */
-  void createAndScheduleSccFromBranch(String repoName, String branchRef);
-
-  /**
-   * Creates SCC events for all commits reachable from commit.
-   *
-   * @param repoName - Name of the repository were the commits exists.
-   * @param branchRef - Ref of the branch to create events for.
-   * @param commit - Creates missing events for all commits reachable from commit.
-   */
-  void createAndScheduleSccFromCommit(String repoName, String branchRef, String commit);
-
-  /**
-   * Creates missing SCS events for repoName, branch.
-   *
-   * @param repoName - Name of the repository where the branch exists.
-   * @param branchRef - Creates missing events for all commits reachable from branchRef.
-   */
-  void createAndScheduleMissingScssFromBranch(String repoName, String branchRef);
-
-  /**
-   * Creates missing SCS events for a submit transaction. submitter and submittedAt is set for all
-   * events created within the submit transaction, i.e. not reachable from commitSha1TransactionEnd.
-   *
-   * @param scs - key for the event that shuold be created, together with parents.
-   * @param commitSha1TransactionEnd - tip before submit transaction.
-   * @param submitter - The submitter
-   * @param submittedAt - When the commit was submitted.
-   */
-  void createAndScheduleMissingScss(
-      SourceChangeEventKey scs,
-      String commitSha1TransactionEnd,
-      AccountInfo submitter,
-      Long submittedAt);
-
-  /**
-   * Creates missing ARTC for a tag, together with CD and (if missing) SCS for referenced commit.
-   *
-   * @param repoName - Name of the repository were the tag exists.
-   * @param tagName - The name of the tag.
-   * @param creationTime - The time at which the tag was created.
-   * @param force - Whether existing events should be replaced or not.
-   */
-  void createAndScheduleArtc(String repoName, String tagName, Long creationTime, boolean force);
-}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParserImpl.java b/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParserImpl.java
new file mode 100644
index 0000000..5d54053
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParserImpl.java
@@ -0,0 +1,429 @@
+// Copyright (C) 2021 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.eventseiffel.parsing;
+
+import static com.googlesource.gerrit.plugins.eventseiffel.eiffel.dto.EiffelEventType.SCC;
+
+import com.github.rholder.retry.RetryException;
+import com.github.rholder.retry.Retryer;
+import com.github.rholder.retry.RetryerBuilder;
+import com.github.rholder.retry.StopStrategies;
+import com.github.rholder.retry.WaitStrategies;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Lists;
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
+import com.google.gerrit.exceptions.NoSuchEntityException;
+import com.google.gerrit.extensions.common.AccountInfo;
+import com.google.gerrit.extensions.common.CommitInfo;
+import com.google.gerrit.extensions.events.RevisionCreatedListener.Event;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.inject.Inject;
+import com.googlesource.gerrit.plugins.eventseiffel.EiffelEventHub;
+import com.googlesource.gerrit.plugins.eventseiffel.cache.EiffelEventIdLookupException;
+import com.googlesource.gerrit.plugins.eventseiffel.eiffel.CompositionDefinedEventKey;
+import com.googlesource.gerrit.plugins.eventseiffel.eiffel.EventKey;
+import com.googlesource.gerrit.plugins.eventseiffel.eiffel.SourceChangeEventKey;
+import com.googlesource.gerrit.plugins.eventseiffel.eiffel.dto.EiffelEvent;
+import com.googlesource.gerrit.plugins.eventseiffel.mapping.EiffelEventMapper;
+import com.googlesource.gerrit.plugins.eventseiffel.parsing.UnprocessedCommitsWalker.EventCreate;
+import com.googlesource.gerrit.plugins.eventseiffel.parsing.UnprocessedCommitsWalker.ScsWalker;
+import java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+
+/** Creates and pushes missing Eiffel events to the Eiffel event queue. */
+public class EiffelEventParserImpl implements EiffelEventParser {
+  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+  private static final int NBR_RETRIES = 3;
+
+  private final EiffelEventHub eventHub;
+  private final GitRepositoryManager repoManager;
+  private final UnprocessedCommitsWalker.Factory walkerFactory;
+  private final EiffelEventMapper mapper;
+
+  @Inject
+  public EiffelEventParserImpl(
+      EiffelEventHub eventQueue,
+      GitRepositoryManager repoManager,
+      EiffelEventMapper mapper,
+      UnprocessedCommitsWalker.Factory walkerFactory) {
+    this.eventHub = eventQueue;
+    this.repoManager = repoManager;
+    this.walkerFactory = walkerFactory;
+    this.mapper = mapper;
+  }
+
+  /* (non-Javadoc)
+   * @see com.googlesource.gerrit.plugins.eventseiffel.parsing.EiffelEventParser#createAndScheduleSccFromEvent(com.google.gerrit.extensions.events.RevisionCreatedListener.Event)
+   */
+  @Override
+  public void createAndScheduleSccFromEvent(Event event) {
+    CommitInfo commit = event.getRevision().commit;
+    SourceChangeEventKey scc =
+        SourceChangeEventKey.sccKey(
+            event.getChange().project, event.getChange().branch, commit.commit);
+    try {
+      if (eventHub.getExistingId(scc).isPresent()) {
+        logger.atWarning().log(
+            "Event %s already pushed for %d/%d",
+            scc, event.getChange()._number, event.getRevision()._number);
+        return;
+      }
+      List<UUID> parentUuids = Lists.newArrayList();
+      for (CommitInfo parent : commit.parents) {
+        Optional<UUID> parentUuid = eventHub.getExistingId(scc.copy(parent.commit));
+        if (parentUuid.isPresent()) {
+          parentUuids.add(parentUuid.get());
+        }
+      }
+
+      /* Eiffel events have been scheduled or published for all parents. */
+      if (parentUuids.size() == commit.parents.size()) {
+        pushToHub(mapper.toScc(event, parentUuids));
+      } else {
+        createAndScheduleMissingSccs(scc);
+      }
+    } catch (IOException
+        | ConfigInvalidException
+        | NoSuchEntityException
+        | EiffelEventIdLookupException
+        | InterruptedException e) {
+      logger.atSevere().withCause(e).log(
+          "Event creation failed for: %s, %s, %s to SCC.",
+          event.getChange().project, event.getChange().branch, event.getRevision().commit.commit);
+    }
+  }
+
+  /* (non-Javadoc)
+   * @see com.googlesource.gerrit.plugins.eventseiffel.parsing.EiffelEventParser#createAndScheduleSccFromBranch(java.lang.String, java.lang.String)
+   */
+  @Override
+  public void createAndScheduleSccFromBranch(String repoName, String targetBranch) {
+    ObjectId tip = getTipOf(repoName, targetBranch);
+    if (tip == null) {
+      return;
+    }
+    createAndScheduleSccFromCommit(repoName, targetBranch, tip.getName());
+  }
+
+  /* (non-Javadoc)
+   * @see com.googlesource.gerrit.plugins.eventseiffel.parsing.EiffelEventParser#createAndScheduleSccFromCommit(java.lang.String, java.lang.String, java.lang.String)
+   */
+  @Override
+  public void createAndScheduleSccFromCommit(String repoName, String targetBranch, String commit) {
+
+    SourceChangeEventKey scc = SourceChangeEventKey.sccKey(repoName, targetBranch, commit);
+    try {
+      createAndScheduleMissingSccs(scc);
+    } catch (IOException
+        | EiffelEventIdLookupException
+        | NoSuchEntityException
+        | ConfigInvalidException
+        | InterruptedException e) {
+      logger.atSevere().withCause(e).log("Event creation failed for: %s", scc);
+    }
+  }
+
+  /* (non-Javadoc)
+   * @see com.googlesource.gerrit.plugins.eventseiffel.parsing.EiffelEventParser#createAndScheduleMissingScssFromBranch(java.lang.String, java.lang.String)
+   */
+  @Override
+  public void createAndScheduleMissingScssFromBranch(String repoName, String branch) {
+    ObjectId tip = getTipOf(repoName, branch);
+    if (tip == null) {
+      return;
+    }
+    SourceChangeEventKey scs = SourceChangeEventKey.scsKey(repoName, branch, tip.getName());
+    createAndScheduleMissingScss(scs, null, null, null);
+  }
+
+  /* (non-Javadoc)
+   * @see com.googlesource.gerrit.plugins.eventseiffel.parsing.EiffelEventParser#createAndScheduleMissingScss(com.googlesource.gerrit.plugins.eventseiffel.eiffel.SourceChangeEventKey, java.lang.String, com.google.gerrit.extensions.common.AccountInfo, java.lang.Long)
+   */
+  @Override
+  public void createAndScheduleMissingScss(
+      SourceChangeEventKey scs,
+      String commitSha1TransactionEnd,
+      AccountInfo submitter,
+      Long submittedAt) {
+    SourceChangeEventKey currentScs = scs;
+    SourceChangeEventKey scc = scs.copy(SCC);
+    try {
+      try (ScsWalker scsFinder =
+          walkerFactory.scsWalker(scs, commitSha1TransactionEnd, submitter, submittedAt)) {
+
+        if (eventHub.getExistingId(scc).isEmpty()) {
+          /* One or several SCC events are missing, create them first */
+          try (UnprocessedCommitsWalker sccFinder = scsFinder.sccWalker()) {
+            createAndScheduleMissingSccs(scc, sccFinder);
+          }
+        }
+
+        if (!scsFinder.hasNext()) {
+          logger.atInfo().log("All events were already published for %s", scs);
+        } else {
+          logger.atFine().log("Start publishing events for: %s", scs);
+        }
+        while (scsFinder.hasNext()) {
+          EventCreate create = scsFinder.next();
+          currentScs = create.key;
+          Optional<UUID> sccId = eventHub.getExistingId(create.key.copy(SCC));
+          if (sccId.isEmpty()) {
+            throw new EiffelEventIdLookupException(
+                "Unable to find SCC event id: %s", create.key.copy(SCC));
+          }
+          List<UUID> scsParentEventIds = Lists.newArrayList();
+          for (RevCommit parent : create.commit.getParents()) {
+            Optional<UUID> parentUuid = eventHub.getExistingId(create.key.copy(parent.getName()));
+            if (parentUuid.isPresent()) {
+              scsParentEventIds.add(parentUuid.get());
+            } else {
+              exceptionForMissingParent(create, parent);
+            }
+          }
+          pushToHub(
+              mapper.toScs(
+                  create.commit,
+                  create.key.repo(),
+                  create.key.branch(),
+                  create.submitter,
+                  create.submittedAt,
+                  scsParentEventIds,
+                  sccId.get()));
+        }
+      }
+      logger.atFine().log("Done publishing events for: %s", scs);
+    } catch (IOException
+        | EiffelEventIdLookupException
+        | InterruptedException
+        | ConfigInvalidException
+        | NoSuchEntityException e) {
+      logger.atSevere().withCause(e).log("Failed to create Eiffel event(s) for %s.", currentScs);
+    }
+  }
+
+  /* (non-Javadoc)
+   * @see com.googlesource.gerrit.plugins.eventseiffel.parsing.EiffelEventParser#createAndScheduleArtc(java.lang.String, java.lang.String, java.lang.Long, boolean)
+   */
+  @Override
+  public void createAndScheduleArtc(
+      String projectName, String tagName, Long creationTime, boolean force) {
+    try {
+      CompositionDefinedEventKey cd =
+          CompositionDefinedEventKey.create(mapper.tagCompositionName(projectName), tagName);
+      Optional<UUID> oldCdId = eventHub.getExistingId(cd);
+      if (oldCdId.isEmpty() || force) {
+        createAndScheduleCd(projectName, tagName, creationTime, force);
+        Optional<UUID> cdId = eventHub.getExistingId(cd);
+        if (cdId.isPresent() && !cdId.equals(oldCdId)) {
+          pushToHub(mapper.toArtc(projectName, tagName, creationTime, cdId.get()), force);
+          if (oldCdId.isPresent()) {
+            logger.atInfo().log(
+                "Event Artc has been forcibly created for: %s, %s", projectName, tagName);
+          } else {
+            logger.atFine().log("Event Artc has been created for: %s, %s", projectName, tagName);
+          }
+        }
+      } else {
+        /* Artc event has already been created */
+        logger.atWarning().log(
+            "Event Artc has already been created for: %s, %s", projectName, tagName);
+      }
+    } catch (EiffelEventIdLookupException | InterruptedException e) {
+      logger.atSevere().withCause(e).log(
+          "Event creation failed for: %s, %s to Artc", projectName, tagName);
+    }
+  }
+
+  private void createAndScheduleCd(
+      String projectName, String tagName, Long creationTime, boolean force) {
+    SourceChangeEventKey scs = null;
+    Optional<UUID> scsId = Optional.empty();
+
+    try {
+      String commitId = peelTag(projectName, tagName);
+      if (commitId == null) {
+        return;
+      }
+      scs = SourceChangeEventKey.scsKey(projectName, RefNames.REFS_HEADS + "master", commitId);
+      scsId = eventHub.getExistingId(scs);
+
+      if (scsId.isEmpty()) {
+        Retryer<Optional<UUID>> retryer =
+            RetryerBuilder.<Optional<UUID>>newBuilder()
+                .retryIfResult(Optional::isEmpty)
+                .withWaitStrategy(WaitStrategies.fixedWait(10, TimeUnit.SECONDS))
+                .withStopStrategy(StopStrategies.stopAfterAttempt(2))
+                .build();
+        try {
+          scsId = retryer.call(() -> findSourceChangeEventKey(projectName, commitId));
+        } catch (RetryException | ExecutionException e) {
+          logger.atSevere().withCause(e).log(
+              "Failed to find SCS for %s in %s", commitId, projectName);
+          return;
+        }
+      }
+      if (scsId.isEmpty()) {
+        logger.atSevere().log("Could not find SCS for: %s in %s", commitId, projectName);
+        return;
+      }
+      pushToHub(mapper.toCd(projectName, tagName, creationTime, scsId.get()), force);
+    } catch (EiffelEventIdLookupException | InterruptedException e) {
+      logger.atSevere().withCause(e).log(
+          "Event creation failed for: %s",
+          CompositionDefinedEventKey.create(mapper.tagCompositionName(projectName), tagName));
+    }
+  }
+
+  private String peelTag(String projectName, String tagName) {
+    try (Repository repo = repoManager.openRepository(Project.nameKey(projectName))) {
+      Ref tagRef = repo.getRefDatabase().exactRef(Constants.R_TAGS + tagName);
+      if (tagRef != null) {
+        ObjectId peeled = repo.getRefDatabase().peel(tagRef).getPeeledObjectId();
+        return peeled != null ? peeled.getName() : tagRef.getObjectId().getName();
+      }
+      logger.atSevere().log("Cannot find tag: %s:%s", projectName, tagName);
+    } catch (IOException e) {
+      logger.atSevere().withCause(e).log("Unable to peel tag: %s:%s", projectName, tagName);
+    }
+    return null;
+  }
+
+  private Optional<UUID> findSourceChangeEventKey(String projectName, String commitId)
+      throws EiffelEventIdLookupException {
+    List<String> branches;
+    try (Repository repo = repoManager.openRepository(Project.nameKey(projectName))) {
+      branches =
+          repo.getRefDatabase().getRefsByPrefix(RefNames.REFS_HEADS).stream()
+              .map(Ref::getName)
+              .collect(Collectors.toList());
+    } catch (IOException ioe) {
+      logger.atSevere().withCause(ioe).log("Unable to find branches of %s.", projectName);
+      return Optional.empty();
+    }
+    return eventHub.getScsForCommit(projectName, commitId, branches);
+  }
+
+  private void pushToHub(EiffelEvent toPush) throws InterruptedException {
+    pushToHub(toPush, false);
+  }
+
+  private void pushToHub(EiffelEvent toPush, boolean force) throws InterruptedException {
+    int failureCount = 0;
+    EventKey key = EventKey.fromEvent(toPush);
+    while (true) {
+      try {
+        eventHub.push(toPush, force);
+        logger.atFine().log("Successfully pushed %s to EventHub", key);
+        return;
+      } catch (InterruptedException e) {
+        if (!eventHub.isOpen()) {
+          logger.atInfo().log("EventHub is closed, aborting.");
+          throw e;
+        }
+        failureCount++;
+        if (failureCount < NBR_RETRIES) {
+          logger.atWarning().withCause(e).log(
+              "Interrupted while pushing %s to EventHub, attempt %d/%d",
+              key, failureCount, NBR_RETRIES);
+        } else {
+          throw e;
+        }
+      }
+    }
+  }
+
+  @VisibleForTesting
+  void createAndScheduleMissingSccs(SourceChangeEventKey scc)
+      throws MissingObjectException, IncorrectObjectTypeException, IOException,
+          EiffelEventIdLookupException, RepositoryNotFoundException, NoSuchEntityException,
+          ConfigInvalidException, InterruptedException {
+    logger.atFine().log("Start publishing events for: %s", scc);
+    try (UnprocessedCommitsWalker commitFinder = walkerFactory.sccWalker(scc)) {
+      createAndScheduleMissingSccs(scc, commitFinder);
+    }
+    logger.atFine().log("Done publishing events for: %s", scc);
+  }
+
+  /* Callers are responsible for closing commitFinder. */
+  private void createAndScheduleMissingSccs(
+      SourceChangeEventKey scc, UnprocessedCommitsWalker commitFinder)
+      throws MissingObjectException, EiffelEventIdLookupException, IOException,
+          NoSuchEntityException, ConfigInvalidException, InterruptedException {
+    if (!commitFinder.hasNext()) {
+      logger.atInfo().log("All events were already published for %s", scc);
+    } else {
+      logger.atFine().log("Start publishing events for: %s", scc);
+    }
+    while (commitFinder.hasNext()) {
+      EventCreate job = commitFinder.next();
+      logger.atFine().log("Processing event-creation for: %s", job.key);
+      List<UUID> parentIds = Lists.newArrayList();
+      for (RevCommit parent : job.commit.getParents()) {
+        SourceChangeEventKey parentKey = scc.copy(parent.getName());
+        Optional<UUID> parentId = eventHub.getExistingId(parentKey);
+        if (parentId.isPresent()) {
+          parentIds.add(parentId.get());
+        } else {
+          exceptionForMissingParent(job, parent);
+        }
+      }
+      try {
+        pushToHub(mapper.toScc(job.commit, job.key.repo(), job.key.branch(), parentIds));
+      } catch (InterruptedException e) {
+        logger.atSevere().log("Interrupted while pushing %s to EventHub.", job.key);
+        throw e;
+      }
+    }
+  }
+
+  private ObjectId getTipOf(String repoName, String branch) {
+    try (Repository repo = repoManager.openRepository(Project.nameKey(repoName))) {
+      Ref branchRef = repo.exactRef(branch);
+      if (branchRef == null) {
+        logger.atWarning().log("Could not find ref: %s in project: %s", branch, repoName);
+        return null;
+      }
+      return branchRef.getTarget().getObjectId();
+    } catch (IOException ioe) {
+      logger.atSevere().withCause(ioe).log("Unable to identify tip of (%s:%s).", repoName, branch);
+      return null;
+    }
+  }
+
+  private void exceptionForMissingParent(EventCreate create, RevCommit parent)
+      throws NoSuchEntityException {
+    throw new NoSuchEntityException(
+        String.format(
+            "Unable to lookup parent (%s) event UUID for %s even though it should exist.",
+            parent.abbreviate(7).name(), create.key));
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParsingQueue.java b/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParsingQueue.java
index b457b6f..c5d10b8 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParsingQueue.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParsingQueue.java
@@ -36,12 +36,12 @@
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
   private volatile EiffelEventParsingExecutor pool;
-  private final EiffelEventParserIf eventParser;
+  private final EiffelEventParser eventParser;
   private final ConcurrentHashMap<EventParsingWorker, ScheduledFuture<?>> pending =
       new ConcurrentHashMap<>();
 
   @Inject
-  public EiffelEventParsingQueue(EiffelEventParsingExecutor pool, EiffelEventParserIf eventParser) {
+  public EiffelEventParsingQueue(EiffelEventParsingExecutor pool, EiffelEventParser eventParser) {
     this.pool = pool;
     this.eventParser = eventParser;
   }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/eventseiffel/EiffelEventsTestModule.java b/src/test/java/com/googlesource/gerrit/plugins/eventseiffel/EiffelEventsTestModule.java
index 579f3b6..17b5acd 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/eventseiffel/EiffelEventsTestModule.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/eventseiffel/EiffelEventsTestModule.java
@@ -26,7 +26,7 @@
 import com.googlesource.gerrit.plugins.eventseiffel.eiffel.api.EventStorage;
 import com.googlesource.gerrit.plugins.eventseiffel.mapping.EiffelEventFactory;
 import com.googlesource.gerrit.plugins.eventseiffel.parsing.EiffelEventParser;
-import com.googlesource.gerrit.plugins.eventseiffel.parsing.EiffelEventParserIf;
+import com.googlesource.gerrit.plugins.eventseiffel.parsing.EiffelEventParserImpl;
 import com.googlesource.gerrit.plugins.eventseiffel.parsing.EiffelEventParsingExecutor;
 import com.googlesource.gerrit.plugins.eventseiffel.parsing.EiffelEventParsingQueue;
 import org.junit.Ignore;
@@ -44,7 +44,7 @@
     bind(EiffelEventParsingExecutor.class).to(EiffelEventParsingExecutor.Direct.class);
     bind(TestEventPublisher.class).in(Scopes.SINGLETON);
     bind(EiffelEventHub.Consumer.class).to(TestEventPublisher.class);
-    bind(EiffelEventParserIf.class).to(EiffelEventParser.class);
+    bind(EiffelEventParser.class).to(EiffelEventParserImpl.class);
     bind(EiffelEventParsingQueue.class);
     bind(EiffelEventFactory.class).toProvider(TestEventFactoryProvider.class);
 
diff --git a/src/test/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParserIT.java b/src/test/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParserIT.java
index d2d87eb..d20034b 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParserIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParserIT.java
@@ -57,11 +57,11 @@
 public class EiffelEventParserIT extends EiffelEventsTest {
   private static final Long EPOCH_MILLIS = 978307261000l;
 
-  private EiffelEventParser eventParser;
+  private EiffelEventParserImpl eventParser;
 
   @Before
   public void setUp() {
-    eventParser = plugin.getSysInjector().getInstance(EiffelEventParser.class);
+    eventParser = plugin.getSysInjector().getInstance(EiffelEventParserImpl.class);
     TestEventHub.EVENTS.clear();
   }
 
@@ -342,7 +342,7 @@
       bind(EiffelEventHub.class).to(TestEventHub.class);
       bind(EventMappingConfig.class).toProvider(EventMappingConfig.Provider.class);
       bind(EiffelEventMapper.class);
-      bind(EiffelEventParserIf.class).to(EiffelEventParser.class);
+      bind(EiffelEventParser.class).to(EiffelEventParserImpl.class);
       bind(EiffelConfig.class).toProvider(EiffelConfig.Provider.class).in(Scopes.SINGLETON);
       bind(EiffelEventFactory.class)
           .toProvider(EiffelEventFactory.Provider.class)