Replace ExitFunction with BuildEventServiceAbruptExitCallback to properly report transport failures back to the BuildEventServiceModule.

The purpose of deleting this function is twofold:
i) Unify the way we report errors to the main thread since now all the transports
use the same callback.
ii) Separate UI messages from actions that fail the build (ModuleEnvironment.exit).

With these improvements we're one step closer of making the BES module the
sole responsible of posting messages to the CommandEnvironment.EventHandler
(instead of letting the transports or the streamer post messages too which has
caused deadlocks in the past). Additionally, we greatly simplify the handling
of --bes_results_url and improve our UI by printing more clear messages.

PiperOrigin-RevId: 236386523
diff --git a/src/main/java/com/google/devtools/build/lib/buildeventservice/BazelBuildEventServiceModule.java b/src/main/java/com/google/devtools/build/lib/buildeventservice/BazelBuildEventServiceModule.java
index ad9312d..3176da0 100644
--- a/src/main/java/com/google/devtools/build/lib/buildeventservice/BazelBuildEventServiceModule.java
+++ b/src/main/java/com/google/devtools/build/lib/buildeventservice/BazelBuildEventServiceModule.java
@@ -15,6 +15,7 @@
 package com.google.devtools.build.lib.buildeventservice;
 
 import com.google.auto.value.AutoValue;
+import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
 import com.google.devtools.build.lib.authandtls.AuthAndTLSOptions;
 import com.google.devtools.build.lib.authandtls.GoogleAuthUtils;
@@ -87,4 +88,14 @@
   protected Set<String> whitelistedCommands(BuildEventServiceOptions besOptions) {
     return WHITELISTED_COMMANDS;
   }
