Newer
Older
/*
* This file is part of vospace-rest
* Copyright (C) 2021 Istituto Nazionale di Astrofisica
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package it.inaf.oats.vospace;
import it.inaf.ia2.aa.data.User;
import it.inaf.oats.vospace.datamodel.NodeUtils;
import it.inaf.oats.vospace.exception.NodeBusyException;
import it.inaf.oats.vospace.exception.NodeNotFoundException;
import it.inaf.oats.vospace.exception.PermissionDeniedException;
import it.inaf.oats.vospace.persistence.NodeDAO.ShortNodeDescriptor;
import java.util.List;
import java.util.Optional;
import net.ivoa.xml.vospace.v2.Transfer;
import org.springframework.dao.CannotSerializeTransactionException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
@Service
@EnableTransactionManagement
public class CopyService extends AbstractNodeService {
@Transactional(rollbackFor = {Exception.class}, isolation = Isolation.REPEATABLE_READ)
public List<String> processCopyNodes(Transfer transfer, String jobId, User user) {
String sourcePath = URIUtils.returnVosPathFromNodeURI(transfer.getTarget(), authority);
// Get Destination Vos Path (it's in transfer direction)
String destinationPath = URIUtils.returnVosPathFromNodeURI(transfer.getDirection(), authority);
// Destination source to be returned, null if no copy was performed
String destinationCopyRoot = null;
this.validatePath(sourcePath);
this.validatePath(destinationPath);
if (sourcePath.equals(destinationPath)) {
throw new IllegalArgumentException("Cannot copy node to itself");
}
// Check if destination is subpath of source
// Linux-like: "cannot copy to a subdirectory of itself"
if (destinationPath.startsWith(sourcePath + "/")) {
throw new IllegalArgumentException("Cannot copy node to a subdirectory of its own path");
// Check if destination equals parent path of source
if (NodeUtils.getParentPath(sourcePath).equals(destinationPath)) {
throw new IllegalArgumentException("Cannot duplicate node at same path without renaming it");
try {
// check source branch for read and lock it
this.checkBranchForReadAndLock(sourcePath, jobId, user);
// Check destination
Optional<ShortNodeDescriptor> destShortNodeDescriptor
= nodeDao.getShortNodeDescriptor(destinationPath, user.getName(), user.getGroups());
if (destShortNodeDescriptor.isPresent()) {
this.validateDestinationContainer(destShortNodeDescriptor.get(), destinationPath);
destinationCopyRoot = destinationPath + "/" + NodeUtils.getNodeName(sourcePath);
} else {
// Check if parent exists
String destinationParentPath = NodeUtils.getParentPath(destinationPath);
Optional<ShortNodeDescriptor> destShortNodeDescriptorParent
= nodeDao.getShortNodeDescriptor(destinationParentPath, user.getName(), user.getGroups());
if (destShortNodeDescriptorParent.isPresent()) {
this.validateDestinationContainer(destShortNodeDescriptorParent.get(), destinationParentPath);
destinationCopyRoot = destinationPath;
} else {
throw new UnsupportedOperationException("Creation of destination upon copy not supported");
}
}
nodeDao.copyBranch(
sourcePath,
destinationCopyRoot);
} catch (CannotSerializeTransactionException ex) {
// Concurrent transactions attempted to modify this set of nodes
throw new NodeBusyException(sourcePath);
}
return List.of(sourcePath, destinationCopyRoot);
private void checkBranchForReadAndLock(String sourcePath, String jobId, User user) {
// Get source node
Optional<Long> sourceIdOpt = nodeDao.getNodeId(sourcePath);
long sourceId = sourceIdOpt.orElseThrow(() -> new NodeNotFoundException(sourcePath));
if (nodeDao.isBranchBusy(sourceId)) {
throw new NodeBusyException(sourcePath);
}
if (!nodeDao.isBranchReadable(sourceId, user.getName(), user.getGroups())) {
throw new PermissionDeniedException(sourcePath);
}
nodeDao.setBranchJobId(sourceId, jobId);
}