Skip to content
ArchiveService.java 5.47 KiB
Newer Older
/*
 * This file is part of vospace-file-service
 * Copyright (C) 2021 Istituto Nazionale di Astrofisica
 * SPDX-License-Identifier: GPL-3.0-or-later
 */
package it.inaf.ia2.transfer.service;

import it.inaf.ia2.transfer.persistence.FileDAO;
import it.inaf.ia2.transfer.persistence.model.FileInfo;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.List;
import org.kamranzafar.jtar.TarEntry;
import org.kamranzafar.jtar.TarOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.FileSystemUtils;

@Service
public class ArchiveService {

    private static final Logger LOG = LoggerFactory.getLogger(ArchiveService.class);

    private final static int BUFFER_SIZE = 100 * 1024;

    @Autowired
    private FileDAO fileDAO;

    @Autowired
    private AuthorizationService authorizationService;

    private final File generatedDir;

    public ArchiveService(@Value("${generated.dir}") String generatedDir) {
        this.generatedDir = new File(generatedDir);
        if (!this.generatedDir.exists()) {
            if (!this.generatedDir.mkdirs()) {
                throw new IllegalStateException("Unable to create directory " + this.generatedDir.getAbsolutePath());
            }
        }
    }

    public void createArchive(ArchiveJob job) {

        LOG.trace("Started archive job " + job.getJobId());

        try {
            // TODO: check total size limit
            // TODO: switch on archive type
            File parentDir = getArchiveParentDir(job.getPrincipal());

            if (!parentDir.exists()) {
                if (!parentDir.mkdirs()) {
                    throw new IllegalStateException("Unable to create directory " + parentDir.getAbsolutePath());
                }
            }

            File archiveFile = parentDir.toPath().resolve(job.getJobId() + "." + job.getType().getExtension()).toFile();
            if (!archiveFile.createNewFile()) {
                throw new IllegalStateException("Unable to create file " + archiveFile.getAbsolutePath());
            }

            String commonParent = getCommonParent(job.getVosPaths());
            // support directory used to generate folder inside tar files (path is redefined each time by TarEntry class)
            File supportDir = Files.createTempDirectory("dir").toFile();

            try ( TarOutputStream tos = new TarOutputStream(
                    new BufferedOutputStream(new FileOutputStream(archiveFile)))) {

                for (FileInfo fileInfo : fileDAO.getArchiveFileInfos(job.getVosPaths())) {

                    String relPath = fileInfo.getVirtualPath().substring(commonParent.length());

                    if (fileInfo.isDirectory()) {
                        tos.putNextEntry(new TarEntry(supportDir, relPath));
                        continue;
                    }

                    // TODO: handle different locations
                    if (!authorizationService.isDownloadable(fileInfo, job.getPrincipal())) {
                        // TODO: proper exception type
                        throw new RuntimeException("Unauthorized");
                    }

                    File file = new File(fileInfo.getOsPath());
                    LOG.trace("Adding file " + file.getAbsolutePath() + " to tar archive");
                    writeFileIntoTarArchive(file, relPath, tos);
            } finally {
                FileSystemUtils.deleteRecursively(supportDir);
            }
            // TODO: update job status

        } catch (Throwable t) {
            LOG.error("Error happened creating archive", t);
        }
    }

    public File getArchiveParentDir(Principal principal) {
        return generatedDir.toPath().resolve(principal.getName()).toFile();
    }

    private String getCommonParent(List<String> vosPaths) {
        String commonParent = null;
        for (String vosPath : vosPaths) {
            if (commonParent == null) {
                commonParent = vosPath;
            } else {
                StringBuilder newCommonParent = new StringBuilder();
                boolean same = true;
                for (int i = 0; same && i < Math.min(commonParent.length(), vosPath.length()); i++) {
                    if (commonParent.charAt(i) == vosPath.charAt(i)) {
                        newCommonParent.append(commonParent.charAt(i));
                    } else {
                        same = false;
                    }
                }
                commonParent = newCommonParent.toString();
            }
        }
        return commonParent;
    }

    private void writeFileIntoTarArchive(File file, String path, TarOutputStream tos) throws IOException {
        TarEntry tarEntry = new TarEntry(file, path);

        try ( InputStream is = new FileInputStream(file)) {
            tos.putNextEntry(tarEntry);
            try ( BufferedInputStream origin = new BufferedInputStream(is)) {
                int count;
                byte data[] = new byte[BUFFER_SIZE];

                while ((count = origin.read(data)) != -1) {
                    tos.write(data, 0, count);
                }

                tos.flush();
            }
        }
    }
}