diff --git a/src/main/java/it/inaf/ia2/transfer/controller/ArchiveFileController.java b/src/main/java/it/inaf/ia2/transfer/controller/ArchiveFileController.java index 7f5ed6c7bab1fe79619e07eaaf532c44ce23bd38..4e7a9fb3b8d0458db07bce63a4883fd8a573ec8e 100644 --- a/src/main/java/it/inaf/ia2/transfer/controller/ArchiveFileController.java +++ b/src/main/java/it/inaf/ia2/transfer/controller/ArchiveFileController.java @@ -6,7 +6,6 @@ package it.inaf.ia2.transfer.controller; import it.inaf.ia2.transfer.auth.TokenPrincipal; -import it.inaf.ia2.transfer.exception.PermissionDeniedException; import it.inaf.ia2.transfer.service.ArchiveJob; import it.inaf.ia2.transfer.service.ArchiveJob.Type; import it.inaf.ia2.transfer.service.ArchiveService; diff --git a/src/main/java/it/inaf/ia2/transfer/controller/CopyController.java b/src/main/java/it/inaf/ia2/transfer/controller/CopyController.java index 72041fb2085d56ff64244de9a033212d895fe0b9..96df91a43aa85cd11c3d79b189f713c6a2cdda7f 100644 --- a/src/main/java/it/inaf/ia2/transfer/controller/CopyController.java +++ b/src/main/java/it/inaf/ia2/transfer/controller/CopyController.java @@ -10,10 +10,14 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +import org.springframework.beans.factory.annotation.Autowired; +import it.inaf.ia2.transfer.service.CopyService; @RestController public class CopyController extends AuthenticatedFileController { - + + @Autowired + private CopyService copyService; @PostMapping(value = "/copy", consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<?> copyFiles(@RequestBody CopyRequest copyRequest) { diff --git a/src/main/java/it/inaf/ia2/transfer/controller/CopyRequest.java b/src/main/java/it/inaf/ia2/transfer/controller/CopyRequest.java index d725d46efa30dc0fe5174ecc0f94657f24aabb75..186e085175f96124caa1814cbd3732af8ea4e1a6 100644 --- a/src/main/java/it/inaf/ia2/transfer/controller/CopyRequest.java +++ b/src/main/java/it/inaf/ia2/transfer/controller/CopyRequest.java @@ -8,8 +8,8 @@ package it.inaf.ia2.transfer.controller; public class CopyRequest { String jobId; - String target; - String direction; + String sourceRootVosPath; + String destinationRootVosPath; public String getJobId() { return jobId; @@ -19,20 +19,20 @@ public class CopyRequest { this.jobId = jobId; } - public String getTarget() { - return target; + public String getSourceRootVosPath() { + return sourceRootVosPath; } - public void setTarget(String target) { - this.target = target; + public void setSourceRootVosPath(String sourceRootVosPath) { + this.sourceRootVosPath = sourceRootVosPath; } - public String getDirection() { - return direction; + public String getDestinationRootVosPath() { + return destinationRootVosPath; } - public void setDirection(String direction) { - this.direction = direction; + public void setDestinationRootVosPath(String destinationRootVosPath) { + this.destinationRootVosPath = destinationRootVosPath; } } diff --git a/src/main/java/it/inaf/ia2/transfer/service/CopyService.java b/src/main/java/it/inaf/ia2/transfer/service/CopyService.java new file mode 100644 index 0000000000000000000000000000000000000000..1e31e655bc472cb92614fec0cc72cd216cfec88c --- /dev/null +++ b/src/main/java/it/inaf/ia2/transfer/service/CopyService.java @@ -0,0 +1,153 @@ +/* + * 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.auth.TokenPrincipal; +import it.inaf.ia2.transfer.exception.InsufficientStorageException; +import it.inaf.ia2.transfer.exception.JobException; +import it.inaf.ia2.transfer.exception.JobException.Type; +import it.inaf.ia2.transfer.persistence.FileDAO; +import it.inaf.ia2.transfer.persistence.JobDAO; +import it.inaf.ia2.transfer.persistence.LocationDAO; +import it.inaf.ia2.transfer.persistence.model.FileInfo; +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.io.OutputStream; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.security.Principal; +import java.util.List; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; +import net.ivoa.xml.uws.v1.ExecutionPhase; +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.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Service; +import org.springframework.util.FileSystemUtils; +import org.springframework.util.unit.DataSize; +import org.springframework.web.client.RestTemplate; + +@Service +public class CopyService { + + private static final Logger LOG = LoggerFactory.getLogger(CopyService.class); + + @Autowired + private FileDAO fileDAO; + + @Autowired + private LocationDAO locationDAO; + + @Autowired + private JobDAO jobDAO; + + @Autowired + private AuthorizationService authorizationService; + + @Autowired + private RestTemplate restTemplate; + + @Value("${upload_location_id}") + private int uploadLocationId; + + // Maximum size of the working directory for each registered user + @Value("${generated.dir.max-size}") + private DataSize generatedDirMaxSize; + + + + + public void copyFiles(String sourceRootVosPath, + String destinationRootVosPath, String jobId) { + // We use jobId to identify nodes created by the REST part of CopyNode + // We expect them to be locked + + + + + } + + + + 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 <O extends OutputStream, E> void downloadFileIntoArchive(FileInfo fileInfo, String relPath, TokenPrincipal tokenPrincipal, ArchiveHandler<O, E> handler, String baseUrl) { + + if (baseUrl == null) { + LOG.error("Location URL not found for location " + fileInfo.getLocationId()); + throw new JobException(Type.FATAL, "Internal Fault") + .setErrorDetail("InternalFault: Unable to retrieve location of file " + fileInfo.getVirtualPath()); + } + + String url = baseUrl + "/" + fileInfo.getVirtualName(); + + LOG.trace("Downloading file from " + url); + + restTemplate.execute(url, HttpMethod.GET, req -> { + HttpHeaders headers = req.getHeaders(); + if (tokenPrincipal.getToken() != null) { + headers.setBearerAuth(tokenPrincipal.getToken()); + } + }, res -> { + File tmpFile = Files.createTempFile("download", null).toFile(); + try ( FileOutputStream os = new FileOutputStream(tmpFile)) { + res.getBody().transferTo(os); + handler.putNextEntry(tmpFile, relPath); + try ( FileInputStream is = new FileInputStream(tmpFile)) { + is.transferTo(handler.getOutputStream()); + } + } finally { + tmpFile.delete(); + } + return null; + }, new Object[]{}); + } + + private <O extends OutputStream, E> void writeFileIntoArchive(FileInfo fileInfo, String relPath, TokenPrincipal tokenPrincipal, ArchiveHandler<O, E> handler) throws IOException { + if (!authorizationService.isDownloadable(fileInfo, tokenPrincipal)) { + throw new JobException(Type.FATAL, "Permission Denied") + .setErrorDetail("PermissionDenied: " + fileInfo.getVirtualPath()); + } + + File file = new File(fileInfo.getOsPath()); + LOG.trace("Adding file " + file.getAbsolutePath() + " to tar archive"); + + try ( InputStream is = new FileInputStream(file)) { + handler.putNextEntry(file, relPath); + is.transferTo(handler.getOutputStream()); + } + } +}