Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • cleanup
  • master
  • v0.0.1
3 results

Target

Select target project
No results found
Select Git revision
  • cleanup
  • master
  • v0.0.1
3 results
Show changes

Commits on Source 6

22 files
+ 143
345
Compare changes
  • Side-by-side
  • Inline

Files

+15 −106
Original line number Diff line number Diff line
@@ -3,10 +3,9 @@
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.5</version>
        <relativePath/> <!-- lookup parent from repository -->
        <groupId>it.inaf.ia2</groupId>
        <artifactId>vospace-parent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <groupId>it.inaf.ia2</groupId>
    <artifactId>vospace-file-service</artifactId>
@@ -18,61 +17,14 @@
        <finalName>${project.artifactId}-${project.version}</finalName>
        <!-- File catalog repository directory -->
        <init_database_scripts_path>../../../vospace-file-catalog</init_database_scripts_path>
        <zonky.postgres-binaries.version>12.5.0</zonky.postgres-binaries.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>rap-client</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
             
        <!-- Embedded PostgreSQL: -->
        <dependency>
            <groupId>com.opentable.components</groupId>
            <artifactId>otj-pg-embedded</artifactId>
            <version>0.13.3</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>it.oats.inaf</groupId>
            <artifactId>vospace-datamodel</artifactId>
            <version>1.0-SNAPSHOT</version>
            <groupId>it.inaf.ia2</groupId>
            <artifactId>vospace-parent-classes</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        
        <dependency>
            <groupId>org.kamranzafar</groupId>
            <artifactId>jtar</artifactId>
@@ -80,49 +32,6 @@
        </dependency>
    </dependencies>

    <profiles>
        <profile>
            <id>platform-linux</id>
            <activation>
                <os>
                    <family>unix</family> 
                </os>
            </activation>
            <dependencies>
                <dependency>
                    <groupId>io.zonky.test.postgres</groupId>
                    <artifactId>embedded-postgres-binaries-linux-amd64</artifactId>
                    <version>${zonky.postgres-binaries.version}</version>
                    <scope>test</scope>
                </dependency>
            </dependencies>
        </profile>
        <profile>
            <id>platform-windows</id>
            <activation>
                <os>
                    <family>windows</family>
                </os>
            </activation>
            <dependencies>
                <dependency>
                    <groupId>io.zonky.test.postgres</groupId>
                    <artifactId>embedded-postgres-binaries-windows-amd64</artifactId>
                    <version>${zonky.postgres-binaries.version}</version>
                    <scope>test</scope>
                </dependency>
            </dependencies>
        </profile>
    </profiles>

    <repositories>
        <repository>
            <id>ia2-snapshots</id>
            <name>your custom repo</name>
            <url>http://repo.ia2.inaf.it/maven/repository/snapshots</url>
        </repository>
    </repositories>
    
    <build>
        <finalName>${finalName}</finalName>
        <testResources>
@@ -145,13 +54,6 @@
            </testResource>
        </testResources>
        <plugins>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.2</version>
                <configuration>
                    <trimStackTrace>false</trimStackTrace>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
@@ -162,7 +64,6 @@
            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <version>0.8.6</version>
                <executions>
                    <execution>
                        <goals>
@@ -181,4 +82,12 @@
        </plugins>
    </build>
    
    <repositories>
        <repository>
            <id>ia2-snapshots</id>
            <name>IA2 snapshot repository</name>
            <url>http://repo.ia2.inaf.it/maven/repository/snapshots</url>
        </repository>
    </repositories>

</project>
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@ import it.inaf.ia2.transfer.auth.TokenPrincipal;
import it.inaf.ia2.transfer.service.ArchiveJob;
import it.inaf.ia2.transfer.service.ArchiveJob.Type;
import it.inaf.ia2.transfer.service.ArchiveService;
import it.inaf.oats.vospace.exception.PermissionDeniedException;
import java.io.File;
import java.util.concurrent.CompletableFuture;
import javax.servlet.http.HttpServletResponse;
Original line number Diff line number Diff line
@@ -6,7 +6,7 @@
package it.inaf.ia2.transfer.controller;

import it.inaf.ia2.transfer.auth.TokenPrincipal;
import it.inaf.ia2.transfer.exception.PermissionDeniedException;
import it.inaf.oats.vospace.exception.PermissionDeniedException;