+
+  @Override
+  protected String getInvocationIdPrefix() {
+    if (Strings.isNullOrEmpty(besOptions.besResultsUrl)) {
+      return "";
+    }
+    return besOptions.besResultsUrl.endsWith("/")
+        ? besOptions.besResultsUrl
+        : besOptions.besResultsUrl + "/";
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/buildeventservice/BuildEventServiceModule.java b/src/main/java/com/google/devtools/build/lib/buildeventservice/BuildEventServiceModule.java
index 7e7e6d4..9dceed4 100644
--- a/src/main/java/com/google/devtools/build/lib/buildeventservice/BuildEventServiceModule.java
+++ b/src/main/java/com/google/devtools/build/lib/buildeventservice/BuildEventServiceModule.java
@@ -25,6 +25,7 @@
 import com.google.devtools.build.lib.buildeventservice.client.BuildEventServiceClient;
 import com.google.devtools.build.lib.buildeventstream.BuildEventArtifactUploader;
 import com.google.devtools.build.lib.buildeventstream.BuildEventProtocolOptions;
+import com.google.devtools.build.lib.buildeventstream.BuildEventServiceAbruptExitCallback;
 import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.Aborted.AbortReason;
 import com.google.devtools.build.lib.buildeventstream.BuildEventTransport;
 import com.google.devtools.build.lib.buildeventstream.LocalFilesArtifactUploader;
@@ -51,7 +52,6 @@
 import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.util.Set;
-import java.util.function.Consumer;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import javax.annotation.Nullable;
@@ -67,15 +67,19 @@
 
   private OutErr outErr;
   private BuildEventStreamer streamer;
-  private BESOptionsT besOptions;
   private BuildEventProtocolOptions bepOptions;
   private AuthAndTLSOptions authTlsOptions;
   private BuildEventStreamOptions besStreamOptions;
   private ImmutableSet<BuildEventTransport> bepTransports;
+  private String invocationId;
+  private EventHandler cmdLineReporter;
 
-  /** Whether an error in the Build Event Service upload causes the build to fail. */
-  protected boolean errorsShouldFailTheBuild() {
-    return true;
+  protected BESOptionsT besOptions;
+
+  /** Callback used by the transports to report errors and possible exit abruptly. */
+  protected BuildEventServiceAbruptExitCallback getAbruptExitCallback(
+      ModuleEnvironment moduleEnvironment) {
+    return moduleEnvironment::exit;
   }
 
   /** Report errors in the command line and possibly fail the build. */
@@ -105,6 +109,8 @@
 
   @Override
   public void beforeCommand(CommandEnvironment cmdEnv) {
+    this.invocationId = cmdEnv.getCommandId().toString();
+    this.cmdLineReporter = cmdEnv.getReporter();
     // Reset to null in case afterCommand was not called.
     // TODO(lpino): Remove this statement once {@link BlazeModule#afterCommmand()} is guaranteed
     // to be executed for every invocation.
@@ -192,6 +198,10 @@
   @Override
   public void afterCommand() {
     if (streamer != null) {
+      if (!Strings.isNullOrEmpty(besOptions.besBackend)) {
+        constructAndMaybeReportInvocationIdUrl();
+      }
+
       if (!streamer.isClosed()) {
         // This should not occur, but close with an internal error if a {@link BuildEventStreamer}
         // bug manifests as an unclosed streamer.
@@ -208,31 +218,24 @@
     }
     this.outErr = null;
     this.bepTransports = null;
+    this.invocationId = null;
+    this.cmdLineReporter = null;
   }
 
-  // TODO(b/115961387): Remove the @Nullable and print one line for the IDs and another optional
-  //  line for the results URL instead. Currently it's not straightforward since
-  //  {@link ExitFunction} (accidentally) depends on the nullability of besResultsUrl.
-  @Nullable
-  private String constructAndReportBesResultsMessage(
-      String invocationId, String buildRequestId, EventHandler reporter) {
-    final String besResultsUrl;
-    if (!Strings.isNullOrEmpty(besOptions.besResultsUrl)) {
-      besResultsUrl =
-          besOptions.besResultsUrl.endsWith("/")
-              ? besOptions.besResultsUrl + invocationId
-              : besOptions.besResultsUrl + "/" + invocationId;
-      reporter.handle(Event.info("Streaming Build Event Protocol to " + besResultsUrl));
-    } else {
-      besResultsUrl = null;
-      reporter.handle(
-          Event.info(
-              String.format(
-                  "Streaming Build Event Protocol to %s build_request_id: %s "
-                      + "invocation_id: %s",
-                  besOptions.besBackend, buildRequestId, invocationId)));
+  private void constructAndMaybeReportInvocationIdUrl() {
+    if (!getInvocationIdPrefix().isEmpty()) {
+      cmdLineReporter.handle(
+          Event.info("Streaming build results to: " + getInvocationIdPrefix() + invocationId));
     }
-    return besResultsUrl;
+  }
+
+  private void constructAndReportIds(String buildRequestId) {
+    cmdLineReporter.handle(
+        Event.info(
+            String.format(
+                "Streaming Build Event Protocol to '%s' with build_request_id: '%s'"
+                    + " and invocation_id: '%s'",
+                besOptions.besBackend, buildRequestId, invocationId)));
   }
 
   @Nullable
@@ -245,9 +248,7 @@
       return null;
     }
 
-    String besResultsUrl =
-        constructAndReportBesResultsMessage(
-            cmdEnv.getCommandId().toString(), cmdEnv.getBuildRequestId(), cmdEnv.getReporter());
+    constructAndReportIds(cmdEnv.getBuildRequestId());
 
     final BuildEventServiceClient besClient;
     try {
@@ -279,12 +280,7 @@
         .artifactGroupNamer(artifactGroupNamer)
         .bepOptions(bepOptions)
         .clock(cmdEnv.getRuntime().getClock())
-        .exitFunction(
-            ExitFunction.standardExitFunction(
-                cmdEnv.getReporter(),
-                cmdEnv.getBlazeModuleEnvironment(),
-                besResultsUrl,
-                errorsShouldFailTheBuild()))
+        .abruptExitCallback(getAbruptExitCallback(cmdEnv.getBlazeModuleEnvironment()))
         .eventBus(cmdEnv.getEventBus())
         .build();
   }
@@ -294,7 +290,6 @@
       Supplier<BuildEventArtifactUploader> uploaderSupplier,
       CountingArtifactGroupNamer artifactGroupNamer) {
     ImmutableSet.Builder<BuildEventTransport> bepTransportsBuilder = new ImmutableSet.Builder<>();
-    Consumer<AbruptExitException> abruptExitCallback = cmdEnv.getBlazeModuleEnvironment()::exit;
 
     if (!Strings.isNullOrEmpty(besStreamOptions.buildEventTextFile)) {
       try {
@@ -311,7 +306,7 @@
                 bepTextOutputStream,
                 bepOptions,
                 localFileUploader,
-                abruptExitCallback,
+                getAbruptExitCallback(cmdEnv.getBlazeModuleEnvironment()),
                 artifactGroupNamer));
       } catch (IOException exception) {
         // TODO(b/125216340): Consider making this a warning instead of an error once the
@@ -342,7 +337,7 @@
                 bepBinaryOutputStream,
                 bepOptions,
                 localFileUploader,
-                abruptExitCallback,
+                getAbruptExitCallback(cmdEnv.getBlazeModuleEnvironment()),
                 artifactGroupNamer));
       } catch (IOException exception) {
         // TODO(b/125216340): Consider making this a warning instead of an error once the
@@ -372,7 +367,7 @@
                 bepJsonOutputStream,
                 bepOptions,
                 localFileUploader,
-                abruptExitCallback,
+                getAbruptExitCallback(cmdEnv.getBlazeModuleEnvironment()),
                 artifactGroupNamer));
       } catch (IOException exception) {
         // TODO(b/125216340): Consider making this a warning instead of an error once the
@@ -391,6 +386,7 @@
     BuildEventServiceTransport besTransport =
         createBesTransport(cmdEnv, uploaderSupplier, artifactGroupNamer);
     if (besTransport != null) {
+      constructAndMaybeReportInvocationIdUrl();
       bepTransportsBuilder.add(besTransport);
     }
 
@@ -414,6 +410,9 @@
         .collect(ImmutableSet.toImmutableSet());
   }
 
