IcapTask.java

/*
 * This file is part of Waarp Project (named also Waarp or GG).
 *
 *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
 *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
 * individual contributors.
 *
 *  All Waarp Project is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along with
 * Waarp . If not, see <http://www.gnu.org/licenses/>.
 */
package org.waarp.openr66.context.task;

import org.apache.commons.exec.CommandLine;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
import org.waarp.icap.IcapScanFile;
import org.waarp.openr66.context.R66Session;
import org.waarp.openr66.context.task.exception.OpenR66RunnerErrorException;

/**
 * ICAP task:<br>
 * <p>
 * List of arguments will be as follow.<br>
 * -file filename <br>
 * -to hostname <br>
 * [-port port, default 1344] <br>
 * -service name | -model name <br>
 * [-previewSize size, default none] <br>
 * [-blockSize size, default 8192] <br>
 * [-receiveSize size, default 65536] <br>
 * [-maxSize size, default MAX_INTEGER] <br>
 * [-timeout in_ms, default equiv to 10 min] <br>
 * [-keyPreview key -stringPreview string, default none] <br>
 * [-key204 key -string204 string, default none] <br>
 * [-key200 key -string200 string, default none] <br>
 * [-stringHttp string, default none] <br>
 * [-logger DEBUG|INFO|WARN|ERROR, default none] <br>
 * [-errorMove path | -errorDelete | -sendOnError] <br>
 * [-ignoreNetworkError] <br>
 * [-ignoreTooBigFileError] <br>
 * <br>
 * Then if r66send command in case of -sendOnError option is specified, the
 * very last option of ICAP must be "--" then followed by usual r66send
 * options.<br>
 * Format is like r66send command in any order except "-info" which should be
 * the last item, and "-copyinfo"
 * will copy at first place the original transfer information as the new one,
 * while still having the
 * possibility to add new informations through "-info":<br>
 * "-file filepath -to requestedHost -rule rule [-md5] [-start yyyyMMddHHmmss or
 * -delay (delay or +delay)] [-nofollow)
 * [-copyinfo] [-info information]" <br>
 * <br>
 * INFO is the only one field that can contains blank character.<br>
 * <br>
 * Example:<br>
 * -file #TRUEFULLPATH# -to hostname -service name -previewSize size
 * -blockSize size -receiveSize size -maxSize size -timeout in_ms -keyPreview
 * key -stringPreview string -key204 key -string204 string -key200 key
 * -string200 string -stringHttp string -logger WARN -errorDelete
 * -ignoreNetworkError<br>
 * <br>
 * -file #TRUEFULLPATH# -to hostname -model name -previewSize size
 * -blockSize size -receiveSize size -maxSize size -timeout in_ms -keyPreview
 * key -stringPreview string -key204 key -string204 string -key200 key
 * -string200 string -stringHttp string -logger WARN -errorMove path
 * -ignoreNetworkError<br>
 * <br>
 * -file #TRUEFULLPATH# -to hostname -model name -previewSize size
 * -blockSize size -receiveSize size -maxSize size -timeout in_ms -keyPreview
 * key -stringPreview string -key204 key -string204 string -key200 key
 * -string200 string -stringHttp string -logger WARN -sendOnError
 * -ignoreNetworkError -- -file #TRUEFULLPATH# -to
 * requestedHost -rule rule [-copyinfo] [-info information]<br>
 */
public class IcapTask extends AbstractTask {
  /**
   * Internal Logger
   */
  private static final WaarpLogger logger =
      WaarpLoggerFactory.getLogger(IcapTask.class);

  /**
   * @param argRule
   * @param delay
   * @param argTransfer
   * @param session
   */
  public IcapTask(final String argRule, final int delay,
                  final String argTransfer, final R66Session session) {
    super(TaskType.ICAP, delay, argRule, argTransfer, session);
  }

