Loading src/main/java/it/inaf/oats/vospace/AbstractNodeService.java 0 → 100644 +46 −0 Original line number Original line Diff line number Diff line /* * 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.oats.vospace.exception.InternalFaultException; import it.inaf.oats.vospace.exception.NodeBusyException; import it.inaf.oats.vospace.exception.PermissionDeniedException; import it.inaf.oats.vospace.persistence.NodeDAO; import it.inaf.oats.vospace.persistence.NodeDAO.ShortNodeDescriptor; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; public abstract class AbstractNodeService { @Autowired protected NodeDAO nodeDao; @Value("${vospace-authority}") protected String authority; protected void validatePath(String path) { if (path.equals("/")) { throw new IllegalArgumentException("Cannot move root node or to root node"); } } protected void validateDestinationContainer(ShortNodeDescriptor snd, String destinationVosPath) { if (snd.isBusy()) { throw new NodeBusyException(destinationVosPath); } if (snd.isPermissionDenied()) { throw new PermissionDeniedException(destinationVosPath); } if (!snd.isWritable()) { throw new InternalFaultException("Destination is not writable: " + destinationVosPath); } if (!snd.isContainer()) { throw new InternalFaultException("Existing destination is not a container: " + destinationVosPath); } } } src/main/java/it/inaf/oats/vospace/CopyService.java 0 → 100644 +117 −0 Original line number Original line Diff line number Diff line /* * 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) { // Get Source Vos Path 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); } } src/main/java/it/inaf/oats/vospace/FileServiceClient.java +60 −1 Original line number Original line Diff line number Diff line Loading @@ -87,6 +87,65 @@ public class FileServiceClient { }, new Object[]{}); }, new Object[]{}); } } public void startFileCopyJob(String sourceVosPath, String destiantionVosPath, String jobId, User user) { CopyRequest copyRequest = new CopyRequest(); copyRequest.setJobId(jobId); copyRequest.setSourceRootVosPath(sourceVosPath); copyRequest.setDestinationRootVosPath(destiantionVosPath); String url = fileServiceUrl + "/copy"; String token = user.getAccessToken(); restTemplate.execute(url, HttpMethod.POST, req -> { HttpHeaders headers = req.getHeaders(); if (token != null) { headers.setBearerAuth(token); } headers.setContentType(MediaType.APPLICATION_JSON); try (OutputStream os = req.getBody()) { MAPPER.writeValue(os, copyRequest); } }, res -> { return null; }, new Object[]{}); } public static class CopyRequest { private String jobId; private String sourceRootVosPath; private String destinationRootVosPath; public String getJobId() { return jobId; } public void setJobId(String jobId) { this.jobId = jobId; } public String getSourceRootVosPath() { return sourceRootVosPath; } public void setSourceRootVosPath(String sourceRootVosPath) { this.sourceRootVosPath = sourceRootVosPath; } public String getDestinationRootVosPath() { return destinationRootVosPath; } public void setDestinationRootVosPath(String destinationRootVosPath) { this.destinationRootVosPath = destinationRootVosPath; } } public static class ArchiveRequest { public static class ArchiveRequest { private String type; private String type; Loading src/main/java/it/inaf/oats/vospace/JobService.java +40 −1 Original line number Original line Diff line number Diff line Loading @@ -18,6 +18,8 @@ import it.inaf.oats.vospace.exception.InvalidArgumentException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.stereotype.Service; import it.inaf.oats.vospace.exception.VoSpaceErrorSummarizableException; import it.inaf.oats.vospace.exception.VoSpaceErrorSummarizableException; import it.inaf.oats.vospace.persistence.NodeDAO; import java.util.List; import java.util.Optional; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture; import java.util.function.Function; import java.util.function.Function; Loading @@ -40,12 +42,21 @@ public class JobService { @Autowired @Autowired private MoveService moveService; private MoveService moveService; @Autowired private CopyService copyService; @Autowired @Autowired private AsyncTransferService asyncTransfService; private AsyncTransferService asyncTransfService; @Autowired @Autowired private HttpServletRequest servletRequest; private HttpServletRequest servletRequest; @Autowired private FileServiceClient fileServiceClient; @Autowired private NodeDAO nodeDao; public enum JobDirection { public enum JobDirection { pullToVoSpace, pullToVoSpace, pullFromVoSpace, pullFromVoSpace, Loading Loading @@ -119,6 +130,9 @@ public class JobService { case moveNode: case moveNode: handleMoveNode(job, transfer); handleMoveNode(job, transfer); break; break; case copyNode: handleCopyNode(job, transfer); break; default: default: throw new UnsupportedOperationException("Not implemented yet"); throw new UnsupportedOperationException("Not implemented yet"); } } Loading Loading @@ -177,6 +191,31 @@ public class JobService { }); }); } } private void handleCopyNode(JobSummary jobSummary, Transfer transfer) { // User data must be extracted before starting the new thread // to avoid the "No thread-bound request found" exception User user = (User) servletRequest.getUserPrincipal(); CompletableFuture.runAsync(() -> { handleJobErrors(jobSummary, job -> { String jobId = jobSummary.getJobId(); // Index 0: source 1: destination List<String> sourceAndDestination = copyService.processCopyNodes(transfer, jobId, user); // Call file service and command copy try{ fileServiceClient.startFileCopyJob(sourceAndDestination.get(0), sourceAndDestination.get(1), jobId, user); } catch (Exception e) { // We decided not to purge metadata in case of failure // just release busy nodes setting job_id = null nodeDao.releaseBusyNodesByJobId(jobId); throw e; } return null; }); }); } private void handleJobErrors(JobSummary job, Function<JobSummary, Transfer> jobConsumer) { private void handleJobErrors(JobSummary job, Function<JobSummary, Transfer> jobConsumer) { Transfer negotiatedTransfer = null; Transfer negotiatedTransfer = null; try { try { Loading src/main/java/it/inaf/oats/vospace/MoveService.java +7 −18 Original line number Original line Diff line number Diff line Loading @@ -11,12 +11,9 @@ import it.inaf.oats.vospace.exception.InternalFaultException; import it.inaf.oats.vospace.exception.NodeBusyException; import it.inaf.oats.vospace.exception.NodeBusyException; import it.inaf.oats.vospace.exception.NodeNotFoundException; import it.inaf.oats.vospace.exception.NodeNotFoundException; import it.inaf.oats.vospace.exception.PermissionDeniedException; import it.inaf.oats.vospace.exception.PermissionDeniedException; import it.inaf.oats.vospace.persistence.NodeDAO; import it.inaf.oats.vospace.persistence.NodeDAO.ShortNodeDescriptor; import it.inaf.oats.vospace.persistence.NodeDAO.ShortNodeDescriptor; import java.util.Optional; import java.util.Optional; import net.ivoa.xml.vospace.v2.Transfer; import net.ivoa.xml.vospace.v2.Transfer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.dao.CannotSerializeTransactionException; import org.springframework.dao.CannotSerializeTransactionException; import org.springframework.stereotype.Service; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.EnableTransactionManagement; Loading @@ -25,13 +22,7 @@ import org.springframework.transaction.annotation.Transactional; @Service @Service @EnableTransactionManagement @EnableTransactionManagement public class MoveService { public class MoveService extends AbstractNodeService { @Autowired private NodeDAO nodeDao; @Value("${vospace-authority}") private String authority; /** /** * Perform modeNode operation. User is passed as parameter because this method * Perform modeNode operation. User is passed as parameter because this method Loading @@ -52,7 +43,7 @@ public class MoveService { this.validatePath(destinationPath); this.validatePath(destinationPath); if (sourcePath.equals(destinationPath)) { if (sourcePath.equals(destinationPath)) { return; throw new IllegalArgumentException("Cannot move node to itself"); } } // Check if destination is subpath of source // Check if destination is subpath of source Loading @@ -61,6 +52,11 @@ public class MoveService { throw new IllegalArgumentException("Cannot move node to a subdirectory of its own path"); throw new IllegalArgumentException("Cannot move node to a subdirectory of its own path"); } } // Check if destination equals parent path of source if(NodeUtils.getParentPath(sourcePath).equals(destinationPath)){ return; } try { try { // Get source node // Get source node Optional<Long> sourceIdOpt = nodeDao.getNodeId(sourcePath); Optional<Long> sourceIdOpt = nodeDao.getNodeId(sourcePath); Loading Loading @@ -112,11 +108,4 @@ public class MoveService { } } } } private void validatePath(String path) { if (path.equals("/")) { throw new IllegalArgumentException("Cannot move root node or to root node"); } } } } Loading
src/main/java/it/inaf/oats/vospace/AbstractNodeService.java 0 → 100644 +46 −0 Original line number Original line Diff line number Diff line /* * 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.oats.vospace.exception.InternalFaultException; import it.inaf.oats.vospace.exception.NodeBusyException; import it.inaf.oats.vospace.exception.PermissionDeniedException; import it.inaf.oats.vospace.persistence.NodeDAO; import it.inaf.oats.vospace.persistence.NodeDAO.ShortNodeDescriptor; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; public abstract class AbstractNodeService { @Autowired protected NodeDAO nodeDao; @Value("${vospace-authority}") protected String authority; protected void validatePath(String path) { if (path.equals("/")) { throw new IllegalArgumentException("Cannot move root node or to root node"); } } protected void validateDestinationContainer(ShortNodeDescriptor snd, String destinationVosPath) { if (snd.isBusy()) { throw new NodeBusyException(destinationVosPath); } if (snd.isPermissionDenied()) { throw new PermissionDeniedException(destinationVosPath); } if (!snd.isWritable()) { throw new InternalFaultException("Destination is not writable: " + destinationVosPath); } if (!snd.isContainer()) { throw new InternalFaultException("Existing destination is not a container: " + destinationVosPath); } } }
src/main/java/it/inaf/oats/vospace/CopyService.java 0 → 100644 +117 −0 Original line number Original line Diff line number Diff line /* * 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) { // Get Source Vos Path 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); } }
src/main/java/it/inaf/oats/vospace/FileServiceClient.java +60 −1 Original line number Original line Diff line number Diff line Loading @@ -87,6 +87,65 @@ public class FileServiceClient { }, new Object[]{}); }, new Object[]{}); } } public void startFileCopyJob(String sourceVosPath, String destiantionVosPath, String jobId, User user) { CopyRequest copyRequest = new CopyRequest(); copyRequest.setJobId(jobId); copyRequest.setSourceRootVosPath(sourceVosPath); copyRequest.setDestinationRootVosPath(destiantionVosPath); String url = fileServiceUrl + "/copy"; String token = user.getAccessToken(); restTemplate.execute(url, HttpMethod.POST, req -> { HttpHeaders headers = req.getHeaders(); if (token != null) { headers.setBearerAuth(token); } headers.setContentType(MediaType.APPLICATION_JSON); try (OutputStream os = req.getBody()) { MAPPER.writeValue(os, copyRequest); } }, res -> { return null; }, new Object[]{}); } public static class CopyRequest { private String jobId; private String sourceRootVosPath; private String destinationRootVosPath; public String getJobId() { return jobId; } public void setJobId(String jobId) { this.jobId = jobId; } public String getSourceRootVosPath() { return sourceRootVosPath; } public void setSourceRootVosPath(String sourceRootVosPath) { this.sourceRootVosPath = sourceRootVosPath; } public String getDestinationRootVosPath() { return destinationRootVosPath; } public void setDestinationRootVosPath(String destinationRootVosPath) { this.destinationRootVosPath = destinationRootVosPath; } } public static class ArchiveRequest { public static class ArchiveRequest { private String type; private String type; Loading
src/main/java/it/inaf/oats/vospace/JobService.java +40 −1 Original line number Original line Diff line number Diff line Loading @@ -18,6 +18,8 @@ import it.inaf.oats.vospace.exception.InvalidArgumentException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.stereotype.Service; import it.inaf.oats.vospace.exception.VoSpaceErrorSummarizableException; import it.inaf.oats.vospace.exception.VoSpaceErrorSummarizableException; import it.inaf.oats.vospace.persistence.NodeDAO; import java.util.List; import java.util.Optional; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture; import java.util.function.Function; import java.util.function.Function; Loading @@ -40,12 +42,21 @@ public class JobService { @Autowired @Autowired private MoveService moveService; private MoveService moveService; @Autowired private CopyService copyService; @Autowired @Autowired private AsyncTransferService asyncTransfService; private AsyncTransferService asyncTransfService; @Autowired @Autowired private HttpServletRequest servletRequest; private HttpServletRequest servletRequest; @Autowired private FileServiceClient fileServiceClient; @Autowired private NodeDAO nodeDao; public enum JobDirection { public enum JobDirection { pullToVoSpace, pullToVoSpace, pullFromVoSpace, pullFromVoSpace, Loading Loading @@ -119,6 +130,9 @@ public class JobService { case moveNode: case moveNode: handleMoveNode(job, transfer); handleMoveNode(job, transfer); break; break; case copyNode: handleCopyNode(job, transfer); break; default: default: throw new UnsupportedOperationException("Not implemented yet"); throw new UnsupportedOperationException("Not implemented yet"); } } Loading Loading @@ -177,6 +191,31 @@ public class JobService { }); }); } } private void handleCopyNode(JobSummary jobSummary, Transfer transfer) { // User data must be extracted before starting the new thread // to avoid the "No thread-bound request found" exception User user = (User) servletRequest.getUserPrincipal(); CompletableFuture.runAsync(() -> { handleJobErrors(jobSummary, job -> { String jobId = jobSummary.getJobId(); // Index 0: source 1: destination List<String> sourceAndDestination = copyService.processCopyNodes(transfer, jobId, user); // Call file service and command copy try{ fileServiceClient.startFileCopyJob(sourceAndDestination.get(0), sourceAndDestination.get(1), jobId, user); } catch (Exception e) { // We decided not to purge metadata in case of failure // just release busy nodes setting job_id = null nodeDao.releaseBusyNodesByJobId(jobId); throw e; } return null; }); }); } private void handleJobErrors(JobSummary job, Function<JobSummary, Transfer> jobConsumer) { private void handleJobErrors(JobSummary job, Function<JobSummary, Transfer> jobConsumer) { Transfer negotiatedTransfer = null; Transfer negotiatedTransfer = null; try { try { Loading
src/main/java/it/inaf/oats/vospace/MoveService.java +7 −18 Original line number Original line Diff line number Diff line Loading @@ -11,12 +11,9 @@ import it.inaf.oats.vospace.exception.InternalFaultException; import it.inaf.oats.vospace.exception.NodeBusyException; import it.inaf.oats.vospace.exception.NodeBusyException; import it.inaf.oats.vospace.exception.NodeNotFoundException; import it.inaf.oats.vospace.exception.NodeNotFoundException; import it.inaf.oats.vospace.exception.PermissionDeniedException; import it.inaf.oats.vospace.exception.PermissionDeniedException; import it.inaf.oats.vospace.persistence.NodeDAO; import it.inaf.oats.vospace.persistence.NodeDAO.ShortNodeDescriptor; import it.inaf.oats.vospace.persistence.NodeDAO.ShortNodeDescriptor; import java.util.Optional; import java.util.Optional; import net.ivoa.xml.vospace.v2.Transfer; import net.ivoa.xml.vospace.v2.Transfer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.dao.CannotSerializeTransactionException; import org.springframework.dao.CannotSerializeTransactionException; import org.springframework.stereotype.Service; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.EnableTransactionManagement; Loading @@ -25,13 +22,7 @@ import org.springframework.transaction.annotation.Transactional; @Service @Service @EnableTransactionManagement @EnableTransactionManagement public class MoveService { public class MoveService extends AbstractNodeService { @Autowired private NodeDAO nodeDao; @Value("${vospace-authority}") private String authority; /** /** * Perform modeNode operation. User is passed as parameter because this method * Perform modeNode operation. User is passed as parameter because this method Loading @@ -52,7 +43,7 @@ public class MoveService { this.validatePath(destinationPath); this.validatePath(destinationPath); if (sourcePath.equals(destinationPath)) { if (sourcePath.equals(destinationPath)) { return; throw new IllegalArgumentException("Cannot move node to itself"); } } // Check if destination is subpath of source // Check if destination is subpath of source Loading @@ -61,6 +52,11 @@ public class MoveService { throw new IllegalArgumentException("Cannot move node to a subdirectory of its own path"); throw new IllegalArgumentException("Cannot move node to a subdirectory of its own path"); } } // Check if destination equals parent path of source if(NodeUtils.getParentPath(sourcePath).equals(destinationPath)){ return; } try { try { // Get source node // Get source node Optional<Long> sourceIdOpt = nodeDao.getNodeId(sourcePath); Optional<Long> sourceIdOpt = nodeDao.getNodeId(sourcePath); Loading Loading @@ -112,11 +108,4 @@ public class MoveService { } } } } private void validatePath(String path) { if (path.equals("/")) { throw new IllegalArgumentException("Cannot move root node or to root node"); } } } }