IngestRequestFactory.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.vitam.ingest;

import fr.gouv.vitam.common.exception.InvalidParseOperationException;
import fr.gouv.vitam.common.json.JsonHandler;
import fr.gouv.vitam.ingest.external.client.IngestExternalClient;
import fr.gouv.vitam.ingest.external.client.IngestExternalClientFactory;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.waarp.common.guid.GUID;
import org.waarp.common.logging.SysErrLogger;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
import org.waarp.common.utility.SystemPropertyUtil;
import org.waarp.vitam.common.waarp.ManagerToWaarp;
import org.waarp.vitam.common.waarp.ManagerToWaarpFactory;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * Factory that handles IngestRequest within a directory
 */
public class IngestRequestFactory {
  public static final String DEFAULT_INGEST_FACTORY =
      "/waarp/data/r66/IngestFactory";
  static final String ORG_WAARP_INGEST_BASEDIR = "org.waarp.ingest.basedir";
  /**
   * Internal Logger
   */
  private static final WaarpLogger logger =
      WaarpLoggerFactory.getLogger(IngestRequestFactory.class);
  private static final String WORK = "work";
  private static final String BASENAME =
      IngestRequest.class.getSimpleName() + ".";
  private static final String EXTENSION = ".json";
  private static final String RESULT_EXTENSION = ".xml";
  private static final FilenameFilter JSON_ONLY =
      (dir, name) -> name.startsWith(BASENAME) && name.endsWith(EXTENSION);
  private static final IngestRequestFactory FACTORY =
      new IngestRequestFactory();
  static boolean vitamTakeCareLocalFile = true;

  static {
    setBaseDir(new File(DEFAULT_INGEST_FACTORY));
  }

  private File baseDir;
  private File workDir;
  private IngestExternalClientFactory clientFactory =
      IngestExternalClientFactory.getInstance();

  private IngestRequestFactory() {
    // empty
  }

  /**
   * Common options
   *
   * @return common Options
   */
  static Option getDirectoryOption() {
    Option property =
        new Option("D", "Use value for property " + ORG_WAARP_INGEST_BASEDIR);
    property.setArgName("property=value");
    property.setArgs(2);
    property.setValueSeparator('=');
    return property;
  }

  /**
   * Common parse command line and setup Base Directory
   *
   * @param cmd
   */
  static void parseDirectoryOption(CommandLine cmd) {
    if (cmd.hasOption('D')) {
      Properties properties = cmd.getOptionProperties("D");
      setBaseDir(new File(properties.getProperty(ORG_WAARP_INGEST_BASEDIR,
                                                 DEFAULT_INGEST_FACTORY)));
    } else if (SystemPropertyUtil.contains(ORG_WAARP_INGEST_BASEDIR)) {
      setBaseDir(new File(SystemPropertyUtil.get(ORG_WAARP_INGEST_BASEDIR,
                                                 DEFAULT_INGEST_FACTORY)));
    } else {
      setBaseDir(new File(DEFAULT_INGEST_FACTORY));
    }
  }

  /**
   * Set Base Directory
   *
   * @param baseDirToSet
   */
  static void setBaseDir(File baseDirToSet) {
    FACTORY.baseDir = baseDirToSet;
    FACTORY.baseDir.mkdirs();
    FACTORY.workDir = new File(FACTORY.baseDir, WORK);
    FACTORY.workDir.mkdirs();
  }

  /**
   * @return the instance of the factory
   */
  public static IngestRequestFactory getInstance() {
    return FACTORY;
  }

  /**
   * Used in JUnit
   */
  void setBaseDir() {
    baseDir = FACTORY.baseDir;
    workDir = FACTORY.workDir;
  }

  /**
   * @return the base directory
   */
  File getBaseDir() {
    return baseDir;
  }

  /**
   * @return the Ingest Vitam client
   */
  public IngestExternalClient getClient() {
    return clientFactory.getClient();
  }

  /**
   * @param ingestRequest
   *
   * @return the associated ManagerToWaarp
   */
  public ManagerToWaarp getManagerToWaarp(IngestRequest ingestRequest) {
    return ManagerToWaarpFactory
        .getManagerToWaarp(ingestRequest.getWaarpModel());
  }

