From 409361775fcbc14ab04562ab352f62cc29dc1436 Mon Sep 17 00:00:00 2001 From: Timm Fitschen <t.fitschen@indiscale.com> Date: Tue, 13 Dec 2022 17:05:55 +0100 Subject: [PATCH] WIP: file storage refactoring: directory --- .../java/org/caosdb/server/entity/Entity.java | 15 ++-- .../org/caosdb/server/entity/MagicTypes.java | 8 +- .../java/org/caosdb/server/entity/Role.java | 5 +- .../server/filesystem/FSODescriptor.java | 8 +- .../filesystem/FileStorageInterface.java | 3 +- .../caosdb/server/filesystem/FileSystem.java | 10 +-- .../filesystem/FileSystemInterface.java | 10 --- .../server/filesystem/LocalFileStorage.java | 24 +++--- .../VirtualFSODescriptorInterface.java | 2 + .../server/jobs/core/AutoCreateDirs.java | 82 +++++++++++++++++++ .../core/CheckFileStorageConsistency.java | 4 +- .../java/org/caosdb/server/query/CQLLexer.g4 | 4 + .../java/org/caosdb/server/query/CQLParser.g4 | 1 + .../java/org/caosdb/server/query/Query.java | 1 + 14 files changed, 129 insertions(+), 48 deletions(-) create mode 100644 src/main/java/org/caosdb/server/jobs/core/AutoCreateDirs.java diff --git a/src/main/java/org/caosdb/server/entity/Entity.java b/src/main/java/org/caosdb/server/entity/Entity.java index 48a92fd0..317aea4d 100644 --- a/src/main/java/org/caosdb/server/entity/Entity.java +++ b/src/main/java/org/caosdb/server/entity/Entity.java @@ -809,14 +809,13 @@ public abstract class Entity extends AbstractObservable implements EntityInterfa if (tmpIdentifier != null || checksum != null || path != null || size != null) { // legacy clients (which use this api) always use sha512. Hash hash = Hash.create(checksum, 0, Hasher.Default); - setFSODescriptor( - new FSODescriptor( - FileSystem.DEFAULT_BACKEND, - new Path(path).toString(), - hash, - new Path(path), - size, - tmpIdentifier)); + if (getRole() == Role.Directory) { + setFSODescriptor(FSODescriptor.createDir(FileSystem.DEFAULT_BACKEND, new Path(path))); + } else { + setFSODescriptor( + new FSODescriptor( + FileSystem.DEFAULT_BACKEND, null, hash, new Path(path), size, tmpIdentifier)); + } } // Parse flags diff --git a/src/main/java/org/caosdb/server/entity/MagicTypes.java b/src/main/java/org/caosdb/server/entity/MagicTypes.java index 39556385..289fae6e 100644 --- a/src/main/java/org/caosdb/server/entity/MagicTypes.java +++ b/src/main/java/org/caosdb/server/entity/MagicTypes.java @@ -31,11 +31,13 @@ import org.caosdb.server.transaction.Retrieve; public enum MagicTypes { UNIT, NAME, - DESCRIPTION; + DESCRIPTION, + ROOT_DIRECTORY; private static final EntityID UNIT_ID = new EntityID(21); private static final EntityID DESCRIPTION_ID = new EntityID(24); private static final EntityID NAME_ID = new EntityID(20); + private static final EntityID ROOT_DIRECTORY_ID = new EntityID(51); public EntityID getId() { switch (this) { @@ -45,6 +47,8 @@ public enum MagicTypes { return NAME_ID; case DESCRIPTION: return DESCRIPTION_ID; + case ROOT_DIRECTORY: + return ROOT_DIRECTORY_ID; default: return null; } @@ -58,6 +62,8 @@ public enum MagicTypes { return NAME; case 24: return DESCRIPTION; + case 51: + return ROOT_DIRECTORY; default: return null; } diff --git a/src/main/java/org/caosdb/server/entity/Role.java b/src/main/java/org/caosdb/server/entity/Role.java index 1ce6329d..53bc3a47 100644 --- a/src/main/java/org/caosdb/server/entity/Role.java +++ b/src/main/java/org/caosdb/server/entity/Role.java @@ -35,7 +35,6 @@ public enum Role { Domain, File, Directory, - Link, Property, DataType, QueryTemplate; @@ -75,14 +74,12 @@ public enum Role { return new FileToElementStrategy(toString()); case Directory: return new FileToElementStrategy(toString()); - case Link: - return new FileToElementStrategy(toString()); default: return new EntityToElementStrategy(toString()); } } public boolean hasFSODescriptor() { - return this == File; // || this == Directory; + return this == File || this == Directory; } } diff --git a/src/main/java/org/caosdb/server/filesystem/FSODescriptor.java b/src/main/java/org/caosdb/server/filesystem/FSODescriptor.java index d1b59ca8..5290f869 100644 --- a/src/main/java/org/caosdb/server/filesystem/FSODescriptor.java +++ b/src/main/java/org/caosdb/server/filesystem/FSODescriptor.java @@ -187,8 +187,9 @@ public class FSODescriptor implements VirtualFSODescriptorInterface { this.children = children; } - public static FSODescriptor createDir(final Path dirPath) { + public static FSODescriptor createDir(String fileStorageId, final Path dirPath) { final FSODescriptor ret = new FSODescriptor(dirPath); + ret.fileStorageId = fileStorageId; ret.type = ObjectType.DIRECTORY; return ret; } @@ -252,4 +253,9 @@ public class FSODescriptor implements VirtualFSODescriptorInterface { public void setTwin(RealFSODescriptorInterface twin) { this.twin = twin; } + + @Override + public void setKey(String key) { + this.key = key; + } } diff --git a/src/main/java/org/caosdb/server/filesystem/FileStorageInterface.java b/src/main/java/org/caosdb/server/filesystem/FileStorageInterface.java index 19420a85..e2bca5af 100644 --- a/src/main/java/org/caosdb/server/filesystem/FileStorageInterface.java +++ b/src/main/java/org/caosdb/server/filesystem/FileStorageInterface.java @@ -39,7 +39,8 @@ public interface FileStorageInterface { */ public abstract RealFSODescriptorInterface resolve(String fileKey); - public abstract Undoable move(RealFSODescriptorInterface file, String fileKey) throws Message; + public abstract Undoable move( + RealFSODescriptorInterface file, VirtualFSODescriptorInterface target) throws Message; public abstract Undoable delete(String fileKey) throws Message; diff --git a/src/main/java/org/caosdb/server/filesystem/FileSystem.java b/src/main/java/org/caosdb/server/filesystem/FileSystem.java index e016d795..800a003b 100644 --- a/src/main/java/org/caosdb/server/filesystem/FileSystem.java +++ b/src/main/java/org/caosdb/server/filesystem/FileSystem.java @@ -46,12 +46,6 @@ public class FileSystem implements FileSystemInterface { return fs.delete(file.getKey()); } - @Override - public Undoable store(final RealFSODescriptorInterface file) throws Message { - final FileStorageInterface fs = getFileStorage(file); - return fs.move(file, file.getKey()); - } - public static FileSystem getInstance() { return instance; } @@ -98,10 +92,10 @@ public class FileSystem implements FileSystemInterface { return null; } - public Undoable move(RealFSODescriptorInterface fso, FSODescriptorInterface target) + public Undoable move(RealFSODescriptorInterface fso, VirtualFSODescriptorInterface target) throws Message { FileStorageInterface fileStorage = getFileStorage(target); - return fileStorage.move(fso, target.getKey()); + return fileStorage.move(fso, target); } // public static boolean resolve(FSODescriptorInterface fso) { diff --git a/src/main/java/org/caosdb/server/filesystem/FileSystemInterface.java b/src/main/java/org/caosdb/server/filesystem/FileSystemInterface.java index 33cd2204..a28cff1d 100644 --- a/src/main/java/org/caosdb/server/filesystem/FileSystemInterface.java +++ b/src/main/java/org/caosdb/server/filesystem/FileSystemInterface.java @@ -10,16 +10,6 @@ import org.caosdb.server.utils.Undoable; */ public interface FileSystemInterface { - /** - * Store the file described by the FileDescriptorInterface. - * - * @param file - * @return an {@link Undoable} object, which reverts the storage procedure and puts the file back - * to where it was. To be called when failures occured and the transaction needs to roll back. - * @throws Message - */ - public abstract Undoable store(RealFSODescriptorInterface file) throws Message; - /** * Delete the file described by the FileDescriptorInterface. * diff --git a/src/main/java/org/caosdb/server/filesystem/LocalFileStorage.java b/src/main/java/org/caosdb/server/filesystem/LocalFileStorage.java index e981071c..54fb6980 100644 --- a/src/main/java/org/caosdb/server/filesystem/LocalFileStorage.java +++ b/src/main/java/org/caosdb/server/filesystem/LocalFileStorage.java @@ -285,10 +285,17 @@ public abstract class LocalFileStorage implements FileStorageInterface { } @Override - public Undoable move(final RealFSODescriptorInterface file, String key) throws Message { + public Undoable move(final RealFSODescriptorInterface file, VirtualFSODescriptorInterface target) + throws Message { + String key = target.getKey(); + if (key == null) { + key = target.getPath().toString(); + target.setKey(key); + } + final Path targetPath = getRoot().resolve(key); - if (file.getObjectType() == ObjectType.DIRECTORY) { + if (target.getObjectType() == ObjectType.DIRECTORY) { final File newDirectory = createDirs(targetPath.toFile()); return new Undoable() { @@ -319,7 +326,7 @@ public abstract class LocalFileStorage implements FileStorageInterface { public Undoable delete(final String fileKey) throws Message { final Path targetPath = getRoot().resolve(fileKey); - return deleteWithEmptyParentDir(targetPath.toFile(), true); + return delete(targetPath.toFile(), true); } public Path getRoot() { @@ -613,17 +620,6 @@ public abstract class LocalFileStorage implements FileStorageInterface { }; } - Undoable deleteWithEmptyParentDir(final File f, final boolean recursive) throws Message { - final File parent = f.getParentFile(); - if (!getRoot().toFile().equals(parent)) { - final File[] siblings = parent.listFiles(); - if (siblings == null || siblings.length == 1 && siblings[0].equals(f)) { - return deleteWithEmptyParentDir(parent, recursive); - } - } - return delete(f, recursive); - } - public Long getSize(String key) { File file = getFile(key); if (file == null) { diff --git a/src/main/java/org/caosdb/server/filesystem/VirtualFSODescriptorInterface.java b/src/main/java/org/caosdb/server/filesystem/VirtualFSODescriptorInterface.java index 6b1e8e1e..fa5cf556 100644 --- a/src/main/java/org/caosdb/server/filesystem/VirtualFSODescriptorInterface.java +++ b/src/main/java/org/caosdb/server/filesystem/VirtualFSODescriptorInterface.java @@ -120,4 +120,6 @@ public interface VirtualFSODescriptorInterface extends FSODescriptorInterface { @Override public abstract List<? extends VirtualFSODescriptorInterface> listChildren(); + + public abstract void setKey(String key); } diff --git a/src/main/java/org/caosdb/server/jobs/core/AutoCreateDirs.java b/src/main/java/org/caosdb/server/jobs/core/AutoCreateDirs.java new file mode 100644 index 00000000..afbadcbe --- /dev/null +++ b/src/main/java/org/caosdb/server/jobs/core/AutoCreateDirs.java @@ -0,0 +1,82 @@ +package org.caosdb.server.jobs.core; + +import org.caosdb.server.database.backend.transaction.GetFileEntityByPath; +import org.caosdb.server.database.exceptions.EntityDoesNotExistException; +import org.caosdb.server.entity.EntityInterface; +import org.caosdb.server.entity.InsertEntity; +import org.caosdb.server.entity.MagicTypes; +import org.caosdb.server.entity.Role; +import org.caosdb.server.entity.UpdateEntity; +import org.caosdb.server.filesystem.FSODescriptor; +import org.caosdb.server.filesystem.Path; +import org.caosdb.server.filesystem.VirtualFSODescriptorInterface; +import org.caosdb.server.jobs.EntityFlagJob; +import org.caosdb.server.jobs.JobAnnotation; +import org.caosdb.server.permissions.EntityACL; + +@JobAnnotation(flag = "autoCreateDirs", defaultValue = "true", loadAlways = true) +public class AutoCreateDirs extends EntityFlagJob { + + @Override + protected void job(final String value) { + final boolean createDirs = "true".equalsIgnoreCase(value); + + if (createDirs + && (getEntity() instanceof InsertEntity || getEntity() instanceof UpdateEntity) + && getEntity().hasFSODescriptor()) { + createDirs(getEntity().getFSODescriptor()); + } + } + + private void createDirs(final VirtualFSODescriptorInterface fso) { + VirtualFSODescriptorInterface nextFSO = fso; + String fileStorageId = nextFSO.getFileStorageId(); + Path parentDir = nextFSO.getPath().getParent(); + while (parentDir != null) { + final VirtualFSODescriptorInterface parentFSO = exists(parentDir); + if (parentFSO != null) { + // the parent does exist. + fso.setParentDirectory(parentFSO.getEntityId()); + break; + } + nextFSO = createDir(fileStorageId, parentDir, nextFSO); + parentDir = parentDir.getParent(); + } + if (parentDir == null) { // root of file system + nextFSO.setParentDirectory(MagicTypes.ROOT_DIRECTORY.getId()); + } + } + + private VirtualFSODescriptorInterface exists(final Path dirPath) { + for (final EntityInterface e : getContainer()) { + if (e.getRole() == Role.Directory + && (e instanceof InsertEntity || e instanceof UpdateEntity) + && e.getFSODescriptor().getPath().equals(dirPath)) { + // Found the directory in the container + return e.getFSODescriptor(); + } + } + final GetFileEntityByPath t = new GetFileEntityByPath(dirPath); + try { + execute(t); + return t.getEntity().getFSODescriptor(); + } catch (final EntityDoesNotExistException e) { + return null; + } + } + + private VirtualFSODescriptorInterface createDir( + final String fileStorageId, final Path dirPath, final VirtualFSODescriptorInterface child) { + final String name = dirPath.getLastSegment(); + final VirtualFSODescriptorInterface newFD = FSODescriptor.createDir(fileStorageId, dirPath); + + child.setParentDirectory(newFD.getEntityId()); + + final EntityInterface newDir = new InsertEntity(name, Role.Directory); + newDir.setEntityACL(EntityACL.getOwnerACLFor(getUser())); + newDir.setFSODescriptor(newFD); + getContainer().add(newDir); + + return newFD; + } +} diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckFileStorageConsistency.java b/src/main/java/org/caosdb/server/jobs/core/CheckFileStorageConsistency.java index bd6b42d2..8cc5d26e 100644 --- a/src/main/java/org/caosdb/server/jobs/core/CheckFileStorageConsistency.java +++ b/src/main/java/org/caosdb/server/jobs/core/CheckFileStorageConsistency.java @@ -38,6 +38,7 @@ import org.caosdb.server.database.exceptions.TransactionException; import org.caosdb.server.entity.Message; import org.caosdb.server.entity.Message.MessageType; import org.caosdb.server.entity.xml.ToElementable; +import org.caosdb.server.filesystem.FSODescriptor; import org.caosdb.server.filesystem.FileSystem; import org.caosdb.server.filesystem.Path; import org.caosdb.server.filesystem.RealFSODescriptorInterface; @@ -187,7 +188,8 @@ public class CheckFileStorageConsistency extends FlagJob { if (f1 == null) { return; } - Undoable undo1 = FileSystem.getInstance().getFileStorage(f1).move(f1, "somewhere.else"); + FSODescriptor target = new FSODescriptor(new Path("debug/somewhere.else")); + Undoable undo1 = FileSystem.getInstance().getFileStorage(f1).move(f1, target); getTransaction() .acceptObserver( diff --git a/src/main/java/org/caosdb/server/query/CQLLexer.g4 b/src/main/java/org/caosdb/server/query/CQLLexer.g4 index 99c9879d..1b113a27 100644 --- a/src/main/java/org/caosdb/server/query/CQLLexer.g4 +++ b/src/main/java/org/caosdb/server/query/CQLLexer.g4 @@ -343,6 +343,10 @@ FILE: [Ff][Ii][Ll][Ee]([Ss])? WHITE_SPACE_f? ; +DIRECTORY: + [Dd][Ii][Rr][Ee][Cc][Tt][Oo][Rr]([Yy]|[Ii][Ee][Ss]) WHITE_SPACE_f? +; + ENTITY: [Ee][Nn][Tt][Ii][Tt]([Yy]|[Ii][Ee][Ss]) WHITE_SPACE_f? ; diff --git a/src/main/java/org/caosdb/server/query/CQLParser.g4 b/src/main/java/org/caosdb/server/query/CQLParser.g4 index d44674b2..a9656ca1 100644 --- a/src/main/java/org/caosdb/server/query/CQLParser.g4 +++ b/src/main/java/org/caosdb/server/query/CQLParser.g4 @@ -99,6 +99,7 @@ role returns [Query.Role r]: | RECORD {$r = Query.Role.RECORD;} | PROPERTY {$r = Query.Role.PROPERTY;} | FILE {$r = Query.Role.FILE;} + | DIRECTORY {$r = Query.Role.DIRECTORY;} | QUERYTEMPLATE {$r = Query.Role.QUERYTEMPLATE;} | ENTITY {$r = Query.Role.ENTITY;} ; diff --git a/src/main/java/org/caosdb/server/query/Query.java b/src/main/java/org/caosdb/server/query/Query.java index 88da39bc..ac1a41a2 100644 --- a/src/main/java/org/caosdb/server/query/Query.java +++ b/src/main/java/org/caosdb/server/query/Query.java @@ -160,6 +160,7 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac PROPERTY, ENTITY, FILE, + DIRECTORY, QUERYTEMPLATE } -- GitLab