+  /** A prefix used when printing the invocation ID in the command line */
+  protected abstract String getInvocationIdPrefix();
+
   // TODO(b/115961387): This method shouldn't exist. It only does because some tests are relying on
   //  the transport creation logic of this module directly.
   @VisibleForTesting
diff --git a/src/main/java/com/google/devtools/build/lib/buildeventservice/BuildEventServiceTransport.java b/src/main/java/com/google/devtools/build/lib/buildeventservice/BuildEventServiceTransport.java
index 7c684de..9874a74 100644
--- a/src/main/java/com/google/devtools/build/lib/buildeventservice/BuildEventServiceTransport.java
+++ b/src/main/java/com/google/devtools/build/lib/buildeventservice/BuildEventServiceTransport.java
@@ -26,8 +26,10 @@
 import com.google.devtools.build.lib.buildeventstream.BuildEvent;
 import com.google.devtools.build.lib.buildeventstream.BuildEventArtifactUploader;
 import com.google.devtools.build.lib.buildeventstream.BuildEventProtocolOptions;
+import com.google.devtools.build.lib.buildeventstream.BuildEventServiceAbruptExitCallback;
 import com.google.devtools.build.lib.buildeventstream.BuildEventTransport;
 import com.google.devtools.build.lib.clock.Clock;
+import com.google.devtools.build.lib.runtime.BlazeModule.ModuleEnvironment;
 import com.google.devtools.build.lib.util.JavaSleeper;
 import com.google.devtools.build.lib.util.Sleeper;
 import java.time.Duration;
