java.lang.Object
io.github.flink.gcp.connector.base.lifecycle.Closers

@Internal public final class Closers extends Object
Releasing several resources at once, so that one refusing to close never strands the rest.
  • Method Details

    • closeAll

      public static void closeAll(Iterable<? extends AutoCloseable> closeables) throws Exception
      Closes every given resource, then reports the first failure with any later one suppressed onto it.

      null entries are skipped, so a caller can pass a local it had not reached yet. A null collection is not, unlike IOUtils.closeAll — nothing here can produce one, and a caller that does has a bug worth surfacing where it happens rather than silently closing nothing. A resource is never left open because an earlier one refused to close — the loop finishes before anything is thrown, which is what lets every close() in this repository put its failure handler last and still have it closed.

      The reported failure keeps its own type, and that is the whole reason this loop is written out rather than delegated to Flink's IOUtils.closeAll(Iterable, Class). That method rethrows from inside its loop anything the given class does not cover, so Exception.class — what its one-argument form passes — abandons every later resource on an Error, which is the bug #276 fixed at nine call sites. Its Throwable.class form closes everything, but collects a non-Exception as new Exception(e): Flink's Task.preProcessException tests the throwable itself and halts the JVM on isJvmFatalError(t) || t instanceof OutOfMemoryError, so a wrapped OutOfMemoryError from a teardown would fail the task into a restart loop instead of taking the TaskManager down. ExceptionUtils.rethrowException(Throwable) throws an Error as an Error.

      Parameters:
      closeables - the resources to release, in order; entries may be null
      Throws:
      Exception - the first close failure, or an Error thrown as itself
    • closeAll

      public static void closeAll(AutoCloseable... closeables) throws Exception
      Closes every given resource, then reports the first failure with any later one suppressed onto it.
      Parameters:
      closeables - the resources to release, in order; entries may be null
      Throws:
      Exception - the first close failure, or an Error thrown as itself
      See Also:
    • closeAllSuppressing

      public static void closeAllSuppressing(Throwable failure, @Nullable AutoCloseable first, AutoCloseable... rest)
      Closes every given resource, reporting any close failure as a suppressed exception on the failure already in flight rather than in place of it.

      The caller keeps its own exception and rethrows it: what went wrong is why the resources are being released, so it must not be replaced by whatever a close then did. null entries are skipped, so a caller can pass a local it had not reached yet.

      This is the one thing closeAll(Iterable) does not do: it throws its collected failure, which is right when closing is the operation and wrong when something else already failed. Catching Throwable is what makes this complete — closeAll rethrows an Error as an Error, and one escaping here would skip the caller's rethrow and replace its failure, the leak this class exists to prevent reached through it.

      A JVM-fatal close failure is the one exception, and it takes failure's place with failure suppressed onto it. Flink's Task.preProcessException inspects only the throwable it is handed, so one arriving as a suppressed entry is one nothing halts on — and for an OutOfMemoryError that silently overrides the operator's taskmanager.jvm-exit-on-oom, which is the same shape of defect as #276 itself. The set is Flink's own ExceptionUtils.isJvmFatalOrOutOfMemoryError(Throwable), so it is narrow: NoClassDefFoundError — the realistic first-classload failure — is not in it and is suppressed like anything else. Escalation happens after the loop has finished, so every resource is still closed first.

      The bound, stated because it is known rather than overlooked: what is inspected is the throwable closeAll(Iterable) reports, which is the first close failure. Two closes failing where only the second is fatal leaves that one suppressed and unescalated. Reordering which failure is reported to chase it would cost closeAll its "the first failure wins" contract for a doubly-rare case, and Flink inspects only top-level throwables everywhere anyway.

      Two or more close failures arrive nested rather than as siblings — the second is suppressed onto the first, which is suppressed onto failure. Nothing is lost; printing a stack trace walks suppressed exceptions recursively.

      Parameters:
      failure - the exception the caller is about to rethrow
      first - the first resource to release; may be null
      rest - any further resources, released in order; entries may be null