diff --git a/Makefile b/Makefile index 29b7d5a51cb110e35f999da0a7c6b366b5bd0d05..caa14420e64acb8847f3ebf454a0d62f3000ddac 100644 --- a/Makefile +++ b/Makefile @@ -26,8 +26,8 @@ CAOSDB_SERVER_VERSION ?= $(shell mvn org.apache.maven.plugins:maven-help-plugin:3.1.0:evaluate -Dexpression=project.version -q -DforceStdout) CAOSDB_COMMAND_LINE_OPTIONS ?= SHELL:=/bin/bash -JPDA_PORT ?= 9000 -JMX_PORT ?= 9090 +JPDA_PORT ?= 9001 +JMX_PORT ?= 9091 compile: print-version easy-units mvn compile diff --git a/conf/core/jobs.csv b/conf/core/jobs.csv index 6f6be8723aabc7205d9cacbfdf0b8bf2b65c8bf7..013f4984c7220669546a55f3d2717cc8e892072a 100644 --- a/conf/core/jobs.csv +++ b/conf/core/jobs.csv @@ -56,6 +56,7 @@ 0,2,UPDATE,SetImpToFix,ERROR ## file rules +0,3,INSERT,ImportFSO,ERROR 0,3,INSERT,CheckNamePresent,WARN 0,3,INSERT,MatchFileProp,ERROR 0,3,INSERT,CheckTargetPathValid,ERROR @@ -67,6 +68,7 @@ 0,3,UPDATE,SetImpToFix,ERROR ## directory rules +0,9,INSERT,ImportFSO,ERROR 0,9,INSERT,CheckNamePresent,WARN 0,9,INSERT,MatchFileProp,ERROR 0,9,INSERT,CheckTargetPathValid,ERROR @@ -78,6 +80,7 @@ 0,9,UPDATE,SetImpToFix,ERROR ## link rules +0,10,INSERT,ImportFSO,ERROR 0,10,INSERT,CheckNamePresent,WARN 0,10,INSERT,MatchFileProp,ERROR 0,10,INSERT,CheckTargetPathValid,ERROR diff --git a/mvn_install_jar.sh b/mvn_install_jar.sh deleted file mode 100755 index b7bdb6cf820e05327caa8aed5e68c4f6b3f2e86b..0000000000000000000000000000000000000000 --- a/mvn_install_jar.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -# -# ** header v3.0 -# This file is a part of the CaosDB Project. -# -# Copyright (C) 2018 Research Group Biomedical Physics, -# Max-Planck-Institute for Dynamics and Self-Organization Göttingen -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <https://www.gnu.org/licenses/>. -# -# ** end header -# - -# Alexander Schlemmer, 01/2013 -# Installiert die jar-Datei des Servers in das lokale mvn-Repository. - -mvn install:install-file -DgroupId=ds.mpg.de -DartifactId=caosdb-server \ - -Dversion=1.0 -Dpackaging=jar -Dfile=dist/CaosDBServer.jar diff --git a/src/main/java/org/caosdb/server/entity/Entity.java b/src/main/java/org/caosdb/server/entity/Entity.java index 7ea937ef07de61dfbc178a4c576859eca005a4ff..4c4aed5caabea82b4958a62dc27db4295e290386 100644 --- a/src/main/java/org/caosdb/server/entity/Entity.java +++ b/src/main/java/org/caosdb/server/entity/Entity.java @@ -837,7 +837,12 @@ public abstract class Entity extends AbstractObservable implements EntityInterfa // Parse IMPORT boolean isImport = false; - if (element.getAttribute("import") != null + boolean isRecursiveImport = false; + if (element.getAttribute("recursive_import") != null + && element.getAttributeValue("recursive_import").equals("true")) { + isImport = true; + isRecursiveImport = true; + } else if (element.getAttribute("import") != null && element.getAttributeValue("import").equals("true")) { isImport = true; } @@ -859,6 +864,7 @@ public abstract class Entity extends AbstractObservable implements EntityInterfa FileSystem.DEFAULT_BACKEND, null, hash, new Path(path), size, tmpIdentifier); } fso.setImport(isImport); + fso.setRecursiveImport(isRecursiveImport); setFSODescriptor(fso); } diff --git a/src/main/java/org/caosdb/server/entity/InsertEntity.java b/src/main/java/org/caosdb/server/entity/InsertEntity.java index b88b319fc956d9d0ee4f34d99d9abef94819ea86..2992beaca01bb8fd9c3b2b33dcabcea50a9c6916 100644 --- a/src/main/java/org/caosdb/server/entity/InsertEntity.java +++ b/src/main/java/org/caosdb/server/entity/InsertEntity.java @@ -34,6 +34,10 @@ public class InsertEntity extends WritableEntity { super(name, role); } + public InsertEntity(Role role) { + super((String) null, role); + } + public InsertEntity(Element e) { super(e); } diff --git a/src/main/java/org/caosdb/server/filesystem/FSODescriptor.java b/src/main/java/org/caosdb/server/filesystem/FSODescriptor.java index 9c4d62bdc6741671b7efd020eb0136a7d2013905..008e5703999a8a6fe07b8345ea99f7bd73b9c8ba 100644 --- a/src/main/java/org/caosdb/server/filesystem/FSODescriptor.java +++ b/src/main/java/org/caosdb/server/filesystem/FSODescriptor.java @@ -44,6 +44,7 @@ public class FSODescriptor implements VirtualFSODescriptorInterface { private RealFSODescriptorInterface twin; private EntityID linkTarget = null; private boolean isImport = false; + private boolean isRecursiveImport; public FSODescriptor( final String fileStorageId, @@ -91,6 +92,17 @@ public class FSODescriptor implements VirtualFSODescriptorInterface { this.size = size; } + public FSODescriptor(RealFSODescriptorInterface twin, Path path) { + this.path = path; + this.type = twin.getObjectType(); + this.hash = twin.getHash(); + this.size = twin.getSize(); + this.fileStorageId = twin.getFileStorageId(); + this.key = twin.getKey(); + this.mimeType = twin.getMimeType(); + this.twin = twin; + } + @Override public EntityStatus getEntityStatus() { return status; @@ -291,7 +303,16 @@ public class FSODescriptor implements VirtualFSODescriptorInterface { return isImport; } + @Override + public boolean isRecursiveImport() { + return isRecursiveImport; + } + public void setImport(boolean isImport) { this.isImport = isImport; } + + public void setRecursiveImport(boolean isRecursiveImport) { + this.isRecursiveImport = isRecursiveImport; + } } diff --git a/src/main/java/org/caosdb/server/filesystem/VirtualFSODescriptorInterface.java b/src/main/java/org/caosdb/server/filesystem/VirtualFSODescriptorInterface.java index 4e65b45ba964062ce6d7a7afc37eae98ce699f3a..e907dac8ac41b0cc0cecdd958dc7097519e42a3a 100644 --- a/src/main/java/org/caosdb/server/filesystem/VirtualFSODescriptorInterface.java +++ b/src/main/java/org/caosdb/server/filesystem/VirtualFSODescriptorInterface.java @@ -128,4 +128,6 @@ public interface VirtualFSODescriptorInterface extends FSODescriptorInterface { public abstract void setLinkTargetPath(Path target); public abstract boolean isImport(); + + boolean isRecursiveImport(); } diff --git a/src/main/java/org/caosdb/server/filesystem/consistency/ConsistencyCheck.java b/src/main/java/org/caosdb/server/filesystem/consistency/ConsistencyCheck.java index 71e1a2cd7dea2573d8bb0b10382b4362171aafdd..af33421d589b819661269f3eb779abd925e29bfc 100644 --- a/src/main/java/org/caosdb/server/filesystem/consistency/ConsistencyCheck.java +++ b/src/main/java/org/caosdb/server/filesystem/consistency/ConsistencyCheck.java @@ -83,7 +83,6 @@ class RunThroughInternalFileSystem extends ConsistencyCheckStrategy { while (iterator != null && iterator.hasNext()) { long timestamp = System.currentTimeMillis(); - System.err.println("here"); if (DatabaseAccessManager.whoHasReservedWriteAccess() != null) { // there is a thread waiting to write. pause this one and @@ -92,11 +91,9 @@ class RunThroughInternalFileSystem extends ConsistencyCheckStrategy { this.getAccess().release(); this.setAccess(DatabaseAccessManager.getInstance().acquireReadAccess(this)); iterator = execute(it, this.getAccess()).getIterator(); - System.err.println("here2"); } final VirtualFSODescriptorInterface howItShouldBe = iterator.next(); - System.err.println("check: " + howItShouldBe.toString()); check(howItShouldBe); diff --git a/src/main/java/org/caosdb/server/jobs/FilesJob.java b/src/main/java/org/caosdb/server/jobs/FilesJob.java index 70b099ea7e8d77fb88f66a570bc679b7e4656a9b..f746e7f7ac0c1fc2697383083652ad70f05065da 100644 --- a/src/main/java/org/caosdb/server/jobs/FilesJob.java +++ b/src/main/java/org/caosdb/server/jobs/FilesJob.java @@ -52,6 +52,8 @@ public abstract class FilesJob extends EntityJob { throw ServerMessages.FILE_IMPORT_FAILED_FILE_DOES_NOT_EXIST; } } - return getContainer().getFiles().get(fso.getTmpIdentifier()); + RealFSODescriptorInterface twin = getContainer().getFiles().get(fso.getTmpIdentifier()); + fso.setTwin(twin); + return twin; } } diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckTargetPathValid.java b/src/main/java/org/caosdb/server/jobs/core/CheckTargetPathValid.java index 60e5ddd5f60fcfea75c39637bd9a72aa134c8d13..6d3815537f8420109a2207ada1e8f115b11aa6cc 100644 --- a/src/main/java/org/caosdb/server/jobs/core/CheckTargetPathValid.java +++ b/src/main/java/org/caosdb/server/jobs/core/CheckTargetPathValid.java @@ -23,7 +23,6 @@ package org.caosdb.server.jobs.core; import org.caosdb.server.database.backend.transaction.CheckTargetPath; -import org.caosdb.server.filesystem.FileSystem; import org.caosdb.server.filesystem.VirtualFSODescriptorInterface; import org.caosdb.server.jobs.FilesJob; import org.caosdb.server.utils.EntityStatus; @@ -52,15 +51,6 @@ public class CheckTargetPathValid extends FilesJob { // check that target path is not owned by another entity. final CheckTargetPath t = new CheckTargetPath(getEntity(), getContainer()); execute(t); - - // check if the fso exists in the file storage - if (FileSystem.exists(getEntity().getFSODescriptor())) { - if (getEntity().getFSODescriptor().isImport()) { - // ok? - } else { - // is update? - } - } } } } diff --git a/src/main/java/org/caosdb/server/jobs/core/ImportFSO.java b/src/main/java/org/caosdb/server/jobs/core/ImportFSO.java new file mode 100644 index 0000000000000000000000000000000000000000..a86da810c5961aa655ccc45aacf1c8023e1da0f9 --- /dev/null +++ b/src/main/java/org/caosdb/server/jobs/core/ImportFSO.java @@ -0,0 +1,103 @@ +package org.caosdb.server.jobs.core; + +import java.util.LinkedList; +import java.util.List; +import org.caosdb.server.entity.EntityInterface; +import org.caosdb.server.entity.InsertEntity; +import org.caosdb.server.entity.Message; +import org.caosdb.server.entity.Role; +import org.caosdb.server.filesystem.FSODescriptor; +import org.caosdb.server.filesystem.FileSystem.ObjectType; +import org.caosdb.server.filesystem.Path; +import org.caosdb.server.filesystem.RealFSODescriptorInterface; +import org.caosdb.server.filesystem.VirtualFSODescriptorInterface; +import org.caosdb.server.jobs.FilesJob; +import org.caosdb.server.jobs.Job; +import org.caosdb.server.jobs.JobAnnotation; +import org.caosdb.server.jobs.TransactionStage; +import org.caosdb.server.utils.ServerMessages; + +@JobAnnotation(stage = TransactionStage.PRE_CHECK) +public class ImportFSO extends FilesJob { + + @Override + protected void run() throws Message { + if (getEntity().hasFSODescriptor() && getEntity().getFSODescriptor().isImport()) { + VirtualFSODescriptorInterface fso = getEntity().getFSODescriptor(); + RealFSODescriptorInterface realFSO = getRealFSODescriptor(fso); + + if (realFSO == null) { + // handle + } + + if (getEntity().getFSODescriptor().isRecursiveImport() + && fso.getObjectType() == ObjectType.DIRECTORY) { + assertIsDirectory(realFSO); + fso.setChildren(importRecursively(getEntity(), realFSO, fso.getPath())); + } + // else warn not a Directory? + } + } + + private List<VirtualFSODescriptorInterface> importRecursively( + EntityInterface parentDirectory, RealFSODescriptorInterface directory, Path path) { + List<? extends RealFSODescriptorInterface> children = directory.listChildren(); + List<VirtualFSODescriptorInterface> results = new LinkedList<>(); + for (RealFSODescriptorInterface child : children) { + Path childPath = path.append(child.getName()); + switch (child.getObjectType()) { + case DIRECTORY: + EntityInterface newDirectory = importDirectory(parentDirectory, child, childPath); + results.addAll(importRecursively(newDirectory, child, childPath)); + break; + case LINK: + results.add(importLink(parentDirectory, child, childPath)); + break; + default: + results.add(importFile(parentDirectory, child, childPath)); + break; + } + } + return results; + } + + private EntityInterface importDirectory( + EntityInterface parentDirectory, RealFSODescriptorInterface toBeImported, Path path) { + InsertEntity directoryEntity = new InsertEntity(Role.Directory); + importFSO(directoryEntity, parentDirectory, toBeImported, path); + return directoryEntity; + } + + private VirtualFSODescriptorInterface importLink( + EntityInterface parentDirectory, RealFSODescriptorInterface toBeImported, Path path) { + throw new UnsupportedOperationException(); + } + + private VirtualFSODescriptorInterface importFile( + EntityInterface parentDirectory, RealFSODescriptorInterface toBeImported, Path path) { + InsertEntity fileEntity = new InsertEntity(Role.File); + return importFSO(fileEntity, parentDirectory, toBeImported, path); + } + + private VirtualFSODescriptorInterface importFSO( + InsertEntity entity, + EntityInterface parentDirectory, + RealFSODescriptorInterface toBeImported, + Path path) { + entity.setEntityACL(getEntity().getEntityACL()); + VirtualFSODescriptorInterface fso = new FSODescriptor(toBeImported, path); + fso.setParentDirectory(parentDirectory.getId()); + entity.setFSODescriptor(fso); + getContainer().add(entity); + + final List<Job> loadJobs = this.loadJobs(entity, getTransaction()); + getTransaction().getSchedule().addAll(loadJobs); + return fso; + } + + private void assertIsDirectory(RealFSODescriptorInterface realFSO) throws Message { + if (realFSO.getObjectType() != ObjectType.DIRECTORY) { + throw ServerMessages.CANNOT_PERFORM_RECURSIVE_IMPORT_ON_NON_DIRECTORIES; + } + } +} diff --git a/src/main/java/org/caosdb/server/transaction/WriteTransaction.java b/src/main/java/org/caosdb/server/transaction/WriteTransaction.java index a35b7a595a15f2b9ace5d902c7492cc0fb182eb4..4193a0df9cc2d5b830c62b2ab6d896574224e89a 100644 --- a/src/main/java/org/caosdb/server/transaction/WriteTransaction.java +++ b/src/main/java/org/caosdb/server/transaction/WriteTransaction.java @@ -52,10 +52,12 @@ import org.caosdb.server.entity.wrapper.Parent; import org.caosdb.server.entity.wrapper.Property; import org.caosdb.server.filesystem.FSODescriptorInterface; import org.caosdb.server.filesystem.FileSystem; +import org.caosdb.server.filesystem.FileSystem.ObjectType; import org.caosdb.server.filesystem.RealFSODescriptorInterface; import org.caosdb.server.filesystem.VirtualFSODescriptorInterface; import org.caosdb.server.jobs.Job; import org.caosdb.server.jobs.Schedule; +import org.caosdb.server.jobs.core.ImportFSO; import org.caosdb.server.permissions.EntityACL; import org.caosdb.server.permissions.EntityPermission; import org.caosdb.server.permissions.Permission; @@ -478,6 +480,18 @@ public class WriteTransaction extends Transaction<WritableContainer> needPermissions.add(EntityPermission.UPDATE_REMOVE_FILE); needPermissions.add(EntityPermission.UPDATE_ADD_FILE); updateFile = true; + } else if (newFile.isImport()) { + RealFSODescriptorInterface realFso = FileSystem.getInstance().resolve(oldFile); + if (realFso.getSize() != oldFile.getSize()) { + updateFile = true; + } else if (!oldFile.getHash().equals(realFso.getHash())) { + updateFile = true; + } else if (newFile.isRecursiveImport() + && realFso.getObjectType() == ObjectType.DIRECTORY) { + // run import job now + getSchedule().runJob(newEntity, ImportFSO.class); + updateFile = matchFso(newEntity.getFSODescriptor(), oldEntity.getFSODescriptor()); + } } } @@ -549,12 +563,38 @@ public class WriteTransaction extends Transaction<WritableContainer> // nothing to be updated if (!updatetable) { newEntity.setEntityStatus(EntityStatus.VALID); + newEntity.setVersion(oldEntity.getVersion()); newEntity.addInfo("Nothing to be updated."); } return needPermissions; } + private boolean matchFso( + VirtualFSODescriptorInterface newFsoDescriptor, + VirtualFSODescriptorInterface oldFsoDescriptor) { + boolean updateFso = false; + + if(oldFsoDescriptor.getObjectType() != newFsoDescriptor.getObjectType()) { + throw new UnsupportedOperationException("Cannot update the file system object type."); + } + + // now it is clear that both have the same object type. + switch(newFsoDescriptor.getObjectType()) { + case DIRECTORY: + break; + case LINK: + break; + default: + boolean pathEquals = Objects.equals(newFsoDescriptor.getPath(), oldFsoDescriptor.getPath()); + boolean + + break; + } + + return updateFso; + } + /** * Attempt to find a (sparse) entity among a list of entities. * diff --git a/src/main/java/org/caosdb/server/utils/ServerMessages.java b/src/main/java/org/caosdb/server/utils/ServerMessages.java index cc963ccf759ca73cc4f54160ae40db1d83c87546..5ea9aa558cc145c3dcbe85d30fca457839a5a8cb 100644 --- a/src/main/java/org/caosdb/server/utils/ServerMessages.java +++ b/src/main/java/org/caosdb/server/utils/ServerMessages.java @@ -607,6 +607,12 @@ public class ServerMessages { MessageCode.MESSAGE_CODE_INTEGRITY_VIOLATION, "This entity caused an unexpected integrity violation. This is a strong indicator for a server bug. Please report."); + public static final Message CANNOT_PERFORM_RECURSIVE_IMPORT_ON_NON_DIRECTORIES = + new Message( + MessageType.Error, + MessageCode.MESSAGE_CODE_UNKNOWN, + "Cannot perform recursive import on non-directory objects."); + public static final Message INVALID_USER_NAME(String policy) { return new Message( MessageType.Error,