Newer
Older
package it.inaf.ia2.transfer.controller;
import it.inaf.ia2.transfer.persistence.FileDAO;
import it.inaf.ia2.transfer.persistence.ListOfFilesDAO;
Nicola Fulvio Calabria
committed
import it.inaf.ia2.transfer.persistence.JobDAO;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
Nicola Fulvio Calabria
committed
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
Nicola Fulvio Calabria
committed
import javax.xml.bind.DatatypeConverter;
import net.ivoa.xml.uws.v1.ExecutionPhase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
Nicola Fulvio Calabria
committed
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.http.HttpHeaders;
@RestController
public class PutFileController extends FileController {
private static final Logger LOG = LoggerFactory.getLogger(PutFileController.class);
Nicola Fulvio Calabria
committed
@Autowired
private FileDAO fileDAO;
Nicola Fulvio Calabria
committed
@Autowired
private JobDAO jobDAO;
@Autowired
private ListOfFilesDAO listOfFilesDAO;
@PutMapping("/**")
Nicola Fulvio Calabria
committed
public ResponseEntity<?> putFile(@RequestHeader(value = HttpHeaders.CONTENT_ENCODING, required = false) String contentEncoding,
@RequestParam("file") MultipartFile file,
@RequestParam(value = "jobid", required = false) String jobId) throws IOException, NoSuchAlgorithmException {
Nicola Fulvio Calabria
committed
String path = getPath();
Nicola Fulvio Calabria
committed
if (jobId == null) {
LOG.debug("putFile called for path {}", path);
} else {
LOG.debug("putFile called for path {} with jobId {}", path, jobId);
Nicola Fulvio Calabria
committed
if(!jobDAO.isJobExisting(jobId))
Nicola Fulvio Calabria
committed
{
return new ResponseEntity<>("Job "+jobId+ " not found", NOT_FOUND);
Nicola Fulvio Calabria
committed
}
Nicola Fulvio Calabria
committed
}
Optional<FileInfo> optFileInfo = fileDAO.getFileInfo(path);
if (optFileInfo.isPresent()) {
try (InputStream in = file.getInputStream()) {
Nicola Fulvio Calabria
committed
FileInfo fileInfo = optFileInfo.get();
if (fileInfo.getAcceptViews() != null && fileInfo.getAcceptViews().contains("urn:list-of-files")) {
storeListOfFiles(fileInfo, in);
} else {
fileInfo.setContentType(file.getContentType());
fileInfo.setContentEncoding(contentEncoding);
Nicola Fulvio Calabria
committed
storeGenericFile(fileInfo, in, jobId);
Nicola Fulvio Calabria
committed
}
}
return ResponseEntity.ok().build();
} else {
return new ResponseEntity<>("File " + path + " not found", NOT_FOUND);
}
}
private void storeListOfFiles(FileInfo fileInfo, InputStream is) throws IOException {
List<String> filePaths = parseListOfFiles(is);
listOfFilesDAO.createList(fileInfo.getVirtualPath(), filePaths);
}
private List<String> parseListOfFiles(InputStream is) throws IOException {
List<String> filePaths = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
String line;
while ((line = br.readLine()) != null && !line.isBlank()) {
filePaths.add(line.trim());
}
}
return filePaths;
}
Nicola Fulvio Calabria
committed
private void storeGenericFile(FileInfo fileInfo, InputStream is, String jobId) throws IOException, NoSuchAlgorithmException {
File file = new File(fileInfo.getOsPath());
/**
* This block must be synchronized, to avoid concurrency issues when
* multiple files are uploaded to a new folder in parallel.
*/
synchronized (this) {
if (!file.getParentFile().exists()) {
if (!file.getParentFile().mkdirs()) {
throw new IllegalStateException("Unable to create parent folder: " + file.getParentFile().getAbsolutePath());
}
String originalFileName = file.getName();
file = getEmptyFile(file, 1);
if (!originalFileName.equals(file.getName())) {
fileDAO.setOsName(fileInfo.getNodeId(), file.getName());
}
try {
if(jobId != null){
fileDAO.setBusy(fileInfo.getNodeId(), true);
}
Files.copy(is, file.toPath());
Nicola Fulvio Calabria
committed
Long fileSize = Files.size(file.toPath());
String md5Checksum = makeMD5Checksum(file);
fileDAO.updateFileAttributes(fileInfo.getNodeId(),
fileInfo.getContentType(),
fileInfo.getContentEncoding(),
fileSize,
md5Checksum);
Nicola Fulvio Calabria
committed
if (jobId != null) {
jobDAO.updateJobPhase(ExecutionPhase.COMPLETED, jobId);
Nicola Fulvio Calabria
committed
}
} catch (IOException | NoSuchAlgorithmException ex) {
Nicola Fulvio Calabria
committed
if (jobId != null) {
jobDAO.updateJobPhase(ExecutionPhase.ERROR, jobId);
Nicola Fulvio Calabria
committed
}
throw ex;
} finally {
if(jobId != null){
fileDAO.setBusy(fileInfo.getNodeId(), false);
}
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
}
}
/**
* Handles duplicate file uploads generating a new non existent path. This
* is necessary in some edge cases, like when a file has been renamed in
* VOSpace only but the original file on disk still has the old name or if a
* file has been marked for deletion and a file with the same name is
* uploaded before the cleanup.
*/
private File getEmptyFile(File file, int index) {
if (file.exists()) {
String fileName = file.getName();
String nameWithoutExtension;
String extension = null;
if (fileName.contains(".")) {
nameWithoutExtension = fileName.substring(0, fileName.lastIndexOf("."));
extension = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length());
} else {
nameWithoutExtension = fileName;
}
String newName = nameWithoutExtension + "-" + index;
if (extension != null) {
newName += "." + extension;
}
File newFile = file.toPath().getParent().resolve(newName).toFile();
return getEmptyFile(newFile, index + 1);
}
return file;
Nicola Fulvio Calabria
committed
private String makeMD5Checksum(File file) throws NoSuchAlgorithmException, IOException {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(Files.readAllBytes(file.toPath()));
byte[] digest = md.digest();
String checksum = DatatypeConverter.printHexBinary(digest);
return checksum;
}