  @Override
  public final void run() {
    logger.info("ICAP with " + argRule + ':' + argTransfer + " and {}",
                session);
    String finalname = applyTransferSubstitutions(argRule);
    finalname = finalname.replaceAll("#([A-Z]+)#", "\\${$1}");
    final CommandLine commandLine = new CommandLine("dummy");
    commandLine.setSubstitutionMap(getSubstitutionMap());
    commandLine.addArguments(finalname, false);
    final String[] args = commandLine.getArguments();

    if (args.length < 6) {
      futureCompletion.setFailure(
          new OpenR66RunnerErrorException("Not enough argument in ICAP"));
      return;
    }
    final int status = IcapScanFile.scanFile(args);
    switch (status) {
      case IcapScanFile.STATUS_OK:
        futureCompletion.setSuccess();
        return;
      case IcapScanFile.STATUS_BAD_ARGUMENT:
        logger.error(
            "ICAP Bad argument error with " + argRule + ':' + argTransfer +
            ':' + delay + " and " + session);
        futureCompletion.setFailure(
            new OpenR66RunnerErrorException("ICAP Bad argument error"));
        return;
      case IcapScanFile.STATUS_ICAP_ISSUE:
        logger.error(
            "ICAP Bad protocol error with " + argRule + ':' + argTransfer +
            ':' + delay + " and " + session);
        futureCompletion.setFailure(
            new OpenR66RunnerErrorException("ICAP Bad protocol error"));
        return;
      case IcapScanFile.STATUS_NETWORK_ISSUE:
        logger.error(
            "ICAP Network error with " + argRule + ':' + argTransfer + ':' +
            delay + " and " + session);
        futureCompletion.setFailure(
            new OpenR66RunnerErrorException("ICAP Network error"));
        return;
      case IcapScanFile.STATUS_KO_SCAN:
        if (finalizeIcapOnError(args)) {
          return;
        }
        // No send required so real error
        logger.error(
            "ICAP KO error with " + argRule + ':' + argTransfer + ':' + delay +
            " and " + session);
        futureCompletion.setFailure(
            new OpenR66RunnerErrorException("ICAP KO error"));
        return;
      case IcapScanFile.STATUS_KO_SCAN_POST_ACTION_ERROR:
        logger.error(
            "ICAP KO post error with " + argRule + ':' + argTransfer + ':' +
            delay + " and " + session);
        futureCompletion.setFailure(
            new OpenR66RunnerErrorException("ICAP KO post error"));
        return;
      default:
        logger.error(
            "ICAP Unknown error with " + argRule + ':' + argTransfer + ':' +
            delay + " and " + session);
        futureCompletion.setFailure(
            new OpenR66RunnerErrorException("ICAP Unknown error"));
    }
  }

  /**
   * finalize Icap on Error
   *
   * @param args
   *
   * @return True if OK
   */
  private boolean finalizeIcapOnError(final String[] args) {
    for (int i = 0; i < args.length; i++) {
      if (IcapScanFile.ERROR_SEND_ARG.equalsIgnoreCase(args[i])) {
        for (i++; i < args.length; i++) {
          if (IcapScanFile.SEPARATOR_SEND.equals(args[i])) {
            final StringBuilder newArgs = new StringBuilder();
            for (i++; i < args.length; i++) {
              newArgs.append(args[i]).append(' ');
            }
            // Now launch send
            final TransferTask transferTask =
                new TransferTask(newArgs.toString(), 0, argTransfer, session);
            transferTask.run();
            transferTask.futureCompletion.awaitOrInterruptible();
            if (transferTask.futureCompletion.isSuccess()) {
              futureCompletion.setResult(
                  transferTask.futureCompletion.getResult());
              logger.info(
                  "ICAP ended in KO on file but resend as requested is OK for {}",
                  session.getFile().getTrueFile().getAbsolutePath());
              futureCompletion.setFailure(new OpenR66RunnerErrorException(
                  "ICAP ended in KO on file but resend as requested is OK"));
            } else {
              logger.error(
                  "ICAP KO with Resend in error with " + argRule + ':' +
                  argTransfer + ':' + delay + " and " + session,
                  transferTask.futureCompletion.getCause());
              if (transferTask.futureCompletion.getCause() == null) {
                futureCompletion.setFailure(new OpenR66RunnerErrorException(
                    "ICAP ended in KO on file and Resend is KO too"));
              } else {
                futureCompletion.setFailure(new OpenR66RunnerErrorException(
                    "ICAP ended in KO on file and Resend is KO too",
                    transferTask.futureCompletion.getCause()));
              }
            }
            return true;
          }
        }
      }
    }
    return false;
  }

}