docs: @Override
      public ScanResult call() throws InterruptedException, CancellationException, ExecutionException {
          ScanResult scanResult = null;
          Exception exception = null;
          final long scanStart = System.currentTimeMillis();
          try {
              // Perform the scan
              scanResult = openClasspathElementsThenScan();
  
              // Log total time after scan completes, and flush log
              if (topLevelLog != null) {
                  topLevelLog.log("~",
                          String.format("Total time: %.3f sec", (System.currentTimeMillis() - scanStart) * .001));
                  topLevelLog.flush();
              }
  
              // Call the ScanResultProcessor, if one was provided
              if (scanResultProcessor != null) {
                  try {
                      scanResultProcessor.processScanResult(scanResult);
                  } finally {
                      scanResult.close();
                  }
              }
  
          } catch (final InterruptedException e) {
              if (topLevelLog != null) {
                  topLevelLog.log("~", "Scan interrupted");
              }
              exception = e;
              interruptionChecker.interrupt();
              if (failureHandler == null) {
                  // Re-throw
                  throw e;
              }
          } catch (final CancellationException e) {
              if (topLevelLog != null) {
                  topLevelLog.log("~", "Scan cancelled");
              }
              exception = e;
              if (failureHandler == null) {
                  // Re-throw
                  throw e;
              }
          } catch (final ExecutionException e) {
              if (topLevelLog != null) {
                  topLevelLog.log("~", "Uncaught exception during scan", InterruptionChecker.getCause(e));
              }
              exception = e;
              if (failureHandler == null) {
                  // Re-throw
                  throw e;
              }
          } catch (final RuntimeException e) {
              if (topLevelLog != null) {
                  topLevelLog.log("~", "Uncaught exception during scan", e);
              }
              exception = e;
              if (failureHandler == null) {
                  // Wrap unchecked exceptions in a new ExecutionException
                  throw new ExecutionException("Exception while scanning", e);
              }
  
          } finally {
              if (exception != null || scanSpec.removeTemporaryFilesAfterScan) {
                  // If an exception was thrown or removeTemporaryFilesAfterScan was set, remove temporary files
                  // and close resources, zipfiles, and modules
                  nestedJarHandler.close(topLevelLog);
              }
          }
  
          if (exception != null) {
              // If an exception was thrown, log the cause, and flush the toplevel log
              if (topLevelLog != null) {
                  final Throwable cause = InterruptionChecker.getCause(exception);
                  topLevelLog.log("~", "An uncaught exception was thrown:", cause);
                  topLevelLog.flush();
              }
  
              // If exception is null, then failureHandler must be non-null at this point
              try {
                  // Call the FailureHandler
                  failureHandler.onFailure(exception);
              } catch (final Exception f) {
                  // The failure handler failed
                  if (topLevelLog != null) {
                      topLevelLog.log("~", "The failure handler threw an exception:", f);
                  }
                  // Group the two exceptions into one, using the suppressed exception mechanism
                  // to show the scan exception below the failure handler exception
                  final ExecutionException failureHandlerException = new ExecutionException(
                          "Exception while calling failure handler", f);
                  failureHandlerException.addSuppressed(exception);
                  // Throw a new ExecutionException (although this will probably be ignored,
                  // since any job with a FailureHandler was started with ExecutorService::execute
                  // rather than ExecutorService::submit)  
                  throw failureHandlerException;
              }
          }
          return scanResult;
      }