TarUtility.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.common.tar;

import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.utils.IOUtils;
import org.waarp.common.file.FileUtils;
import org.waarp.common.logging.SysErrLogger;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * TAR support
 */
public final class TarUtility {
  private static final File[] FILE_0_LENGTH = {};

  private TarUtility() {
  }

  /**
   * Create a new Tar from a root directory
   *
   * @param directory the base directory
   * @param filename the output filename
   * @param absolute store absolute filepath (from directory) or only
   *     filename
   *
   * @return True if OK
   */
  public static boolean createTarFromDirectory(final String directory,
                                               final String filename,
                                               final boolean absolute) {
    final File rootDir = new File(directory);
    final File saveFile = new File(filename);
    // recursive call
    final TarArchiveOutputStream taos;
    try {
      taos = new TarArchiveOutputStream(new FileOutputStream(saveFile));
    } catch (final FileNotFoundException e) {
      return false;
    }
    taos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
    try {
      recurseFiles(rootDir, rootDir, taos, absolute);
    } catch (final IOException e2) {
      FileUtils.close(taos);
      return false;
    }
    try {
      taos.finish();
    } catch (final IOException e1) {
      // ignore
    }
    FileUtils.close(taos);
    return true;
  }

  /**
   * Recursive traversal to add files
   *
   * @param root
   * @param file
   * @param taos
   * @param absolute
   *
   * @throws IOException
   */
  private static void recurseFiles(final File root, final File file,
                                   final TarArchiveOutputStream taos,
                                   final boolean absolute) throws IOException {
    if (file.isDirectory()) {
      // recursive call
      final File[] files = file.listFiles();
      for (final File file2 : files) {
        recurseFiles(root, file2, taos, absolute);
      }
    } else if (!file.getName().endsWith(".tar") &&
               !file.getName().endsWith(".TAR")) {
      final String filename;
      if (absolute) {
        filename =
            file.getAbsolutePath().substring(root.getAbsolutePath().length());
      } else {
        filename = file.getName();
      }
      final TarArchiveEntry tae = new TarArchiveEntry(filename);
      tae.setSize(file.length());
      taos.putArchiveEntry(tae);
      final FileInputStream fis = new FileInputStream(file);
      IOUtils.copy(fis, taos);
      taos.closeArchiveEntry();
    }
  }

  /**
   * Create a new Tar from a list of Files (only name of files will be used)
   *
   * @param files list of files to add
   * @param filename the output filename
   *
   * @return True if OK
   */
  public static boolean createTarFromFiles(final List<File> files,
                                           final String filename) {
    return createTarFromFiles(files.toArray(FILE_0_LENGTH), filename);
  }

  /**
   * Create a new Tar from an array of Files (only name of files will be used)
   *
   * @param files array of files to add
   * @param filename the output filename
   *
   * @return True if OK
   */
  public static boolean createTarFromFiles(final File[] files,
                                           final String filename) {
    final File saveFile = new File(filename);
    // recursive call
    final TarArchiveOutputStream taos;
    try {
      taos = new TarArchiveOutputStream(new FileOutputStream(saveFile));
    } catch (final FileNotFoundException e) {
      return false;
    }
    taos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
    for (final File file : files) {
      try {
        addFile(file, taos);
      } catch (final IOException e) {
        FileUtils.close(taos);
        return false;
      }
    }
    try {
      taos.finish();
    } catch (final IOException e1) {
      // ignore
    }
    FileUtils.close(taos);
    return true;
  }

  /**
   * Recursive traversal to add files
   *
   * @param file
   * @param taos
   *
   * @throws IOException
   */
  private static void addFile(final File file,
                              final TarArchiveOutputStream taos)
      throws IOException {
    final String filename;
    filename = file.getName();
    final TarArchiveEntry tae = new TarArchiveEntry(filename);
    tae.setSize(file.length());
    taos.putArchiveEntry(tae);
    final FileInputStream fis = new FileInputStream(file);
    IOUtils.copy(fis, taos);
    taos.closeArchiveEntry();
  }

  /**
   * Extract all files from Tar into the specified directory
   *
   * @param tarFile
   * @param directory
   *
   * @return the list of extracted filenames
   *
   * @throws IOException
   */
  public static List<String> unTar(final File tarFile, final File directory)
      throws IOException {
    final List<String> result = new ArrayList<String>();
    final InputStream inputStream = new FileInputStream(tarFile);
    final TarArchiveInputStream in = new TarArchiveInputStream(inputStream);
    try {
      TarArchiveEntry entry = in.getNextTarEntry();
      while (entry != null) {
        if (entry.isDirectory()) {
          entry = in.getNextTarEntry();
          continue;
        }
        final File curfile = new File(directory, entry.getName());
        final File parent = curfile.getParentFile();
        if (!parent.exists()) {
          parent.mkdirs();//NOSONAR
        }
        final OutputStream out = new FileOutputStream(curfile);
        try {
          IOUtils.copy(in, out);
        } finally {
          FileUtils.close(out);
        }
        result.add(entry.getName());
        entry = in.getNextTarEntry();
      }
    } finally {
      FileUtils.close(in);
    }
    return result;
  }

  public static void main(final String[] args) {
    if (args.length < 3) {
      SysErrLogger.FAKE_LOGGER.syserr("You need to provide 3 arguments:\n" +
                                      "   option filedest.tar \"source\"\n" +
                                      "   where option=1 means untar and source is a directory\n" +
                                      "   option=2 means tar and source is a directory\n" +
                                      "   option=3 means tar and source is a list of files comma separated");
      System.exit(1);//NOSONAR
    }
    final int option = Integer.parseInt(args[0]);
    final String tarfile = args[1];
    final String tarsource = args[2];
    final String[] tarfiles;
    if (option == 3) {
      tarfiles = args[2].split(",");
      final File[] files = new File[tarfiles.length];
      for (int i = 0; i < tarfiles.length; i++) {
        files[i] = new File(tarfiles[i]);
      }
      if (createTarFromFiles(files, tarfile)) {
        SysErrLogger.FAKE_LOGGER.sysout("TAR OK from multiple files");
      } else {
        SysErrLogger.FAKE_LOGGER.syserr("TAR KO from multiple files");
      }
    } else if (option == 2) {
      if (createTarFromDirectory(tarsource, tarfile, false)) {
        SysErrLogger.FAKE_LOGGER.sysout("TAR OK from directory");
      } else {
        SysErrLogger.FAKE_LOGGER.syserr("TAR KO from directory");
      }
    } else if (option == 1) {
      final File tarFile = new File(tarfile);
      final File directory = new File(tarsource);
      List<String> result = null;
      try {
        result = unTar(tarFile, directory);
      } catch (final IOException e) {
        SysErrLogger.FAKE_LOGGER.syserr(e);
      }
      if (result == null || result.isEmpty()) {
        SysErrLogger.FAKE_LOGGER.syserr("UNTAR KO from directory");
      } else {
        for (final String string : result) {
          SysErrLogger.FAKE_LOGGER.sysout("File: " + string);
        }
      }
    }

  }
}