@@ -36,6 +38,7 @@
 /** A {@link BuildEventTransport} that streams {@link BuildEvent}s to BuildEventService. */
 public class BuildEventServiceTransport implements BuildEventTransport {
   private final BuildEventServiceUploader besUploader;
+  private ModuleEnvironment moduleEnvironment;
 
   private BuildEventServiceTransport(
       BuildEventServiceClient besClient,
@@ -43,7 +46,7 @@
       BuildEventProtocolOptions bepOptions,
       BuildEventServiceProtoUtil besProtoUtil,
       Clock clock,
-      ExitFunction exitFunc,
+      BuildEventServiceAbruptExitCallback abruptExitCallback,
       boolean publishLifecycleEvents,
       ArtifactGroupNamer artifactGroupNamer,
       EventBus eventBus,
@@ -56,7 +59,7 @@
             .bepOptions(bepOptions)
             .besProtoUtil(besProtoUtil)
             .clock(clock)
-            .exitFunc(exitFunc)
+            .abruptExitCallback(abruptExitCallback)
             .publishLifecycleEvents(publishLifecycleEvents)
             .sleeper(sleeper)
             .artifactGroupNamer(artifactGroupNamer)
@@ -105,7 +108,7 @@
     private ArtifactGroupNamer artifactGroupNamer;
     private BuildEventServiceProtoUtil besProtoUtil;
     private EventBus eventBus;
-    private ExitFunction exitFunction;
+    private BuildEventServiceAbruptExitCallback abruptExitCallback;
     private @Nullable Sleeper sleeper;
 
     public Builder besClient(BuildEventServiceClient value) {
@@ -148,8 +151,8 @@
       return this;
     }
 
-    public Builder exitFunction(ExitFunction value) {
-      this.exitFunction = value;
+    public Builder abruptExitCallback(BuildEventServiceAbruptExitCallback value) {
+      this.abruptExitCallback = value;
       return this;
     }
 
@@ -167,7 +170,7 @@
           checkNotNull(bepOptions),
           checkNotNull(besProtoUtil),
           checkNotNull(clock),
-          checkNotNull(exitFunction),
+          checkNotNull(abruptExitCallback),
           besOptions.besLifecycleEvents,
           checkNotNull(artifactGroupNamer),
           checkNotNull(eventBus),
diff --git a/src/main/java/com/google/devtools/build/lib/buildeventservice/BuildEventServiceUploader.java b/src/main/java/com/google/devtools/build/lib/buildeventservice/BuildEventServiceUploader.java
index 3c48db7f..d6fa826 100644
--- a/src/main/java/com/google/devtools/build/lib/buildeventservice/BuildEventServiceUploader.java
+++ b/src/main/java/com/google/devtools/build/lib/buildeventservice/BuildEventServiceUploader.java
@@ -46,10 +46,12 @@
 import com.google.devtools.build.lib.buildeventstream.BuildEventArtifactUploader;
 import com.google.devtools.build.lib.buildeventstream.BuildEventContext;
 import com.google.devtools.build.lib.buildeventstream.BuildEventProtocolOptions;
+import com.google.devtools.build.lib.buildeventstream.BuildEventServiceAbruptExitCallback;
 import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos;
 import com.google.devtools.build.lib.buildeventstream.LargeBuildEventSerializedEvent;
 import com.google.devtools.build.lib.buildeventstream.PathConverter;
 import com.google.devtools.build.lib.clock.Clock;
+import com.google.devtools.build.lib.util.AbruptExitException;
 import com.google.devtools.build.lib.util.ExitCode;
 import com.google.devtools.build.lib.util.LoggingUtil;
 import com.google.devtools.build.lib.util.Sleeper;
@@ -103,7 +105,7 @@
   private final BuildEventProtocolOptions buildEventProtocolOptions;
   private final boolean publishLifecycleEvents;
   private final Duration closeTimeout;
-  private final ExitFunction exitFunc;
+  private final BuildEventServiceAbruptExitCallback abruptExitCallback;
   private final Sleeper sleeper;
   private final Clock clock;
   private final ArtifactGroupNamer namer;
@@ -155,7 +157,7 @@
       BuildEventProtocolOptions buildEventProtocolOptions,
       boolean publishLifecycleEvents,
       Duration closeTimeout,
-      ExitFunction exitFunc,
+      BuildEventServiceAbruptExitCallback abruptExitCallback,
       Sleeper sleeper,
       Clock clock,
       ArtifactGroupNamer namer,
@@ -166,7 +168,7 @@
     this.buildEventProtocolOptions = buildEventProtocolOptions;
     this.publishLifecycleEvents = publishLifecycleEvents;
     this.closeTimeout = closeTimeout;
-    this.exitFunc = exitFunc;
+    this.abruptExitCallback = abruptExitCallback;
     this.sleeper = sleeper;
     this.clock = clock;
     this.namer = namer;
@@ -251,6 +253,12 @@
     }
   }
 
+  private void logAndExitAbruptly(String message, ExitCode exitCode, Throwable cause) {
+    checkState(exitCode != ExitCode.SUCCESS);
+    logger.info(message);
+    abruptExitCallback.accept(new AbruptExitException(message, exitCode, cause));
+  }
+
   @Override
   public void run() {
     try {
@@ -271,10 +279,6 @@
           publishLifecycleEvent(besProtoUtil.buildFinished(currentTime(), buildStatus));
         }
       }
-      exitFunc.accept(
-          "The Build Event Protocol upload finished successfully",
-          /*cause=*/ null,
-          ExitCode.SUCCESS);
       synchronized (lock) {
         // Invariant: closeFuture is not null.
         // publishBuildEvents() only terminates successfully after SendLastBuildEventCommand
@@ -288,10 +292,10 @@
         synchronized (lock) {
           Preconditions.checkState(
               interruptCausedByTimeout, "Unexpected interrupt on BES uploader thread");
-          exitFunc.accept(
+          logAndExitAbruptly(
               "The Build Event Protocol upload timed out",
-              e,
-              ExitCode.TRANSIENT_BUILD_EVENT_SERVICE_UPLOAD_ERROR);
+              ExitCode.TRANSIENT_BUILD_EVENT_SERVICE_UPLOAD_ERROR,
+              e);
         }
       } finally {
         // TODO(buchgr): Due to b/113035235 exitFunc needs to be called before the close future
@@ -300,24 +304,22 @@
       }
     } catch (StatusException e) {
       try {
-        String message =
-            "The Build Event Protocol upload failed: " + besClient.userReadableError(e);
-        logger.info(message);
-        ExitCode code =
+        logAndExitAbruptly(
+            "The Build Event Protocol upload failed: " + besClient.userReadableError(e),
             shouldRetryStatus(e.getStatus())
                 ? ExitCode.TRANSIENT_BUILD_EVENT_SERVICE_UPLOAD_ERROR
-                : ExitCode.PERSISTENT_BUILD_EVENT_SERVICE_UPLOAD_ERROR;
-        exitFunc.accept(message, e, code);
+                : ExitCode.PERSISTENT_BUILD_EVENT_SERVICE_UPLOAD_ERROR,
+            e);
       } finally {
         failCloseFuture(e);
       }
     } catch (LocalFileUploadException e) {
       Throwables.throwIfUnchecked(e.getCause());
       try {
-        String message =
-            "The Build Event Protocol local file upload failed: " + e.getCause().getMessage();
-        logger.info(message);
-        exitFunc.accept(message, e.getCause(), ExitCode.TRANSIENT_BUILD_EVENT_SERVICE_UPLOAD_ERROR);
+        logAndExitAbruptly(
+            "The Build Event Protocol local file upload failed: " + e.getCause().getMessage(),
+            ExitCode.TRANSIENT_BUILD_EVENT_SERVICE_UPLOAD_ERROR,
+            e.getCause());
       } finally {
         failCloseFuture(e.getCause());
       }
@@ -758,7 +760,7 @@
     private BuildEventProtocolOptions bepOptions;
     private boolean publishLifecycleEvents;
     private Duration closeTimeout;
-    private ExitFunction exitFunc;
+    private BuildEventServiceAbruptExitCallback abruptExitCallback;
     private Sleeper sleeper;
     private Clock clock;
     private ArtifactGroupNamer artifactGroupNamer;
@@ -799,8 +801,8 @@
       return this;
     }
 