public abstract class AuthenticatedFileController extends FileController {

Original line number 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.ia2.transfer.controller;

import it.inaf.oats.vospace.exception.DefaultErrorController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ErrorController extends DefaultErrorController {

    @Autowired
    public ErrorController(ErrorAttributes errorAttributes) {
        super(errorAttributes);
    }
}
Original line number Diff line number Diff line
@@ -5,8 +5,9 @@
 */
package it.inaf.ia2.transfer.controller;

import it.inaf.ia2.transfer.exception.JobException;
import it.inaf.ia2.transfer.persistence.JobDAO;
import it.inaf.oats.vospace.exception.InternalFaultException;
import it.inaf.oats.vospace.exception.VoSpaceErrorSummarizableException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
@@ -44,13 +45,12 @@ public abstract class FileController {
                jobDAO.updateJobPhase(ExecutionPhase.COMPLETED, jobId);
            }
        } catch (Throwable t) {
            JobException jobException;
            if (t instanceof JobException) {
                jobException = (JobException) t;
            VoSpaceErrorSummarizableException jobException;
            if (t instanceof VoSpaceErrorSummarizableException) {
                jobException = (VoSpaceErrorSummarizableException) t;
            } else {
                LOG.error("Unexpected error happened", t);
                jobException = new JobException(JobException.Type.FATAL, "Internal Fault")
                        .setErrorDetail("InternalFault: Unexpected error happened");
                jobException = new InternalFaultException("Unexpected error happened");
            }
            if (jobId != null) {
                jobDAO.setJobError(jobId, jobException);
Original line number Diff line number Diff line
@@ -5,8 +5,8 @@
 */
package it.inaf.ia2.transfer.controller;

import it.inaf.ia2.transfer.exception.FileNotFoundException;
import it.inaf.ia2.transfer.exception.JobException;
import it.inaf.oats.vospace.exception.InternalFaultException;
import it.inaf.oats.vospace.exception.NodeNotFoundException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -33,13 +33,12 @@ public class FileResponseUtil {

        if (!file.exists()) {
            LOG.error("File not found: " + file.getAbsolutePath());
            throw new FileNotFoundException(vosPath == null ? file.getAbsolutePath() : vosPath);
            throw new NodeNotFoundException(vosPath == null ? file.getAbsolutePath() : vosPath);
        }

        if (!file.canRead()) {
            LOG.error("File not readable: " + file.getAbsolutePath());
            throw new JobException(JobException.Type.FATAL, "Internal Fault")
                    .setErrorDetail("InternalFault: File " + file.getName() + " is not readable");
            throw new InternalFaultException("File " + file.getName() + " is not readable");
        }

        response.setHeader("Content-Disposition", "attachment; filename="
Original line number Diff line number Diff line
@@ -7,12 +7,12 @@ package it.inaf.ia2.transfer.controller;

import it.inaf.ia2.transfer.persistence.model.FileInfo;
import it.inaf.ia2.transfer.auth.TokenPrincipal;
import it.inaf.ia2.transfer.exception.FileNotFoundException;
import it.inaf.ia2.transfer.exception.InvalidArgumentException;
import it.inaf.ia2.transfer.exception.PermissionDeniedException;
import it.inaf.ia2.transfer.persistence.FileDAO;
import it.inaf.ia2.transfer.persistence.JobDAO;
import it.inaf.ia2.transfer.service.AuthorizationService;
import it.inaf.oats.vospace.exception.InvalidArgumentException;
import it.inaf.oats.vospace.exception.NodeNotFoundException;
import it.inaf.oats.vospace.exception.PermissionDeniedException;
import java.io.File;
import java.util.Optional;
import javax.servlet.http.HttpServletResponse;
@@ -65,13 +65,13 @@ public class GetFileController extends FileController {
                FileInfo fileInfo = optFileInfo.get();

                if (!authorizationService.isDownloadable(fileInfo, (TokenPrincipal) request.getUserPrincipal())) {
                    throw new PermissionDeniedException("PermissionDenied Path: " + path);
                    throw PermissionDeniedException.forPath(path);
                }

                File file = new File(fileInfo.getOsPath());
                FileResponseUtil.getFileResponse(response, file, path);
            } else {
                throw new FileNotFoundException(path);
                throw new NodeNotFoundException(path);
            }
        }, jobId);
    }
Original line number Diff line number Diff line
@@ -5,12 +5,12 @@
 */
package it.inaf.ia2.transfer.controller;

import it.inaf.ia2.transfer.exception.FileNotFoundException;
import it.inaf.ia2.transfer.exception.InsufficientStorageException;
import it.inaf.ia2.transfer.exception.InvalidArgumentException;
import it.inaf.ia2.transfer.persistence.model.FileInfo;
import it.inaf.ia2.transfer.persistence.FileDAO;
import it.inaf.ia2.transfer.persistence.JobDAO;
import it.inaf.oats.vospace.exception.InvalidArgumentException;
import it.inaf.oats.vospace.exception.NodeNotFoundException;
import it.inaf.oats.vospace.exception.QuotaExceededException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -73,7 +73,7 @@ public class PutFileController extends FileController {
                // if MultipartFile provides file size it is possible to check
                // quota limit before reading the stream
                if (remainingQuota != null && file != null && file.getSize() > remainingQuota) {
                    throw new InsufficientStorageException("QuotaExceeded Path: " + fileInfo.getVirtualPath());
                    throw new QuotaExceededException("Path: " + fileInfo.getVirtualPath());
                }
                
                if (file != null) {
@@ -87,7 +87,7 @@ public class PutFileController extends FileController {
                    throw new RuntimeException(ex);
                }
            } else {
                throw new FileNotFoundException(path);
                throw new NodeNotFoundException(path);
            }
        }, jobId);
    }
