Loading src/main/java/it/inaf/oats/vospace/CopyService.java +13 −10 Original line number Diff line number Diff line Loading @@ -28,7 +28,7 @@ public class CopyService extends AbstractNodeService { private NodeBranchService nodeBranchService; @Transactional(rollbackFor = {Exception.class}, isolation = Isolation.REPEATABLE_READ) public List<String> processCopyNodes(Transfer transfer, String jobId, User user) { public String processCopyNodes(Transfer transfer, String jobId, User user) { if (transfer.getTarget().size() != 1) { throw new InvalidArgumentException("Invalid target size for copyNode: " + transfer.getTarget().size()); } Loading @@ -39,14 +39,16 @@ public class CopyService extends AbstractNodeService { // 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)) { return List.of(); return null; } List<String> copiedNodesPaths = null; try { // check source branch for read and lock it Loading @@ -58,10 +60,7 @@ public class CopyService extends AbstractNodeService { if (destShortNodeDescriptor.isPresent()) { this.validateDestinationContainer(destShortNodeDescriptor.get(), destinationPath); copiedNodesPaths = nodeDao.copyBranch( sourcePath, destinationPath + "/" + NodeUtils.getNodeName(sourcePath), jobId); destinationCopyRoot = destinationPath + "/" + NodeUtils.getNodeName(sourcePath); } else { // Check if parent exists Loading @@ -70,8 +69,7 @@ public class CopyService extends AbstractNodeService { = nodeDao.getShortNodeDescriptor(destinationParentPath, user.getName(), user.getGroups()); if (destShortNodeDescriptorParent.isPresent()) { this.validateDestinationContainer(destShortNodeDescriptorParent.get(), destinationParentPath); copiedNodesPaths = nodeDao.copyBranch(sourcePath, destinationPath, jobId); destinationCopyRoot = destinationPath; } else { throw new UnsupportedOperationException("Creation of destination upon copy not supported"); Loading @@ -79,12 +77,17 @@ public class CopyService extends AbstractNodeService { } nodeDao.copyBranch( sourcePath, destinationCopyRoot, jobId); } catch (CannotSerializeTransactionException ex) { // Concurrent transactions attempted to modify this set of nodes throw new NodeBusyException(sourcePath); } return copiedNodesPaths; return destinationCopyRoot; } Loading src/main/java/it/inaf/oats/vospace/JobService.java +6 −2 Original line number Diff line number Diff line Loading @@ -40,6 +40,9 @@ public class JobService { @Autowired private CopyService copyService; @Autowired private NodeBranchService nodeBranchService; @Autowired private AsyncTransferService asyncTransfService; Loading Loading @@ -180,7 +183,8 @@ public class JobService { copyService.processCopyNodes(transfer, jobSummary.getJobId(), user); // add file service copy logic job.setPhase(ExecutionPhase.COMPLETED); // the file service part will unlock nodes and set job phase // to completed }); }); } Loading src/main/java/it/inaf/oats/vospace/NodeBranchService.java +5 −1 Original line number Diff line number Diff line Loading @@ -64,4 +64,8 @@ public class NodeBranchService { } public void unlockNodes(String jobId) { nodeDao.releaseBusyNodesByJobId(jobId); } } src/main/java/it/inaf/oats/vospace/persistence/NodeDAO.java +14 −10 Original line number Diff line number Diff line Loading @@ -344,7 +344,7 @@ public class NodeDAO { }); } public List<String> copyBranch(String sourceVosPath, String destVosPath, String jobId) { public void copyBranch(String sourceVosPath, String destVosPath, String jobId) { String destVosParentPath = NodeUtils.getParentPath(destVosPath); String destName = NodeUtils.getNodeName(destVosPath); Loading Loading @@ -376,8 +376,6 @@ public class NodeDAO { + "name, type, location_id, creator_id, group_write, group_read, is_public, ?\n" + "FROM copied_nodes_paths\n"; String returning = "RETURNING get_vos_path(node_id) AS returning_path"; String sql = parentInsert + "WITH RECURSIVE path_prefix AS (" + ctePathPrefix + "),\n" Loading @@ -385,19 +383,15 @@ public class NodeDAO { + cteCopiedNodes + "),\n" + "copied_nodes_paths AS (" + cteCopiedNodesPaths + ")\n" + parentSelect + returning; + parentSelect; return jdbcTemplate.query(conn -> { jdbcTemplate.update(conn -> { PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, destVosParentPath); ps.setString(2, destName); ps.setString(3, sourceVosPath); ps.setString(4, jobId); return ps; }, (rs, row) -> { return rs.getString("returning_path"); }); } Loading @@ -424,6 +418,16 @@ public class NodeDAO { }); } public void releaseBusyNodesByJobId(String jobId) { String sql = "UPDATE node SET job_id = NULL WHERE job_id = ?"; jdbcTemplate.update(conn -> { PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, jobId); return ps; }); } public boolean isBranchWritable(long parentNodeId, String userId, List<String> userGroups) { String sql = "SELECT COUNT(c.node_id) = 0 " Loading src/test/java/it/inaf/oats/vospace/CopyServiceTest.java +10 −3 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ import java.util.Arrays; import java.util.List; import java.util.Optional; import net.ivoa.xml.vospace.v2.Transfer; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; Loading Loading @@ -151,7 +152,10 @@ public class CopyServiceTest { assertTrue(destId.isPresent()); // copy copyService.processCopyNodes(getTransfer("/test3/m1", "/test4"), "job_pippo", user); String copyDestination = copyService.processCopyNodes(getTransfer("/test3/m1", "/test4"), "job_pippo", user); assertEquals("/test4/m1", copyDestination); // source has been moved Optional<Long> oldSourceId = nodeDao.getNodeId("/test3/m1"); Loading Loading @@ -183,7 +187,10 @@ public class CopyServiceTest { assertTrue(destId.isEmpty()); // copy copyService.processCopyNodes(getTransfer("/test3/m1/m2", "/test3/m1/m2_copy"), "job_pippo", user); String copyDestination = copyService.processCopyNodes(getTransfer("/test3/m1/m2", "/test3/m1/m2_copy"), "job_pippo", user); assertEquals("/test3/m1/m2_copy", copyDestination); // source has been moved Optional<Long> oldSourceId = nodeDao.getNodeId("/test3/m1"); Loading Loading
src/main/java/it/inaf/oats/vospace/CopyService.java +13 −10 Original line number Diff line number Diff line Loading @@ -28,7 +28,7 @@ public class CopyService extends AbstractNodeService { private NodeBranchService nodeBranchService; @Transactional(rollbackFor = {Exception.class}, isolation = Isolation.REPEATABLE_READ) public List<String> processCopyNodes(Transfer transfer, String jobId, User user) { public String processCopyNodes(Transfer transfer, String jobId, User user) { if (transfer.getTarget().size() != 1) { throw new InvalidArgumentException("Invalid target size for copyNode: " + transfer.getTarget().size()); } Loading @@ -39,14 +39,16 @@ public class CopyService extends AbstractNodeService { // 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)) { return List.of(); return null; } List<String> copiedNodesPaths = null; try { // check source branch for read and lock it Loading @@ -58,10 +60,7 @@ public class CopyService extends AbstractNodeService { if (destShortNodeDescriptor.isPresent()) { this.validateDestinationContainer(destShortNodeDescriptor.get(), destinationPath); copiedNodesPaths = nodeDao.copyBranch( sourcePath, destinationPath + "/" + NodeUtils.getNodeName(sourcePath), jobId); destinationCopyRoot = destinationPath + "/" + NodeUtils.getNodeName(sourcePath); } else { // Check if parent exists Loading @@ -70,8 +69,7 @@ public class CopyService extends AbstractNodeService { = nodeDao.getShortNodeDescriptor(destinationParentPath, user.getName(), user.getGroups()); if (destShortNodeDescriptorParent.isPresent()) { this.validateDestinationContainer(destShortNodeDescriptorParent.get(), destinationParentPath); copiedNodesPaths = nodeDao.copyBranch(sourcePath, destinationPath, jobId); destinationCopyRoot = destinationPath; } else { throw new UnsupportedOperationException("Creation of destination upon copy not supported"); Loading @@ -79,12 +77,17 @@ public class CopyService extends AbstractNodeService { } nodeDao.copyBranch( sourcePath, destinationCopyRoot, jobId); } catch (CannotSerializeTransactionException ex) { // Concurrent transactions attempted to modify this set of nodes throw new NodeBusyException(sourcePath); } return copiedNodesPaths; return destinationCopyRoot; } Loading
src/main/java/it/inaf/oats/vospace/JobService.java +6 −2 Original line number Diff line number Diff line Loading @@ -40,6 +40,9 @@ public class JobService { @Autowired private CopyService copyService; @Autowired private NodeBranchService nodeBranchService; @Autowired private AsyncTransferService asyncTransfService; Loading Loading @@ -180,7 +183,8 @@ public class JobService { copyService.processCopyNodes(transfer, jobSummary.getJobId(), user); // add file service copy logic job.setPhase(ExecutionPhase.COMPLETED); // the file service part will unlock nodes and set job phase // to completed }); }); } Loading
src/main/java/it/inaf/oats/vospace/NodeBranchService.java +5 −1 Original line number Diff line number Diff line Loading @@ -64,4 +64,8 @@ public class NodeBranchService { } public void unlockNodes(String jobId) { nodeDao.releaseBusyNodesByJobId(jobId); } }
src/main/java/it/inaf/oats/vospace/persistence/NodeDAO.java +14 −10 Original line number Diff line number Diff line Loading @@ -344,7 +344,7 @@ public class NodeDAO { }); } public List<String> copyBranch(String sourceVosPath, String destVosPath, String jobId) { public void copyBranch(String sourceVosPath, String destVosPath, String jobId) { String destVosParentPath = NodeUtils.getParentPath(destVosPath); String destName = NodeUtils.getNodeName(destVosPath); Loading Loading @@ -376,8 +376,6 @@ public class NodeDAO { + "name, type, location_id, creator_id, group_write, group_read, is_public, ?\n" + "FROM copied_nodes_paths\n"; String returning = "RETURNING get_vos_path(node_id) AS returning_path"; String sql = parentInsert + "WITH RECURSIVE path_prefix AS (" + ctePathPrefix + "),\n" Loading @@ -385,19 +383,15 @@ public class NodeDAO { + cteCopiedNodes + "),\n" + "copied_nodes_paths AS (" + cteCopiedNodesPaths + ")\n" + parentSelect + returning; + parentSelect; return jdbcTemplate.query(conn -> { jdbcTemplate.update(conn -> { PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, destVosParentPath); ps.setString(2, destName); ps.setString(3, sourceVosPath); ps.setString(4, jobId); return ps; }, (rs, row) -> { return rs.getString("returning_path"); }); } Loading @@ -424,6 +418,16 @@ public class NodeDAO { }); } public void releaseBusyNodesByJobId(String jobId) { String sql = "UPDATE node SET job_id = NULL WHERE job_id = ?"; jdbcTemplate.update(conn -> { PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, jobId); return ps; }); } public boolean isBranchWritable(long parentNodeId, String userId, List<String> userGroups) { String sql = "SELECT COUNT(c.node_id) = 0 " Loading
src/test/java/it/inaf/oats/vospace/CopyServiceTest.java +10 −3 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ import java.util.Arrays; import java.util.List; import java.util.Optional; import net.ivoa.xml.vospace.v2.Transfer; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; Loading Loading @@ -151,7 +152,10 @@ public class CopyServiceTest { assertTrue(destId.isPresent()); // copy copyService.processCopyNodes(getTransfer("/test3/m1", "/test4"), "job_pippo", user); String copyDestination = copyService.processCopyNodes(getTransfer("/test3/m1", "/test4"), "job_pippo", user); assertEquals("/test4/m1", copyDestination); // source has been moved Optional<Long> oldSourceId = nodeDao.getNodeId("/test3/m1"); Loading Loading @@ -183,7 +187,10 @@ public class CopyServiceTest { assertTrue(destId.isEmpty()); // copy copyService.processCopyNodes(getTransfer("/test3/m1/m2", "/test3/m1/m2_copy"), "job_pippo", user); String copyDestination = copyService.processCopyNodes(getTransfer("/test3/m1/m2", "/test3/m1/m2_copy"), "job_pippo", user); assertEquals("/test3/m1/m2_copy", copyDestination); // source has been moved Optional<Long> oldSourceId = nodeDao.getNodeId("/test3/m1"); Loading