-    Builder exitFunc(ExitFunction value) {
-      this.exitFunc = value;
+    Builder abruptExitCallback(BuildEventServiceAbruptExitCallback value) {
+      this.abruptExitCallback = value;
       return this;
     }
 
@@ -827,7 +829,7 @@
           checkNotNull(bepOptions),
           publishLifecycleEvents,
           checkNotNull(closeTimeout),
-          checkNotNull(exitFunc),
+          checkNotNull(abruptExitCallback),
           checkNotNull(sleeper),
           checkNotNull(clock),
           checkNotNull(artifactGroupNamer),
diff --git a/src/main/java/com/google/devtools/build/lib/buildeventservice/ExitFunction.java b/src/main/java/com/google/devtools/build/lib/buildeventservice/ExitFunction.java
deleted file mode 100644
index 01ea842..0000000
--- a/src/main/java/com/google/devtools/build/lib/buildeventservice/ExitFunction.java
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2019 The Bazel Authors. All rights reserved.
-//
-// 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.google.devtools.build.lib.buildeventservice;
-
-import com.google.common.base.Preconditions;
-import com.google.common.base.Strings;
-import com.google.devtools.build.lib.buildeventstream.BuildEventTransport;
-import com.google.devtools.build.lib.events.Event;
-import com.google.devtools.build.lib.events.EventHandler;
-import com.google.devtools.build.lib.runtime.BlazeModule.ModuleEnvironment;
-import com.google.devtools.build.lib.util.AbruptExitException;
-import com.google.devtools.build.lib.util.ExitCode;
-import javax.annotation.Nullable;
-
-/**
- * Function used by a {@link BuildEventTransport} to report errors using the provided {@link
- * EventHandler} and maybe exit abruptly.
- */
-@FunctionalInterface
-public interface ExitFunction {
-  void accept(String message, Throwable cause, ExitCode code);
-
-  // TODO(lpino): The error handling should in the BES module but we can't do that yet because
-  // we use the exit function inside the {@link BuildEventServiceUploader}.
-  static ExitFunction standardExitFunction(
-      EventHandler commandLineReporter,
-      ModuleEnvironment moduleEnvironment,
-      @Nullable String besResultsUrl,
-      boolean errorsFailBuild) {
-    return (String message, Throwable cause, ExitCode exitCode) -> {
-      if (exitCode == ExitCode.SUCCESS) {
-        Preconditions.checkState(cause == null, cause);
-        commandLineReporter.handle(Event.info("Build Event Protocol upload finished successfully"));
-        if (besResultsUrl != null) {
-          commandLineReporter.handle(
-              Event.info("Build Event Protocol results available at " + besResultsUrl));
-        }
-      } else {
-        Preconditions.checkState(cause != null, cause);
-        if (errorsFailBuild) {
-          commandLineReporter.handle(Event.error(message));
-          moduleEnvironment.exit(new AbruptExitException(exitCode, cause));
-        } else {
-          commandLineReporter.handle(Event.warn(message));
-        }
-        if (besResultsUrl != null) {
-          if (!Strings.isNullOrEmpty(besResultsUrl)) {
-            commandLineReporter.handle(
-                Event.info(
-                    "Partial Build Event Protocol results may be available at " + besResultsUrl));
-          }
-        }
-      }
-    };
-  }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/buildeventstream/BuildEventServiceAbruptExitCallback.java b/src/main/java/com/google/devtools/build/lib/buildeventstream/BuildEventServiceAbruptExitCallback.java
new file mode 100644
index 0000000..50c7977
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/buildeventstream/BuildEventServiceAbruptExitCallback.java
@@ -0,0 +1,29 @@
+// Copyright 2019 The Bazel Authors. All rights reserved.
+//
+// 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.google.devtools.build.lib.buildeventstream;
+
+import com.google.devtools.build.lib.util.AbruptExitException;
+import java.util.function.Consumer;
+
+/**
+ * A callback function that the Build Event Service transports can use to notify of an {@link
+ * AbruptExitException} to the main thread which may exit the build abruptly.
+ */
+// TODO(lpino): Delete this callback once all the transports can depend directly on
+//  {@link ModuleEnvironment}.
+public interface BuildEventServiceAbruptExitCallback extends Consumer<AbruptExitException> {
+  /** Executes the callback using the provided {@link AbruptExitException}. */
+  @Override
+  void accept(AbruptExitException e);
+}
diff --git a/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/BinaryFormatFileTransport.java b/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/BinaryFormatFileTransport.java
index ce22b5b..021c55b 100644
--- a/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/BinaryFormatFileTransport.java
+++ b/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/BinaryFormatFileTransport.java
@@ -18,14 +18,13 @@
 import com.google.devtools.build.lib.buildeventstream.BuildEvent;
 import com.google.devtools.build.lib.buildeventstream.BuildEventArtifactUploader;
 import com.google.devtools.build.lib.buildeventstream.BuildEventProtocolOptions;
+import com.google.devtools.build.lib.buildeventstream.BuildEventServiceAbruptExitCallback;
 import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos;
 import com.google.devtools.build.lib.buildeventstream.BuildEventTransport;
-import com.google.devtools.build.lib.util.AbruptExitException;
 import com.google.protobuf.CodedOutputStream;
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.util.function.Consumer;
 
 /**
  * A simple {@link BuildEventTransport} that writes a varint delimited binary representation of
@@ -36,7 +35,7 @@
       BufferedOutputStream outputStream,
       BuildEventProtocolOptions options,
       BuildEventArtifactUploader uploader,
-      Consumer<AbruptExitException> abruptExitCallback,
+      BuildEventServiceAbruptExitCallback abruptExitCallback,
       ArtifactGroupNamer namer) {
     super(outputStream, options, uploader, abruptExitCallback, namer);
   }
diff --git a/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/FileTransport.java b/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/FileTransport.java
index c47427d..c42d3e6 100644
--- a/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/FileTransport.java
+++ b/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/FileTransport.java
@@ -29,6 +29,7 @@
 import com.google.devtools.build.lib.buildeventstream.BuildEventArtifactUploader;
 import com.google.devtools.build.lib.buildeventstream.BuildEventContext;
 import com.google.devtools.build.lib.buildeventstream.BuildEventProtocolOptions;
+import com.google.devtools.build.lib.buildeventstream.BuildEventServiceAbruptExitCallback;
 import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos;
 import com.google.devtools.build.lib.buildeventstream.BuildEventTransport;
 import com.google.devtools.build.lib.buildeventstream.PathConverter;
@@ -71,7 +72,7 @@
       BufferedOutputStream outputStream,
       BuildEventProtocolOptions options,
       BuildEventArtifactUploader uploader,
-      Consumer<AbruptExitException> abruptExitCallback,
+      BuildEventServiceAbruptExitCallback abruptExitCallback,
       ArtifactGroupNamer namer) {
     this.uploader = uploader;
     this.options = options;
@@ -105,7 +106,7 @@
     SequentialWriter(
         BufferedOutputStream outputStream,
         Function<BuildEventStreamProtos.BuildEvent, byte[]> serializeFunc,
-        Consumer<AbruptExitException> abruptExitCallback,
+        BuildEventServiceAbruptExitCallback abruptExitCallback,
         BuildEventArtifactUploader uploader) {
       checkNotNull(outputStream);
       checkNotNull(serializeFunc);
diff --git a/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/JsonFormatFileTransport.java b/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/JsonFormatFileTransport.java
index 67ae8b4..e0fb735 100644
--- a/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/JsonFormatFileTransport.java
+++ b/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/JsonFormatFileTransport.java
@@ -18,13 +18,12 @@
 import com.google.devtools.build.lib.buildeventstream.ArtifactGroupNamer;
 import com.google.devtools.build.lib.buildeventstream.BuildEventArtifactUploader;
 import com.google.devtools.build.lib.buildeventstream.BuildEventProtocolOptions;
+import com.google.devtools.build.lib.buildeventstream.BuildEventServiceAbruptExitCallback;
 import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos;
 import com.google.devtools.build.lib.buildeventstream.BuildEventTransport;
-import com.google.devtools.build.lib.util.AbruptExitException;
 import com.google.protobuf.InvalidProtocolBufferException;
 import com.google.protobuf.util.JsonFormat;
 import java.io.BufferedOutputStream;
-import java.util.function.Consumer;
 
 /**
  * A simple {@link BuildEventTransport} that writes the JSON representation of the protocol-buffer
@@ -35,7 +34,7 @@
       BufferedOutputStream outputStream,
       BuildEventProtocolOptions options,
       BuildEventArtifactUploader uploader,
-      Consumer<AbruptExitException> abruptExitCallback,
+      BuildEventServiceAbruptExitCallback abruptExitCallback,
       ArtifactGroupNamer namer) {
     super(outputStream, options, uploader, abruptExitCallback, namer);
   }
diff --git a/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/TextFormatFileTransport.java b/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/TextFormatFileTransport.java
index f43c291..1cb3faa 100644
--- a/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/TextFormatFileTransport.java
+++ b/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/TextFormatFileTransport.java
@@ -18,12 +18,11 @@
 import com.google.devtools.build.lib.buildeventstream.ArtifactGroupNamer;
 import com.google.devtools.build.lib.buildeventstream.BuildEventArtifactUploader;
 import com.google.devtools.build.lib.buildeventstream.BuildEventProtocolOptions;
+import com.google.devtools.build.lib.buildeventstream.BuildEventServiceAbruptExitCallback;
 import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos;
 import com.google.devtools.build.lib.buildeventstream.BuildEventTransport;
-import com.google.devtools.build.lib.util.AbruptExitException;
 import com.google.protobuf.TextFormat;
 import java.io.BufferedOutputStream;
-import java.util.function.Consumer;
 
 /**
  * A simple {@link BuildEventTransport} that writes the text representation of the protocol-buffer
@@ -36,7 +35,7 @@
       BufferedOutputStream outputStream,
       BuildEventProtocolOptions options,
       BuildEventArtifactUploader uploader,
-      Consumer<AbruptExitException> abruptExitCallback,
+      BuildEventServiceAbruptExitCallback abruptExitCallback,
       ArtifactGroupNamer namer) {
     super(outputStream, options, uploader, abruptExitCallback, namer);
   }
diff --git a/src/test/java/com/google/devtools/build/lib/buildeventstream/transports/BinaryFormatFileTransportTest.java b/src/test/java/com/google/devtools/build/lib/buildeventstream/transports/BinaryFormatFileTransportTest.java
index d2da340..082a0ac 100644
--- a/src/test/java/com/google/devtools/build/lib/buildeventstream/transports/BinaryFormatFileTransportTest.java
+++ b/src/test/java/com/google/devtools/build/lib/buildeventstream/transports/BinaryFormatFileTransportTest.java
@@ -32,6 +32,7 @@
 import com.google.devtools.build.lib.buildeventstream.BuildEventContext;
 import com.google.devtools.build.lib.buildeventstream.BuildEventId;
 import com.google.devtools.build.lib.buildeventstream.BuildEventProtocolOptions;
+import com.google.devtools.build.lib.buildeventstream.BuildEventServiceAbruptExitCallback;
 import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos;
 import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.BuildStarted;
 import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.Progress;
@@ -69,6 +70,7 @@
 public class BinaryFormatFileTransportTest {
   private final BuildEventProtocolOptions defaultOpts =
       Options.getDefaults(BuildEventProtocolOptions.class);
+  private static final BuildEventServiceAbruptExitCallback NO_OP_EXIT_CALLBACK = (e) -> {};
 
   @Rule public TemporaryFolder tmp = new TemporaryFolder();
 
@@ -102,7 +104,7 @@
             outputStream,
             defaultOpts,
             new LocalFilesArtifactUploader(),
-            (e) -> {},
+            NO_OP_EXIT_CALLBACK,
             artifactGroupNamer);
     transport.sendBuildEvent(buildEvent);
 
@@ -150,7 +152,7 @@
         new BufferedOutputStream(Files.newOutputStream(Paths.get(output.getAbsolutePath())));
     BinaryFormatFileTransport transport =
         new BinaryFormatFileTransport(
-            outputStream, defaultOpts, uploader, (e) -> {}, artifactGroupNamer);
+            outputStream, defaultOpts, uploader, NO_OP_EXIT_CALLBACK, artifactGroupNamer);
     transport.sendBuildEvent(event1);
     transport.close().get();
 
@@ -178,7 +180,7 @@
             outputStream,
             defaultOpts,
             new LocalFilesArtifactUploader(),
-            (e) -> {},
+            NO_OP_EXIT_CALLBACK,
             artifactGroupNamer);
 
     // Close the stream.
@@ -212,7 +214,7 @@
             outputStream,
             defaultOpts,
             new LocalFilesArtifactUploader(),
-            (e) -> {},
+            NO_OP_EXIT_CALLBACK,
             artifactGroupNamer);
 
     transport.sendBuildEvent(buildEvent);
@@ -261,7 +263,7 @@
         new BufferedOutputStream(Files.newOutputStream(Paths.get(output.getAbsolutePath())));
     BinaryFormatFileTransport transport =
         new BinaryFormatFileTransport(
-            outputStream, defaultOpts, uploader, (e) -> {}, artifactGroupNamer);
+            outputStream, defaultOpts, uploader, NO_OP_EXIT_CALLBACK, artifactGroupNamer);
     transport.sendBuildEvent(event1);
     transport.sendBuildEvent(event2);
     transport.close().get();
@@ -304,7 +306,7 @@
         new BufferedOutputStream(Files.newOutputStream(Paths.get(output.getAbsolutePath())));
     BinaryFormatFileTransport transport =
         new BinaryFormatFileTransport(
-            outputStream, defaultOpts, uploader, (e) -> {}, artifactGroupNamer);
+            outputStream, defaultOpts, uploader, NO_OP_EXIT_CALLBACK, artifactGroupNamer);
     transport.sendBuildEvent(event1);
     transport.close().get();
 
@@ -342,7 +344,7 @@
         new BufferedOutputStream(Files.newOutputStream(Paths.get(output.getAbsolutePath())));
     BinaryFormatFileTransport transport =
         new BinaryFormatFileTransport(
-            outputStream, defaultOpts, uploader, (e) -> {}, artifactGroupNamer);
+            outputStream, defaultOpts, uploader, NO_OP_EXIT_CALLBACK, artifactGroupNamer);
     transport.sendBuildEvent(event);
     ListenableFuture<Void> closeFuture = transport.close();
 
diff --git a/src/test/java/com/google/devtools/build/lib/buildeventstream/transports/JsonFormatFileTransportTest.java b/src/test/java/com/google/devtools/build/lib/buildeventstream/transports/JsonFormatFileTransportTest.java
index 1a20c0d..1bb0405 100644
--- a/src/test/java/com/google/devtools/build/lib/buildeventstream/transports/JsonFormatFileTransportTest.java
+++ b/src/test/java/com/google/devtools/build/lib/buildeventstream/transports/JsonFormatFileTransportTest.java
@@ -21,6 +21,7 @@
 import com.google.devtools.build.lib.buildeventstream.BuildEvent;
 import com.google.devtools.build.lib.buildeventstream.BuildEventContext;
 import com.google.devtools.build.lib.buildeventstream.BuildEventProtocolOptions;
+import com.google.devtools.build.lib.buildeventstream.BuildEventServiceAbruptExitCallback;
 import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos;
 import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.BuildStarted;
 import com.google.devtools.build.lib.buildeventstream.LocalFilesArtifactUploader;
@@ -53,6 +54,7 @@
 public class JsonFormatFileTransportTest {
   private final BuildEventProtocolOptions defaultOpts =
       Options.getDefaults(BuildEventProtocolOptions.class);
+  private static final BuildEventServiceAbruptExitCallback NO_OP_EXIT_CALLBACK = (e) -> {};
 
   @Rule public TemporaryFolder tmp = new TemporaryFolder();
 
@@ -87,7 +89,7 @@
             outputStream,
             defaultOpts,
             new LocalFilesArtifactUploader(),
-            (e) -> {},
+            NO_OP_EXIT_CALLBACK,
             artifactGroupNamer);
     transport.sendBuildEvent(buildEvent);
 
@@ -162,7 +164,7 @@
             outputStream,
             defaultOpts,
             new LocalFilesArtifactUploader(),
-            (e) -> {},
+            NO_OP_EXIT_CALLBACK,
             artifactGroupNamer);
     WrappedOutputStream out = new WrappedOutputStream(transport.writer.out);
     transport.writer.out = out;
diff --git a/src/test/java/com/google/devtools/build/lib/buildeventstream/transports/TextFormatFileTransportTest.java b/src/test/java/com/google/devtools/build/lib/buildeventstream/transports/TextFormatFileTransportTest.java
index 0cef8d9..966505b 100644
--- a/src/test/java/com/google/devtools/build/lib/buildeventstream/transports/TextFormatFileTransportTest.java
+++ b/src/test/java/com/google/devtools/build/lib/buildeventstream/transports/TextFormatFileTransportTest.java
@@ -23,6 +23,7 @@
 import com.google.devtools.build.lib.buildeventstream.BuildEvent;
 import com.google.devtools.build.lib.buildeventstream.BuildEventContext;
 import com.google.devtools.build.lib.buildeventstream.BuildEventProtocolOptions;
+import com.google.devtools.build.lib.buildeventstream.BuildEventServiceAbruptExitCallback;
 import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos;
 import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.BuildStarted;
 import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.Progress;
@@ -53,6 +54,7 @@
 public class TextFormatFileTransportTest {
   private final BuildEventProtocolOptions defaultOpts =
       Options.getDefaults(BuildEventProtocolOptions.class);
+  private static final BuildEventServiceAbruptExitCallback NO_OP_EXIT_CALLBACK = (e) -> {};
 
   @Rule public TemporaryFolder tmp = new TemporaryFolder();
 
@@ -88,7 +90,7 @@
             outputStream,
             defaultOpts,
             new LocalFilesArtifactUploader(),
-            (e) -> {},
+            NO_OP_EXIT_CALLBACK,
             artifactGroupNamer);
     transport.sendBuildEvent(buildEvent);