package sos.scheduler.ftp;

import java.io.File;
import java.util.Iterator;
import java.util.Vector;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import sos.net.SOSFTP;
import sos.net.SOSFTPS;
import sos.net.SOSFileTransfer;
import sos.net.SOSMail;
import sos.net.SOSSFTP;
import sos.scheduler.job.JobSchedulerJob;
import sos.spooler.Order;
import sos.spooler.Variable_set;
import sos.util.SOSFile;
import sos.util.SOSFileOperations;
import sos.util.SOSGZip;
import sos.util.SOSSchedulerLogger;
import sos.util.SOSString;

/**
 * FTP File Transfer
 *
 @author Andreas P�schel
 @author M�r�vet �ks�z
 */

public class JobSchedulerFTPSend extends JobSchedulerJob {

    /** The FTP server will always reply the ftp error codes,
     * see http://www.the-eggman.com/seminars/ftp_error_codes.html */
  public  static final int ERROR_CODE = 300;

  private SOSFileTransfer ftpClient        = null;

  private String          lastHost         = "";

  private int             lastPort         = 0;

  private String          lastUser         = "";

  private String          lastAccount      = "";

  private SOSString       sosString        = new SOSString();


  public boolean spooler_process() {

    String      protocol                        = "ftp";
    boolean     sshBasedProtocol                = false;

        boolean     rc                              = false;

    String      host                            = "";
    int         port                            = 21;

    String      user                            = "anonymous";
    String      password                        = "";
    String      account                         = "";

    String      transferMode                    = "binary";
    boolean     passiveMode                     = false;

    String      remoteDir                       = "./";
    String      localDir                        = ".";
    String      fileSpec                        = ".*";

        String       atomicSuffix                   = "";
        boolean      checkSize                      = true;
        boolean      overwriteFiles                 = true;
        boolean      appendFiles                    = false;
        boolean      removeFiles                    = false;
        boolean      makeDirs                       = false;
        boolean      forceFiles                     = true;
        boolean      compressFiles                  = false;
        String       compressedFileExtension        = "";

        boolean      zeroByteFiles                  = true;
        boolean      zeroByteFilesStrict            = false;
        boolean      zeroByteFilesRelaxed           = false;

        String       fileNotificationTo             = "";
        String       fileNotificationCC             = "";
        String       fileNotificationBCC            = "";
        String       fileNotificationSubject        = "";
        String       fileNotificationBody           = "";

        String       fileZeroByteNotificationTo     = "";
        String       fileZeroByteNotificationCC     = "";
        String       fileZeroByteNotificationBCC    = "";
        String       fileZeroByteNotificationSubject= "";
        String       fileZeroByteNotificationBody   = "";

    boolean      isLoggedIn                     = false;
    Variable_set params                         = null;

    boolean      keepConnection                 = false;
    boolean      sameConnection                 = false;

        int          count                          = 0;
        int          zeroByteCount                  = 0;

        String       replacing                      = null;
    String       replacement                    = null;

    boolean      recursive                      = false;
    String       filePath                       = "";
    boolean      isFilePath                     = false;

    // alternative Parameter
    String      alternativeHost                 = "";
    int         alternativePort                 = 0;
    String      alternativeUser                 = "";
    String      alternativePassword             = "";
    String      alternativeAccount              = "";
    String      alternativeRemoteDir            = "";
    boolean     alternativePassiveMode          = false;
    String      alternativeTransferMode         = "";

    // Parallel File Transfer
    boolean      parallelTransfer                = false;
        String       parallelTransferCheckSetback    = "00:00:60";
        int          parallelTransferCheckRetry      = 60;

    // Hilfsvariable: wenn gesetzt, dann handelt es sich um einen Wiederholungsauftrag. Also keine Dateien abfrufen
    boolean      checkParallel              = false;
    boolean      orderSelfDestruct          = false;

    // Variablen f�r ssh-basierte Transferprotokolle
      /** optional proxy configuration */
      String proxyHost                  = "";
      int proxyPort                     = 0;
      String proxyUser                  = "";
      String proxyPassword              = "";
      /** authentication method: publickey, password */
      String authenticationMethod       = "publickey";
      /** key file: ~/.ssh/id_rsa or ~/.ssh/id_dsa */
      String authenticationFilename     = "";


    try {
      this.setLogger(new SOSSchedulerLogger(spooler_log));

      try // to get the job parameters and order parameters
        params = spooler.create_variable_set();
        if (spooler_task.params() != nullparams.merge(spooler_task.params());
        if (spooler_job.order_queue() != null && spooler_task.order().params() != nullparams.merge(spooler_task.order().params());

        if (params.var("ftp_keep_connection"!= null && params.var("ftp_keep_connection").length() 0) {
          String keep = params.var("ftp_keep_connection");
          if (keep.equalsIgnoreCase("true"|| keep.equalsIgnoreCase("1")) {
            keepConnection=true;
          else {
            keepConnection= false;
          }
        else {
          keepConnection=false;
        }

        if (params.var("ftp_protocol"!= null && params.var("ftp_protocol").length() 0)
          protocol = params.var("ftp_protocol");
        if (protocol.equalsIgnoreCase("sftp")){
          sshBasedProtocol = true;
          //use other defaults
          port = 22;
        }

        if (params.var("ftp_host"!= null && params.var("ftp_host").length() 0)
          host = params.var("ftp_host");

        if (params.var("ftp_port"!= null && params.var("ftp_port").length() 0)
          port = Integer.parseInt(params.var("ftp_port"));

        if (params.var("ftp_user"!= null && params.var("ftp_user").length() 0)
          user = params.var("ftp_user");

        if (params.var("ftp_password"!= null && params.var("ftp_password").length() 0)
          password = params.var("ftp_password");

        if (params.var("ftp_account"!= null && params.var("ftp_account").length() 0)
          account = params.var("ftp_account");

        if (keepConnection){
          sameConnection = ftpClient!=null && ftpClient.isConnected() && lastHost.equalsIgnoreCase(host)
          && lastPort==port && lastUser.equalsIgnoreCase(user&& lastAccount.equalsIgnoreCase(account);
        }
        if (ftpClient!=null && ftpClient.isConnected() && !sameConnection) {
          try{
            if (isLoggedInftpClient.logout();
            ftpClient.disconnect();
          catch (Exception e){}
        }

        lastHost = host;
        lastPort = port;
        lastUser = user;
        lastAccount = account;


        if (params.var("ftp_transfer_mode"!= null && params.var("ftp_transfer_mode").length() 0)
          transferMode = params.var("ftp_transfer_mode");

        if (params.var("ftp_passive_mode"!= null && params.var("ftp_passive_mode").length() 0)
          passiveMode = (params.var("ftp_passive_mode").equals("1"true false);

        if (params.var("ftp_remote_dir"!= null && params.var("ftp_remote_dir").length() 0)
          remoteDir = params.var("ftp_remote_dir");

        if (params.var("ftp_local_dir"!= null && params.var("ftp_local_dir").length() 0)
          localDir = params.var("ftp_local_dir");

        if (params.var("ftp_file_spec"!= null && params.var("ftp_file_spec").length() 0)
          fileSpec = params.var("ftp_file_spec");

        if (params.var("ftp_atomic_suffix"!= null && params.var("ftp_atomic_suffix").length() 0)
          atomicSuffix = params.var("ftp_atomic_suffix");

        if (params.var("ftp_check_size"!= null && params.var("ftp_check_size").length() 0)
          checkSize = (!params.var("ftp_check_size").equals("1")
              && !params.var("ftp_check_size").equalsIgnoreCase("true")
              && !params.var("ftp_check_size").equalsIgnoreCase("yes"false true);

        if (params.var("ftp_overwrite_files"!= null && params.var("ftp_overwrite_files").length() 0)
          overwriteFiles = (!params.var("ftp_overwrite_files").equals("1")
              && !params.var("ftp_overwrite_files").equalsIgnoreCase("true")
              && !params.var("ftp_overwrite_files").equalsIgnoreCase("yes"false true);

        if (params.var("ftp_append_files"!= null && params.var("ftp_append_files").length() 0)
          appendFiles = (params.var("ftp_append_files").equals("1")
              || params.var("ftp_append_files").equalsIgnoreCase("true")
              || params.var("ftp_append_files").equalsIgnoreCase("yes"true false);

        if (params.var("ftp_remove_files"!= null && params.var("ftp_remove_files").length() 0)
          removeFiles = (params.var("ftp_remove_files").equals("1")
              || params.var("ftp_remove_files").equalsIgnoreCase("true")
              || params.var("ftp_remove_files").equalsIgnoreCase("yes"true false);

        if (params.var("ftp_make_dirs"!= null && params.var("ftp_make_dirs").length() 0)
          makeDirs = (params.var("ftp_make_dirs").equals("1")
              || params.var("ftp_make_dirs").equalsIgnoreCase("true")
              || params.var("ftp_make_dirs").equalsIgnoreCase("yes"true false);

        if (params.var("ftp_force_files"!= null && params.var("ftp_force_files").length() 0)
          forceFiles = (!params.var("ftp_force_files").equals("1")
              && !params.var("ftp_force_files").equalsIgnoreCase("true")
              && !params.var("ftp_force_files").equalsIgnoreCase("yes"false true);

        if (params.var("ftp_compress_files"!= null && params.var("ftp_compress_files").length() 0)
          compressFiles = (params.var("ftp_compress_files").equals("1")
              || params.var("ftp_compress_files").equalsIgnoreCase("true")
              || params.var("ftp_compress_files").equalsIgnoreCase("yes"true false);

        if (params.var("ftp_compressed_file_extension"!= null && params.var("ftp_compressed_file_extension").length() 0) {
          compressedFileExtension = params.var("ftp_compressed_file_extension");
        else {
          compressedFileExtension = ".gz";
        }


        if (params.var("ftp_file_zero_byte_transfer"!= null && params.var("ftp_file_zero_byte_transfer").length() 0) {
          if (params.var("ftp_file_zero_byte_transfer").equals("1")
              || params.var("ftp_file_zero_byte_transfer").equalsIgnoreCase("true")
              || params.var("ftp_file_zero_byte_transfer").equalsIgnoreCase("yes")) {
            zeroByteFiles = true;
            zeroByteFilesStrict = false;
          else if (params.var("ftp_file_zero_byte_transfer").equalsIgnoreCase("strict")) {
            zeroByteFiles = false;
            zeroByteFilesStrict = true;
          else if (params.var("ftp_file_zero_byte_transfer").equalsIgnoreCase("relaxed")) {
            zeroByteFiles = false;
            zeroByteFilesStrict = false;
            zeroByteFilesRelaxed = true;
          else {
            zeroByteFiles = false;
            zeroByteFilesStrict = false;
          }
        }

        if (params.var("ftp_file_notification_to"!= null && params.var("ftp_file_notification_to").length() 0) {
          fileNotificationTo = params.var("ftp_file_notification_to");
        }

        if (params.var("ftp_file_notification_cc"!= null && params.var("ftp_file_notification_cc").length() 0) {
          fileNotificationCC = params.var("ftp_file_notification_cc");
        }

        if (params.var("ftp_file_notification_bcc"!= null && params.var("ftp_file_notification_bcc").length() 0) {
          fileNotificationBCC = params.var("ftp_file_notification_bcc");
        }

        if (params.var("ftp_file_notification_subject"!= null && params.var("ftp_file_notification_subject").length() 0) {
          fileNotificationSubject = params.var("ftp_file_notification_subject");
        }

        if (params.var("ftp_file_notification_body"!= null && params.var("ftp_file_notification_body").length() 0) {
          fileNotificationBody = params.var("ftp_file_notification_body");
        }


        if (params.var("ftp_file_zero_byte_notification_to"!= null && params.var("ftp_file_zero_byte_notification_to").length() 0) {
          fileZeroByteNotificationTo = params.var("ftp_file_zero_byte_notification_to");
        }

        if (params.var("ftp_file_zero_byte_notification_cc"!= null && params.var("ftp_file_zero_byte_notification_cc").length() 0) {
          fileZeroByteNotificationCC = params.var("ftp_file_zero_byte_notification_cc");
        }

        if (params.var("ftp_file_zero_byte_notification_bcc"!= null && params.var("ftp_file_zero_byte_notification_bcc").length() 0) {
          fileZeroByteNotificationBCC = params.var("ftp_file_zero_byte_notification_bcc");
        }

        if (params.var("ftp_file_zero_byte_notification_subject"!= null && params.var("ftp_file_zero_byte_notification_subject").length() 0) {
          fileZeroByteNotificationSubject = params.var("ftp_file_zero_byte_notification_subject");
        }

        if (params.var("ftp_file_zero_byte_notification_body"!= null && params.var("ftp_file_zero_byte_notification_body").length() 0) {
          fileZeroByteNotificationBody = params.var("ftp_file_zero_byte_notification_body");
        }

        if params.value("replacing")!=null && params.value("replacing").length()>) {
          replacing = params.value("replacing");
        }

        if params.value("replacement")!=null && params.value("replacement").length()>) {
          replacement = params.value("replacement");
        }


        if params.value("ftp_recursive")!=null && params.value("ftp_recursive").length()>) {
          String sRecursive = "";
          sRecursive = params.value("ftp_recursive");
          recursive = sosString.parseToBoolean(sRecursive);
        }

        if replacing != null && replacement == null ) {
          throw new Exception("job parameter is missing for specified parameter [replacing]: [replacement]");
        }

        if replacing == null && replacement != null ) {
          throw new Exception("job parameter is missing for specified parameter [replacement]: [replacing]");
        }


        if (appendFiles && compressFiles) {
          throw new Exception("unsupported parameter settings [ftp_append_files, ftp_compress_files]: cannot append to compressed files");
        }

        if (params.var("ftp_file_path"!= null && params.var("ftp_file_path").length() 0) {
          filePath = params.var("ftp_file_path");
          isFilePath = true;
        }

        // alternative parameters
        if (params.var("ftp_alternative_host"!= null && params.var("ftp_alternative_host").length() 0)
          alternativeHost = params.var("ftp_alternative_host");

        if (params.var("ftp_alternative_port"!= null && params.var("ftp_alternative_port").length() 0)
          alternativePort = Integer.parseInt(params.var("ftp_alternative_port"));

        if (params.var("ftp_alternative_password"!= null && params.var("ftp_alternative_password").length() 0)
          alternativePassword = params.var("ftp_alternative_password");

        if (params.var("ftp_alternative_user"!= null && params.var("ftp_alternative_user").length() 0)
          alternativeUser = params.var("ftp_alternative_user");

        if (params.var("ftp_alternative_account"!= null && params.var("ftp_alternative_account").length() 0)
          alternativeAccount = params.var("ftp_alternative_account");

        if (params.var("ftp_alternative_remote_dir"!= null && params.var("ftp_alternative_remote_dir").length() 0)
          alternativeRemoteDir = params.var("ftp_alternative_remote_dir");

        if (params.var("ftp_alternative_passive_mode"!= null && params.var("ftp_alternative_passive_mode").length() 0)
          alternativePassiveMode = (params.var("ftp_alternative_passive_mode").equals("1"true false);

        if (params.var("ftp_alternative_transfer_mode"!= null && params.var("ftp_alternative_transfer_mode").length() 0)
          alternativeTransferMode = params.var("ftp_alternative_transfer_mode");

        if (params.var("ftp_parallel"!= null && params.var("ftp_parallel").length() 0)
          parallelTransfer = sosString.parseToBoolean(sosString.parseToString(params.var("ftp_parallel")));

                if (params.var("ftp_parallel_check_setback"!= null && params.var("ftp_parallel_check_setback").length() 0)
                    parallelTransferCheckSetback = params.var("ftp_parallel_check_setback");

                if (params.var("ftp_parallel_check_retry"!= null && params.var("ftp_parallel_check_retry").length() 0) {
                    try {
                        parallelTransferCheckRetry = Integer.parseInt(params.var("ftp_parallel_check_retry"));
                    catch (Exception e) {
                        throw new Exception("illegal value for parameter [ftp_parallel_check_retry]: " + params.var("ftp_parallel_check_retry"));
                    }
                }

                // implicit parameters
                if(params.var("ftp_check_parallel"!= null && params.var("ftp_check_parallel").length() 0)
          checkParallel = sosString.parseToBoolean(sosString.parseToString(params.var("ftp_check_parallel")));

        if(params.var("ftp_order_self_destruct"!= null && params.var("ftp_order_self_destruct").length() 0)
          orderSelfDestruct = sosString.parseToBoolean(sosString.parseToString(params.var("ftp_order_self_destruct")));

        if (sshBasedProtocol){
          // parameters for ssh-based protocols
          if (params.value("ssh_proxy_host"!= null && params.value("ssh_proxy_host").toString().length() 0) {
            proxyHost=params.value("ssh_proxy_host");
          }

          if (params.value("ssh_proxy_port"!= null && params.value("ssh_proxy_port").length() 0) {
            try {
              proxyPort=(Integer.parseInt(params.value("ssh_proxy_port")));
            catch (Exception ex) {
              throw new Exception("illegal non-numeric value for parameter [ssh_proxy_port]: " + params.value("ssh_proxy_port"));
            }
          else {
            proxyPort=3128;
          }

          if (params.value("ssh_proxy_user"!= null && params.value("ssh_proxy_user").length() 0) {
            proxyUser=(params.value("ssh_proxy_user"));
          }

          if (params.value("ssh_proxy_password"!= null && params.value("ssh_proxy_password").length() 0) {
            proxyPassword=(params.value("ssh_proxy_password"));
          }

          if (params.value("ssh_auth_method"!= null && params.value("ssh_auth_method").length() 0) {
            if (params.value("ssh_auth_method").equalsIgnoreCase("publickey"|| params.value("ssh_auth_method").equalsIgnoreCase("password")) {
              authenticationMethod=(params.value("ssh_auth_method"));
            else {
              throw new Exception("invalid authentication method [publickey, password] specified: " + params.value("ssh_auth_method"));
            }
          else {
            authenticationMethod=("publickey");
          }

          if (params.value("ssh_auth_file"!= null && params.value("ssh_auth_file").length() 0) {
            authenticationFilename=(params.value("ssh_auth_file"));
          else {
            if (authenticationMethod.equalsIgnoreCase("publickey"))
              throw new Exception("no authentication filename was specified as parameter [ssh_auth_file]");
          }
        }


        // check if http proxy set for SSL/TLS connection
        if (params.value("http_proxy_host"!= null && params.value("http_proxy_host").toString().length() 0) {
          proxyHost=params.value("http_proxy_host");
        }

        if (params.value("http_proxy_port"!= null && params.value("http_proxy_port").length() 0) {
          try {
            proxyPort=(Integer.parseInt(params.value("http_proxy_port")));
          catch (Exception ex) {
            throw new NumberFormatException("illegal non-numeric value for parameter [http_proxy_port]: " + params.value("http_proxy_port"));
          }
        }


      catch (Exception e) {
        throw (new Exception("could not process job parameters: " + e.getMessage()));
      }


      if (fileNotificationTo != null && fileNotificationTo.length() 0) {
        if (fileNotificationSubject == null || fileNotificationSubject.length() == 0) {
          if (spooler_job.order_queue() != null) {
            fileNotificationSubject = "[info] Job Chain: " + spooler_task.order().job_chain().name() ", Order: " + spooler_task.order().id() ", Job: " + spooler_job.name() " (" + spooler_job.title() "), Task: " + spooler_task.id();
          else {
            fileNotificationSubject = "[info] Job: " + spooler_job.name() " (" + spooler_job.title() "), Task: " + spooler_task.id();
          }
        }

        if (fileNotificationBody == null || fileNotificationBody.length() == 0) {
          fileNotificationBody = "The following files have been sent:\n\n";
        }
      }

      if (fileZeroByteNotificationTo != null && fileZeroByteNotificationTo.length() 0) {
        if (fileZeroByteNotificationSubject == null || fileZeroByteNotificationSubject.length() == 0) {
          if (spooler_job.order_queue() != null) {
            fileZeroByteNotificationSubject = "[warning] Job Chain: " + spooler_task.order().job_chain().name() ", Order: " + spooler_task.order().id() ", Job: " + spooler_job.name() " (" + spooler_job.title() "), Task: " + spooler_task.id();
          else {
            fileZeroByteNotificationSubject = "[warning] Job: " + spooler_job.name() " (" + spooler_job.title() "), Task: " + spooler_task.id();
          }
        }

        if (fileZeroByteNotificationBody == null || fileZeroByteNotificationBody.length() == 0) {
          fileZeroByteNotificationBody = "The following files have not been sent and were removed due to zero byte constraints:\n\n";
        }
      }

      this.getLogger().debug(".. job parameter [ftp_protocol]                           : " + protocol);
      this.getLogger().debug(".. job parameter [ftp_host]                               : " + host);
      this.getLogger().debug(".. job parameter [ftp_port]                               : " + port);
      this.getLogger().debug(".. job parameter [ftp_user]                               : " + user);
      this.getLogger().debug(".. job parameter [ftp_account]                            : " + account);
      this.getLogger().debug(".. job parameter [ftp_transfer_mode]                      : " + transferMode);
      this.getLogger().debug(".. job parameter [ftp_passive_mode]                       : " + passiveMode);
      this.getLogger().debug(".. job parameter [ftp_remote_dir]                         : " + remoteDir);
      this.getLogger().debug(".. job parameter [ftp_local_dir]                          : " + localDir);
      this.getLogger().debug(".. job parameter [ftp_file_spec]                          : " + fileSpec);
      this.getLogger().debug(".. job parameter [ftp_atomic_suffix]                      : " + atomicSuffix);
      this.getLogger().debug(".. job parameter [ftp_check_size]                         : " + checkSize);
      this.getLogger().debug(".. job parameter [ftp_append_files]                       : " + appendFiles);
      this.getLogger().debug(".. job parameter [ftp_overwrite_files]                    : " + overwriteFiles);
      this.getLogger().debug(".. job parameter [ftp_remove_files]                       : " + removeFiles);
      this.getLogger().debug(".. job parameter [ftp_force_files]                        : " + forceFiles);
      this.getLogger().debug(".. job parameter [ftp_make_dirs]                          : " + makeDirs);
      this.getLogger().debug(".. job parameter [ftp_compress_files]                     : " + compressFiles);
      this.getLogger().debug(".. job parameter [ftp_compressed_file_extension]          : " + compressedFileExtension);

      this.getLogger().debug(".. job parameter [ftp_zero_byte_transfer] zeroByte        : " + zeroByteFiles);
      this.getLogger().debug(".. job parameter [ftp_zero_byte_transfer] strict          : " + zeroByteFilesStrict);
      this.getLogger().debug(".. job parameter [ftp_zero_byte_transfer] relaxed         : " + zeroByteFilesRelaxed);

      this.getLogger().debug(".. job parameter [ftp_file_notification_to]               : " + fileNotificationTo);
      this.getLogger().debug(".. job parameter [ftp_file_notification_cc]               : " + fileNotificationCC);
      this.getLogger().debug(".. job parameter [ftp_file_notification_bcc]              : " + fileNotificationBCC);
      this.getLogger().debug(".. job parameter [ftp_file_notification_subject]          : " + fileNotificationSubject);
      this.getLogger().debug(".. job parameter [ftp_file_notification_body]             : " + fileNotificationBody);

      this.getLogger().debug(".. job parameter [ftp_file_zero_byte_notification_to]     : " + fileZeroByteNotificationTo);
      this.getLogger().debug(".. job parameter [ftp_file_zero_byte_notification_cc]     : " + fileZeroByteNotificationCC);
      this.getLogger().debug(".. job parameter [ftp_file_zero_byte_notification_bcc]    : " + fileZeroByteNotificationBCC);
      this.getLogger().debug(".. job parameter [ftp_file_zero_byte_notification_subject]: " + fileZeroByteNotificationSubject);
      this.getLogger().debug(".. job parameter [ftp_file_zero_byte_notification_body]   : " + fileZeroByteNotificationBody);

      this.getLogger().debug(".. job parameter [replacing]                              : " + replacing);
      this.getLogger().debug(".. job parameter [replacement]                            : " + replacement);

      this.getLogger().debug(".. job parameter [ftp_recursive]                          : " + recursive);

      this.getLogger().debug(".. job parameter [ftp_file_path]                          : " + filePath);

      //alternative Parameter
      this.getLogger().debug(".. job parameter [ftp_alternative_host]                   : " + alternativeHost);
      this.getLogger().debug(".. job parameter [ftp_alternative_port]                   : " + alternativePort);
      this.getLogger().debug(".. job parameter [ftp_alternative_user]                   : " + alternativeUser);
      this.getLogger().debug(".. job parameter [ftp_alternative_account]                : " + alternativeAccount);
      this.getLogger().debug(".. job parameter [ftp_alternative_remote_dir]             : " + alternativeRemoteDir);
      this.getLogger().debug(".. job parameter [ftp_alternative_passive_mode]           : " + alternativePassiveMode);
      this.getLogger().debug(".. job parameter [ftp_alternative_transfer_mode]          : " + alternativeTransferMode);

      this.getLogger().debug(".. job parameter [ftp_parallel]                           : " + parallelTransfer);
            this.getLogger().debug(".. job parameter [ftp_parallel_check_setback]             : " + parallelTransferCheckSetback);
            this.getLogger().debug(".. job parameter [ftp_parallel_check_retry]               : " + parallelTransferCheckRetry);

            this.getLogger().debug(".. job parameter [ftp_check_parallel]                     : " + checkParallel);
      this.getLogger().debug(".. job parameter [ftp_order_self_destruct]                : " + orderSelfDestruct);

      if (sshBasedProtocol){
        this.getLogger().debug(".. job parameter [ssh_proxy_host]                         : " + proxyHost);
        this.getLogger().debug(".. job parameter [ssh_proxy_port]                         : " + proxyPort);
        this.getLogger().debug(".. job parameter [ssh_proxy_user]                         : " + proxyUser);
        this.getLogger().debug(".. job parameter [ssh_auth_method]                        : " + authenticationMethod);
        this.getLogger().debug(".. job parameter [ssh_auth_file]                          : " + authenticationFilename);
      }

      // SSL/TLS
      try {
        this.getLogger().debug(".. job parameter [http_proxy_host]                         : " + proxyHost);
        this.getLogger().debug(".. job parameter [http_proxy_port]                         : " + proxyPort);
      catch(Exception e) {}

      try // to check parameters

        if(checkParallel && spooler_job.order_queue() != null) {
          boolean bSuccess = true;
          String[] paramNames  = sosString.parseToString(spooler.variables().names()).split(";");
          for int i = 0; i < paramNames.length; i++) {

            if(paramNames[i].startsWith("ftp_check_send_" + normalize(spooler_task.order().id()) ".")) {
              if(sosString.parseToString(spooler.var(paramNames[i])).equals("0")) {
                // Anzahl der Wiederholungen merken
                String sRetry = sosString.parseToString(spooler.variables().var("cur_transfer_retry" + normalize(spooler_task.order().id())));
                int retry =  sRetry.length() == : Integer.parseInt(sRetry);
                --retry;
                spooler.variables().set_var("cur_transfer_retry" + normalize(spooler_task.order().id()), String.valueOf(retry));
                if(retry == 0) {
                  getLogger().debug("terminated cause max order setback reached: " + paramNames[i]);
                  spooler.variables().set_var("terminated_cause_max_order_setback_" + normalize(spooler_task.order().id())"1");
                  return false;
                }
                getLogger().debug("launching setback: " + parallelTransferCheckRetry + " * " + parallelTransferCheckSetback);
                spooler_task.order().setback();
                return false;
              else if(sosString.parseToString(spooler.var(paramNames[i])).equals("1")) {
                getLogger().debug("successfully terminated: " + paramNames[i]);
              else if(sosString.parseToString(spooler.var(paramNames[i])).equals("2")) {
                bSuccess = false;
                getLogger().debug("terminated with error : " + paramNames[i]);
              }
            }
          }
          return bSuccess;
        else if(params.var("ftp_parent_order_id"!= null) {
          // Hauptauftrag wurde wegen Erreichens von ftp_parallel_check_retry beendet -> die restlichen Unterauftr�ge sollen nicht durchlaufen
          String state = spooler.variables().var("terminated_cause_max_order_setback_" + normalize(params.var("ftp_parent_order_id")));
          if(state.equals("1"))
            return false;
        }


        if (host == null || host.length() == 0throw new Exception("no host was specified");
        if (user == null || user.length() == 0throw new Exception("no user was specified");

        if(isFilePath) {
          if(sosString.parseToString(fileSpec).length() &&  !fileSpec.equals(".*") ) {
            // getLogger().warn("parameter [ftp_file_spec] will be ignored because parameter ftp_file_path has been specified. ");
          }
          if(sosString.parseToString(remoteDir).length() &&  !remoteDir.equals("./") ) {
            // getLogger().warn("parameter [ftp_remote_dir] will be ignored because parameter ftp_file_path has been specified. ");
          }
        }
      catch (Exception e) {
        throw (new Exception("invalid or insufficient parameters: " + e.getMessage()));
      }


      try // to process ftp

        if (!sameConnection){
                    if(localDir.startsWith("\\\\")) {
                        // replaceAll has bugs
                        while(localDir.indexOf("\\"!= -1) {
                            localDir = localDir.replace('\\''/');
                        }
                    }

                    if (localDir.startsWith("file://")) {
                        if(!new File(createURI(localDir)).exists()) {
                            throw new Exception("local directory does not exist or is not accessible: " + localDir);
                        }
                    }

          this.getLogger().info("connecting by " + protocol + " to host " + host + ", port " + port + ", local directory " + localDir + ", remote directory " + remoteDir +
              (isFilePath ? ", file " + filePath : ", file specification " + fileSpec) );


          boolean alternativeUse = true;
          int isAlternativeParameterUse = 0;
          while (alternativeUse && isAlternativeParameterUse <= 1) {
            try {
              if (protocol.equalsIgnoreCase("ftp")){
                SOSFTP sosftp = new SOSFTP(host, port);
                ftpClient = sosftp;
                this.getLogger().debug("..ftp server reply [init] [host=" + host + "], [port="+ port + "]: " + ftpClient.getReplyString() );

                if (account != null && account.length() 0) {
                  isLoggedIn = sosftp.login(user, password, account);
                  this.getLogger().debug("..ftp server reply [login] [user=" + user + "], [account=" + account + "]: " + ftpClient.getReplyString() );
                else {
                  isLoggedIn = sosftp.login(user, password);
                  this.getLogger().debug("..ftp server reply [login] [user=" + user + "]: " + ftpClient.getReplyString());
                }

                if (!isLoggedIn || sosftp.getReplyCode() > ERROR_CODE) {
                  throw new Exception("..ftp server reply [login failed] [user=" + user + "], [account=" + account + "]: " + ftpClient.getReplyString() );
                }
              else if (protocol.equalsIgnoreCase("sftp")){
                SOSSFTP sftpClient = new SOSSFTP(host,port);
                ftpClient = sftpClient;
                
                sftpClient.setAuthenticationFilename(authenticationFilename);
                sftpClient.setAuthenticationMethod(authenticationMethod);
                sftpClient.setPassword(password);
                sftpClient.setProxyHost(proxyHost);
                sftpClient.setProxyPort(proxyPort);
                sftpClient.setProxyPassword(proxyPassword);
                sftpClient.setProxyUser(proxyUser);
                sftpClient.setUser(user);
                sftpClient.connect();
                
                try{
                  this.getLogger().debug("..sftp server logged in [user=" + user + "], [host=" + host + "]" );
                }catch (Exception e){
                  throw new Exception("..sftp server login failed [user=" + user + "], [host=" + host + "]: " + e );
                }
              else if (protocol.equalsIgnoreCase("ftps")){

                try{
                  if proxyHost != null && proxyPort != 0) {
                      System.getProperties().setProperty("proxyHost", proxyHost);
                      System.getProperties().setProperty("proxyPort", String.valueOf(proxyPort) );
                      System.getProperties().setProperty("proxySet""true");
                    }

                  SOSFTPS sosftp = new SOSFTPS(host, port);
                  ftpClient = sosftp;

                  this.getLogger().debug("..ftp server reply [init] [host=" + host + "], [port="+ port + "]: " + ftpClient.getReplyString() );

                  isLoggedIn = sosftp.login(user, password);
                  this.getLogger().debug("..ftp server reply [login] [user=" + user + "]: " + ftpClient.getReplyString());

                  if (!isLoggedIn || sosftp.getReplyCode() > ERROR_CODE) {
                    throw new Exception("..ftp server reply [login failed] [user=" + user + "], [account=" + account + "]: " + ftpClient.getReplyString() );
                  }

                }catch (Exception e){
                  throw new Exception("..ftps server login failed [user=" + user + "], [host=" + host + "]: " + e );
                }

              else{
                throw new Exception("Unknown protocol: "+protocol);
              }
              alternativeUse = false;

            catch (Exception ex) {

              this.getLogger().info("..error in ftp server init with [host=" + host + "], [port="+ port + "] " + JobSchedulerFTPReceive.getErrorMessage(ex));

              alternativeUse = (alternativeHost.concat(alternativeUser).concat(alternativePassword).
                  concat(alternativeAccount).length() || alternativePort != 0);

              if(alternativeUse && isAlternativeParameterUse == 0) {
                  // try to connect with alternative parameters
                if (ftpClient != null) {
                  int orderQueueLength = 0;
                  if (spooler_job.order_queue()!=nullorderQueueLength = spooler_job.order_queue().length();
                  this.getLogger().debug("..ftp server reply: "  + ftpClient.getReplyString());

                  if ((ftpClient.isConnected() && !keepConnection ||
                      (ftpClient.isConnected() && keepConnection && orderQueueLength<1)){
                    if (isLoggedIntry ftpClient.logout()catch(Exception e) {} // no error handling
                    this.getLogger().debug("..ftp server reply [logout]: " + ftpClient.getReplyString());
                    try ftpClient.disconnect()catch(Exception e) {} // no error handling
                  }

                }

                isAlternativeParameterUse++;
                host = getAlternative(host, alternativeHost);
                port = getAlternative(port, alternativePort);
                user = getAlternative(user, alternativeUser);
                password = getAlternative(password, alternativePassword);
                account = getAlternative(account, alternativeAccount);
                remoteDir = getAlternative(remoteDir, alternativeRemoteDir);
                passiveMode = alternativePassiveMode;
                transferMode = getAlternative(transferMode, alternativeTransferMode);
                this.getLogger().info("..try login with alternative parameter [host=" + host + "], [port="+ port + "] " +
                    "[user=" + user + "], [account=" + account + "], [remoteDir=" + remoteDir +"], [passiveMode=" + passiveMode +"], " +
                    "[transferMode=" + transferMode + "]");

              else {
                throw new Exception ("..error in ftp server init with [host=" + host + "], [port="+ port + "] " + ex,ex);
              }

            }
          }

        else {
          this.getLogger().info("reusing connection from previous transfer");
        }

        if (ftpClient instanceof SOSFTP){
          SOSFTP sosftp = (SOSFTPftpClient;

          if (passiveMode) {
            sosftp.passive();
            if (sosftp.getReplyCode() > ERROR_CODE) {
              throw new Exception("..ftp server reply [passive]: "  + ftpClient.getReplyString());
            else {
              this.getLogger().debug("..ftp server reply [passive]: "  + ftpClient.getReplyString());
            }
          }

          if (transferMode.equalsIgnoreCase("ascii")) {
            if (sosftp.ascii()) {
              this.getLogger().debug("..using ASCII mode for file transfer");
              this.getLogger().debug("..ftp server reply [ascii]: "  + ftpClient.getReplyString());
            else {
              throw new Exception(".. could not switch to ASCII mode for file transfer ..ftp server reply [ascii]: "  + ftpClient.getReplyString());
            }
          else {
            if (sosftp.binary()) {
              this.getLogger().debug("using binary mode for file transfer");
              this.getLogger().debug("..ftp server reply [binary]: "  + ftpClient.getReplyString());
            else {
              throw new Exception(".. could not switch to binary mode for file transfer ..ftp server reply [ascii]: "  + ftpClient.getReplyString());

            }
          }
        }

        boolean cd=true;
        if (makeDirs) {
          if (ftpClient.changeWorkingDirectory(remoteDir)) { // error code signals non-existing directory
            this.getLogger().debug("..ftp server reply [directory exists] [" + remoteDir + "]: "  + ftpClient.getReplyString());
            cd = true;
          else {
            boolean ok = ftpClient.mkdir(remoteDir);
            if (!ok) {
              throw new Exception("..error occurred creating directory [" + remoteDir + "]: "  + ftpClient.getReplyString());
            else {
              this.getLogger().debug("..ftp server reply [mkdir] [" + remoteDir + "]: "  + ftpClient.getReplyString());
              cd = ftpClient.changeWorkingDirectory(remoteDir);
            }
          }
        else if(remoteDir != null && remoteDir.length() 0){
          cd = ftpClient.changeWorkingDirectory(remoteDir);
        }

        if(!cd && sosString.parseToString(alternativeRemoteDir).length() 0) {//alternative Parameter
          this.getLogger().debug("..ftp server reply [cd] [remoteDir=" + remoteDir + "]: "  + ftpClient.getReplyString());
          this.getLogger().info("..try with alternative parameter [remoteDir=" + alternativeRemoteDir +"]");
          cd = ftpClient.changeWorkingDirectory(alternativeRemoteDir);
          remoteDir = alternativeRemoteDir;
        }

        if (!cd) {
          throw new Exception("..ftp server reply [cd] [remoteDir=" + remoteDir + "]: "  + ftpClient.getReplyString());
        else {
          this.getLogger().debug("..ftp server reply [cd] [remoteDir=" + remoteDir + "]: "  + ftpClient.getReplyString());
        }

        Vector filelist = null;
        if (isFilePath) {
          filelist = new Vector();
          filelist.add(filePath);
          fileSpec = ".*";
        else {
          if(this.createFile(localDir).isDirectory()) {
            this.getLogger().info("local directory: " + localDir + ", remote directory: " + remoteDir + ", file specification: " + fileSpec);
            filelist = SOSFile.getFilelist(this.createFile(localDir).getAbsolutePath(), fileSpec, 0, recursive);
          else {
            this.getLogger().info("local file: " + localDir + ", remote directory: " + remoteDir + ", file specification: " + fileSpec);
            filelist = new Vector();
            filelist.add(localDir);
          }
        }
        Iterator iterator = filelist.iterator();

        if(parallelTransfer && !isFilePath) {
          if(spooler_job.order_queue() == null) {
            // parallel transfer for standalone job
            while (iterator.hasNext()) {
              File fileName = (File)iterator.next();
              Variable_set newParams = params;
              newParams.set_var("ftp_file_path", fileName.getCanonicalPath());
              getLogger().info("launching job for parallel transfer with parameter ftp_file_path: " + fileName.getCanonicalPath());
              spooler.job(spooler_task.job().name()).start(params);
            }
            return false;
          else {
            // parallel transfer for order job
            while (iterator.hasNext()) {
              File fileName = (File)iterator.next();
              Variable_set newParams = spooler.create_variable_set();
              if (spooler_task.params() != nullnewParams.merge(params);

              newParams.set_var("ftp_file_path", fileName.getCanonicalPath());
              newParams.set_var("ftp_parent_order_id", spooler_task.order().id());
              newParams.set_var("ftp_order_self_destruct""1");

              Order newOrder = spooler.create_order();
                            newOrder.set_state(spooler_task.order().state());
              newOrder.set_params(newParams);

              spooler.job_chain(spooler_task.order().job_chain().name()).add_order(newOrder);

              getLogger().info("launching order for parallel transfer with parameter ftp_file_path: " + fileName.getCanonicalPath());

              spooler.variables().set_var("ftp_order", normalize(spooler_task.order().id()) "." + normalize(newOrder.id()) "." "0");
              spooler.variables().set_var("ftp_check_send_" + normalize(spooler_task.order().id()) "." + normalize(newOrder.id())"0");

            }
            // am aktuellen Auftrag speichern, dass im Wiederholungsfall per setback() nicht erneut Auftr�ge erzeugt werden sollen, sondern dass deren Erledigungszustand gepr�ft wird:
            spooler_task.order().params().set_var("ftp_check_parallel""yes");

            spooler_job.set_delay_order_after_setback(1, parallelTransferCheckSetback);
            spooler_job.set_max_order_setbacks(parallelTransferCheckRetry);
            spooler_task.order().setback();
            spooler.variables().set_var("cur_transfer_retry" + normalize(spooler_task.order().id()), String.valueOf(parallelTransferCheckRetry));
            return false;
          }
        }
        // end Parallel Transfer

        if (!zeroByteFiles) {
          while(iterator.hasNext()) {
            File checkFile = (Fileiterator.next();
            if (checkFile.exists()) {
              if (checkFile.length() == 0) {
                this.getLogger().info("skipping transfer of local file: " + checkFile.getAbsolutePath() " due to zero byte constraint");
                filelist.remove(checkFile);
                if (removeFiles) {
                  if (!checkFile.delete()) {
                    throw new Exception("..error occurred, could not remove local file: " + checkFile.getAbsolutePath());
                  else {
                    this.getLogger().info("removing file: " + checkFile.getAbsolutePath());
                  }
                }
                zeroByteCount++;
                fileZeroByteNotificationBody += checkFile.getName() "\n";
              }
            }
          }
        }

        iterator = filelist.iterator();
        while(iterator.hasNext()) {
          long bytesSend = 0;
          Object fn = iterator.next();

          File localFile = new File(fn.toString());

          File subParent = null;
          String subPath = "";

          if(!localFile.exists()) {
            throw new Exception(".. file [" + localFile + "] does not exist ");
          }

          if(recursive && !isFilePath) {
            // �berpr�fen, ob das Verzeichnis auf den FTP Server existiert, wenn nicht dann soll das gleiche Verzeichnis generiert werden
            if(localFile.getParent() != null && localFile.getParentFile().isDirectory()) { // es existieren Vaterknoten
              subPath = fn.toString().substring((localDir.length() 1))// Unterverzeichnisse sind alle Verzeichnisse unterhalb der localDir
              subParent = new File(subPath).getParentFile();

              if(subParent != null) {
                subPath = subPath.replaceAll("\\\\""/");
                subPath = subPath.substring(0, subPath.length() new File(fn.toString()).getName().length() 1;
                this.getLogger().debug4("..subpath [" + subPath + "]");
                String[] ftpFiles = ftpClient.listNames(remoteDir+ "/" + subPath);
                if (ftpFiles==null || ftpFiles.length == 0) {
                  boolean ok = ftpClient.mkdir(remoteDir + "/" + subPath);
                  if (!ok) {
                    throw new Exception("..error occurred creating sub-directory [" + remoteDir + "/" + subPath + "]: "  + ftpClient.getReplyString());
                  else {
                    this.getLogger().debug("..ftp server reply [mkdir sub-directory] [" + remoteDir + "/" + subPath + "]: "  + ftpClient.getReplyString());
                  }
                }
              }
            }
          }

          if (localFile == nullcontinue;
          if (!zeroByteFiles && localFile.length() == 0continue;

          File transferFile = null;
          String transferFilename = null;
          File sourceFile = null;
          if (compressFiles) {
            transferFile = new File(localFile.getAbsolutePath() + compressedFileExtension);
            sourceFile = File.createTempFile("sos"".gz");
          else {
            transferFile = new File(localFile.getAbsolutePath());
            sourceFile = new File(localFile.getAbsolutePath());
          }

          transferFilename = transferFile.getName();
          if (replacement != null && replacement.length() && replacing != null && replacing.length() 0) {
            String currTransferFilename = SOSFileOperations.getReplacementFilename(transferFile.getName(), replacing, replacement);
            this.getLogger().info("source filename ["+transferFile.getName()+"] is renamed to: " + currTransferFilename);
            transferFile = new File(currTransferFilename);
            transferFilename = transferFile.getName();
          }

          if(subParent != null && recursive) {
            transferFile = new File((subParent != null ? subParent.getName() "/" ""+ transferFile.getName());
            transferFilename = (subParent != null ? subPath + "/" ""+ transferFile.getName();
          }

          if (!appendFiles && atomicSuffix != null && atomicSuffix.length() 0) {
            if (!overwriteFiles) {
              Vector ftpFiles = ftpClient.nListtransferFile.getName());
              // fehler wird ueber nlist return value verwertet
              //if (ftpClient.getReplyCode() <= ERROR_CODE) { // error code signals non-existing file
                this.getLogger().debug("..ftp server reply [file exists] [" + transferFilename + "]: "  + ftpClient.getReplyString());
                if (!ftpFiles.isEmpty()) {
                  this.getLogger().info("..ftp transfer skipped for file [no overwrite]: " + transferFilename);
                  continue;
                }
              //}
            }

            if (compressFiles) {
              SOSGZip.compressFile(localFile, sourceFile);
              this.getLogger().info("sending file : " + transferFile.getAbsolutePath() + atomicSuffix + " from temporary file " + sourceFile.getAbsolutePath());
            else {
              this.getLogger().info("sending file : " + transferFile.getAbsolutePath() + atomicSuffix);
            }

            /* Implementierung nach SOSFTP verlegt
            java.io.OutputStream outputStream = ftpClient.storeFileStream(transferFilename + atomicSuffix);
            if (ftpClient.getReplyCode() > 300) {
              throw new Exception("..error occurred in storeFileStream() on the FTP server for file [" + transferFilename + atomicSuffix+ "]: "  + ftpClient.getReplyString());
            } else {
              this.getLogger().debug("..ftp server reply [storeFileStream] [" + transferFilename + atomicSuffix+ "]: "  + ftpClient.getReplyString());
            }
            */
            bytesSend = ftpClient.putFile(sourceFile.getAbsolutePath(), transferFilename + atomicSuffix);

            if (overwriteFiles) {
              Vector ftpFiles = ftpClient.nList(transferFilename);
              // fehler wird ueber nlist return value verwertet
              //if (ftpClient.getReplyCode() <= ERROR_CODE) { // error code signals non-existing file
              //  this.getLogger().debug("..ftp server reply [file exists] [" + transferFilename + "]: "  + ftpClient.getReplyString());
                if (!ftpFiles.isEmpty() && ftpFiles.contains(transferFilename)) {
                  boolean ok = ftpClient.delete(transferFilename);
                  if (!ok) {
                    throw new Exception("..error occurred overwriting file [" + transferFilename + "]: "  + ftpClient.getReplyString());
                  else {
                    this.getLogger().debug("..ftp server reply [delete] [" + transferFilename + "]: "  + ftpClient.getReplyString());
                  }
                }
              //}
            }
            this.getLogger().info("renaming file: " + transferFilename);
            ftpClient.rename(transferFilename + atomicSuffix, transferFilename);

          else {
            if (!overwriteFiles && !appendFiles) {
              Vector ftpFiles = ftpClient.nList(transferFilename);
              // fehler wird ueber nlist return value verwertet
              //if (ftpClient.getReplyCode() <= ERROR_CODE) { // error code signals non-existing file
                this.getLogger().debug("..ftp server reply [file exist] [" + transferFilename + "]: "  + ftpClient.getReplyString());
                /* if (!ftpFiles.isEmpty()) {
                  this.getLogger().info("..ftp transfer skipped for file [no overwrite]: " + transferFilename);
                  continue;
                } */
                boolean bWhileContinue = false;
                for (int i=0; i < ftpFiles.size(); i++ ) {
                  String currFtpFilename = sosString.parseToString(ftpFiles.get(i));
                  if(currFtpFilename.equalsIgnoreCase(transferFilename)) {
                    this.getLogger().info("..ftp transfer skipped for file [no overwrite]: " + transferFilename);
                    bWhileContinue = true;
                    continue;
                  }
                }
                if(bWhileContinue)
                    continue;
              //}
            }


            if (compressFiles) {
              SOSGZip.compressFile(localFile, sourceFile);
            }

            if (appendFiles) {
              if (ftpClient instanceof SOSFTP){
                this.getLogger().info("sending file : " + transferFile.getAbsolutePath() " (append)");
                bytesSend = ((SOSFTP)ftpClient).appendFilesourceFile.getAbsolutePath(), transferFilename );
              else{
                throw new Exception("append is not implemented for protocol "+protocol);
              }
            else {
              if (compressFiles) {
                this.getLogger().info("sending file : " + transferFile.getAbsolutePath() " from temporary file " + sourceFile.getAbsolutePath());
              else {
                this.getLogger().info("sending file : " + transferFile.getAbsolutePath());
              }

              /* implementierung in SOSFTP
              java.io.OutputStream outputStream = ftpClient.storeFileStream(transferFilename);
              if (ftpClient.getReplyCode() > 300) {
                throw new Exception("..error occurred in storeFileStream() on the FTP server for file [" + transferFilename +"]: "  + ftpClient.getReplyString());
              } else {
                this.getLogger().debug("..ftp server reply [storeFileStream] [" + transferFilename + "]: "  + ftpClient.getReplyString());
              }*/

              bytesSend = ftpClient.putFile(sourceFile.getAbsolutePath(), transferFilename);
            }
          }

          if (ftpClient instanceof SOSFTP){
            if (((SOSFTP)ftpClient).getReplyCode() > ERROR_CODE) {
              throw new Exception("..error occurred sending file [" + transferFile.getAbsolutePath() "]: "  + ftpClient.getReplyString());
            else {
              this.getLogger().debug("..ftp server reply [put] [" + transferFile.getAbsolutePath() ", size=" + bytesSend + "]: "  + ftpClient.getReplyString());
            }
          }

          if (checkSize && transferFile.length() && transferFile.length() != bytesSend) {
            throw new Exception("..error occurred sending file, target file size [" + transferFile.length() "] does not match number of bytes transferred ["  + bytesSend + "]");
          }

          if (compressFiles) {
            if (!sourceFile.delete()) {
              throw new Exception("..error occurred, could not remove compressed local file: " + sourceFile.getAbsolutePath());
            else {
              this.getLogger().debug1("removing compressed file: " + sourceFile.getAbsolutePath());
            }
          }

          if (removeFiles) {
            if (!localFile.delete()) {
              throw new Exception("..error occurred, could not remove local file: " + localFile.getAbsolutePath());
            else {
              this.getLogger().info("removing file: " + localFile.getAbsolutePath());
            }
          }

          fileNotificationBody += transferFile.getName() "\n";
          count++;
        }


        if (zeroByteCount > && fileZeroByteNotificationTo != null && fileZeroByteNotificationTo.length() 0) {
          sendMail(fileZeroByteNotificationTo, fileZeroByteNotificationCC, fileZeroByteNotificationBCC, fileZeroByteNotificationSubject, fileZeroByteNotificationBody);
        }

        if (count > && fileNotificationTo != null && fileNotificationTo.length() 0) {
          sendMail(fileNotificationTo, fileNotificationCC, fileNotificationBCC, fileNotificationSubject, fileNotificationBody);
        }


        switch (count) {
        case 0if (zeroByteCount > && zeroByteFilesRelaxed) {
                  spooler_job.set_state_text("no matching files found, " + zeroByteCount + " zero byte files skipped");
                  this.getLogger().info("no matching files found, " + zeroByteCount + " zero byte files skipped");
                else if (zeroByteCount > && zeroByteFilesStrict) {
                    throw new Exception("zero byte file(s) found");
                else if (forceFiles) {
                    throw new Exception("no matching files found");
                else {
                    spooler_job.set_state_text("no matching files found");
                    this.getLogger().info("no matching files found");
                }
                rc = (!forceFiles ? true : !zeroByteFilesRelaxed);
                break;
        case 1this.getLogger().info("1 file transferred" ((zeroByteCount > 0", " + zeroByteCount + " files skipped due to zero byte constraint" "") );
                spooler_job.set_state_text("1 file transferred" ((zeroByteCount > 0", " + zeroByteCount + " files skipped due to zero byte constraint" "") );
                rc = true;
                break;
        default:   this.getLogger().info(count + " files transferred" ((zeroByteCount > 0", " + zeroByteCount + " files skipped due to zero byte constraint" "") );
                spooler_job.set_state_text(count + " files transferred" ((zeroByteCount > 0", " + zeroByteCount + " files skipped due to zero byte constraint" "") );
                rc = true;
                break;
        }


        // return the number of transferred files
        if (spooler_job.order_queue() != null) {
          spooler_task.order().params().set_var("ftp_result_files", Integer.toString(count));
          spooler_task.order().params().set_var("ftp_result_zero_byte_files", Integer.toString(zeroByteCount ));
        else {
          spooler_task.params().set_var("ftp_result_files", Integer.toString(count));
          spooler_task.params().set_var("ftp_result_zero_byte_files", Integer.toString(zeroByteCount));
        }

        if(parallelTransfer && isFilePath && spooler_job.order_queue() != null) {
          spooler.variables().set_var("ftp_check_send_" + normalize(params.var("ftp_parent_order_id")) "." + normalize(spooler_task.order().id())"1");
        }
        processResult(rc, "");
        return (spooler_task.job().order_queue() == nullfalse : rc;

      catch (Exception e) {
        rc = false;
        if(parallelTransfer && isFilePath && spooler_job.order_queue() != null) {
          spooler.variables().set_var("ftp_check_send_" + normalize(params.var("ftp_parent_order_id")) "." + normalize(spooler_task.order().id())"2");
        }
        throw (new Exception("could not process file transfer: " + e,e));
      finally {
        if(parallelTransfer) {
          if(orderSelfDestruct) {
               // find positive end state for parallel orders
            String state = "";
            sos.spooler.Job_chain_node node = spooler_task.order().job_chain_node();
            while (node != null) {
              node = node.next_node();
              if(node != null)
                state = node.state();
            }
            this.getLogger().debug9("..set state for parallel order job: "  + state);
            // find positive end state
            spooler_task.order().set_state(state);
          }
        }
        if (ftpClient != null) {
          int orderQueueLength = 0;
          if (spooler_job.order_queue()!=nullorderQueueLength = spooler_job.order_queue().length();
          this.getLogger().debug("..ftp server reply: "  + ftpClient.getReplyString());

          if ((ftpClient.isConnected() && !keepConnection ||
              (ftpClient.isConnected() && keepConnection && orderQueueLength<1)){
            if (isLoggedIntry ftpClient.logout()catch(Exception e) {} // no error handling
            this.getLogger().debug("..ftp server reply [logout]: " + ftpClient.getReplyString());
            try ftpClient.disconnect()catch(Exception e) {} // no error handling
          }
        }
      }

    catch (Exception e){
      processResult(false,e.toString());
      spooler_job.set_state_text("ftp processing failed: " + e);
      spooler_log.warn ("ftp processing failed: " + e);
      return false;
    }
  }

  protected void processResult(boolean rc, String message) {
    // do nothing, entry point for subclasses
    
  }

  public void spooler_exit() {
    if (ftpClient!=null && ftpClient.isConnected()){
      try ftpClient.disconnect()catch(Exception e) {} // no error handling
    }
    super.spooler_exit();
  }


  /**
     * send mail with Job Scheduler settings
     *
     @param recipient
     @param recipientCC carbon copy recipient
     @param recipientBCC blind carbon copy recipient
     @param subject
     @param body
     @throws Exception
     */
    public void sendMail(String recipient, String recipientCC, String recipientBCC, String subject, String bodythrows Exception {

        try {
            SOSMail sosMail = new SOSMail(spooler_log.mail().smtp());

            sosMail.setQueueDir(spooler_log.mail().queue_dir());
            sosMail.setFrom(spooler_log.mail().from());
            sosMail.setContentType("text/plain");
            sosMail.setEncoding("Base64");

            String recipients[] = recipient.split(",");
            for(int i=0; i<recipients.length; i++) {
                sosMail.addRecipient(recipients[i].trim());
            }

            String recipientsCC[] = recipientCC.split(",");
            for(int i=0; i<recipientsCC.length; i++) {
                sosMail.addCC(recipientsCC[i].trim());
            }

            String recipientsBCC[] = recipientBCC.split(",");
            for(int i=0; i<recipientsBCC.length; i++) {
                sosMail.addBCC(recipientsBCC[i].trim());
            }

            sosMail.setSubject(subject);
            sosMail.setBody(body);
            sosMail.setSOSLogger(this.getLogger());

            this.getLogger().info("sending mail: \n" + sosMail.dumpMessageAsString());

            if (!sosMail.send()){
                this.getLogger().warn("mail server is unavailable, mail for recipient [" + recipient + "] is queued in local directory [" + sosMail.getQueueDir() "]:" + sosMail.getLastError());
            }

            sosMail.clearRecipients();
        catch (Exception e) {
            throw new Exception("error occurred sending mai: " + e.getMessage());
        }
    }


    private String getAlternative(String param, String alternativeParam) {
      try {
        if(sosString.parseToString(alternativeParam).length() 0) {
          return alternativeParam;
        else {
          return param;
        }
      catch (Exception e) {
        spooler_log.warn("error in getAlternative(): "   + e.getMessage());
        return param;
      }
    }


    private int getAlternative(int param, int alternativeParam) {
      try {
        if(alternativeParam > 0) {
          return alternativeParam;
        else {
          return param;
        }
      catch (Exception e) {
        spooler_log.warn("error in getAlternative(): "   + e.getMessage());
        return param;
      }
    }

    private String normalize(String str) {
    return str.replaceAll(",""_");
  }

  
}