@@ -127,7 +127,7 @@ public class PutFileController extends FileController {
            // Quota limit is checked again to handle cases where MultipartFile is not used
            if (remainingQuota != null && fileSize > remainingQuota) {
                file.delete();
                throw new InsufficientStorageException("QuotaExceeded Path: " + fileInfo.getVirtualPath());
                throw new QuotaExceededException("Path: " + fileInfo.getVirtualPath());
            }

            String md5Checksum = makeMD5Checksum(file);
Original line number 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.ia2.transfer.exception;

import java.nio.charset.StandardCharsets;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController;
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("${server.error.path:${error.path:/error}}")
public class ErrorController extends AbstractErrorController {

    @Autowired
    public ErrorController(ErrorAttributes errorAttributes) {
        super(errorAttributes);
    }

    @RequestMapping(produces = MediaType.TEXT_XML_VALUE)
    public void errorText(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ErrorAttributeOptions options = ErrorAttributeOptions.of(ErrorAttributeOptions.Include.MESSAGE);
        Map<String, Object> errors = super.getErrorAttributes(request, options);
        response.setContentType("text/plain;charset=UTF-8");
        response.setCharacterEncoding("UTF-8");
        String errorMessage = (String) errors.get("message");
        if (errorMessage != null) {
            response.getOutputStream().write(errorMessage.getBytes(StandardCharsets.UTF_8));
        }
    }

    @Override
    public String getErrorPath() {
        return null;
    }
}
Original line number Diff line number Diff line
/*
 * 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.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(HttpStatus.NOT_FOUND)
public class FileNotFoundException extends JobException {

    public FileNotFoundException(String path) {
        super(Type.FATAL, "Node Not Found");
        setErrorDetail("NodeNotFound Path: " + path);
    }
}
Original line number Diff line number Diff line
/*
 * 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.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(HttpStatus.INSUFFICIENT_STORAGE)
public class InsufficientStorageException extends JobException {

    public InsufficientStorageException(String errorDetail) {
        super(Type.FATAL, "Quota Exceeded");
        setErrorDetail(errorDetail);
    }
}
Original line number Diff line number Diff line
/*
 * 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.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(HttpStatus.BAD_REQUEST)
public class InvalidArgumentException extends JobException {

    public InvalidArgumentException(String message) {
        super(Type.FATAL, "Invalid Argument");
        setErrorDetail("InvalidArgument: " + message);
    }
}
Original line number Diff line number Diff line
/*
 * 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.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public class JobException extends RuntimeException {

    public static enum Type {

        TRANSIENT("transient"),
        FATAL("fatal");

        private final String value;

        private Type(String v) {
            value = v;
        }

        public String value() {
            return value;
        }
    }

    private final Type type;
    private String errorDetail;

    public JobException(Type type, String message) {
        super(message);
        this.type = type;
    }

    public Type getType() {
        return type;
    }

    public String getErrorDetail() {
        return errorDetail;
    }

    public final JobException setErrorDetail(String errorDetail) {
        this.errorDetail = errorDetail;
        return this;
    }
}
Original line number Diff line number Diff line
/*
 * 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.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(value = HttpStatus.FORBIDDEN)
public class PermissionDeniedException extends JobException {

    public PermissionDeniedException(String errorDetail) {
        super(Type.FATAL, "Permission Denied");
        setErrorDetail(errorDetail);
    }
}
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@
 */
package it.inaf.ia2.transfer.persistence;

import it.inaf.ia2.transfer.exception.JobException;
import it.inaf.oats.vospace.exception.VoSpaceErrorSummarizableException;
import java.sql.Types;
import javax.sql.DataSource;
import net.ivoa.xml.uws.v1.ExecutionPhase;
@@ -64,7 +64,7 @@ public class JobDAO {
        return result;
    }

    public void setJobError(String jobId, JobException jobError) {
    public void setJobError(String jobId, VoSpaceErrorSummarizableException jobError) {

        String sql = "UPDATE job SET phase = ?, error_message = ?, error_type = ?,\n"
                + "error_has_detail = ?, error_detail = ?, end_time = NOW()\n"
@@ -74,9 +74,9 @@ public class JobDAO {
            int i = 0;
            ps.setObject(++i, ExecutionPhase.ERROR, Types.OTHER);
            ps.setString(++i, jobError.getMessage());
            ps.setObject(++i, jobError.getType().value(), Types.OTHER);
            ps.setBoolean(++i, jobError.getErrorDetail() != null);
            ps.setString(++i, jobError.getErrorDetail());
            ps.setObject(++i, jobError.getFault().getType().value(), Types.OTHER);
            ps.setBoolean(++i, jobError.getDetailMessage() != null);
            ps.setString(++i, jobError.getDetailMessage());
            ps.setString(++i, jobId);
        });
    }
Original line number Diff line number Diff line
@@ -6,13 +6,13 @@
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 it.inaf.oats.vospace.exception.InternalFaultException;
import it.inaf.oats.vospace.exception.PermissionDeniedException;
import it.inaf.oats.vospace.exception.QuotaExceededException;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -140,8 +140,7 @@ public class ArchiveService {
        if (!parentDir.exists()) {
            if (!parentDir.mkdirs()) {
                LOG.error("Unable to create directory " + parentDir.getAbsolutePath());
                throw new JobException(Type.FATAL, "Internal Fault")
                        .setErrorDetail("InternalFault: Unable to create temporary directory for job");
                throw new InternalFaultException("Unable to create temporary directory for job");
            }
        }

@@ -151,8 +150,7 @@ public class ArchiveService {

        if (!archiveFile.createNewFile()) {
            LOG.error("Unable to create file " + archiveFile.getAbsolutePath());
            throw new JobException(Type.FATAL, "Internal Fault")
                    .setErrorDetail("InternalFault: Unable to create archive file");
            throw new InternalFaultException("Unable to create archive file");
        }

        return archiveFile;
@@ -166,7 +164,7 @@ public class ArchiveService {
        long usedSpace = Files.walk(parentDir.toPath()).mapToLong(p -> p.toFile().length()).sum();

        if (usedSpace > generatedDirMaxSize.toBytes()) {
            throw new InsufficientStorageException("Archive size limit exceeded.");
            throw new QuotaExceededException("Archive size limit exceeded.");
        }
    }

@@ -182,7 +180,8 @@ public class ArchiveService {
            } else {
                StringBuilder newCommonParent = new StringBuilder();
                boolean same = true;
                for (int i = 0; same && i < Math.min(commonParent.length(), vosPath.length()); i++) {
                int lastSlashPos = vosPath.lastIndexOf("/");
                for (int i = 0; same && i < Math.min(commonParent.length(), vosPath.length()) && i <= lastSlashPos; i++) {
                    if (commonParent.charAt(i) == vosPath.charAt(i)) {
                        newCommonParent.append(commonParent.charAt(i));
                    } else {
@@ -277,8 +276,7 @@ public class ArchiveService {

        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());
            throw new InternalFaultException("Unable to retrieve location of file " + fileInfo.getVirtualPath());
        }

        String url = baseUrl + "/" + fileInfo.getVirtualName();
@@ -307,8 +305,7 @@ public class ArchiveService {

    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());
            throw PermissionDeniedException.forPath(fileInfo.getVirtualPath());
        }

        File file = new File(fileInfo.getOsPath());
Original line number Diff line number Diff line
@@ -6,11 +6,11 @@
package it.inaf.ia2.transfer.service;

import it.inaf.ia2.transfer.auth.TokenPrincipal;
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.LocationDAO;
import it.inaf.ia2.transfer.persistence.model.FileInfo;
import it.inaf.oats.vospace.exception.InternalFaultException;
import it.inaf.oats.vospace.exception.PermissionDeniedException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -160,8 +160,7 @@ public class FileCopyService {

        if (baseUrl == null) {
            LOG.error("Location URL not found for location " + sourceFile.getLocationId());
            throw new JobException(Type.FATAL, "Internal Fault")
                    .setErrorDetail("InternalFault: Unable to retrieve location of file " + sourceFile.getVirtualPath());
            throw new InternalFaultException("Unable to retrieve location of file " + sourceFile.getVirtualPath());
        }

        String url = baseUrl + "/" + sourceFile.getVirtualName();
@@ -191,8 +190,7 @@ public class FileCopyService {
    private void copyLocalFile(FileInfo sourceFileInfo,
            FileInfo destinationFileInfo, TokenPrincipal tokenPrincipal) {
        if (!authorizationService.isDownloadable(sourceFileInfo, tokenPrincipal)) {
            throw new JobException(Type.FATAL, "Permission Denied")
                    .setErrorDetail("PermissionDenied: " + sourceFileInfo.getVirtualPath());
            throw PermissionDeniedException.forPath(sourceFileInfo.getVirtualPath());
        }

        File file = new File(sourceFileInfo.getOsPath());
Original line number Diff line number Diff line
@@ -8,9 +8,9 @@ package it.inaf.ia2.transfer.controller;
import it.inaf.ia2.transfer.persistence.model.FileInfo;
import it.inaf.ia2.aa.jwt.TokenParser;
import it.inaf.ia2.transfer.auth.GmsClient;
import it.inaf.ia2.transfer.exception.JobException;
import it.inaf.ia2.transfer.persistence.FileDAO;
import it.inaf.ia2.transfer.persistence.JobDAO;
import it.inaf.oats.vospace.exception.VoSpaceErrorSummarizableException;
import java.io.File;
import java.util.Collections;
import java.util.HashMap;
@@ -226,9 +226,9 @@ public class GetFileControllerTest {
                    .andDo(print())
                    .andReturn().getResolvedException();

            assertTrue(ex instanceof JobException);
            JobException jobEx = (JobException) ex;
            assertTrue(jobEx.getErrorDetail().contains("not readable"), jobEx.getErrorDetail());
            assertTrue(ex instanceof VoSpaceErrorSummarizableException);
            VoSpaceErrorSummarizableException jobEx = (VoSpaceErrorSummarizableException) ex;
            assertTrue(jobEx.getDetailMessage().contains("not readable"), jobEx.getDetailMessage());
        } catch (Throwable t) {
            throw t;
        } finally {
Original line number Diff line number Diff line
@@ -5,10 +5,10 @@
 */
package it.inaf.ia2.transfer.controller;

import it.inaf.ia2.transfer.exception.InsufficientStorageException;
import it.inaf.ia2.transfer.persistence.model.FileInfo;
import it.inaf.ia2.transfer.persistence.FileDAO;
import it.inaf.ia2.transfer.persistence.JobDAO;
import it.inaf.oats.vospace.exception.QuotaExceededException;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
@@ -251,7 +251,7 @@ public class PutFileControllerTest {

        verify(fileDao, times(1)).getRemainingQuota(eq("/path/to"));

        assertTrue(ex instanceof InsufficientStorageException);
        assertTrue(ex instanceof QuotaExceededException);
    }

    @Test
@@ -284,7 +284,7 @@ public class PutFileControllerTest {

        verify(fileDao, times(1)).getRemainingQuota(eq("/path/to"));

        assertTrue(ex instanceof InsufficientStorageException);
        assertTrue(ex instanceof QuotaExceededException);
    }

    private FileInfo createBaseFileInfo() {
Original line number Diff line number Diff line
@@ -12,9 +12,12 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.sql.DataSource;
@@ -24,9 +27,7 @@ import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.datasource.init.ScriptUtils;

/**
 * Generates a DataSource that can be used for testing DAO classes. It loads an
@@ -82,15 +83,59 @@ public class DataSourceConfig {
            assertTrue(scriptDir.exists(), "DAO tests require " + scriptDir.getAbsolutePath() + " to exists.\n"
                    + "Please clone the repository from https://www.ict.inaf.it/gitlab/vospace/vospace-file-catalog.git");

            File[] scripts = scriptDir.listFiles(f -> f.getName().endsWith(".sql"));
            Arrays.sort(scripts); // sort alphabetically
            // load all sql files in vospace-file-catalog repo
            File[] repoScripts = scriptDir.listFiles(f -> f.getName().endsWith(".sql"));
            Arrays.sort(repoScripts); // sort alphabetically

            // add test-data.sql
            List<File> scripts = new ArrayList<>(Arrays.asList(repoScripts));
            scripts.add(new ClassPathResource("test-data.sql").getFile());

            for (File script : scripts) {
                ByteArrayResource scriptResource = replaceDollarQuoting(script.toPath());
                ScriptUtils.executeSqlScript(conn, scriptResource);
                String scriptContent = Files.readString(script.toPath());
                for (String sql : splitScript(scriptContent)) {
                    executeSql(conn, replaceDollarQuoting(sql));
                }
            }
        }
    }

    /**
     * Spring ScriptUtils is not able to correctly split the SQL statements if a
     * function definition contains semicolon characters, so this method is used
     * instead of it.
     */
    private List<String> splitScript(String script) {

        List<String> parts = new ArrayList<>();

        StringBuilder sb = new StringBuilder();

        boolean insideFunc = false;
        for (int i = 0; i < script.length(); i++) {
            char c = script.charAt(i);
            sb.append(c);

            if (insideFunc) {
                if (i > 6 && "$func$".equals(script.substring(i - 6, i))) {
                    insideFunc = false;
                }
            } else {
                if (i > 6 && "$func$".equals(script.substring(i - 6, i))) {
                    insideFunc = true;
                } else if (c == ';') {
                    parts.add(sb.toString());
                    sb = new StringBuilder();
                }
            }
        }

        return parts;
    }

            ScriptUtils.executeSqlScript(conn, new ClassPathResource("test-data.sql"));
    private void executeSql(Connection conn, String sqlStatement) throws SQLException {
        try ( Statement stat = conn.createStatement()) {
            stat.execute(sqlStatement);
        }
    }

@@ -100,9 +145,7 @@ public class DataSourceConfig {
     * instead of inside the original files because dollar quoting provides a
     * better visibility.
     */
    private ByteArrayResource replaceDollarQuoting(Path sqlScriptPath) throws Exception {

        String scriptContent = Files.readString(sqlScriptPath);
    private String replaceDollarQuoting(String scriptContent) {

        if (scriptContent.contains("$func$")) {

@@ -114,7 +157,7 @@ public class DataSourceConfig {
            scriptContent = scriptContent.replace(originalFunction, newFunction);
        }

        return new ByteArrayResource(scriptContent.getBytes());
        return scriptContent;
    }

    private String extractFunctionDefinition(String scriptContent) {
Original line number Diff line number Diff line
@@ -5,8 +5,7 @@
 */
package it.inaf.ia2.transfer.persistence;

import it.inaf.ia2.transfer.exception.JobException;
import it.inaf.ia2.transfer.exception.JobException.Type;
import it.inaf.oats.vospace.exception.InternalFaultException;
import javax.sql.DataSource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -52,8 +51,7 @@ public class JobDAOTest {

        assertEquals(ExecutionPhase.QUEUED, dao.getJobPhase("pippo3"));

        JobException jobError = new JobException(Type.FATAL, "Error message")
                .setErrorDetail("Error detail");
        InternalFaultException jobError = new InternalFaultException("Error detail");

        dao.setJobError("pippo3", jobError);

Original line number Diff line number Diff line
@@ -6,11 +6,11 @@
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.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 it.inaf.oats.vospace.exception.QuotaExceededException;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -151,7 +151,7 @@ public class ArchiveServiceTest {
            fos.write(junk);
        }

        Assertions.assertThrows(InsufficientStorageException.class, () -> {
        Assertions.assertThrows(QuotaExceededException.class, () -> {
            archiveService.createArchive(job);
        });
    }