diff --git a/src/main/java/org/caosdb/server/entity/Entity.java b/src/main/java/org/caosdb/server/entity/Entity.java index 48a92fd053cce0cadb86785c4a13d73db7359e08..317aea4dfc9e15e2115817621a455f083ff3d5b5 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 39556385235e6b72736bed8dff67a178cfe60aff..289fae6e9164c66a8921b765a7275a0ac6a493e3 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 1ce6329d67ac97cb9e520604ceb30d2d1cd938d1..53bc3a47a9c78c8dfd753e3f2e8128788bade3bf 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 d1b59ca881eda0d831a037b0cd189894b4f2ea46..5290f8698e828cd5da4a6b953690a9710834847f 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 19420a8527576c35ba29dc8eba39f1484840b9c9..e2bca5afea193b9727534bdc153c4f4aa825468b 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 e016d7952f96e0a43e5a8f878e41e5f1f44f9223..800a003bf58de1c92d86e636e54a6434136b8d0f 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 33cd2204f8971a6b68c59b3471b80119e8e64b5f..a28cff1df4a8b927f108dc2d06a316812a521fcc 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 e981071cd7fa83d50e5cdd18d07a49292904ee24..54fb698029048939851aa89719c7d6a8f2f2a10c 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 6b1e8e1eb75c544c843efb02ac03e796540eb790..fa5cf556311ab41d6cd60527dfbfbf53d0c3b9fb 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 0000000000000000000000000000000000000000..afbadcbedac4c9f1c0a79f81a7e88b0eddf13678 --- /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 bd6b42d202f7b6acbc7bf16aba37d753fb219007..8cc5d26efa604b91e2fdafd57935307193980560 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 99c9879de7a772c6032decc51486fe7485d869e5..1b113a275ab5bbaa75dde3bb689fb2b1f81e60f1 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 d44674b29d5c35fab2db9f4a8395e064219b79c9..a9656ca1ced8055e3915bb81cdaa566988a2bf92 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 88da39bce24f1cabf651eebf1532cab14bddb82c..ac1a41a254501f895e81a1764c048850f268fcbf 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 }