Skip to content
Commits on Source (6)
......@@ -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>
<groupId>it.inaf.ia2</groupId>
<artifactId>vospace-parent-classes</artifactId>
<version>0.0.1-SNAPSHOT</version>
</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>
</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>
......@@ -144,14 +53,7 @@
</excludes>
</testResource>
</testResources>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<trimStackTrace>false</trimStackTrace>
</configuration>
</plugin>
<plugins>
<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>
......@@ -180,5 +81,13 @@
</plugin>
</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>
......@@ -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;
......
......@@ -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 {
......
/*
* 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);
}
}
......@@ -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);
......
......@@ -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="
......
......@@ -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);
}
......
......@@ -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);
......
/*
* 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;
}
}
/*
* 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);
}
}
/*
* 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);
}
}
/*
* 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);
}
}
/*
* 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;
}
}
/*
* 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);
}
}
......@@ -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);
});
}
......
......@@ -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());
......
......@@ -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());
......
......@@ -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 {
......
......@@ -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() {
......
......@@ -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));
}
}
}
}
ScriptUtils.executeSqlScript(conn, new ClassPathResource("test-data.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;
}
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) {
......