  /**
   * Save the IngestRequest as a new one, creating file
   *
   * @param ingestRequest
   *
   * @throws InvalidParseOperationException
   */
  synchronized void saveNewIngestRequest(IngestRequest ingestRequest)
      throws InvalidParseOperationException {
    File newFile = new File(baseDir, getNewName());
    ingestRequest.setJsonPath(newFile.getName());
    JsonHandler.writeAsFile(ingestRequest, newFile);
  }

  /**
   * @return the unique name for JSON
   */
  private static String getNewName() {
    synchronized (FACTORY) {
      GUID guid = new GUID();
      return BASENAME + guid.getId() + EXTENSION;
    }
  }

  /**
   * Update the IngestRequest
   *
   * @param ingestRequest
   *
   * @return true if saved
   *
   * @throws InvalidParseOperationException
   */
  synchronized boolean saveIngestRequest(IngestRequest ingestRequest)
      throws InvalidParseOperationException {
    File existingFile = new File(baseDir, ingestRequest.getJsonPath());
    if (existingFile.canRead()) {
      JsonHandler.writeAsFile(ingestRequest, existingFile);
      return true;
    }
    throw new InvalidParseOperationException("Json File does not exist");
  }

  /**
   * Clean and remove all traces of this IngestRequest
   *
   * @param ingestRequest
   *
   * @return true if totally done
   */
  synchronized boolean removeIngestRequest(IngestRequest ingestRequest) {
    if (ingestRequest.getJsonPath() != null) {
      File existingFile = new File(baseDir, ingestRequest.getJsonPath());
      boolean status = deleteFile(existingFile);
      // Vitam is supposed to take care of this
      if (vitamTakeCareLocalFile) {
        File sourceFile = new File(ingestRequest.getPath());
        status &= deleteFile(sourceFile);
      }
      // Delete the ATR file if any
      File xmlAtrFile = getXmlAtrFile(ingestRequest);
      status &= deleteFile(xmlAtrFile);
      IngestRequest.IngestStep.endSessionMachineSate(ingestRequest.step);
      // Ensure file are deleted there
      while (existingFile.exists()) {
        try {
          FACTORY.wait(10);
        } catch (InterruptedException ignore) {//NOSONAR
          logger.debug(ignore);
        }
      }
      return status;
    }
    return false;
  }

  /**
   * Internal
   *
   * @param file
   *
   * @return true if done
   */
  private boolean deleteFile(File file) {
    if (file.canRead()) {
      try {
        Files.delete(file.toPath());
      } catch (IOException e) {
        logger.warn("Cannot delete file", e);
        return false;
      }
    }
    return true;
  }

  /**
   * @param ingestRequest
   *
   * @return the File pointer to the XML ATR file
   */
  File getXmlAtrFile(IngestRequest ingestRequest) {
    return new File(workDir, ingestRequest.getJsonPath() + RESULT_EXTENSION);
  }

  /**
   * @return the list of existing IngestRequests. Some can be not ready or ended
   */
  synchronized List<IngestRequest> getExistingIngests() {
    List<IngestRequest> list = new ArrayList<>();
    File[] files = baseDir.listFiles(JSON_ONLY);
    if (files != null) {
      for (File file : files) {
        try {
          IngestRequest ingestRequest =
              JsonHandler.getFromFile(file, IngestRequest.class);
          list.add(ingestRequest);
        } catch (InvalidParseOperationException ignored) {
          // File could be deleted during read operation
          SysErrLogger.FAKE_LOGGER.ignoreLog(ignored);
        }
      }
    }
    return list;
  }

  /**
   * @param filename
   *
   * @return the IngestRequest if found
   *
   * @throws InvalidParseOperationException
   */
  synchronized IngestRequest getSpecificIngestRequest(String filename)
      throws InvalidParseOperationException {
    File file = new File(baseDir, filename);
    if (file.exists()) {
      return JsonHandler.getFromFile(file, IngestRequest.class);
    }
    throw new InvalidParseOperationException("Cannot find " + filename);
  }
}