@Override
  public boolean exportData(String typeId, String dataId, File directory) {
    directory.mkdirs();

    final File exportTempFile;
    try {
      exportTempFile =
          File.createTempFile(
              SafeFilenameUtils.makeSafeFilename(StringUtils.rightPad(dataId, 2, '-') + "-"),
              SafeFilenameUtils.makeSafeFilename("." + typeId),
              directory);
    } catch (IOException e) {
      throw new RuntimeException(
          "Could not create temp file to export " + typeId + " " + dataId, e);
    }

    try {
      final String fileName = this.exportData(typeId, dataId, new StreamResult(exportTempFile));
      if (fileName == null) {
        logger.info("Skipped: type={} id={}", typeId, dataId);
        return false;
      }

      final File destFile = new File(directory, fileName + "." + typeId + ".xml");
      if (destFile.exists()) {
        logger.warn(
            "Exporting "
                + typeId
                + " "
                + dataId
                + " but destination file already exists, it will be overwritten: "
                + destFile);
        destFile.delete();
      }
      FileUtils.moveFile(exportTempFile, destFile);
      logger.info("Exported: {}", destFile);

      return true;
    } catch (Exception e) {
      if (e instanceof RuntimeException) {
        throw (RuntimeException) e;
      }

      throw new RuntimeException("Failed to export " + typeId + " " + dataId, e);
    } finally {
      FileUtils.deleteQuietly(exportTempFile);
    }
  }
  /**
   * Used by batch import and export to wait for queued tasks to complete. Handles fail-fast
   * behavior if any of the tasks threw and exception by canceling all queued futures and logging a
   * summary of the failures. All completed futures are removed from the queue.
   *
   * @param futures Queued futures to check for completeness
   * @param wait If true it will wait for all futures to complete, if false only check for completed
   *     futures
   * @return a list of futures that either threw exceptions or timed out
   */
  protected List<FutureHolder<?>> waitForFutures(
      final Queue<? extends FutureHolder<?>> futures,
      final PrintWriter reportWriter,
      final File reportDirectory,
      final boolean wait)
      throws InterruptedException {

    final List<FutureHolder<?>> failedFutures = new LinkedList<FutureHolder<?>>();

    for (Iterator<? extends FutureHolder<?>> futuresItr = futures.iterator();
        futuresItr.hasNext(); ) {
      final FutureHolder<?> futureHolder = futuresItr.next();

      // If waiting, or if not waiting but the future is already done do the get
      final Future<?> future = futureHolder.getFuture();
      if (wait || (!wait && future.isDone())) {
        futuresItr.remove();

        try {
          // Don't bother doing a get() on canceled futures
          if (!future.isCancelled()) {
            if (this.maxWait > 0) {
              future.get(this.maxWait, this.maxWaitTimeUnit);
            } else {
              future.get();
            }

            reportWriter.printf(
                REPORT_FORMAT,
                "SUCCESS",
                futureHolder.getDescription(),
                futureHolder.getExecutionTimeMillis());
          }
        } catch (CancellationException e) {
          // Ignore cancellation exceptions
        } catch (ExecutionException e) {
          logger.error("Failed: " + futureHolder);

          futureHolder.setError(e);
          failedFutures.add(futureHolder);
          reportWriter.printf(
              REPORT_FORMAT,
              "FAIL",
              futureHolder.getDescription(),
              futureHolder.getExecutionTimeMillis());

          try {
            final String dataReportName =
                SafeFilenameUtils.makeSafeFilename(
                    futureHolder.getDataType() + "_" + futureHolder.getDataName() + ".txt");
            final File dataReportFile = new File(reportDirectory, dataReportName);
            final PrintWriter dataReportWriter =
                new PrintWriter(new BufferedWriter(new FileWriter(dataReportFile)));
            try {
              dataReportWriter.println(
                  "FAIL: " + futureHolder.getDataType() + " - " + futureHolder.getDataName());
              dataReportWriter.println(
                  "--------------------------------------------------------------------------------");
              e.getCause().printStackTrace(dataReportWriter);
            } finally {
              IOUtils.closeQuietly(dataReportWriter);
            }
          } catch (Exception re) {
            logger.warn(
                "Failed to write error report for failed "
                    + futureHolder
                    + ", logging root failure here",
                e.getCause());
          }
        } catch (TimeoutException e) {
          logger.warn("Failed: " + futureHolder);

          futureHolder.setError(e);
          failedFutures.add(futureHolder);
          future.cancel(true);
          reportWriter.printf(
              REPORT_FORMAT,
              "TIMEOUT",
              futureHolder.getDescription(),
              futureHolder.getExecutionTimeMillis());
        }
      }
    }

    return failedFutures;
  }