/** process single mail order */
  public boolean spooler_process() {

    Order order = null;
    Variable_set parameters = null;
    File listFile = null;
    File messageFile = null;
    File failedFile = null;
    String messageId = "";
    long mailOrderId = 0;
    long curDeliveryCounter = 1;

    try {
      // classic or order driven
      if (spooler_task.job().order_queue() == null) {
        parameters = spooler_task.params();
        if (!this.mailOrderIterator.hasNext()) return true;
        listFile = (File) this.mailOrderIterator.next();

      } else {
        order = spooler_task.order();
        parameters = (Variable_set) spooler_task.order().payload();

        if (parameters.var("file") == null || parameters.var("file").toString().length() == 0)
          throw (new Exception("no filename was given in payload"));

        listFile =
            new File(
                this.getQueueDirectory()
                    + ((!this.getQueueDirectory().endsWith("/")
                            && !this.getQueueDirectory().endsWith("\\"))
                        ? "/"
                        : "")
                    + parameters.var("file"));
      }

      File workFile = new File(listFile.getAbsolutePath());
      if (this.getLogger() != null)
        this.getLogger().info("processing mail file: " + workFile.getAbsolutePath());

      if (workFile == null) {
        throw new Exception("empty file name found.");
      } else if (!workFile.exists()) {
        throw new Exception("mail file [" + workFile.getAbsolutePath() + "] does not exist.");
      } else if (!workFile.canRead()) {
        throw new Exception("cannot read from mail file [" + workFile.getAbsolutePath() + "]");
      }

      // prepare a file for later rename operations in case of error
      String failedPath = "";
      if (workFile.getAbsolutePath().lastIndexOf("/") > 0) {
        failedPath =
            workFile.getAbsolutePath().substring(0, workFile.getAbsolutePath().lastIndexOf("/"));
      } else if (workFile.getAbsolutePath().lastIndexOf("\\") > 0) {
        failedPath =
            workFile.getAbsolutePath().substring(0, workFile.getAbsolutePath().lastIndexOf("\\"));
      }
      if (!failedPath.endsWith("/") && !failedPath.endsWith("\\")) failedPath += "/";

      String failedName = workFile.getName();
      if (failedName.endsWith("~")) {
        failedName = failedName.substring(0, failedName.length() - 1);
      }
      failedFile = new File(failedPath + this.getFailedPrefix() + failedName);

      messageFile = new File(workFile.getAbsolutePath() + "~");
      if (messageFile.exists()) messageFile.delete();
      workFile.renameTo(messageFile);

      // populate mailer with settings
      if (this.sosMailSettings != null) {
        sosMail = new SOSMail(this.sosMailSettings);
      } else {
        sosMail = new SOSMail(spooler_log.mail().smtp());
      }

      if (this.getLogger() != null) sosMail.setSOSLogger(this.getLogger());

      // job scheduler sets mail host if no default was specified by settings
      if (sosMail.getHost() == null
          || sosMail.getHost().length() == 0
          || sosMail.getHost().equalsIgnoreCase("-queue")) {
        if (!spooler_log.mail().smtp().equalsIgnoreCase("-queue")) {
          sosMail.setHost(spooler_log.mail().smtp());
        } else {
          throw new Exception("no SMTP host was configured, global settings contain smtp=-queue");
        }
      }

      // job scheduler sets mail queue directory if no default was specified by parameters or
      // settings
      if (sosMail.getQueueDir() == null || sosMail.getQueueDir().length() == 0)
        sosMail.setQueueDir(this.getQueueDirectory());

      // set queue prefix "sos" to enable dequeueing by JobSchedulerMailDequeueJob
      sosMail.setQueuePraefix(this.getQueuePrefix());

      SOSSettings smtpSettings = new SOSProfileSettings(spooler.ini_path());
      Properties smtpProperties = smtpSettings.getSection("smtp");

      if (!smtpProperties.isEmpty()) {
        if (smtpProperties.getProperty("mail.smtp.user") != null
            && smtpProperties.getProperty("mail.smtp.user").length() > 0) {
          sosMail.setUser(smtpProperties.getProperty("mail.smtp.user"));
        }
        if (smtpProperties.getProperty("mail.smtp.password") != null
            && smtpProperties.getProperty("mail.smtp.password").length() > 0) {
          sosMail.setPassword(smtpProperties.getProperty("mail.smtp.password"));
        }
      }

      try { // to load mail and check delivery counter
        sosMail.loadFile(messageFile);
        messageId = sosMail.getLoadedMessageId();
      } catch (Exception e) {
        throw new Exception("mail file [" + workFile.getAbsolutePath() + "]: " + e.getMessage());
      }

      try { // to retrieve mail order from database
        if (this.getConnection() != null && this.hasDatabase()) {
          String id =
              this.getConnection()
                  .getSingleValue(
                      "SELECT \"ID\" FROM "
                          + this.getTableMails()
                          + " WHERE \"MESSAGE_ID\"='"
                          + messageId
                          + "'");
          if (id == null || id.length() == 0) {
            this.getLogger()
                .debug1(
                    "no entry found in database for message id ["
                        + messageId
                        + "] of mail file: "
                        + workFile.getAbsolutePath());
          } else {
            mailOrderId = Integer.parseInt(id);
          }
        }
      } catch (Exception e) {
        throw new Exception(
            "error occurred retrieving entry in database for message id ["
                + messageId
                + "] of mail file ["
                + workFile.getAbsolutePath()
                + "]: "
                + e.getMessage());
      }

      try { // to check the delivery counter
        if (sosMail.getMessage().getHeader("X-SOSMail-delivery-counter") != null
            && sosMail.getMessage().getHeader("X-SOSMail-delivery-counter").length > 0) {
          try {
            curDeliveryCounter =
                Integer.parseInt(
                    sosMail
                        .getMessage()
                        .getHeader("X-SOSMail-delivery-counter")[0]
                        .toString()
                        .trim());
          } catch (Exception ex) {
            throw new Exception(
                "illegal header value for X-SOSMail-delivery-counter: "
                    + sosMail.getMessage().getHeader("X-SOSMail-delivery-counter")[0].toString());
          }
          if (++curDeliveryCounter > this.getMaxDeliveryCounter()
              && this.getMaxDeliveryCounter() > 0) {
            if (this.getLogger() != null)
              this.getLogger()
                  .debug3(
                      "mail file ["
                          + workFile.getAbsolutePath()
                          + "] exceeds number of trials ["
                          + this.getMaxDeliveryCounter()
                          + "] to send mail and will not be dequeued");
            sosMail.setQueueDir("");
          }
        }
        sosMail
            .getMessage()
            .setHeader("X-SOSMail-delivery-counter", String.valueOf(curDeliveryCounter));
        sosMail.getMessage().saveChanges();
      } catch (Exception e) {
        throw new Exception("mail file [" + workFile.getAbsolutePath() + "]: " + e.getMessage());
      }

      try { // to send
        boolean shouldSend = true;
        File mailFile = null;
        String message = "";

        if (this.getLogDirectory() != null && this.getLogDirectory().length() > 0) {
          // dump message with attachments
          mailFile = this.getMailFile(this.getLogDirectory());
          sosMail.dumpMessageToFile(mailFile, true);
        }

        if (this.isLogOnly()) {
          message =
              "mail was NOT sent"
                  + ((this.getMaxDeliveryCounter() > 0)
                      ? " (trial "
                          + curDeliveryCounter
                          + " of "
                          + this.getMaxDeliveryCounter()
                          + ")"
                      : "")
                  + " but stored to file: "
                  + mailFile.getAbsolutePath();
          if (this.getLogger() != null) this.getLogger().info(message);
          message = (message.length() > 250 ? message.substring(message.length() - 250) : message);
          message = message.replaceAll("'", "''");
          shouldSend = false;
        } else if (!sosMail.send()) {
          message =
              "mail was NOT sent but stored for later dequeueing"
                  + ((this.getMaxDeliveryCounter() > 0)
                      ? " (trial "
                          + curDeliveryCounter
                          + " of "
                          + this.getMaxDeliveryCounter()
                          + ")"
                      : "")
                  + ", reason was: "
                  + sosMail.getLastError();
          if (this.getLogger() != null) this.getLogger().info(message);
          message = (message.length() > 250 ? message.substring(message.length() - 250) : message);
          message = message.replaceAll("'", "''");
          shouldSend = false;
        }

        try { // to check the delivery counter if mail could not be sent
          if (!shouldSend
              && curDeliveryCounter > this.getMaxDeliveryCounter()
              && this.getMaxDeliveryCounter() > 0) {
            throw new Exception(
                "number of trials ["
                    + this.getMaxDeliveryCounter()
                    + "] exceeded to send mail from file: "
                    + workFile.getAbsolutePath());
          }
        } catch (Exception e) {
          throw new Exception(e.getMessage());
        }

        if (this.getLogger() != null) {
          if (this.isLogOnly()) {
            this.getLogger()
                .info(
                    "mail was processed from file ["
                        + workFile.getAbsolutePath()
                        + "] to: "
                        + sosMail.getRecipientsAsString()
                        + " into: "
                        + mailFile.getAbsolutePath());
          } else {
            this.getLogger()
                .info(
                    "mail was sent from file ["
                        + workFile.getAbsolutePath()
                        + "] to: "
                        + sosMail.getRecipientsAsString());
          }
        }
        if (this.getConnection() != null && this.hasDatabase() && mailOrderId > 0) {
          this.getConnection()
              .executeUpdate(
                  "UPDATE "
                      + this.getTableMails()
                      + " SET \"JOB_ID\"="
                      + spooler_task.id()
                      + ", \"STATUS\"="
                      + STATE_SUCCESS
                      + ", \"STATUS_TEXT\"='"
                      + message
                      + "'"
                      + ", \"MESSAGE_ID\"='"
                      + sosMail.getMessage().getMessageID()
                      + "'"
                      + ", \"MODIFIED\"=%now, \"MODIFIED_BY\"='"
                      + spooler_task.job().name()
                      + "', \"DELIVERED\"=%now"
                      + " WHERE \"ID\"="
                      + mailOrderId);
          this.getConnection().commit();
        }
        if (this.getLogger() != null)
          this.getLogger().debug3("mail was sent with headers: " + sosMail.dumpHeaders());
      } catch (Exception ex) {
        throw new Exception(
            "mail was NOT sent from file ["
                + workFile.getAbsolutePath()
                + "]"
                + ((mailOrderId > 0) ? " for order [" + mailOrderId + "]" : "")
                + ": "
                + ex.getMessage());
      }

      messageFile.delete();

      // return value for classic and order driven processing
      if (spooler_task.job().order_queue() == null) {
        return this.mailOrderIterator.hasNext();
      } else {
        return true;
      }
    } catch (Exception e) {
      String message = "";
      if (spooler_task.job().order_queue() != null) {
        message = "error occurred processing mail [" + mailOrderId + "] : " + e.getMessage();
      } else {
        message = "error occurred processing mail: " + e.getMessage();
      }

      // do not create warnings (that are sent by mail) if the number of dequeue trials has exceeded
      // no: always create warnings
      if ((curDeliveryCounter > this.getMaxDeliveryCounter() && this.getMaxDeliveryCounter() > 0)
          && (sosMail.getQueueDir() == null || sosMail.getQueueDir().length() == 0)) {
        spooler_log.warn(message);
      } else {
        spooler_log.warn(message);
      }

      try { // to update database if mail order has been identified
        if (this.getConnection() != null && this.hasDatabase() && mailOrderId > 0) {
          message =
              (e.getMessage().length() > 250
                  ? e.getMessage().substring(e.getMessage().length() - 250)
                  : e.getMessage());
          message = message.replaceAll("'", "''");
          this.getConnection()
              .executeUpdate(
                  "UPDATE "
                      + this.getTableMails()
                      + " SET \"JOB_ID\"="
                      + spooler_task.id()
                      + ", \"STATUS\"="
                      + STATE_ERROR
                      + ", \"STATUS_TEXT\"='"
                      + message
                      + "',"
                      + "\"MODIFIED\"=%now, \"MODIFIED_BY\"='"
                      + spooler_task.job().name()
                      + "', \"DELIVERED\"=NULL"
                      + " WHERE \"ID\"="
                      + mailOrderId);
          this.getConnection().commit();
        }
      } catch (Exception ex) {
        spooler_log.warn("error occurred updating mail order: " + ex.getMessage());
      }

      // return value for classic and order driven processing
      if (spooler_task.job().order_queue() == null) {
        return this.mailOrderIterator.hasNext();
      } else {
        return false;
      }
    } finally {
      try {
        // this file should only exist in case of errors, we rename it with a prefix to prevent
        // further processing
        if (messageFile.exists()) {
          spooler_log.info(
              "mail file is renamed to exclude it from further processing: "
                  + failedFile.getAbsolutePath());
          messageFile.renameTo(failedFile);
        }
      } catch (Exception ex) {
      } // gracefully ignore this error to preserve the original exception

      try {
        if (this.getConnection() != null) this.getConnection().rollback();
      } catch (Exception ex) {
      } // gracefully ignore this error to preserve the original exception
    }
  }
  public boolean spooler_process() {

    try {
      super.spooler_process();
    } catch (Exception e1) {
      // TODO Auto-generated catch block
      e1.printStackTrace();
    }

    boolean checkParallel = false;
    boolean parallelTransfer = false;
    String parallelTransferCheckSetback = "00:00:60";
    int parallelTransferCheckRetry = 60;
    Variable_set params = null;
    boolean rc = false;
    boolean isFilePath = false;
    boolean orderSelfDestruct = false;
    //		Properties schedulerParams = null;
    HashMap<String, String> schedulerParams = null;

    try {
      this.setLogger(new SOSSchedulerLogger(spooler_log));
      getLogger().info(JSVersionInfo.getVersionString());
      getLogger().info(conSVNVersion);

      try { // to get the job and order parameters
        params = getParameters();
        schedulerParams =
            objOptions.DeletePrefix(super.getSchedulerParameterAsProperties(params), "ftp_");
        schedulerParams.putAll(getParameterDefaults(params));
        checkParallel =
            sosString.parseToBoolean(
                sosString.parseToString(schedulerParams.get(conParameterCHECK_PARALLEL)));
        parallelTransfer =
            sosString.parseToBoolean(
                sosString.parseToString(schedulerParams.get(conParameterPARALLEL)));

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

      try {
        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() == 0 ? 0 : 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 (sosString.parseToString(params.var("ftp_parent_order_id")).length() > 0) {
          // 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 (sosString.parseToString(schedulerParams.get("file_path")).length() > 0) {
          isFilePath = true;
        } else {
          isFilePath = false;
        }

      } catch (Exception e) {
        throw (new Exception("invalid or insufficient parameters: " + e.getMessage()));
      }

      try { // to process ftp

        if (parallelTransfer && !isFilePath) {
          // nur die filelist holen um Parallelen transfer zu erm�glichen
          Properties p = new Properties();
          p.putAll((Properties) schedulerParams.clone());
          p.put("skip_transfer", "yes");
          // kb 2011-04--27 no longer needed due to too much trouble with this file / concept
          //					 createIncludeConfigurationFile("sos/net/sosftp/Configuration.xml",
          // "sos.net.sosftp.Configuration.xml");//Alle
          // Parametern sind hier auch g�ltig
          SOSConfiguration con =
              new SOSConfiguration(
                  null,
                  p,
                  sosString.parseToString(schedulerParams.get(conParameterSETTINGS)),
                  sosString.parseToString(schedulerParams.get(conParameterPROFILE)),
                  //							"sos/scheduler/ftp/SOSFTPConfiguration.xml", new
                  // SOSSchedulerLogger(spooler_log));
                  null,
                  new SOSSchedulerLogger(spooler_log));
          con.checkConfigurationItems();

          sos.net.sosftp.SOSFTPCommandSend ftpCommand =
              new sos.net.sosftp.SOSFTPCommandSend(con, new SOSSchedulerLogger(spooler_log));
          ftpCommand.setSchedulerJob(this);
          rc = ftpCommand.transfer();
          Vector<File> filelist = ftpCommand.getTransferredFilelist();
          Iterator iterator = filelist.iterator();

          if (isJobchain() == false) {
            // 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());
              newParams.set_var("ftp_local_dir", "");
              getLogger()
                  .info(
                      "launching job for parallel transfer with parameter ftp_file_path: "
                          + fileName.getCanonicalPath());
              spooler.job(spooler_task.job().name()).start(params);
            }
            return signalSuccess();
          } 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() != null) newParams.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
        //				createIncludeConfigurationFile("sos/net/sosftp/Configuration.xml",
        // "sos.net.sosftp.Configuration.xml");// Alle Parametern
        //																														// sind hier auch
        //																														// g�ltig
        SOSConfiguration con =
            new SOSConfiguration(
                null,
                mapToProperties(schedulerParams),
                sosString.parseToString(schedulerParams.get(conParameterSETTINGS)),
                sosString.parseToString(schedulerParams.get(conParameterPROFILE)),
                //						"sos/scheduler/ftp/SOSFTPConfiguration.xml", new
                // SOSSchedulerLogger(spooler_log));
                null,
                new SOSSchedulerLogger(spooler_log));
        con.checkConfigurationItems();

        sos.net.sosftp.SOSFTPCommandSend ftpCommand =
            new sos.net.sosftp.SOSFTPCommandSend(con, new SOSSchedulerLogger(spooler_log));
        ftpCommand.setSchedulerJob(this);
        rc = ftpCommand.transfer();

        // return the number of transferred files
        createReturnParameter(ftpCommand);

        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, "");

        spooler_job.set_state_text(ftpCommand.getState() != null ? ftpCommand.getState() : "");
        return (spooler_task.job().order_queue() == null) ? false : 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);
          }
        }
      }

    } 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;
    }
  }