Commit 683a3d08 authored by Matija Obreza's avatar Matija Obreza
Browse files

WIP: mkdir/rmdir on temporary session-based folders

parent 8460335f
......@@ -58,6 +58,15 @@ public interface RepositoryFilePersistence extends JpaRepository<RepositoryFile,
@Query("select distinct(rf.path) from RepositoryFile rf where rf.path like ?1%")
List<String> listDistinctPaths(String prefix, Pageable pageable);
/**
* List all distinct paths (FTP)
*
* @param prefix the prefix
* @return the list of paths
*/
@Query("select distinct(rf.path) from RepositoryFile rf where rf.path like ?1%")
List<String> listDistinctPaths(String prefix);
/**
* Find files with missing hashes
*
......@@ -65,4 +74,5 @@ public interface RepositoryFilePersistence extends JpaRepository<RepositoryFile,
*/
@Query("select rf from RepositoryFile rf where rf.sha1Sum is null or rf.md5Sum is null")
List<RepositoryFile> findByMissingHashSums();
}
......@@ -75,6 +75,16 @@ public interface RepositoryService {
*/
RepositoryFile getFile(UUID fileUuid) throws NoSuchRepositoryFileException;
/**
* Get repository file by its path and filename
*
* @param path
* @param filename
* @return
* @throws NoSuchRepositoryFileException
*/
RepositoryFile getFile(String path, String filename) throws NoSuchRepositoryFileException;
/**
* Get repository file bytes by its path and filename.
*
......@@ -174,6 +184,14 @@ public interface RepositoryService {
*/
List<String> listPaths(String prefix, Pageable pageRequest);
/**
* List all paths
*
* @param prefix
* @return
*/
List<String> listPaths(String prefix);
/**
* Update image metadata. The update is based on the record UUID.
*
......
......@@ -95,8 +95,8 @@ public class RepositoryServiceImpl implements RepositoryService, InitializingBea
/*
* (non-Javadoc)
*
* @see org.genesys.filerepository.service.RepositoryService#addFile(java .lang.String, java.lang.String, java.lang.String, byte[],
* org.genesys.filerepository.model.RepositoryFile)
* @see org.genesys.filerepository.service.RepositoryService#addFile(java .lang.String, java.lang.String,
* java.lang.String, byte[], org.genesys.filerepository.model.RepositoryFile)
*/
@Override
@Transactional
......@@ -140,8 +140,8 @@ public class RepositoryServiceImpl implements RepositoryService, InitializingBea
/*
* (non-Javadoc)
*
* @see org.genesys.filerepository.service.RepositoryService#addImage( java.lang.String, java.lang.String, java.lang.String, byte[],
* org.genesys.filerepository.model.RepositoryImage)
* @see org.genesys.filerepository.service.RepositoryService#addImage( java.lang.String, java.lang.String,
* java.lang.String, byte[], org.genesys.filerepository.model.RepositoryImage)
*/
@Override
@Transactional
......@@ -223,6 +223,16 @@ public class RepositoryServiceImpl implements RepositoryService, InitializingBea
return file;
}
@Override
public RepositoryFile getFile(final String path, final String filename) throws NoSuchRepositoryFileException {
try {
UUID uuid = filename.contains(".") ? UUID.fromString(filename.substring(0, filename.indexOf('.'))) : UUID.fromString(filename);
return getFile(uuid);
} catch (IllegalArgumentException e) {
throw new NoSuchRepositoryFileException();
}
}
/*
* (non-Javadoc)
*
......@@ -262,7 +272,8 @@ public class RepositoryServiceImpl implements RepositoryService, InitializingBea
/*
* (non-Javadoc)
*
* @see org.genesys.filerepository.service.RepositoryService#updateFile( org.genesys.filerepository.model .RepositoryFile)
* @see org.genesys.filerepository.service.RepositoryService#updateFile( org.genesys.filerepository.model
* .RepositoryFile)
*/
@Override
@Transactional
......@@ -284,7 +295,8 @@ public class RepositoryServiceImpl implements RepositoryService, InitializingBea
/*
* (non-Javadoc)
*
* @see org.genesys.filerepository.service.RepositoryService#updateMetadata(org.genesys.filerepository. model.RepositoryImage)
* @see org.genesys.filerepository.service.RepositoryService#updateMetadata(org.genesys.filerepository.
* model.RepositoryImage)
*/
@Override
@Transactional
......@@ -303,7 +315,8 @@ public class RepositoryServiceImpl implements RepositoryService, InitializingBea
/*
* (non-Javadoc)
*
* @see org.genesys.filerepository.service.RepositoryService#updateBytes( org.genesys.filerepository.model .RepositoryFile, java.lang.String, byte[])
* @see org.genesys.filerepository.service.RepositoryService#updateBytes( org.genesys.filerepository.model
* .RepositoryFile, java.lang.String, byte[])
*/
@Override
@Transactional
......@@ -328,7 +341,8 @@ public class RepositoryServiceImpl implements RepositoryService, InitializingBea
/*
* (non-Javadoc)
*
* @see org.genesys.filerepository.service.RepositoryService#updateBytes( org.genesys.filerepository.model .RepositoryImage, java.lang.String, byte[])
* @see org.genesys.filerepository.service.RepositoryService#updateBytes( org.genesys.filerepository.model
* .RepositoryImage, java.lang.String, byte[])
*/
@Override
@Transactional
......@@ -359,7 +373,8 @@ public class RepositoryServiceImpl implements RepositoryService, InitializingBea
/*
* (non-Javadoc)
*
* @see org.genesys.filerepository.service.RepositoryService#removeFile( org.genesys.filerepository.model .RepositoryFile)
* @see org.genesys.filerepository.service.RepositoryService#removeFile( org.genesys.filerepository.model
* .RepositoryFile)
*/
@Override
@Transactional
......@@ -392,7 +407,8 @@ public class RepositoryServiceImpl implements RepositoryService, InitializingBea
/*
* (non-Javadoc)
*
* @see org.genesys.filerepository.service.RepositoryService#moveFile(org .genesys2.server.filerepository.model .RepositoryFile, java.lang.String)
* @see org.genesys.filerepository.service.RepositoryService#moveFile(org .genesys2.server.filerepository.model
* .RepositoryFile, java.lang.String)
*/
@Override
@Transactional
......@@ -462,4 +478,11 @@ public class RepositoryServiceImpl implements RepositoryService, InitializingBea
return paths;
}
@Override
public List<String> listPaths(String prefix) {
final List<String> paths = repositoryFilePersistence.listDistinctPaths(prefix);
paths.remove(prefix);
return paths;
}
}
package org.genesys.filerepository.service.ftp;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import org.apache.ftpserver.ftplet.FtpFile;
public abstract class CanBeAnythingFile implements FtpFile {
private String parent;
private String name;
public CanBeAnythingFile(String parent, String name) {
this.parent = parent;
this.name = name;
}
@Override
public String getAbsolutePath() {
return parent.length() > 1 ? parent.concat("/").concat(name) : parent.concat(name);
}
@Override
public String getName() {
return name;
}
@Override
public boolean isHidden() {
return false;
}
@Override
public boolean isDirectory() {
return true;
}
@Override
public boolean isFile() {
return true;
}
@Override
public boolean doesExist() {
return false;
}
@Override
public boolean isReadable() {
return true;
}
@Override
public boolean isWritable() {
return true;
}
@Override
public boolean isRemovable() {
return true;
}
@Override
public String getOwnerName() {
return null;
}
@Override
public String getGroupName() {
return null;
}
@Override
public int getLinkCount() {
return 0;
}
@Override
public long getLastModified() {
return 0;
}
@Override
public boolean setLastModified(long time) {
return false;
}
@Override
public long getSize() {
return 0;
}
@Override
public Object getPhysicalFile() {
return null;
}
@Override
public abstract boolean mkdir();
@Override
public boolean delete() {
return false;
}
@Override
public boolean move(FtpFile destination) {
return false;
}
@Override
public List<? extends FtpFile> listFiles() {
return null;
}
@Override
public OutputStream createOutputStream(long offset) throws IOException {
return null;
}
@Override
public InputStream createInputStream(long offset) throws IOException {
return null;
}
}
......@@ -18,7 +18,13 @@ package org.genesys.filerepository.service.ftp;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.ftpserver.ftplet.FileSystemFactory;
......@@ -26,31 +32,37 @@ import org.apache.ftpserver.ftplet.FileSystemView;
import org.apache.ftpserver.ftplet.FtpException;
import org.apache.ftpserver.ftplet.FtpFile;
import org.apache.ftpserver.ftplet.User;
import org.genesys.filerepository.NoSuchRepositoryFileException;
import org.genesys.filerepository.model.RepositoryFile;
import org.genesys.filerepository.service.RepositoryService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
public class RepositoryFileSystemFactory implements FileSystemFactory {
@Component
public class RepositoryFileSystemFactory implements FileSystemFactory, InitializingBean {
private final static Logger LOG = LoggerFactory.getLogger(RepositoryFileSystemFactory.class);
@Autowired
@Autowired(required = true)
private RepositoryService repositoryService;
private RepositoryFtpFile file(RepositoryFile repositoryFile) {
LOG.debug("Making RepositoryFtpDirectory repositoryFile={}", repositoryFile);
RepositoryFtpFile rff = new RepositoryFtpFile(repositoryFile) {
@Override
public String getOwnerName() {
// TODO Auto-generated method stub
return null;
return "root";
}
@Override
public String getGroupName() {
// TODO Auto-generated method stub
return null;
return "wheel";
}
@Override
......@@ -61,8 +73,14 @@ public class RepositoryFileSystemFactory implements FileSystemFactory {
@Override
public boolean delete() {
// TODO Auto-generated method stub
return false;
LOG.info("Delete file={}", this.getAbsolutePath());
try {
repositoryService.removeFile(repositoryFile);
return true;
} catch (NoSuchRepositoryFileException | IOException e) {
LOG.warn(e.getMessage());
return false;
}
}
@Override
......@@ -81,13 +99,14 @@ public class RepositoryFileSystemFactory implements FileSystemFactory {
public InputStream createInputStream(long offset) throws IOException {
return null;
}
};
return rff;
}
private RepositoryFtpDirectory directory(String path) {
private RepositoryFtpDirectory directory(String path, RepositoryFileSystemView session) {
LOG.debug("Making RepositoryFtpDirectory path={}", path);
RepositoryFtpDirectory rfd = new RepositoryFtpDirectory(path) {
@Override
......@@ -106,24 +125,43 @@ public class RepositoryFileSystemFactory implements FileSystemFactory {
@Override
public List<? extends FtpFile> listFiles() {
return repositoryService.getFiles(this.getAbsolutePath()).stream().map(rf -> file(fr));.collect(Collectors::toList);
LOG.info("Listing files in path={} service={}", this.getAbsolutePath(), repositoryService);
ArrayList<FtpFile> all = new ArrayList<>();
all.addAll(session.temporaryDirs.stream().map(path -> directory(path, session)).collect(Collectors.toList()));
all.addAll(repositoryService.getFiles(this.getAbsolutePath()).stream().map(rf -> file(rf)).collect(Collectors.toList()));
all.addAll(repositoryService.listPaths(getAbsolutePath()).stream().map(path -> directory(path, session)).collect(Collectors.toList()));
all.sort((a, b) -> {
return a.getName().compareTo(b.getName());
});
return Collections.unmodifiableList(all);
}
@Override
public String getOwnerName() {
// TODO Auto-generated method stub
return null;
return "root";
}
@Override
public String getGroupName() {
// TODO Auto-generated method stub
return null;
return "wheel";
}
@Override
public boolean delete() {
// TODO Auto-generated method stub
LOG.info("Delete this={}", getAbsolutePath());
if (session.temporaryDirs.contains(getAbsolutePath())) {
session.temporaryDirs.remove(getAbsolutePath());
return true;
}
LOG.warn("Not deleting repository folder={}", getAbsolutePath());
return false;
}
@Override
public boolean changeWorkingDirectory(String dir) {
LOG.info("CWD this={} dir={}", getAbsolutePath(), dir);
return false;
}
};
......@@ -136,48 +174,83 @@ public class RepositoryFileSystemFactory implements FileSystemFactory {
LOG.info("Creating new repository view for {}", user.getName());
// TODO Auto-generated method stub
return new FileSystemView() {
private final String username = user.getName();
private RepositoryFtpDirectory cwd;
private RepositoryFtpDirectory homeDir;
return new RepositoryFileSystemView(user) {
@Override
public boolean isRandomAccessible() throws FtpException {
// TODO Auto-generated method stub
return false;
}
public FtpFile getFile(String file) throws FtpException {
LOG.debug("getFile file={} for user={}", file, username);
Path path = Paths.get(cwd.getAbsolutePath(), file).normalize();
LOG.info("Resolved normalized={}", path.toString());
@Override
public FtpFile getWorkingDirectory() throws FtpException {
LOG.debug("getWorkingDirectory for user={}", username);
return this.cwd;
}
if (temporaryDirs.contains(path.toString())) {
LOG.debug("dir={} is a temporary session-bound directory", path);
return directory(path.toString(), this);
}
@Override
public FtpFile getHomeDirectory() throws FtpException {
LOG.debug("getHomeDirectory for user={}", username);
return this.homeDir;
}
try {
return path.endsWith("/") ? directory(path.toString(), this) : file(repositoryService.getFile(path.getParent().toString(), path.getFileName().toString()));
} catch (NoSuchRepositoryFileException e) {
return new CanBeAnythingFile(path.getParent().toString(), path.getFileName().toString()) {
@Override
public FtpFile getFile(String file) throws FtpException {
LOG.debug("getFile file={} for user={}", file, username);
// TODO Auto-generated method stub
return null;
}
@Override
public boolean mkdir() {
LOG.info("Mkdir path={}", this.getAbsolutePath());
temporaryDirs.add(this.getAbsolutePath());
return true;
}
@Override
public void dispose() {
LOG.info("Disposing repository view for user={}", username);
};
}
}
@Override
public boolean changeWorkingDirectory(String dir) throws FtpException {
LOG.debug("CWD dir={} for user={}", dir, username);
this.cwd.changeWorkingDirectory(dir);
return false;
}
};
}
@Override
public void afterPropertiesSet() throws Exception {
assert (this.repositoryService != null);
LOG.warn("Initialized RFSF with service={}", this.repositoryService);
}
private abstract class RepositoryFileSystemView implements FileSystemView {
protected User user;
protected String username;
protected RepositoryFtpDirectory cwd = directory("/", this);
protected RepositoryFtpDirectory homeDir = directory("/", this);
protected Set<String> temporaryDirs = new HashSet<>();
public RepositoryFileSystemView(User user) {
username = user.getName();
this.user = user;
}
@Override
public boolean isRandomAccessible() throws FtpException {
// TODO Auto-generated method stub
return false;
}
@Override
public FtpFile getWorkingDirectory() throws FtpException {
LOG.debug("getWorkingDirectory for user={}", username);
return this.cwd;
}
@Override
public FtpFile getHomeDirectory() throws FtpException {
LOG.debug("getHomeDirectory for user={}", username);
return this.homeDir;
}
@Override
public void dispose() {
LOG.info("Disposing repository view for user={}", username);
}
@Override
public boolean changeWorkingDirectory(String dir) throws FtpException {
LOG.debug("CWD dir={} for user={}", dir, username);
return this.cwd.changeWorkingDirectory(dir);
}
}
}
......@@ -37,12 +37,16 @@ public abstract class RepositoryFtpDirectory implements FtpFile {
}
public RepositoryFtpDirectory(String path) {
this.parent = path;
final String name = path.substring(path.lastIndexOf('/'));
// System.err.println("RFD path=" + path + " name=" + name);
this.name = name;
this.parent = path.substring(0, path.length() - name.length());
// System.err.println("RFD path=" + path + " name=" + name + " parent=" + parent);
}
@Override
public String getAbsolutePath() {
return this.parent == null ? this.name : this.parent.concat("/").concat(this.name);
return this.parent.length() == 0 || this.parent == null ? this.name : this.parent.concat("/").concat(this.name);
}
@Override
......@@ -139,4 +143,6 @@ public abstract class RepositoryFtpDirectory implements FtpFile {