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.security.Principal;
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();
}
}
}
}