/**
   * Primary user method for importing a number of import candidates.
   *
   * @param config The configuration information.
   * @param candidates Hosts information about the files to import.
   * @return if the import did not exit because of an error
   */
  public boolean importCandidates(ImportConfig config, ImportCandidates candidates) {
    List<ImportContainer> containers = candidates.getContainers();
    if (containers != null) {
      int numDone = 0;
      for (int index = 0; index < containers.size(); index++) {
        ImportContainer ic = containers.get(index);
        ImportTarget target = config.getTarget();
        if (target != null) {
          try {
            IObject obj = target.load(store, ic);
            if (!(obj instanceof Annotation)) {
              ic.setTarget(obj);
            } else {
              // This is likely a "post-processing" annotation
              // so that we don't have to resolve the target
              // until later.
              ic.getCustomAnnotationList().add((Annotation) obj);
            }
          } catch (Exception e) {
            log.error("Could not load target: {}", target);
            throw new RuntimeException("Failed to load target", e);
          }
        }

        if (config.checksumAlgorithm.get() != null) {
          ic.setChecksumAlgorithm(config.checksumAlgorithm.get());
        }

        try {
          importImage(ic, index, numDone, containers.size());
          numDone++;
        } catch (Throwable t) {
          String message = "Error on import";
          if (t instanceof ServerError) {
            final ServerError se = (ServerError) t;
            if (StringUtils.isNotBlank(se.message)) {
              message += ": " + se.message;
            }
          }
          log.error(message, t);
          if (!config.contOnError.get()) {
            log.info("Exiting on error");
            return false;
          } else {
            log.info("Continuing after error");
          }
        }
      }
    }
    return true;
  }
  /**
   * Provide initial configuration to the server in order to create the {@link ImportProcessPrx}
   * which will manage state server-side.
   *
   * @param container the import container
   * @return the new import process from the server
   * @throws ServerError if the import process could not be created
   * @throws IOException if the used files' absolute path could not be found
   */
  public ImportProcessPrx createImport(final ImportContainer container)
      throws ServerError, IOException {
    checkManagedRepo();
    String[] usedFiles = container.getUsedFiles();
    File target = container.getFile();
    if (log.isDebugEnabled()) {
      log.debug("Main file: " + target.getAbsolutePath());
      log.debug("Used files before:");
      for (String f : usedFiles) {
        log.debug(f);
      }
    }

    notifyObservers(
        new ImportEvent.FILESET_UPLOAD_PREPARATION(null, 0, usedFiles.length, null, null, null));

    // Copied to ClientPathExclusion
    // TODO: allow looser sanitization according to server configuration
    final FilePathRestrictions portableRequiredRules =
        FilePathRestrictionInstance.getFilePathRestrictions(
            FilePathRestrictionInstance.WINDOWS_REQUIRED,
            FilePathRestrictionInstance.UNIX_REQUIRED);
    final ClientFilePathTransformer sanitizer =
        new ClientFilePathTransformer(new MakePathComponentSafe(portableRequiredRules));

    final ImportSettings settings = new ImportSettings();
    final Fileset fs = new FilesetI();
    container.fillData(settings, fs, sanitizer, transfer);

    String caStr = container.getChecksumAlgorithm();
    if (caStr != null) {
      settings.checksumAlgorithm = ChecksumAlgorithmMapper.getChecksumAlgorithm(caStr);
    } else {
      // check if the container object has ChecksumAlgorithm
      // present and pass it into the settings object
      settings.checksumAlgorithm = repo.suggestChecksumAlgorithm(availableChecksumAlgorithms);
      if (settings.checksumAlgorithm == null) {
        throw new RuntimeException("no supported checksum algorithm negotiated with server");
      }
    }
    return repo.importFileset(fs, settings);
  }
  /**
   * Primary user method for importing a number
   *
   * @param config The configuration information.
   * @param candidates Hosts information about the files to import.
   */
  public boolean importCandidates(ImportConfig config, ImportCandidates candidates) {
    List<ImportContainer> containers = candidates.getContainers();
    if (containers != null) {
      int numDone = 0;
      for (int index = 0; index < containers.size(); index++) {
        ImportContainer ic = containers.get(index);
        if (DATASET_CLASS.equals(config.targetClass.get())) {
          ic.setTarget(store.getTarget(Dataset.class, config.targetId.get()));
        } else if (SCREEN_CLASS.equals(config.targetClass.get())) {
          ic.setTarget(store.getTarget(Screen.class, config.targetId.get()));
        }

        if (config.checksumAlgorithm.get() != null) {
          ic.setChecksumAlgorithm(config.checksumAlgorithm.get());
        }

        try {
          importImage(ic, index, numDone, containers.size());
          numDone++;
        } catch (Throwable t) {
          String message = "Error on import";
          if (t instanceof ServerError) {
            final ServerError se = (ServerError) t;
            if (StringUtils.isNotBlank(se.message)) {
              message += ": " + se.message;
            }
          }
          log.error(message, t);
          if (!config.contOnError.get()) {
            log.info("Exiting on error");
            return false;
          } else {
            log.info("Continuing after error");
          }
        }
      }
    }
    return true;
  }
 /**
  * Overridden to handle the end of the process.
  *
  * @see CmdCallbackI#onFinished(Response, Status, Current)
  */
 @Override
 public void onFinished(Response rsp, Status status, Current c) {
   waitOnInitialization(); // Need non-null container
   ImportResponse rv = null;
   final ImportRequest req = (ImportRequest) handle.getRequest();
   final Fileset fs = req.activity.getParent();
   if (rsp instanceof ERR) {
     final ERR err = (ERR) rsp;
     final RuntimeException rt =
         new RuntimeException(
             String.format(
                 "Failure response on import!\n"
                     + "Category: %s\n"
                     + "Name: %s\n"
                     + "Parameters: %s\n",
                 err.category, err.name, err.parameters));
     notifyObservers(
         new ErrorHandler.INTERNAL_EXCEPTION(
             container.getFile().getAbsolutePath(),
             rt,
             container.getUsedFiles(),
             container.getReader()));
   } else if (rsp instanceof ImportResponse) {
     rv = (ImportResponse) rsp;
     if (this.importResponse == null) {
       // Only respond once.
       notifyObservers(
           new ImportEvent.IMPORT_DONE(
               0, container, null, null, 0, null, rv.pixels, fs, rv.objects));
     }
     this.importResponse = rv;
   } else {
     final RuntimeException rt = new RuntimeException("Unknown response: " + rsp);
     notifyObservers(
         new ErrorHandler.INTERNAL_EXCEPTION(
             container.getFile().getAbsolutePath(),
             rt,
             container.getUsedFiles(),
             container.getReader()));
   }
   onFinishedDone();
 }
  /**
   * Perform an image import uploading files if necessary.
   *
   * @param container The import container which houses all the configuration values and target for
   *     the import.
   * @param index Index of the import in a set. <code>0</code> is safe if this is a singular import.
   * @param numDone Number of imports completed in a set. <code>0</code> is safe if this is a
   *     singular import.
   * @param total Total number of imports in a set. <code>1</code> is safe if this is a singular
   *     import.
   * @return List of Pixels that have been imported.
   * @throws FormatException If there is a Bio-Formats image file format error during import.
   * @throws IOException If there is an I/O error.
   * @throws ServerError If there is an error communicating with the OMERO server we're importing
   *     into.
   * @since OMERO Beta 4.2.1.
   */
  public List<Pixels> importImage(
      final ImportContainer container, int index, int numDone, int total)
      throws FormatException, IOException, Throwable {
    HandlePrx handle;
    for (FileExclusion exclusion : exclusions) {
      Boolean veto = exclusion.suggestExclusion(store.getServiceFactory(), container);
      if (Boolean.TRUE.equals(veto)) {
        notifyObservers(
            new ImportEvent.FILESET_EXCLUSION(
                container.getFile().getAbsolutePath(), 0, container.getUsedFiles().length));
        return Collections.emptyList();
      }
    }
    final ImportProcessPrx proc = createImport(container);
    final String[] srcFiles = container.getUsedFiles();
    final List<String> checksums = new ArrayList<String>();
    final byte[] buf = new byte[store.getDefaultBlockSize()];
    final TimeEstimator estimator =
        new ProportionalTimeEstimatorImpl(container.getUsedFilesTotalSize());
    Map<Integer, String> failingChecksums = new HashMap<Integer, String>();

    notifyObservers(
        new ImportEvent.FILESET_UPLOAD_START(null, index, srcFiles.length, null, null, null));

    for (int i = 0; i < srcFiles.length; i++) {
      checksums.add(uploadFile(proc, srcFiles, i, checksumProviderFactory, estimator, buf));
    }

    try {
      handle = proc.verifyUpload(checksums);
    } catch (ChecksumValidationException cve) {
      failingChecksums = cve.failingChecksums;
      throw cve;
    } finally {

      try {
        proc.close();
      } catch (Exception e) {
        log.warn("Exception while closing proc", e);
      }

      notifyObservers(
          new ImportEvent.FILESET_UPLOAD_END(
              null,
              index,
              srcFiles.length,
              null,
              null,
              srcFiles,
              checksums,
              failingChecksums,
              null));
    }

    // At this point the import is running, check handle for number of
    // steps.
    ImportCallback cb = null;
    try {
      cb = createCallback(proc, handle, container);

      if (minutesToWait == 0) {
        log.info("Disconnecting from import process...");
        cb.close(false);
        cb = null;
        handle = null;
        return Collections.emptyList(); // EARLY EXIT
      }

      if (minutesToWait < 0) {
        while (true) {
          if (cb.block(5000)) {
            break;
          }
        }
      } else {
        cb.loop(minutesToWait * 30, 2000);
      }

      final ImportResponse rsp = cb.getImportResponse();
      if (rsp == null) {
        throw new Exception("Import failure");
      }
      return rsp.pixels;
    } finally {
      if (cb != null) {
        cb.close(true); // Allow cb to close handle
      } else if (handle != null) {
        handle.close();
      }
    }
  }