diff --git a/CHANGELOG.md b/CHANGELOG.md index be47de54cd33c917b8997eebaa55c31edfe71543..77982cc0837f905f10274f23f83bd53b6414bbb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,9 +17,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed ### +* Inheritance of the unit is not working. (GRPC API) + [linkahead-server#264](https://gitlab.indiscale.com/caosdb/src/caosdb-server/-/issues/264) +* Curly brackets in query lead to unexpected server error. + [linkahead-server#138](https://gitlab.com/linkahead/linkahead-server/-/issues/138) * Wrong url returned by FileSystem resource behind proxy. * `NullPointerException` in GRPC API converters when executing SELECT query on NULL values. +* Fix parsing of decimal numbers. Fixes https://gitlab.com/linkahead/linkahead-server/-/issues/239 ## [0.10.0] - 2023-06-02 ## (Florian Spreckelsen) diff --git a/README_SETUP.md b/README_SETUP.md index acb3a79ad256fbba6a0d0a69887bb7c7ba7b28b9..e3b6e3bc3e8a69c2ff1e162476819ec76b93dbe9 100644 --- a/README_SETUP.md +++ b/README_SETUP.md @@ -163,6 +163,9 @@ sources (if you called `make run` previously). `$ make test` +You can run single unit test with +`mvn test -X -Dtest=TestCQL#testDecimalNumber` + ## Setup Eclipse diff --git a/conf/core/jobs.csv b/conf/core/jobs.csv index 84795bee8657288473188cd9e7a6af81f6295221..3da052ff8529770646669c2f997d830dcb57d649 100644 --- a/conf/core/jobs.csv +++ b/conf/core/jobs.csv @@ -1,8 +1,8 @@ # # This file is a part of the CaosDB Project. # -# Copyright (C) 2021 Timm Fitsche <t.fitschen@indiscale.com> -# Copyright (C) 2021 IndiScale GmbH <info@indiscale.com> +# Copyright (C) 2021-2023 Timm Fitsche <t.fitschen@indiscale.com> +# Copyright (C) 2021-2023 IndiScale GmbH <info@indiscale.com> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -22,6 +22,7 @@ # DOMAIN_ID, ENTITY_ID, TRANSACTION, JOB, JOB_FAILURE_SEVERITY # general rules +0,0,INSERT,GenerateEntityId,ERROR 0,0,INSERT,CheckPropValid,ERROR 0,0,INSERT,CheckParValid,ERROR 0,0,INSERT,CheckParOblPropPresent,ERROR @@ -30,8 +31,7 @@ 0,0,UPDATE,CheckParValid,ERROR 0,0,UPDATE,CheckParOblPropPresent,ERROR 0,0,UPDATE,CheckValueParsable,ERROR -0,0,DELETE,CheckReferenceDependencyExistent,ERROR -0,0,DELETE,CheckChildDependencyExistent,ERROR +0,0,DELETE,CheckDependenciesBeforeDeletion,ERROR # role specific rules diff --git a/conf/core/server.conf b/conf/core/server.conf index 5df6c40ca04a9c6f22d03f61c937ab5fbeab565f..1e22c5c7a3aad2d0aa53b098eb4666e25e591f31 100644 --- a/conf/core/server.conf +++ b/conf/core/server.conf @@ -69,7 +69,7 @@ MYSQL_USER_NAME=caosdb # Password for the user MYSQL_USER_PASSWORD=random1234 # Schema of mysql procedures and tables which is required by this CaosDB instance -MYSQL_SCHEMA_VERSION=v5.0 +MYSQL_SCHEMA_VERSION=v6.0.0-SNAPSHOT-EXTID # -------------------------------------------------- diff --git a/pom.xml b/pom.xml index 05d1aa4b7d8e01b5cd3794b6fabbd01ea81a24ac..4fb8f7ac8f12b06c77f93906f29a950a2c6e083d 100644 --- a/pom.xml +++ b/pom.xml @@ -381,9 +381,9 @@ </executions> </plugin> <plugin> - <groupId>com.coveo</groupId> + <groupId>com.spotify.fmt</groupId> <artifactId>fmt-maven-plugin</artifactId> - <version>2.5.1</version> + <version>2.21.1</version> <configuration> <skip> <!-- Set skip to `true` to prevent auto-formatting while coding. --> diff --git a/src/main/java/org/caosdb/datetime/FragmentDateTime.java b/src/main/java/org/caosdb/datetime/FragmentDateTime.java index 38c16a988a0103a121767b99078c0ca41c6263d5..78ef7199970ac3aa52a53cae96744cd90fb23c20 100644 --- a/src/main/java/org/caosdb/datetime/FragmentDateTime.java +++ b/src/main/java/org/caosdb/datetime/FragmentDateTime.java @@ -21,7 +21,9 @@ * ** end header */ -/** @review Daniel Hornung 2022-03-04 */ +/** + * @review Daniel Hornung 2022-03-04 + */ package org.caosdb.datetime; import java.util.Objects; diff --git a/src/main/java/org/caosdb/datetime/UTCDateTime.java b/src/main/java/org/caosdb/datetime/UTCDateTime.java index 1dd86cc975d4b92066f2cf44fff51493ade5babe..a7719d5a3f30d862be0d7646796a4f2800149b06 100644 --- a/src/main/java/org/caosdb/datetime/UTCDateTime.java +++ b/src/main/java/org/caosdb/datetime/UTCDateTime.java @@ -21,7 +21,9 @@ * ** end header */ -/** @review Daniel Hornung 2022-03-04 */ +/** + * @review Daniel Hornung 2022-03-04 + */ package org.caosdb.datetime; import java.util.ArrayList; diff --git a/src/main/java/org/caosdb/server/CaosDBServer.java b/src/main/java/org/caosdb/server/CaosDBServer.java index 2a115c5cfa06ed0e3db85847c27fb4c8424eadaf..8c75cf51e9a85685c19c3de271f56b519b81add7 100644 --- a/src/main/java/org/caosdb/server/CaosDBServer.java +++ b/src/main/java/org/caosdb/server/CaosDBServer.java @@ -3,8 +3,8 @@ * * Copyright (C) 2018 Research Group Biomedical Physics, * Max-Planck-Institute for Dynamics and Self-Organization Göttingen - * Copyright (C) 2019-2021 IndiScale GmbH <info@indiscale.com> - * Copyright (C) 2019-2021 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2019-2021,2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2019-2021,2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -129,6 +129,7 @@ public class CaosDBServer extends Application { private static boolean NO_TLS = false; public static final String REQUEST_TIME_LOGGER = "REQUEST_TIME_LOGGER"; public static final String REQUEST_ERRORS_LOGGER = "REQUEST_ERRORS_LOGGER"; + private static boolean USE_CACHE = true; private static Scheduler SCHEDULER; public static String getServerProperty(final String key) { @@ -153,6 +154,7 @@ public class CaosDBServer extends Application { parseArguments(args); initScheduler(); initServerProperties(); + initCaching(); initTimeZone(); initOneTimeTokens(); initShiro(); @@ -198,6 +200,11 @@ public class CaosDBServer extends Application { SERVER_PROPERTIES = ServerProperties.initServerProperties(); } + public static void initCaching() { + USE_CACHE = + !Boolean.parseBoolean(CaosDBServer.getServerProperty(ServerProperties.KEY_CACHE_DISABLE)); + } + /** * Precedence order: * @@ -664,7 +671,7 @@ public class CaosDBServer extends Application { return 0; } return -1; - }; + } @Override public int match(final String formattedString) { @@ -886,6 +893,10 @@ public class CaosDBServer extends Application { throws SchedulerException { SCHEDULER.scheduleJob(job, trigger); } + + public static boolean useCache() { + return USE_CACHE; + } } class CaosDBComponent extends Component { diff --git a/src/main/java/org/caosdb/server/accessControl/Pam.java b/src/main/java/org/caosdb/server/accessControl/Pam.java index 992665c1db3ef42d3770c71e89fa5444f43f9d95..76fde331dc97b51ed82083999e4d49b94176695d 100644 --- a/src/main/java/org/caosdb/server/accessControl/Pam.java +++ b/src/main/java/org/caosdb/server/accessControl/Pam.java @@ -145,7 +145,9 @@ public class Pam implements UserSource { return this.map; } - /** @see {@link UserSource#resolveRolesForUsername(String)} */ + /** + * @see {@link UserSource#resolveRolesForUsername(String)} + */ @Override public Set<String> resolveRolesForUsername(final String username) { final Set<String> resulting_roles = new HashSet<String>(); @@ -266,7 +268,9 @@ public class Pam implements UserSource { return username != null && isIncorporated(username); } - /** @see {@link UserSource#isValid(String, String)}. */ + /** + * @see {@link UserSource#isValid(String, String)}. + */ @Override public boolean isValid(final String username, final String password) { if (username != null && isIncorporated(username)) { diff --git a/src/main/java/org/caosdb/server/caching/JCSCacheHelper.java b/src/main/java/org/caosdb/server/caching/JCSCacheHelper.java index 1613b8be8194425ab14e5e68b9215a0ba92be8d3..f294e4bb029b7e03d2864ae832b3044685536404 100644 --- a/src/main/java/org/caosdb/server/caching/JCSCacheHelper.java +++ b/src/main/java/org/caosdb/server/caching/JCSCacheHelper.java @@ -62,8 +62,7 @@ public class JCSCacheHelper implements CacheHelper { } public static void init() { - final boolean disabled = - Boolean.parseBoolean(CaosDBServer.getServerProperty(ServerProperties.KEY_CACHE_DISABLE)); + final boolean disabled = !CaosDBServer.useCache(); init(CaosDBServer.getServerProperty(ServerProperties.KEY_CACHE_CONF_LOC), disabled); } diff --git a/src/main/java/org/caosdb/server/database/BackendTransaction.java b/src/main/java/org/caosdb/server/database/BackendTransaction.java index 05ea26c255ae5b2bd1e75df7f3fdc4d94fca5e5a..c014972ac52246e87a1d9e6f575a645125ab4c57 100644 --- a/src/main/java/org/caosdb/server/database/BackendTransaction.java +++ b/src/main/java/org/caosdb/server/database/BackendTransaction.java @@ -3,8 +3,8 @@ * * Copyright (C) 2018 Research Group Biomedical Physics, * Max-Planck-Institute for Dynamics and Self-Organization Göttingen - * Copyright (C) 2019-2021 IndiScale GmbH <info@indiscale.com> - * Copyright (C) 2019-2021 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2019-2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2019-2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -29,7 +29,6 @@ import org.caosdb.server.database.backend.implementation.MySQL.MySQLDeleteRole; import org.caosdb.server.database.backend.implementation.MySQL.MySQLDeleteSparseEntity; import org.caosdb.server.database.backend.implementation.MySQL.MySQLDeleteUser; import org.caosdb.server.database.backend.implementation.MySQL.MySQLGetAllNames; -import org.caosdb.server.database.backend.implementation.MySQL.MySQLGetChildren; import org.caosdb.server.database.backend.implementation.MySQL.MySQLGetDependentEntities; import org.caosdb.server.database.backend.implementation.MySQL.MySQLGetFileRecordByPath; import org.caosdb.server.database.backend.implementation.MySQL.MySQLGetIDByName; @@ -49,6 +48,7 @@ import org.caosdb.server.database.backend.implementation.MySQL.MySQLListUsers; import org.caosdb.server.database.backend.implementation.MySQL.MySQLLogUserVisit; import org.caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveAll; import org.caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveAllUncheckedFiles; +import org.caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveCurrentMaxId; import org.caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveDatatypes; import org.caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveEntityACL; import org.caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveLogRecord; @@ -86,7 +86,6 @@ import org.caosdb.server.database.backend.interfaces.FileCheckSize; import org.caosdb.server.database.backend.interfaces.FileExists; import org.caosdb.server.database.backend.interfaces.FileWasModifiedAfter; import org.caosdb.server.database.backend.interfaces.GetAllNamesImpl; -import org.caosdb.server.database.backend.interfaces.GetChildrenImpl; import org.caosdb.server.database.backend.interfaces.GetDependentEntitiesImpl; import org.caosdb.server.database.backend.interfaces.GetFileIteratorImpl; import org.caosdb.server.database.backend.interfaces.GetFileRecordByPathImpl; @@ -107,6 +106,7 @@ import org.caosdb.server.database.backend.interfaces.ListUsersImpl; import org.caosdb.server.database.backend.interfaces.LogUserVisitImpl; import org.caosdb.server.database.backend.interfaces.RetrieveAllImpl; import org.caosdb.server.database.backend.interfaces.RetrieveAllUncheckedFilesImpl; +import org.caosdb.server.database.backend.interfaces.RetrieveCurrentMaxIdImpl; import org.caosdb.server.database.backend.interfaces.RetrieveDatatypesImpl; import org.caosdb.server.database.backend.interfaces.RetrieveEntityACLImpl; import org.caosdb.server.database.backend.interfaces.RetrieveLogRecordImpl; @@ -133,6 +133,18 @@ import org.caosdb.server.database.misc.TransactionBenchmark; import org.caosdb.server.utils.UndoHandler; import org.caosdb.server.utils.Undoable; +/** + * Abstract class for backend transactions. + * + * <p>This class is the glue between the Transaction layer and the actual implementation of the + * communication with the back end. + * + * <p>This class also acts as a registry which stores which implementation is being used for which + * backend transaction interface. + * + * @see {@link Transaction} + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ public abstract class BackendTransaction implements Undoable { private final UndoHandler undoHandler = new UndoHandler(); @@ -153,7 +165,7 @@ public abstract class BackendTransaction implements Undoable { } /** - * Intialiaze the adapters to the database backend. + * Initialize the adapters to the database backend. * * <p>Currently this is hard-coded to the MySQL-Backend but the architecture of this class is * designed to make it easy in the future to choose other implementations (i.e. other back-ends) @@ -163,7 +175,6 @@ public abstract class BackendTransaction implements Undoable { setImpl(GetAllNamesImpl.class, MySQLGetAllNames.class); setImpl(DeleteEntityPropertiesImpl.class, MySQLDeleteEntityProperties.class); setImpl(DeleteSparseEntityImpl.class, MySQLDeleteSparseEntity.class); - setImpl(GetChildrenImpl.class, MySQLGetChildren.class); setImpl(GetDependentEntitiesImpl.class, MySQLGetDependentEntities.class); setImpl(GetIDByNameImpl.class, MySQLGetIDByName.class); setImpl(GetInfoImpl.class, MySQLGetInfo.class); @@ -213,6 +224,7 @@ public abstract class BackendTransaction implements Undoable { setImpl(ListUsersImpl.class, MySQLListUsers.class); setImpl(LogUserVisitImpl.class, MySQLLogUserVisit.class); setImpl(RetrieveEntityACLImpl.class, MySQLRetrieveEntityACL.class); + setImpl(RetrieveCurrentMaxIdImpl.class, MySQLRetrieveCurrentMaxId.class); } } diff --git a/src/main/java/org/caosdb/server/database/DatabaseAccessManager.java b/src/main/java/org/caosdb/server/database/DatabaseAccessManager.java index f12c78e31bf28799b8c8ead1c9ff311a65bb01ef..478953cbbcd25693dca3df92a3ede6371b4f2212 100644 --- a/src/main/java/org/caosdb/server/database/DatabaseAccessManager.java +++ b/src/main/java/org/caosdb/server/database/DatabaseAccessManager.java @@ -60,6 +60,7 @@ class ReadAccessSemaphore extends Semaphore implements Releasable { private AtomicInteger acquired = new AtomicInteger(0); // how many threads have read access Semaphore writersBlock = new Semaphore(1, true); // This semaphore is blocked as long as there are any + // unreleased read permits. public ReadAccessSemaphore() { diff --git a/src/main/java/org/caosdb/server/database/access/AbstractAccess.java b/src/main/java/org/caosdb/server/database/access/AbstractAccess.java index 1d30bd3fee7187b9c44378e5b94c1c6d97287f00..574dabedd0db28ded611411ed8848f00be56a293 100644 --- a/src/main/java/org/caosdb/server/database/access/AbstractAccess.java +++ b/src/main/java/org/caosdb/server/database/access/AbstractAccess.java @@ -1,9 +1,10 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,8 +18,6 @@ * * 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 */ package org.caosdb.server.database.access; @@ -26,11 +25,18 @@ import java.util.HashMap; import org.caosdb.server.database.misc.DBHelper; import org.caosdb.server.database.misc.RollBackHandler; import org.caosdb.server.transaction.TransactionInterface; +import org.caosdb.server.utils.UseCacheResourceDelegate; -public abstract class AbstractAccess<T extends TransactionInterface> implements Access { +/** + * Abstract implementation of an Access. + * + * @see {@link Access} + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ +public abstract class AbstractAccess<T extends TransactionInterface> + extends UseCacheResourceDelegate implements Access { private HashMap<String, DBHelper> helpers = new HashMap<String, DBHelper>(); - private Boolean useCache = true; private final T transaction; private boolean released = false; @@ -73,22 +79,6 @@ public abstract class AbstractAccess<T extends TransactionInterface> implements } } - @Override - public void setUseCache(final Boolean useCache) { - this.useCache = useCache; - } - /** - * Whether the transaction allows to use the query cache or other caches. This is controlled by - * the "cache" flag. - * - * @see {@link NoCache} - * @return true if caching is encouraged. - */ - @Override - public boolean useCache() { - return this.useCache; - } - @Override public void commit() throws Exception { synchronized (this.helpers) { diff --git a/src/main/java/org/caosdb/server/database/access/Access.java b/src/main/java/org/caosdb/server/database/access/Access.java index 228e8e004aea7454a287bccd898794c2bb4f1b9c..6b8a521cffd5f0d817f9a9b0a4ecce357fb82d76 100644 --- a/src/main/java/org/caosdb/server/database/access/Access.java +++ b/src/main/java/org/caosdb/server/database/access/Access.java @@ -1,9 +1,10 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,14 +18,29 @@ * * 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 */ package org.caosdb.server.database.access; import org.caosdb.server.database.misc.DBHelper; +import org.caosdb.server.utils.UseCacheResource; -public interface Access { +/** + * Access Interface. + * + * <p>The access object is part of a Transactions state and it is the glue between the Transaction + * and the actual backend implementation of those transactions. It represents different levels of + * access (RO, RW) and provides all the helper objects shared by all the single calls to the backend + * (package .../database/backend/implementation/...). + * + * <p>It is further used to commit or roll-back the owning transaction. + * + * <p>The DatabaseAccessManager issues the Access to a requesting Transaction. + * + * @see {@link Transaction} + * @see {@link DatabaseAccessManager} + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ +public interface Access extends UseCacheResource { public abstract void setHelper(String name, DBHelper helper); @@ -33,8 +49,4 @@ public interface Access { public abstract void release(); public abstract void commit() throws Exception; - - public abstract void setUseCache(Boolean useCache); - - public boolean useCache(); } diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseConnectionPool.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseConnectionPool.java index 997de3141db6fd49ac510fa61150a2d16e7d5416..8234d948eb3af1dd490e6ef1062de98504e3575d 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseConnectionPool.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseConnectionPool.java @@ -23,7 +23,6 @@ package org.caosdb.server.database.backend.implementation.MySQL; import com.google.common.base.Objects; -import java.sql.CallableStatement; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -75,8 +74,12 @@ class DatabaseConnectionPool { private static ConnectionPool instance = null; private static synchronized ConnectionPool createConnectionPool() - throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException, - ConnectionException, CaosDBException { + throws ClassNotFoundException, + InstantiationException, + IllegalAccessException, + SQLException, + ConnectionException, + CaosDBException { final String url = "jdbc:mysql://" @@ -244,20 +247,6 @@ class DatabaseConnectionPool { System.exit(1); } - // set auto_increment on table entities to the maximum entity_id of - // table transaction_log; - // Why? MYSQL seems to reset the - // auto_increment value each time the MySQL server is being - // restarted to the maximum of ids in the entities table. But if - // some of the ids had been used yet, we don't want to use them - // again. - final CallableStatement prepareCall = con.prepareCall("call initAutoIncrement()"); - try { - prepareCall.execute(); - } catch (final SQLException e) { - logger.error("Could inititialize the autoincrement value for the entities table.", e); - System.exit(1); - } } finally { con.close(); } diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseUtils.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseUtils.java index f7d364ae6376524ef892de3353d699547417717e..6528627d0e7080452827f8b70b87c34c78ac6c57 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseUtils.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseUtils.java @@ -152,9 +152,10 @@ public class DatabaseUtils { public static void parseOverrides(final List<ProtoProperty> properties, final ResultSet rs) throws SQLException { while (rs.next()) { - final int property_id = rs.getInt("property_id"); + String property_id = rs.getString("property_id"); + if (rs.wasNull()) property_id = rs.getString("InternalPropertyID"); for (final FlatProperty p : properties) { - if (p.id == property_id) { + if (p.id.equals(property_id)) { final String name = bytes2UTF8(rs.getBytes("name_override")); if (name != null) { p.name = name; @@ -180,7 +181,10 @@ public class DatabaseUtils { final List<ProtoProperty> ret = new LinkedList<>(); while (rs.next()) { ProtoProperty pp = new ProtoProperty(); - pp.id = rs.getInt("PropertyID"); + pp.id = rs.getString("PropertyID"); + if (rs.wasNull()) { + pp.id = rs.getString("InternalPropertyID"); + } pp.value = bytes2UTF8(rs.getBytes("PropertyValue")); pp.status = bytes2UTF8(rs.getBytes("PropertyStatus")); pp.idx = rs.getInt("PropertyIndex"); @@ -215,7 +219,7 @@ public class DatabaseUtils { */ public static SparseEntity parseEntityResultSet(final ResultSet rs) throws SQLException { final SparseEntity ret = parseNameRoleACL(rs); - ret.id = rs.getInt("EntityID"); + ret.id = rs.getString("EntityID"); ret.description = bytes2UTF8(rs.getBytes("EntityDesc")); ret.datatype = bytes2UTF8(rs.getBytes("Datatype")); ret.collection = bytes2UTF8(rs.getBytes("Collection")); @@ -233,7 +237,7 @@ public class DatabaseUtils { final LinkedList<VerySparseEntity> ret = new LinkedList<>(); while (rs.next()) { final VerySparseEntity vsp = new VerySparseEntity(); - vsp.id = rs.getInt("ParentID"); + vsp.id = rs.getString("ParentID"); vsp.name = bytes2UTF8(rs.getBytes("ParentName")); vsp.description = bytes2UTF8(rs.getBytes("ParentDescription")); vsp.role = bytes2UTF8(rs.getBytes("ParentRole")); @@ -247,7 +251,7 @@ public class DatabaseUtils { EntityInterface entity, List<ProtoProperty> protos) { final ArrayList<Property> ret = new ArrayList<Property>(); for (final ProtoProperty pp : protos) { - if (pp.id.equals(entity.getId().toInteger())) { + if (pp.id.equals(entity.getId().toString())) { if (pp.value != null) { entity.setValue(new GenericValue(pp.value)); } @@ -328,8 +332,7 @@ public class DatabaseUtils { return ((Entry<Integer, String>) x).getKey() - ((Entry<Integer, String>) y).getKey(); }); pp.collValues = - pp.collValues - .stream() + pp.collValues.stream() .map((x) -> (Object) ((Entry<Integer, String>) x).getValue()) .collect(Collectors.toList()); } diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLDeleteEntityProperties.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLDeleteEntityProperties.java index f8cba7d4c9734c1e6e12b72498ceaa6c57ed1a17..747cf50d8390d943fce5ffbf596debe5659b8df9 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLDeleteEntityProperties.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLDeleteEntityProperties.java @@ -1,9 +1,10 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,8 +18,6 @@ * * 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 */ package org.caosdb.server.database.backend.implementation.MySQL; @@ -44,7 +43,7 @@ public class MySQLDeleteEntityProperties extends MySQLTransaction try { final PreparedStatement stmt = prepareStatement(STMT_DELETE_ENTITY_PROPERTIES); - stmt.setInt(1, id.toInteger()); + stmt.setString(1, id.toString()); stmt.execute(); } catch (final SQLIntegrityConstraintViolationException exc) { throw new IntegrityException(exc); diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLDeleteSparseEntity.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLDeleteSparseEntity.java index 7fe8ac303b53ec26dc947c1e4a84e648d7b6517d..a1ab389b6878c06c5fd965d0952d422f11134c0e 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLDeleteSparseEntity.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLDeleteSparseEntity.java @@ -1,9 +1,10 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,8 +18,6 @@ * * 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 */ package org.caosdb.server.database.backend.implementation.MySQL; @@ -44,7 +43,7 @@ public class MySQLDeleteSparseEntity extends MySQLTransaction implements DeleteS try { final PreparedStatement stmt = prepareStatement(STMT_DELETE_SPARSE_ENTITY); - stmt.setInt(1, id.toInteger()); + stmt.setString(1, id.toString()); stmt.execute(); } catch (final SQLIntegrityConstraintViolationException exc) { throw new IntegrityException(exc); diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLGetChildren.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLGetChildren.java deleted file mode 100644 index 3c1cebf1e20a720a5f726e378bb22b1ad61c850a..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLGetChildren.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * ** 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 - */ -package org.caosdb.server.database.backend.implementation.MySQL; - -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.util.LinkedList; -import java.util.List; -import org.caosdb.server.database.access.Access; -import org.caosdb.server.database.backend.interfaces.GetChildrenImpl; -import org.caosdb.server.database.exceptions.TransactionException; -import org.caosdb.server.entity.EntityID; - -public class MySQLGetChildren extends MySQLTransaction implements GetChildrenImpl { - - public MySQLGetChildren(final Access access) { - super(access); - } - - public static final String STMT_GET_CHILDREN = - "Select child from isa_cache where parent=? and rpath=child"; - - @Override - public List<EntityID> execute(final EntityID entity) throws TransactionException { - try { - final PreparedStatement stmt = prepareStatement(STMT_GET_CHILDREN); - - stmt.setInt(1, entity.toInteger()); - - ResultSet rs = null; - try { - rs = stmt.executeQuery(); - final List<EntityID> ret = new LinkedList<>(); - while (rs.next()) { - ret.add(new EntityID(rs.getInt(1))); - } - return ret; - } finally { - if (rs != null && !rs.isClosed()) { - rs.close(); - } - } - } catch (final Exception e) { - throw new TransactionException(e); - } - } -} diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLGetDependentEntities.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLGetDependentEntities.java index 9384c8a04861a01f1e56236b0ab42433d87990f5..2af80f802ca19eca806856bb653788b50ae5ecf9 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLGetDependentEntities.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLGetDependentEntities.java @@ -1,9 +1,10 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,8 +18,6 @@ * * 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 */ package org.caosdb.server.database.backend.implementation.MySQL; @@ -31,6 +30,15 @@ import org.caosdb.server.database.backend.interfaces.GetDependentEntitiesImpl; import org.caosdb.server.database.exceptions.TransactionException; import org.caosdb.server.entity.EntityID; +/** + * Get a list of ids of all entities which require the given entity to exists. + * + * <p>That is the list of all direct child entities (by is-a relation), referenced entities (i.e. + * the given entity is a property's value), all properties which have this entity as their data type + * and all implemented properties which use this entity as their abtract property. + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ public class MySQLGetDependentEntities extends MySQLTransaction implements GetDependentEntitiesImpl { @@ -45,13 +53,13 @@ public class MySQLGetDependentEntities extends MySQLTransaction try { final PreparedStatement stmt = prepareStatement(STMT_GET_DEPENDENT_ENTITIES); - stmt.setInt(1, entity.toInteger()); + stmt.setString(1, entity.toString()); final ResultSet rs = stmt.executeQuery(); try { final List<EntityID> ret = new LinkedList<>(); while (rs.next()) { - ret.add(new EntityID(rs.getInt(1))); + ret.add(new EntityID(rs.getString(1))); } return ret; } finally { diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLGetFileRecordByPath.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLGetFileRecordByPath.java index 9b147d289b337f63dd359ce604719d9e00a149be..0dd10ec1bcdc97a095da661cb6cb2e2209da0955 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLGetFileRecordByPath.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLGetFileRecordByPath.java @@ -1,9 +1,10 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,8 +18,6 @@ * * 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 */ package org.caosdb.server.database.backend.implementation.MySQL; @@ -30,6 +29,11 @@ import org.caosdb.server.database.backend.interfaces.GetFileRecordByPathImpl; import org.caosdb.server.database.exceptions.TransactionException; import org.caosdb.server.database.proto.SparseEntity; +/** + * Retrieve the entity id of a file with the given path (or null). + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ public class MySQLGetFileRecordByPath extends MySQLTransaction implements GetFileRecordByPathImpl { public MySQLGetFileRecordByPath(final Access access) { @@ -37,7 +41,7 @@ public class MySQLGetFileRecordByPath extends MySQLTransaction implements GetFil } public static final String STMT_GET_ID_BY_PATH = - "SELECT file_id, size, hex(hash) AS file_hash, checked_timestamp FROM files WHERE path=?"; + "SELECT (Select id from entity_ids WHERE internal_id = files.file_id) as entity_id, size, hex(hash) AS file_hash, checked_timestamp FROM files WHERE path=?"; @Override public SparseEntity execute(final String path) throws TransactionException { @@ -49,7 +53,7 @@ public class MySQLGetFileRecordByPath extends MySQLTransaction implements GetFil try { if (rs.next()) { final SparseEntity ret = new SparseEntity(); - ret.id = rs.getInt("file_id"); + ret.id = rs.getString("entity_id"); ret.fileHash = rs.getString("file_hash"); ret.fileSize = rs.getLong("size"); ret.fileChecked = rs.getLong("checked_timestamp"); diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLGetIDByName.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLGetIDByName.java index c2e28cd39929f71e874fb4a69c38261ed290b18a..5d3e3a7d707c0d7d451d0229426a959fc827e8ef 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLGetIDByName.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLGetIDByName.java @@ -1,9 +1,10 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,13 +18,12 @@ * * 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 */ package org.caosdb.server.database.backend.implementation.MySQL; import java.sql.PreparedStatement; import java.sql.ResultSet; +import java.sql.Types; import java.util.LinkedList; import java.util.List; import org.caosdb.server.database.access.Access; @@ -31,6 +31,11 @@ import org.caosdb.server.database.backend.interfaces.GetIDByNameImpl; import org.caosdb.server.database.exceptions.TransactionException; import org.caosdb.server.entity.EntityID; +/** + * Retrieve the entity id of an entity with the given name (or null). + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ public class MySQLGetIDByName extends MySQLTransaction implements GetIDByNameImpl { public MySQLGetIDByName(final Access access) { @@ -42,34 +47,29 @@ public class MySQLGetIDByName extends MySQLTransaction implements GetIDByNameImp * Therefore {@link #STMT_AND_ROLE}, {@link #STMT_NOT_ROLE}, and {@link #STMT_LIMIT} can as * additional conditions. */ - public static final String STMT_GET_ID_BY_NAME = - "Select n.entity_id AS id " - + "FROM name_data AS n JOIN entities AS e " - + "ON (n.domain_id=0 AND n.property_id=20 AND e.id = n.entity_id)" - + "WHERE n.value=?"; - - public static final String STMT_AND_ROLE = " AND e.role=?"; - public static final String STMT_NOT_ROLE = " AND e.role!='ROLE'"; - public static final String STMT_LIMIT = " LIMIT "; + public static final String STMT_GET_ID_BY_NAME = "call getIdByName(?, ?, ?)"; @Override public List<EntityID> execute(final String name, final String role, final String limit) throws TransactionException { try { - final String stmtStr = - STMT_GET_ID_BY_NAME - + (role != null ? STMT_AND_ROLE : STMT_NOT_ROLE) - + (limit != null ? STMT_LIMIT + limit : ""); - final PreparedStatement stmt = prepareStatement(stmtStr); + final PreparedStatement stmt = prepareStatement(STMT_GET_ID_BY_NAME); stmt.setString(1, name); if (role != null) { stmt.setString(2, role); + } else { + stmt.setNull(2, Types.VARCHAR); + } + if (limit != null) { + stmt.setString(3, limit); + } else { + stmt.setNull(3, Types.VARCHAR); } try (ResultSet rs = stmt.executeQuery()) { final List<EntityID> ret = new LinkedList<>(); while (rs.next()) { - ret.add(new EntityID(rs.getInt("id"))); + ret.add(new EntityID(rs.getString("id"))); } return ret; diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLGetUpdateableChecksums.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLGetUpdateableChecksums.java index 28c55032e0ae6b6c2fcfb779339a72b96c703ba0..81c0dfd6d489a104183e052aba6156372a96f8e9 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLGetUpdateableChecksums.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLGetUpdateableChecksums.java @@ -1,9 +1,10 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,8 +18,6 @@ * * 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 */ package org.caosdb.server.database.backend.implementation.MySQL; @@ -30,11 +29,17 @@ import org.caosdb.server.database.backend.interfaces.GetUpdateableChecksumsImpl; import org.caosdb.server.database.exceptions.TransactionException; import org.caosdb.server.entity.EntityID; +/** + * Retrieve the entity ids of all files which have a checksum which should be updated (because the + * checksum is NULL, currently). + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ public class MySQLGetUpdateableChecksums extends MySQLTransaction implements GetUpdateableChecksumsImpl { private final String GET_UPDATEABLE_CHECKSUMS = - "SELECT file_id FROM files WHERE hash IS NULL LIMIT 1"; + "SELECT (SELECT id FROM entity_ids WHERE internal_id = files.file_id) AS entity_id FROM files WHERE hash IS NULL LIMIT 1"; public MySQLGetUpdateableChecksums(final Access access) { super(access); @@ -46,7 +51,7 @@ public class MySQLGetUpdateableChecksums extends MySQLTransaction final PreparedStatement stmt = prepareStatement(this.GET_UPDATEABLE_CHECKSUMS); final ResultSet rs = stmt.executeQuery(); if (rs.next()) { - return new EntityID(rs.getInt(1)); + return new EntityID(rs.getString(1)); } else { return null; } diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityDatatype.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityDatatype.java index c4cb7cf98e5da51f98d71b4c5f6fc91e6961892e..4260ae03e76eec7fab1e33957a00dedc2ef77e17 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityDatatype.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityDatatype.java @@ -1,3 +1,22 @@ +/* + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> + * + * 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/>. + */ package org.caosdb.server.database.backend.implementation.MySQL; import java.sql.PreparedStatement; @@ -8,6 +27,15 @@ import org.caosdb.server.database.exceptions.IntegrityException; import org.caosdb.server.database.exceptions.TransactionException; import org.caosdb.server.database.proto.SparseEntity; +/** + * Insert an entity's (meaning: an abstract property's) data type. + * + * <p>This inserts the abstract property's data type as opposed to the (overridden data type which + * is being inserted/updated via InsertEntityPropertiesImpl. + * + * @see {@link InsertEntityPropertiesImpl} + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ public class MySQLInsertEntityDatatype extends MySQLTransaction implements InsertEntityDatatypeImpl { @@ -19,18 +47,13 @@ public class MySQLInsertEntityDatatype extends MySQLTransaction * Inserts atomic data types of properties into the data_type table. Has two parameters, the * property_id and the data type name. */ - public static final String STMT_INSERT_ENTITY_DATATYPE = - "INSERT INTO data_type (domain_id, entity_id, property_id, datatype) " - + "SELECT 0, 0, ?, " - + "( SELECT entity_id FROM name_data WHERE domain_id = 0 AND property_id = 20 AND value = ? LIMIT 1);"; + public static final String STMT_INSERT_ENTITY_DATATYPE = "call insertEntityDataType(?, ?)"; /** * Inserts collection data types of properties into the data_type table. Has two parameters, the * property_id and the type of collection (e.g. 'LIST'). */ - public static final String STMT_INSERT_ENTITY_COLLECTION = - "INSERT INTO collection_type (domain_id, entity_id, property_id, collection) " - + "SELECT 0, 0, ?, ?;"; + public static final String STMT_INSERT_ENTITY_COLLECTION = "call insertEntityCollection(?, ?)"; @Override public void execute(final SparseEntity entity) { @@ -38,7 +61,7 @@ public class MySQLInsertEntityDatatype extends MySQLTransaction final PreparedStatement insertEntityDatatypeStmt = prepareStatement(STMT_INSERT_ENTITY_DATATYPE); - insertEntityDatatypeStmt.setInt(1, entity.id); + insertEntityDatatypeStmt.setString(1, entity.id); insertEntityDatatypeStmt.setString(2, entity.datatype); insertEntityDatatypeStmt.execute(); @@ -47,7 +70,7 @@ public class MySQLInsertEntityDatatype extends MySQLTransaction final PreparedStatement insertEntityCollectionStmt = prepareStatement(STMT_INSERT_ENTITY_COLLECTION); - insertEntityCollectionStmt.setInt(1, entity.id); + insertEntityCollectionStmt.setString(1, entity.id); insertEntityCollectionStmt.setString(2, entity.collection); insertEntityCollectionStmt.execute(); diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityProperties.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityProperties.java index becb56a2397fffa323f005507463a586bdecc0cf..cef624dce3a022043a2f86e56acd6ca589d493d1 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityProperties.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityProperties.java @@ -1,9 +1,10 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,8 +18,6 @@ * * 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 */ package org.caosdb.server.database.backend.implementation.MySQL; @@ -48,6 +47,17 @@ import org.caosdb.server.entity.EntityID; import org.caosdb.server.entity.EntityInterface; import org.caosdb.server.entity.wrapper.Property; +/** + * Insert the entity's properties. + * + * <p>This implementation transforms a deep tree of properties into a flat representation. This + * transformation is a MySQL-backend implementation detail and should not be leaked to the clients. + * + * <p>The reverse transformation happens in MySQLRetrieveProperties. + * + * @see {@link MySQLRetrieveProperties} + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ public class MySQLInsertEntityProperties extends MySQLTransaction implements InsertEntityPropertiesImpl { @@ -89,9 +99,9 @@ public class MySQLInsertEntityProperties extends MySQLTransaction try { final PreparedStatement stmt = prepareStatement(STMT_INSERT_ENTITY_PROPERTY); - stmt.setInt(1, domain.toInteger()); - stmt.setInt(2, entity.toInteger()); - stmt.setInt(3, fp.id); + stmt.setString(1, domain.toString()); + stmt.setString(2, entity.toString()); + stmt.setString(3, fp.id); stmt.setString(4, table.toString()); stmt.setString(5, fp.value); @@ -124,7 +134,7 @@ public class MySQLInsertEntityProperties extends MySQLTransaction final FlatProperty fp = new FlatProperty(); Table table = Table.null_data; Long unit_sig = null; - fp.id = property.getId().toInteger(); + fp.id = property.getId().toString(); fp.idx = property.getPIdx(); fp.status = property.getStatementStatus().name(); @@ -140,7 +150,7 @@ public class MySQLInsertEntityProperties extends MySQLTransaction fp.value = fp.id.toString(); // id is to be the replacement id (an internally used/private id) - fp.id = ((ReferenceValue) property.getValue()).getId().toInteger(); + fp.id = ((ReferenceValue) property.getValue()).getId().toString(); table = Table.reference_data; } else { @@ -215,7 +225,7 @@ public class MySQLInsertEntityProperties extends MySQLTransaction } } - public static final String STMT_REGISTER_SUBDOMAIN = "call registerSubdomain(?)"; + public static final String STMT_REGISTER_SUBDOMAIN = "call registerReplacementIds(?)"; public Deque<EntityID> registerReplacementId(final int domainCount) throws SQLException, ConnectionException { @@ -224,7 +234,7 @@ public class MySQLInsertEntityProperties extends MySQLTransaction try (final ResultSet rs = stmt.executeQuery()) { final Deque<EntityID> ret = new ArrayDeque<>(); while (rs.next()) { - ret.add(new EntityID(rs.getInt(1))); + ret.add(new EntityID(rs.getString(1))); } return ret; } diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertParents.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertParents.java index 61a5e8c48cdc85098e1b0a2f5531c358d6c6058d..0e44c687b05d4184d81f05eeb8640a15b1e88662 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertParents.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertParents.java @@ -40,8 +40,8 @@ public class MySQLInsertParents extends MySQLTransaction implements InsertParent public void execute(final EntityID entity, final EntityID parent) throws TransactionException { try { final PreparedStatement stmt = prepareStatement(STMT_INSERT_ISA); - stmt.setInt(1, entity.toInteger()); - stmt.setInt(2, parent.toInteger()); + stmt.setString(1, entity.toString()); + stmt.setString(2, parent.toString()); stmt.execute(); } catch (final Exception e) { throw new TransactionException(e); diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertSparseEntity.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertSparseEntity.java index 27227926156fc9b35ab75473e1efa2cce55f152e..fa70a1453b8dd2f2c761cf3e4e658cb77c5a445f 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertSparseEntity.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertSparseEntity.java @@ -1,9 +1,10 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,8 +18,6 @@ * * 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 */ package org.caosdb.server.database.backend.implementation.MySQL; @@ -38,9 +37,8 @@ public class MySQLInsertSparseEntity extends MySQLTransaction implements InsertS super(access); } - public static final String STMT_INSERT_SPARSE_ENTITY = "call insertEntity(?,?,?,?)"; - public static final String STMT_INSERT_FILE_PROPERTIES = - "INSERT INTO files (file_id, hash, size, path) VALUES (?, unhex(?), ?, ?);"; + public static final String STMT_INSERT_SPARSE_ENTITY = "call insertEntity(?,?,?,?,?)"; + public static final String STMT_INSERT_FILE_PROPERTIES = "call setFileProperties(?,?,?,?)"; @Override public void execute(final SparseEntity entity) { @@ -48,29 +46,29 @@ public class MySQLInsertSparseEntity extends MySQLTransaction implements InsertS final PreparedStatement insertEntityStmt = prepareStatement(STMT_INSERT_SPARSE_ENTITY); final PreparedStatement insertFilePropsStmt = prepareStatement(STMT_INSERT_FILE_PROPERTIES); - insertEntityStmt.setString(1, entity.name); - insertEntityStmt.setString(2, entity.description); - insertEntityStmt.setString(3, entity.role); - insertEntityStmt.setString(4, entity.acl); + insertEntityStmt.setString(1, entity.id); + insertEntityStmt.setString(2, entity.name); + insertEntityStmt.setString(3, entity.description); + insertEntityStmt.setString(4, entity.role); + insertEntityStmt.setString(5, entity.acl); try (final ResultSet rs = insertEntityStmt.executeQuery()) { if (rs.next()) { - entity.id = rs.getInt("EntityID"); entity.versionId = DatabaseUtils.bytes2UTF8(rs.getBytes("Version")); } else { - throw new TransactionException("Didn't get new EntityID back."); + throw new TransactionException("Didn't get the version id back."); } } if (entity.filePath != null) { - insertFilePropsStmt.setInt(1, entity.id); + insertFilePropsStmt.setString(1, entity.id); + insertFilePropsStmt.setString(2, entity.filePath); + insertFilePropsStmt.setLong(3, entity.fileSize); if (entity.fileHash != null) { - insertFilePropsStmt.setString(2, entity.fileHash); + insertFilePropsStmt.setString(4, entity.fileHash); } else { - insertFilePropsStmt.setNull(2, Types.VARCHAR); + insertFilePropsStmt.setNull(4, Types.VARCHAR); } - insertFilePropsStmt.setLong(3, entity.fileSize); - insertFilePropsStmt.setString(4, entity.filePath); insertFilePropsStmt.execute(); } } catch (final SQLIntegrityConstraintViolationException exc) { diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertTransactionHistory.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertTransactionHistory.java index 0832790a0b56446570fbd7b9c3a4383390c443a7..155cb2d0b8c8db4948b4a2970785eecb636dcf9b 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertTransactionHistory.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertTransactionHistory.java @@ -56,7 +56,7 @@ public class MySQLInsertTransactionHistory extends MySQLTransaction logStmt.setString(3, user); logStmt.setLong(4, seconds); logStmt.setInt(5, nanos); - logStmt.setInt(6, entity.toInteger()); + logStmt.setString(6, entity.toString()); logStmt.execute(); } catch (final Exception e) { throw new TransactionException(e); diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLIsSubType.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLIsSubType.java index f6c2daf8d4a0bf4defcaa550cf715032d57cd9cd..0d469a79482069d1f19de3931433e90faf5c667a 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLIsSubType.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLIsSubType.java @@ -43,8 +43,8 @@ public class MySQLIsSubType extends MySQLTransaction implements IsSubTypeImpl { try { final PreparedStatement isSubtypeStmt = prepareStatement(STMT_IS_SUBTYPE); - isSubtypeStmt.setInt(1, child.toInteger()); - isSubtypeStmt.setInt(2, parent.toInteger()); + isSubtypeStmt.setString(1, child.toString()); + isSubtypeStmt.setString(2, parent.toString()); final ResultSet rs = isSubtypeStmt.executeQuery(); try { diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLListUsers.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLListUsers.java index 83ebffe679e6625da720fd2933a5d8defb7264c3..3b68c45d0ff60473ae837f3c9ebc17ae82e68c2d 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLListUsers.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLListUsers.java @@ -53,8 +53,8 @@ public class MySQLListUsers extends MySQLTransaction implements ListUsersImpl { user.name = rs.getString("name"); user.realm = rs.getString("realm"); user.email = rs.getString("email"); - user.entity = rs.getInt("entity"); - if (user.entity == 0) { + user.entity = rs.getString("entity"); + if (user.entity.isBlank() || user.entity.equals("0")) { user.entity = null; } user.status = UserStatus.valueOf(rs.getString("status")); diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLLogUserVisit.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLLogUserVisit.java index 23050da87e324026be1eefa02690959d0a0590cc..d3b9167e13fb92df3b69e9418f9d3c0d61458233 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLLogUserVisit.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLLogUserVisit.java @@ -36,6 +36,7 @@ public class MySQLLogUserVisit extends MySQLTransaction implements LogUserVisitI public static final String LOG_USER_VISIT = "SELECT status FROM user_info WHERE realm = ? AND name = ?"; + // TODO Replace by "UPDATE user_info SET last_seen = ?"; /** Return true if this is not the first visit of this user. */ diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveAll.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveAll.java index 9a2ea81629853e3baf9de7757e1abe6c8f01fe28..55b8f194ea0be90b5a77fa0c50930c836c48b3f7 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveAll.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveAll.java @@ -75,7 +75,7 @@ public class MySQLRetrieveAll extends MySQLTransaction implements RetrieveAllImp final String acl = DatabaseUtils.bytes2UTF8(rs.getBytes("ACL")); if (EntityACL.deserialize(acl) .isPermitted(SecurityUtils.getSubject(), EntityPermission.RETRIEVE_ENTITY)) { - ret.add(new EntityID(rs.getInt("ID"))); + ret.add(new EntityID(rs.getString("ID"))); } } return ret; diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveAllUncheckedFiles.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveAllUncheckedFiles.java index 09a4e7c2edabea49617224fdb9159116f2cd742b..cfbf2281b920382b30be10f2ff9e8a0ab16728ab 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveAllUncheckedFiles.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveAllUncheckedFiles.java @@ -51,7 +51,7 @@ public class MySQLRetrieveAllUncheckedFiles extends MySQLTransaction final ResultSet rs = stmt.executeQuery(); if (rs.next()) { final SparseEntity first = new SparseEntity(); - first.id = rs.getInt("file_id"); + first.id = rs.getString("file_id"); first.filePath = rs.getString("path"); first.fileHash = rs.getString("file_hash"); first.fileSize = rs.getLong("size"); @@ -71,7 +71,7 @@ public class MySQLRetrieveAllUncheckedFiles extends MySQLTransaction try { if (rs.next()) { this.next = new SparseEntity(); - this.next.id = rs.getInt("file_id"); + this.next.id = rs.getString("file_id"); this.next.filePath = rs.getString("path"); this.next.fileHash = rs.getString("file_hash"); this.next.fileSize = rs.getLong("size"); diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveCurrentMaxId.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveCurrentMaxId.java new file mode 100644 index 0000000000000000000000000000000000000000..9f56324f84cea47081f4f9901f10c772508309d4 --- /dev/null +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveCurrentMaxId.java @@ -0,0 +1,61 @@ +/* + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> + * + * 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/>. + */ +package org.caosdb.server.database.backend.implementation.MySQL; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import org.caosdb.server.database.access.Access; +import org.caosdb.server.database.backend.interfaces.RetrieveCurrentMaxIdImpl; +import org.caosdb.server.database.exceptions.TransactionException; + +/** + * Implements {@link RetrieveCurrentMaxIdImpl} for a MySQL/MariaDB back-end. + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ +public class MySQLRetrieveCurrentMaxId extends MySQLTransaction + implements RetrieveCurrentMaxIdImpl { + + public MySQLRetrieveCurrentMaxId(Access access) { + super(access); + } + + public static final String STMT = + "SELECT max(CAST(entity_id AS UNSIGNED INT)) AS max_id FROM transaction_log WHERE transaction='Insert'"; + + @Override + public Integer execute() { + try { + try (PreparedStatement stmt = prepareStatement(STMT)) { + + try (ResultSet rs = stmt.executeQuery()) { + if (rs.next()) { + return rs.getInt("max_id"); + } else { + return 100; + } + } + } + } catch (SQLException | ConnectionException e) { + throw new TransactionException(e); + } + } +} diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveEntityACL.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveEntityACL.java index 5db8ed7597b1c0d357273848fb7c36febdcfed13..716b7ff65814d1464ccac39ef64af275996e096d 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveEntityACL.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveEntityACL.java @@ -39,9 +39,9 @@ public class MySQLRetrieveEntityACL extends MySQLTransaction implements Retrieve "SELECT a.acl FROM entities AS e LEFT JOIN entity_acl AS a ON (a.id = e.acl) WHERE e.id = ? LIMIT 1"; @Override - public VerySparseEntity execute(Integer id) { + public VerySparseEntity execute(String id) { try (PreparedStatement stmt = prepareStatement(STMT)) { - stmt.setInt(1, id); + stmt.setString(1, id); ResultSet rs = stmt.executeQuery(); if (rs.next()) { VerySparseEntity result = new VerySparseEntity(); diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveParents.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveParents.java index 1e2a6621d2fb85a5a5260b1a607670e970f54701..6f48e755537b7cd891d0254c9f2255f537a1d10a 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveParents.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveParents.java @@ -49,7 +49,7 @@ public class MySQLRetrieveParents extends MySQLTransaction implements RetrievePa try { final PreparedStatement prepareStatement = prepareStatement(stmtStr); - prepareStatement.setInt(1, id.toInteger()); + prepareStatement.setString(1, id.toString()); if (version == null) { prepareStatement.setNull(2, Types.VARBINARY); } else { diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveProperties.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveProperties.java index 100df4ce72ebae75d3022866b345bacc9752ad91..97fe4dc1b2453aac7e501db796da23ae25f2e317 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveProperties.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveProperties.java @@ -1,9 +1,10 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,11 +18,10 @@ * * 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 */ package org.caosdb.server.database.backend.implementation.MySQL; +import com.mysql.cj.jdbc.exceptions.MysqlDataTruncation; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -34,6 +34,16 @@ import org.caosdb.server.database.exceptions.TransactionException; import org.caosdb.server.database.proto.ProtoProperty; import org.caosdb.server.entity.EntityID; +/** + * Retrieve the entity's properties. + * + * <p>This implemation transforms the flat structure of properties which is stored in the back end + * to a deep tree of properties (if applicable) which is the reverse transformation to that + * happening in MySQLInsertEntityProperties. + * + * @see {@link MySQLInsertEntityProperties} + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ public class MySQLRetrieveProperties extends MySQLTransaction implements RetrievePropertiesImpl { public MySQLRetrieveProperties(final Access access) { @@ -50,12 +60,12 @@ public class MySQLRetrieveProperties extends MySQLTransaction implements Retriev final PreparedStatement prepareStatement = prepareStatement(stmtStr); final List<ProtoProperty> propsStage1 = - retrieveFlatPropertiesStage1(0, entity.toInteger(), version, prepareStatement); + retrieveFlatPropertiesStage1("0", entity.toString(), version, prepareStatement); for (final ProtoProperty p : propsStage1) { p.subProperties = - retrieveFlatPropertiesStage1(entity.toInteger(), p.id, version, prepareStatement); + retrieveFlatPropertiesStage1(entity.toString(), p.id, version, prepareStatement); } return DatabaseUtils.transformToDeepPropertyTree(propsStage1, isHead); } catch (final SQLException e) { @@ -66,18 +76,11 @@ public class MySQLRetrieveProperties extends MySQLTransaction implements Retriev } private List<ProtoProperty> retrieveFlatPropertiesStage1( - final Integer domain, - final Integer entity, - final String version, - final PreparedStatement stmt) + final String domain, final String entity, final String version, final PreparedStatement stmt) throws SQLException, ConnectionException { - if (domain != null && domain >= 0) { - stmt.setInt(1, domain); - } else { - stmt.setInt(1, 0); - } - stmt.setInt(2, entity); + stmt.setString(1, domain); + stmt.setString(2, entity); if (version == null) { stmt.setNull(3, Types.VARBINARY); } else { @@ -94,15 +97,19 @@ public class MySQLRetrieveProperties extends MySQLTransaction implements Retriev final PreparedStatement retrieveOverrides = prepareStatement(stmtStr2); - retrieveOverrides(domain, entity, version, retrieveOverrides, properties); + if (properties != null & !properties.isEmpty()) { + retrieveOverrides(domain, entity, version, retrieveOverrides, properties); + } return properties; + } catch (MysqlDataTruncation e) { + throw e; } } private void retrieveOverrides( - final Integer domain, - final Integer entity, + final String domain, + final String entity, final String version, final PreparedStatement retrieveOverrides, final List<ProtoProperty> props) @@ -110,8 +117,8 @@ public class MySQLRetrieveProperties extends MySQLTransaction implements Retriev ResultSet rs = null; try { - retrieveOverrides.setInt(1, domain); - retrieveOverrides.setInt(2, entity); + retrieveOverrides.setString(1, domain); + retrieveOverrides.setString(2, entity); if (version == null) { retrieveOverrides.setNull(3, Types.VARBINARY); } else { diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveQueryTemplateDefinition.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveQueryTemplateDefinition.java index 1ab95ae7afc756742475ff2f3c79eddfd632aae9..5e96353af089d469faef2ef5c970fb4de725fab0 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveQueryTemplateDefinition.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveQueryTemplateDefinition.java @@ -46,7 +46,7 @@ public class MySQLRetrieveQueryTemplateDefinition extends MySQLTransaction try { final PreparedStatement stmt = prepareStatement(STMT_RETRIEVE_QUERY_TEMPLATE_DEF); - stmt.setInt(1, id.toInteger()); + stmt.setString(1, id.toString()); if (version == null) { stmt.setNull(2, Types.VARBINARY); } else { diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveSparseEntity.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveSparseEntity.java index 48cd84b3869262ba7f1d8129fdde2335bed6cd8e..c4a9b6262a7506c38b8dff6215682014203da1c1 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveSparseEntity.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveSparseEntity.java @@ -51,7 +51,7 @@ public class MySQLRetrieveSparseEntity extends MySQLTransaction try { final PreparedStatement preparedStatement = prepareStatement(stmtStr); - preparedStatement.setInt(1, id.toInteger()); + preparedStatement.setString(1, id.toString()); if (version == null) { preparedStatement.setNull(2, Types.VARBINARY); } else { diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveUser.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveUser.java index 40f036f1d63753c0be9e6153f741db7d4f9d04ad..b149fae264549f88c21d996f854662401d744beb 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveUser.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveUser.java @@ -66,7 +66,7 @@ public class MySQLRetrieveUser extends MySQLTransaction implements RetrieveUserI if (rs.getString("entity") == null) { ret.entity = null; } else { - ret.entity = rs.getInt("entity"); + ret.entity = rs.getString("entity"); } } } finally { diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveVersionHistory.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveVersionHistory.java index 6c4c2629a5effee25128f478bef20f5558a027ba..9493b382ec61f11953f1ba045f5c914b7502f690 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveVersionHistory.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveVersionHistory.java @@ -53,7 +53,7 @@ public class MySQLRetrieveVersionHistory extends MySQLTransaction final HashMap<String, VersionHistoryItem> result = new HashMap<>(); try { final PreparedStatement s = prepareStatement(VERSION_HISTORY_STMT); - s.setInt(1, entityId.toInteger()); + s.setString(1, entityId.toString()); final ResultSet rs = s.executeQuery(); while (rs.next()) { diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLSetFileCheckedTimestampImpl.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLSetFileCheckedTimestampImpl.java index 18d7d9b7f510f5cb24e1fe844ac4caac86ff4913..b08d05170dbc45168f597181389c18a1454ee103 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLSetFileCheckedTimestampImpl.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLSetFileCheckedTimestampImpl.java @@ -43,7 +43,7 @@ public class MySQLSetFileCheckedTimestampImpl extends MySQLTransaction try { final PreparedStatement stmt = getMySQLHelper().prepareStatement(STMT_SET_TS); stmt.setLong(1, ts); - stmt.setInt(2, id.toInteger()); + stmt.setString(2, id.toString()); } catch (final SQLException e) { throw new TransactionException(e); } catch (final ConnectionException e) { diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLSetFileChecksum.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLSetFileChecksum.java index fb6d4188c835fbc895ed4d5e427bb1b2bbc0bb4c..79c7253103f1ed578c45102ee2bf1a920b2c6049 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLSetFileChecksum.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLSetFileChecksum.java @@ -1,3 +1,22 @@ +/* + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> + * + * 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/>. + */ package org.caosdb.server.database.backend.implementation.MySQL; import java.sql.PreparedStatement; @@ -10,7 +29,7 @@ import org.caosdb.server.entity.EntityID; /** * Implements {@link SetFileChecksumImpl} for a MySQL/MariaDB back-end. * - * @author Timm Fitschen (t.fitschen@indiscale.com) + * @author Timm Fitschen <t.fitschen@indiscale.com> */ public class MySQLSetFileChecksum extends MySQLTransaction implements SetFileChecksumImpl { @@ -19,13 +38,13 @@ public class MySQLSetFileChecksum extends MySQLTransaction implements SetFileChe } public static final String STMT_SET_CHECKSUM = - "UPDATE files SET hash = unhex(?) WHERE file_id = ?"; + "UPDATE files SET hash = unhex(?) WHERE EXISTS (SELECT 1 FROM entity_ids AS eids WHERE eids.id = ? AND eids.internal_id = files.file_id)"; @Override public void execute(final EntityID id, final String checksum) { try { final PreparedStatement stmt = prepareStatement(STMT_SET_CHECKSUM); - stmt.setInt(2, id.toInteger()); + stmt.setString(2, id.toString()); stmt.setString(1, checksum); stmt.execute(); } catch (SQLException | ConnectionException e) { diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLSetQueryTemplateDefinition.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLSetQueryTemplateDefinition.java index f3ded949383c7d250d4ad58e6ae964aa9f96324c..70293edb974c2f12ba78caefc8474a768774e9d2 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLSetQueryTemplateDefinition.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLSetQueryTemplateDefinition.java @@ -1,9 +1,10 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,8 +18,6 @@ * * 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 */ package org.caosdb.server.database.backend.implementation.MySQL; @@ -29,6 +28,11 @@ import org.caosdb.server.database.backend.interfaces.SetQueryTemplateDefinitionI import org.caosdb.server.database.exceptions.TransactionException; import org.caosdb.server.entity.EntityID; +/** + * Implements {@link SetQueryTemplateDefinitionImpl} for a MariaDB back-end. + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ public class MySQLSetQueryTemplateDefinition extends MySQLTransaction implements SetQueryTemplateDefinitionImpl { @@ -37,13 +41,13 @@ public class MySQLSetQueryTemplateDefinition extends MySQLTransaction } public static final String STMT_INSERT_QUERY_TEMPLATE_DEF = - "INSERT INTO query_template_def (id, definition) VALUES (?,?) ON DUPLICATE KEY UPDATE definition=?;"; + "INSERT INTO query_template_def (id, definition) VALUES ((SELECT internal_id FROM entity_ids WHERE entity_ids.id = ?),?) ON DUPLICATE KEY UPDATE definition=?;"; @Override public void insert(final EntityID id, final String definition) { try { final PreparedStatement stmt = prepareStatement(STMT_INSERT_QUERY_TEMPLATE_DEF); - stmt.setInt(1, id.toInteger()); + stmt.setString(1, id.toString()); stmt.setString(2, definition); stmt.setString(3, definition); stmt.execute(); diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLUpdateSparseEntity.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLUpdateSparseEntity.java index b2d0fb9e3b7615e7e95243a2a7bde2fa99e98ca2..8d2af662f32d2f5a788d6470cf6cd526e15d0976 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLUpdateSparseEntity.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLUpdateSparseEntity.java @@ -47,7 +47,7 @@ public class MySQLUpdateSparseEntity extends MySQLTransaction implements UpdateS try { // file properties; final PreparedStatement updateFilePropsStmt = prepareStatement(STMT_UPDATE_FILE_PROPS); - updateFilePropsStmt.setInt(1, spe.id); + updateFilePropsStmt.setString(1, spe.id); if (spe.filePath != null) { updateFilePropsStmt.setString(2, spe.filePath); updateFilePropsStmt.setLong(3, spe.fileSize); @@ -65,7 +65,7 @@ public class MySQLUpdateSparseEntity extends MySQLTransaction implements UpdateS // very sparse entity final PreparedStatement updateEntityStmt = prepareStatement(STMT_UPDATE_ENTITY); - updateEntityStmt.setInt(1, spe.id); + updateEntityStmt.setString(1, spe.id); updateEntityStmt.setString(2, spe.name); updateEntityStmt.setString(3, spe.description); updateEntityStmt.setString(4, spe.role); diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLUpdateUser.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLUpdateUser.java index bd368698956163a8473ead7507aac0ba2b83d552..43f71e386fcad42b4a3e39e14eeedf7cb924a535 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLUpdateUser.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLUpdateUser.java @@ -49,7 +49,7 @@ public class MySQLUpdateUser extends MySQLTransaction implements UpdateUserImpl stmt.setString(3, user.status.toString()); stmt.setString(4, user.email); if (user.entity != null) { - stmt.setInt(5, user.entity); + stmt.setString(5, user.entity); } else { stmt.setNull(5, Types.INTEGER); } @@ -57,7 +57,7 @@ public class MySQLUpdateUser extends MySQLTransaction implements UpdateUserImpl stmt.setString(6, user.status.toString()); stmt.setString(7, user.email); if (user.entity != null) { - stmt.setInt(8, user.entity); + stmt.setString(8, user.entity); } else { stmt.setNull(8, Types.INTEGER); } diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/Replacement.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/Replacement.java index 61e431bab60b9973f6aa1f0b824ffc3e3b60744e..8ec47231d7d3644bc4c21a2b2125175a75f4bc97 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/Replacement.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/Replacement.java @@ -28,6 +28,7 @@ import org.caosdb.server.entity.wrapper.Property; enum ReplacementStatus implements StatementStatusInterface { REPLACEMENT } + /** * A wrapper of {@link Property} objects used by the back-end implementation for the MySQL/MariaDB * back-end. diff --git a/src/main/java/org/caosdb/server/database/backend/interfaces/GetChildrenImpl.java b/src/main/java/org/caosdb/server/database/backend/interfaces/RetrieveCurrentMaxIdImpl.java similarity index 63% rename from src/main/java/org/caosdb/server/database/backend/interfaces/GetChildrenImpl.java rename to src/main/java/org/caosdb/server/database/backend/interfaces/RetrieveCurrentMaxIdImpl.java index 8b0211a0b56bec22e4823058dfa72c355e59d19d..ca711d52c3983b4f71bf41aa5ef63583430866a8 100644 --- a/src/main/java/org/caosdb/server/database/backend/interfaces/GetChildrenImpl.java +++ b/src/main/java/org/caosdb/server/database/backend/interfaces/RetrieveCurrentMaxIdImpl.java @@ -1,9 +1,8 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,16 +16,17 @@ * * 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 */ package org.caosdb.server.database.backend.interfaces; -import java.util.List; -import org.caosdb.server.database.exceptions.TransactionException; -import org.caosdb.server.entity.EntityID; +import org.caosdb.server.database.backend.transaction.RetrieveCurrentMaxId; -public interface GetChildrenImpl extends BackendTransactionImpl { +/** + * Interface for the backend implementation of {@link RetrieveCurrentMaxId} + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ +public interface RetrieveCurrentMaxIdImpl extends BackendTransactionImpl { - public abstract List<EntityID> execute(EntityID entity) throws TransactionException; + public Integer execute(); } diff --git a/src/main/java/org/caosdb/server/database/backend/interfaces/RetrieveEntityACLImpl.java b/src/main/java/org/caosdb/server/database/backend/interfaces/RetrieveEntityACLImpl.java index 230d4d34103603d5499532a7dc1a941cf82068e7..cc61461996dc46ae5ea4a997bd0938587959cd24 100644 --- a/src/main/java/org/caosdb/server/database/backend/interfaces/RetrieveEntityACLImpl.java +++ b/src/main/java/org/caosdb/server/database/backend/interfaces/RetrieveEntityACLImpl.java @@ -25,5 +25,5 @@ import org.caosdb.server.database.proto.VerySparseEntity; public interface RetrieveEntityACLImpl extends BackendTransactionImpl { - public VerySparseEntity execute(Integer id); + public VerySparseEntity execute(String id); } diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/GetChildren.java b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveCurrentMaxId.java similarity index 51% rename from src/main/java/org/caosdb/server/database/backend/transaction/GetChildren.java rename to src/main/java/org/caosdb/server/database/backend/transaction/RetrieveCurrentMaxId.java index d0e2a809547ad2be4621ccab3f79d793dde70006..4dd76d30cd0cf1c63d5eb8ad48054093f064629f 100644 --- a/src/main/java/org/caosdb/server/database/backend/transaction/GetChildren.java +++ b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveCurrentMaxId.java @@ -1,9 +1,8 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,33 +16,33 @@ * * 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 */ package org.caosdb.server.database.backend.transaction; -import java.util.List; import org.caosdb.server.database.BackendTransaction; -import org.caosdb.server.database.backend.interfaces.GetChildrenImpl; -import org.caosdb.server.database.exceptions.TransactionException; -import org.caosdb.server.entity.EntityID; - -public class GetChildren extends BackendTransaction { +import org.caosdb.server.database.backend.interfaces.RetrieveCurrentMaxIdImpl; - private final EntityID entity; - private List<EntityID> list; +/** + * Retrieve the maximum currently known entity id. + * + * <p>This is used by the EntityIdRegistry for the legacy ids (i.e. integer ids) to generate + * sequential ids which have not been used yet. + * + * @see {@link EntityIdRegistry} + * @see {@link LegacyIds} + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ +public class RetrieveCurrentMaxId extends BackendTransaction { - public GetChildren(final EntityID entity) { - this.entity = entity; - } + private Integer maxId; @Override - public void execute() throws TransactionException { - final GetChildrenImpl t = getImplementation(GetChildrenImpl.class); - this.list = t.execute(this.entity); + protected void execute() { + RetrieveCurrentMaxIdImpl t = getImplementation(RetrieveCurrentMaxIdImpl.class); + this.maxId = t.execute(); } - public List<EntityID> getList() { - return this.list; + public Integer getCurrentMaxId() { + return this.maxId; } } diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveEntityACLTransaction.java b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveEntityACLTransaction.java index 8ef143b44e292e5b100ff30ca023e91c0adb190e..00f8c51026278d3c3660c798b5b0854aff5900eb 100644 --- a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveEntityACLTransaction.java +++ b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveEntityACLTransaction.java @@ -43,7 +43,7 @@ public class RetrieveEntityACLTransaction @Override public VerySparseEntity executeNoCache() throws TransactionException { RetrieveEntityACLImpl t = getImplementation(RetrieveEntityACLImpl.class); - return t.execute(getKey().toInteger()); + return t.execute(getKey().toString()); } @Override diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveFullEntityTransaction.java b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveFullEntityTransaction.java index eee73735a07cc2e152e0099bf5d027d1413631b4..92f5aa841aed5f759d433076700a8b1ce13e26dc 100644 --- a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveFullEntityTransaction.java +++ b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveFullEntityTransaction.java @@ -160,6 +160,7 @@ public class RetrieveFullEntityTransaction extends BackendTransaction { } return false; } + /** * Return true iff the parents need to be retrieved. * diff --git a/src/main/java/org/caosdb/server/database/misc/TransactionBenchmark.java b/src/main/java/org/caosdb/server/database/misc/TransactionBenchmark.java index 397a1f52201727d17b057dc92567893798b63cf5..6b14bd1d627a4dbe1b2ef131f76917586155a385 100644 --- a/src/main/java/org/caosdb/server/database/misc/TransactionBenchmark.java +++ b/src/main/java/org/caosdb/server/database/misc/TransactionBenchmark.java @@ -132,6 +132,7 @@ class RootBenchmark extends TransactionBenchmark implements ServerStat { private static final long serialVersionUID = -95212746021180579L; private transient boolean synced = false; + /** * Fetch old data (from before last shutdown) and fill it into this instance. * @@ -286,7 +287,8 @@ public abstract class TransactionBenchmark implements Serializable { protected TransactionBenchmark() { stackTraceElements = Thread.currentThread().getStackTrace(); - }; + } + ; public Iterable<SubBenchmark> getSubBenchmarks() { return this.subBenchmarks.values(); diff --git a/src/main/java/org/caosdb/server/database/proto/FlatProperty.java b/src/main/java/org/caosdb/server/database/proto/FlatProperty.java index b92d3def38d26f0f5f6cd6a0455d472b8f4f3ccb..b2123ce206ce01acee3ef70b1c22151b94f59bd9 100644 --- a/src/main/java/org/caosdb/server/database/proto/FlatProperty.java +++ b/src/main/java/org/caosdb/server/database/proto/FlatProperty.java @@ -27,7 +27,7 @@ import java.io.Serializable; public class FlatProperty implements Serializable { private static final long serialVersionUID = 6039288034435124195L; - public Integer id = null; + public String id = null; public String value = null; public String status = null; public Integer idx = null; diff --git a/src/main/java/org/caosdb/server/database/proto/ProtoUser.java b/src/main/java/org/caosdb/server/database/proto/ProtoUser.java index 643f4250216c7829c8218113a8842ff139b313ec..8b2d97a85044881d61fa6ea795443ae13f3cabc2 100644 --- a/src/main/java/org/caosdb/server/database/proto/ProtoUser.java +++ b/src/main/java/org/caosdb/server/database/proto/ProtoUser.java @@ -36,7 +36,7 @@ public class ProtoUser implements Serializable { public UserStatus status = null; public String name = null; public String email = null; - public Integer entity = null; + public String entity = null; public String realm = null; public HashSet<String> roles = null; } diff --git a/src/main/java/org/caosdb/server/database/proto/VerySparseEntity.java b/src/main/java/org/caosdb/server/database/proto/VerySparseEntity.java index 8f32bf52038a00275568688b016b830b19fba5f0..5ceca456abc58d945599cc978987eca783637f95 100644 --- a/src/main/java/org/caosdb/server/database/proto/VerySparseEntity.java +++ b/src/main/java/org/caosdb/server/database/proto/VerySparseEntity.java @@ -35,7 +35,7 @@ public class VerySparseEntity implements Serializable { private static final long serialVersionUID = 7370925076064714740L; - public Integer id = null; + public String id = null; public String name = null; public String description = null; public String role = null; diff --git a/src/main/java/org/caosdb/server/datatype/AbstractEnumValue.java b/src/main/java/org/caosdb/server/datatype/AbstractEnumValue.java index be88002543155771d2937e216c64d52931051c25..ffe630baa1e519df2044a487ea3ad0ffc4c4bd34 100644 --- a/src/main/java/org/caosdb/server/datatype/AbstractEnumValue.java +++ b/src/main/java/org/caosdb/server/datatype/AbstractEnumValue.java @@ -19,7 +19,9 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -/** @review Daniel Hornung 2022-03-04 */ +/** + * @review Daniel Hornung 2022-03-04 + */ package org.caosdb.server.datatype; import com.google.common.base.Objects; diff --git a/src/main/java/org/caosdb/server/datatype/CollectionValue.java b/src/main/java/org/caosdb/server/datatype/CollectionValue.java index fc94f660490284e560a34d1c407e2d74c51f6358..9fb8c6b3012dd4e9a3fa263aab149a92ce1c7643 100644 --- a/src/main/java/org/caosdb/server/datatype/CollectionValue.java +++ b/src/main/java/org/caosdb/server/datatype/CollectionValue.java @@ -21,7 +21,9 @@ * ** end header */ -/** @review Daniel Hornung 2022-03-04 */ +/** + * @review Daniel Hornung 2022-03-04 + */ package org.caosdb.server.datatype; import java.util.ArrayList; diff --git a/src/main/java/org/caosdb/server/datatype/ReferenceValue.java b/src/main/java/org/caosdb/server/datatype/ReferenceValue.java index a24e2ddfda7faf333fd7d7f64420d4a6c7fdc290..e07b1b807c3226cb5bc074aeafd4d8c712ce2ec1 100644 --- a/src/main/java/org/caosdb/server/datatype/ReferenceValue.java +++ b/src/main/java/org/caosdb/server/datatype/ReferenceValue.java @@ -1,5 +1,4 @@ /* - * ** header v3.0 * This file is a part of the CaosDB Project. * * Copyright (C) 2018 Research Group Biomedical Physics, @@ -20,11 +19,11 @@ * * 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 */ -/** @review Daniel Hornung 2022-03-04 */ +/** + * @review Daniel Hornung 2022-03-04 + */ package org.caosdb.server.datatype; import java.util.Objects; @@ -41,7 +40,13 @@ import org.jdom2.Element; * <p>Differently from other properties, they may be versioned, i.e. they may reference to a * specific version of an entity. * - * <p>TODO: Ways to specify a reference value, what are the consequences of versioned references? + * <p>Ways to specify a reference value: {id}@{version} using either the version id (a hex'ed sha256 + * string) or "HEAD", "HEAD~1", "HEAD~2" and so on. Note: "HEAD" always means the "HEAD" + * <i>after</i> the transaction. So, if you are changing the referenced entity in the same + * transaction and you want to reference the entity in the version before the change you need to use + * "HEAD~1" because that is the old version of the future. + * + * @author Timm Fitschen <t.fitschen@indiscale.com> */ public class ReferenceValue implements SingleValue { private EntityInterface entity = null; @@ -66,7 +71,7 @@ public class ReferenceValue implements SingleValue { throw ServerMessages.DATA_TYPE_DOES_NOT_ACCEPT_COLLECTION_VALUES; } else { try { - return new ReferenceValue(Integer.parseInt(reference.toString())); + return new ReferenceValue(reference.toString()); } catch (final NumberFormatException e) { return new ReferenceValue(reference.toString()); } @@ -81,9 +86,9 @@ public class ReferenceValue implements SingleValue { public static ReferenceValue parseIdVersion(final String str) { final String[] split = str.split("@", 2); if (split.length == 2) { - return new ReferenceValue(Integer.parseInt(split[0]), split[1]); + return new ReferenceValue(split[0], split[1]); } else { - return new ReferenceValue(Integer.parseInt(str)); + return new ReferenceValue(str); } } @@ -150,14 +155,13 @@ public class ReferenceValue implements SingleValue { /** If the reference is given by name, versioning is not possible (at the moment). */ public ReferenceValue(final String name) { this.name = name; + this.id = new EntityID(name); } - public ReferenceValue(final int id, final String name) { - this(new EntityID(id), name); - } - - public ReferenceValue(final int id) { - this(new EntityID(id)); + public ReferenceValue(String name, String version) { + this.name = name; + this.id = new EntityID(name); + this.version = version; } public final EntityInterface getEntity() { diff --git a/src/main/java/org/caosdb/server/entity/Entity.java b/src/main/java/org/caosdb/server/entity/Entity.java index 614397c50526606176fc50b06e2f07e76cbfb9ff..b27eaa28e5111d783d1d8d3779f36a1938898173 100644 --- a/src/main/java/org/caosdb/server/entity/Entity.java +++ b/src/main/java/org/caosdb/server/entity/Entity.java @@ -1,11 +1,10 @@ /* - * ** 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 - * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com> - * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2020 - 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2020 - 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -19,8 +18,6 @@ * * 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 */ package org.caosdb.server.entity; @@ -62,6 +59,17 @@ import org.caosdb.server.utils.TransactionLogMessage; import org.caosdb.unit.Unit; import org.jdom2.Element; +/** + * Abstract base class for all the important entity classes. + * + * <p>This is the central data class of the server. It represents Records, RecordTypes, Properties, + * and Files. + * + * <p>This class holds the messages and other transient information as well as the actual data that + * is being retrieved from or written to the back-end during a transaction. + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ public abstract class Entity extends AbstractObservable implements EntityInterface { public static final String DATATYPE_CHANGED_EVENT = "DatatypeChangedEvent"; @@ -73,7 +81,7 @@ public abstract class Entity extends AbstractObservable implements EntityInterfa private String name = null; private String description = null; private Value value = null; - private final EntityID domain = new EntityID(0); + private final EntityID domain = new EntityID("0"); private final EntityID id = new EntityID(); private AbstractDatatype datatype = null; private final ParentContainer parents = new ParentContainer(this); @@ -349,7 +357,7 @@ public abstract class Entity extends AbstractObservable implements EntityInterfa @Override public SparseEntity getSparseEntity() { final SparseEntity ret = new SparseEntity(); - ret.id = getId().toInteger(); + ret.id = getId().toString(); ret.name = getName(); ret.description = getDescription(); ret.acl = getEntityACL().serialize(); @@ -646,13 +654,7 @@ public abstract class Entity extends AbstractObservable implements EntityInterfa // Parse ID. Generate error if it isn't an integer. if (element.getAttribute("id") != null && !element.getAttributeValue("id").equals("")) { - try { - setId(new EntityID(Integer.parseInt(element.getAttributeValue("id")))); - } catch (final NumberFormatException e) { - addInfo("Id was " + element.getAttributeValue("id") + "."); - addError(ServerMessages.PARSING_FAILED); - setEntityStatus(EntityStatus.UNQUALIFIED); - } + this.setId(element.getAttributeValue("id")); } // Parse NAME. @@ -727,6 +729,9 @@ public abstract class Entity extends AbstractObservable implements EntityInterfa // record. final Property property = new Property(new WritableEntity(pe, Role.Property)); + + // Ignore parents of properties + property.getParents().clear(); property.setPIdx(pidx++); addProperty(property); } else if (pe.getName().equalsIgnoreCase("Parent")) { diff --git a/src/main/java/org/caosdb/server/entity/EntityID.java b/src/main/java/org/caosdb/server/entity/EntityID.java index 6a2d2ca368088e54c2f3c77ae669c6feda07bb4e..9f61127db2969578300fbe2f12091c0f9f258d90 100644 --- a/src/main/java/org/caosdb/server/entity/EntityID.java +++ b/src/main/java/org/caosdb/server/entity/EntityID.java @@ -1,3 +1,22 @@ +/* + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2022 - 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2022 - 2023 Timm Fitschen <t.fitschen@indiscale.com> + * + * 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/>. + */ package org.caosdb.server.entity; import java.io.Serializable; @@ -11,39 +30,37 @@ import java.util.Objects; * <p>It also allows to link entities together, e.g. an Entity's Property with the corresponding * abstract Property, even when the abstract Property does not have a valid ID yet (during bulk * inserts). + * + * @author Timm Fitschen <t.fitschen@indiscale.com> */ public class EntityID implements Serializable { - private static final long serialVersionUID = -9092133023135548179L; + private static final long serialVersionUID = -9062285447437546105L; - public static final EntityID DEFAULT_DOMAIN = new EntityID(0); - private Integer i = null; + public static final EntityID DEFAULT_DOMAIN = new EntityID("0"); + private String id = null; private EntityID link = null; public EntityID() {} - public EntityID(final Integer id) { - this.i = id; + public EntityID(final String id) { + this.id = id; } - public void setId(final Integer i) { - this.i = i; - } - - public Integer toInteger() { - if (this.link != null) { - return this.link.toInteger(); - } - return this.i; + public void setId(final String id) { + this.id = id; } @Override public String toString() { - return toInteger().toString(); + if (this.link != null) { + return this.link.toString(); + } + return this.id; } boolean hasId() { - return this.i != null || this.link != null && this.link.hasId(); + return this.id != null || this.link != null && this.link.hasId(); } public void link(final EntityInterface entity) { @@ -57,7 +74,7 @@ public class EntityID implements Serializable { @Override public boolean equals(final Object obj) { if (obj instanceof EntityID) { - return Objects.equals(((EntityID) obj).toInteger(), this.toInteger()); + return Objects.equals(((EntityID) obj).toString(), this.toString()); } return false; } @@ -65,16 +82,20 @@ public class EntityID implements Serializable { @Override public int hashCode() { if (this.hasId()) { - return toInteger(); + return toString().hashCode(); } return super.hashCode(); } public boolean isTemporary() { - return toInteger() < 0; + return toString().startsWith("-"); } public static boolean isReserved(final EntityID id) { - return id.toInteger() < 100; + try { + return id.hasId() && Integer.parseInt(id.toString()) < 100; + } catch (NumberFormatException e) { + return false; + } } } diff --git a/src/main/java/org/caosdb/server/entity/EntityIdRegistry.java b/src/main/java/org/caosdb/server/entity/EntityIdRegistry.java new file mode 100644 index 0000000000000000000000000000000000000000..e4e1158447decf9f98ece6f4de805919b404a5c5 --- /dev/null +++ b/src/main/java/org/caosdb/server/entity/EntityIdRegistry.java @@ -0,0 +1,80 @@ +/* + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> + * + * 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/>. + */ +package org.caosdb.server.entity; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import org.caosdb.server.database.exceptions.TransactionException; +import org.caosdb.server.transaction.Transaction; + +/** + * An abstract layer for managing entity ids. + * + * <p>That is, checking whether something is a well-formed id, generating well-formed ids and more. + * + * <p>This class follows a Strategy Pattern. The actual implemenation can be found in one of the + * implementations of the {@link EntityIdRegistryStrategy} interface. + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ +public class EntityIdRegistry { + + private EntityIdRegistryStrategy strategy; + + public EntityIdRegistry(Transaction<?> t) { + // This is the place where we will inject the new string id behavior in the + // future: + // + // this("org.caosdb.server.entity.StringIds", t); + this("org.caosdb.server.entity.LegacyIds", t); + } + + public EntityIdRegistry(EntityIdRegistryStrategy strategy) { + this.strategy = strategy; + } + + /** Return a well-formed, new and unused entity id. */ + public String generate() { + return this.strategy.generate(); + } + + public EntityIdRegistry(String strategy, Transaction<?> t) { + try { + @SuppressWarnings("unchecked") + Class<? extends EntityIdRegistry> clazz = + (Class<? extends EntityIdRegistry>) Class.forName(strategy); + Constructor<?> constructor = clazz.getConstructor(Transaction.class); + this.strategy = (EntityIdRegistryStrategy) constructor.newInstance(t); + } catch (ClassNotFoundException + | NoSuchMethodException + | SecurityException + | InstantiationException + | IllegalAccessException + | IllegalArgumentException + | InvocationTargetException e) { + throw new TransactionException(e); + } + } + + /** Return true if the id is a well-formed id. */ + public boolean matchIdPattern(String id) { + return this.strategy.matchIdPattern(id); + } +} diff --git a/src/main/java/org/caosdb/server/entity/EntityIdRegistryStrategy.java b/src/main/java/org/caosdb/server/entity/EntityIdRegistryStrategy.java new file mode 100644 index 0000000000000000000000000000000000000000..e70f4f52d2e402e62d3442bfe4af91652247216b --- /dev/null +++ b/src/main/java/org/caosdb/server/entity/EntityIdRegistryStrategy.java @@ -0,0 +1,44 @@ +/* + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> + * + * 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/>. + */ +package org.caosdb.server.entity; + +import org.caosdb.server.transaction.Transaction; + +/** + * Strategy interface for {@link EntityIdRegistry}. + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ +public abstract class EntityIdRegistryStrategy { + + private Transaction<?> transaction; + + public EntityIdRegistryStrategy(Transaction<?> transaction) { + this.transaction = transaction; + } + + public abstract String generate(); + + public Transaction<?> getTransaction() { + return transaction; + } + + public abstract boolean matchIdPattern(String id); +} diff --git a/src/main/java/org/caosdb/server/entity/EntityInterface.java b/src/main/java/org/caosdb/server/entity/EntityInterface.java index ba77b5c4ac8b5c833d2046dec506655531ff62cb..4a74920e7ac35d9d503de3802fbae10b2a7eaa3f 100644 --- a/src/main/java/org/caosdb/server/entity/EntityInterface.java +++ b/src/main/java/org/caosdb/server/entity/EntityInterface.java @@ -194,4 +194,8 @@ public interface EntityInterface public abstract boolean isReferenceList(); public abstract SerializeFieldStrategy getSerializeFieldStrategy(); + + public default void setId(String id) { + getId().setId(id); + } } diff --git a/src/main/java/org/caosdb/server/entity/LegacyIds.java b/src/main/java/org/caosdb/server/entity/LegacyIds.java new file mode 100644 index 0000000000000000000000000000000000000000..0454129397b90772a505ad5b791a823eabd3ea97 --- /dev/null +++ b/src/main/java/org/caosdb/server/entity/LegacyIds.java @@ -0,0 +1,76 @@ +/* + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> + * + * 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/>. + */ +package org.caosdb.server.entity; + +import org.caosdb.server.database.backend.transaction.RetrieveCurrentMaxId; +import org.caosdb.server.transaction.Transaction; + +/** + * Implementation of {@link EntityIdRegistryStrategy} which re-implements the legacy integer-based + * entity ids. + * + * <p>This implementation will generate sequences of entity ids and matches all strings which can be + * cast to integer as well-formed entity ids. + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ +public class LegacyIds extends EntityIdRegistryStrategy { + + private boolean isInit; + private Integer currentMaxId; + + public LegacyIds(Transaction<?> t) { + super(t); + this.isInit = false; + } + + private void init() { + isInit = true; + initCurrentMaxId(); + } + + private void initCurrentMaxId() { + this.currentMaxId = + Math.max( + 101, + getTransaction() + .execute(new RetrieveCurrentMaxId(), getTransaction().getAccess()) + .getCurrentMaxId()); + } + + @Override + public String generate() { + if (!isInit) { + init(); + } + ++currentMaxId; + return Integer.toString(++currentMaxId); + } + + @Override + public boolean matchIdPattern(String id) { + try { + Integer.parseInt(id); + return true; + } catch (NumberFormatException e) { + return false; + } + } +} diff --git a/src/main/java/org/caosdb/server/entity/MagicTypes.java b/src/main/java/org/caosdb/server/entity/MagicTypes.java index 39556385235e6b72736bed8dff67a178cfe60aff..6e987af95c0188fa21a50f19d11a493d97bbe8d8 100644 --- a/src/main/java/org/caosdb/server/entity/MagicTypes.java +++ b/src/main/java/org/caosdb/server/entity/MagicTypes.java @@ -33,9 +33,9 @@ public enum MagicTypes { NAME, DESCRIPTION; - 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 UNIT_ID = new EntityID("21"); + private static final EntityID DESCRIPTION_ID = new EntityID("24"); + private static final EntityID NAME_ID = new EntityID("20"); public EntityID getId() { switch (this) { @@ -51,12 +51,12 @@ public enum MagicTypes { } public static MagicTypes getType(final EntityID id) { - switch (id.toInteger()) { - case 21: + switch (id.toString()) { + case "21": return UNIT; - case 20: + case "20": return NAME; - case 24: + case "24": return DESCRIPTION; default: return null; @@ -82,7 +82,7 @@ public enum MagicTypes { final RetrieveContainer container = new RetrieveContainer(null, System.currentTimeMillis(), null, null); for (final MagicTypes mt : MagicTypes.values()) { - container.add(mt.getId()); + container.add(mt.getId(), null); } final Retrieve retrieve = new Retrieve(container); retrieve.execute(); diff --git a/src/main/java/org/caosdb/server/entity/RetrieveEntity.java b/src/main/java/org/caosdb/server/entity/RetrieveEntity.java index 2d1ca6f2175ca2377b7f1a73f847a48377cc4694..00a120fbf023024eae8acc5788a9260d020a956d 100644 --- a/src/main/java/org/caosdb/server/entity/RetrieveEntity.java +++ b/src/main/java/org/caosdb/server/entity/RetrieveEntity.java @@ -47,6 +47,11 @@ public class RetrieveEntity extends Entity { this.setVersion(new Version(version)); } + public RetrieveEntity(final EntityID id, String name, final String version) { + this(id, version); + this.setName(name); + } + public RetrieveEntity(final String name, final String version) { super(name); this.setVersion(new Version(version)); diff --git a/src/main/java/org/caosdb/server/entity/Role.java b/src/main/java/org/caosdb/server/entity/Role.java index f86158a99be307867c17f136a26e1097e6e7bc5f..a2a97f92546b2e63bac61ff059eeb6128d8bab6b 100644 --- a/src/main/java/org/caosdb/server/entity/Role.java +++ b/src/main/java/org/caosdb/server/entity/Role.java @@ -32,7 +32,6 @@ import org.caosdb.server.entity.xml.ToElementStrategy; public enum Role { RecordType, Record, - Domain, File, Property, DataType, diff --git a/src/main/java/org/caosdb/server/entity/StringIds.java b/src/main/java/org/caosdb/server/entity/StringIds.java new file mode 100644 index 0000000000000000000000000000000000000000..e1f5cb04be995253cd16cdc0a809421122b3dc8e --- /dev/null +++ b/src/main/java/org/caosdb/server/entity/StringIds.java @@ -0,0 +1,51 @@ +/* + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> + * + * 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/>. + */ +package org.caosdb.server.entity; + +import java.util.UUID; +import org.caosdb.server.transaction.Transaction; + +/** + * Example implementation for string ids based on UUID. + * + * @see {@link EntityIdRegistryStrategy} + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ +public class StringIds extends EntityIdRegistryStrategy { + + public StringIds(Transaction<?> t) { + super(t); + } + + @Override + public String generate() { + return UUID.randomUUID().toString(); + } + + @Override + public boolean matchIdPattern(String id) { + try { + UUID.fromString(id); + return true; + } catch (IllegalArgumentException e) { + return false; + } + } +} diff --git a/src/main/java/org/caosdb/server/entity/container/Container.java b/src/main/java/org/caosdb/server/entity/container/Container.java index 1618fbed6a1594891ea3ddc615f48869227f2c92..fdbcbf0de68d77c3eef026ef68d559a69f2f0956 100644 --- a/src/main/java/org/caosdb/server/entity/container/Container.java +++ b/src/main/java/org/caosdb/server/entity/container/Container.java @@ -36,9 +36,11 @@ public class Container<T extends EntityInterface> extends ArrayList<T> { * Return the entity with the matching id, if it can be found inside this Container, else null. */ public T getEntityById(final EntityID id) { - for (final T e : this) { - if (e.hasId() && e.getId().equals(id)) { - return e; + if (id != null) { + for (final T e : this) { + if (e.hasId() && e.getId().equals(id)) { + return e; + } } } return null; diff --git a/src/main/java/org/caosdb/server/entity/container/EntityByIdContainer.java b/src/main/java/org/caosdb/server/entity/container/EntityByIdContainer.java index a8c3e6a832848903884edda270e003a0c0dd6feb..1e756e3313fc4a816c096351527df7022a79f47b 100644 --- a/src/main/java/org/caosdb/server/entity/container/EntityByIdContainer.java +++ b/src/main/java/org/caosdb/server/entity/container/EntityByIdContainer.java @@ -37,7 +37,5 @@ public abstract class EntityByIdContainer extends TransactionContainer { super(user, timestamp, srid, flags); } - public abstract void add(EntityID id); - public abstract void add(EntityID id, String version); } diff --git a/src/main/java/org/caosdb/server/entity/container/RetrieveContainer.java b/src/main/java/org/caosdb/server/entity/container/RetrieveContainer.java index bc0da7c1de2bb876551d29ad7daffb50494f9720..99262f56d15d90ba86fb1a9cd8fd76b90c4dbf22 100644 --- a/src/main/java/org/caosdb/server/entity/container/RetrieveContainer.java +++ b/src/main/java/org/caosdb/server/entity/container/RetrieveContainer.java @@ -1,9 +1,10 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,8 +18,6 @@ * * 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 */ package org.caosdb.server.entity.container; @@ -27,7 +26,24 @@ import org.apache.shiro.subject.Subject; import org.caosdb.server.entity.EntityID; import org.caosdb.server.entity.RetrieveEntity; -public class RetrieveContainer extends EntityByIdContainer { +/** + * A container of entities which are about to be retrieved. + * + * <p>A suitable name would also be `ReadOnlyEntitiesContainer` because entities in this container + * will only be retrieved, not changed, inserted or deleted. + * + * <p>Because all entities are expected to be existing, all entities can theoretically be identified + * via their (valid) entity id (plus version id, if applicable). + * + * <p>However, sometimes, entities are being retrieved via the name instead of the id. Then the id + * has to be resolved first. This happens during the init stage of the owning transaction. + * + * <p>The container also holds the flags of the current transaction (e.g. cache=false, ACL=true, + * ...). + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ +public class RetrieveContainer extends TransactionContainer { private static final long serialVersionUID = 4816050921531043503L; @@ -39,21 +55,11 @@ public class RetrieveContainer extends EntityByIdContainer { super(user, timestamp, srid, flags); } - @Override - public void add(final EntityID id) { - add(new RetrieveEntity(id)); - } - - public void add(final String name) { - add(new RetrieveEntity(name)); - } - - public void add(final String name, final String version) { - add(new RetrieveEntity(name, version)); - } - - @Override public void add(final EntityID id, final String version) { add(new RetrieveEntity(id, version)); } + + public void add(final EntityID id, final String name, final String version) { + add(new RetrieveEntity(id, name, version)); + } } diff --git a/src/main/java/org/caosdb/server/entity/container/TransactionContainer.java b/src/main/java/org/caosdb/server/entity/container/TransactionContainer.java index d396345508a028700db6fd8e973390fd634e54d9..049f54cd1ded0ece71c7082ddc960c4246ef0b28 100644 --- a/src/main/java/org/caosdb/server/entity/container/TransactionContainer.java +++ b/src/main/java/org/caosdb/server/entity/container/TransactionContainer.java @@ -1,9 +1,10 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,8 +18,6 @@ * * 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 */ package org.caosdb.server.entity.container; @@ -38,6 +37,12 @@ import org.caosdb.server.query.Query; import org.caosdb.server.utils.EntityStatus; import org.jdom2.Element; +/** + * Base class for transaction containers used by classes such as {@link RetrieveContainer} or {@link + * WritableContainer}. + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ public class TransactionContainer extends Container<EntityInterface> implements ToElementable, JobTarget { @@ -152,9 +157,11 @@ public class TransactionContainer extends Container<EntityInterface> * @param name */ public EntityInterface getEntityByName(final String name) { - for (final EntityInterface e : this) { - if (e.hasName() && e.getName().equals(name)) { - return e; + if (name != null) { + for (final EntityInterface e : this) { + if (e.hasName() && e.getName().equals(name)) { + return e; + } } } return null; diff --git a/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java b/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java index 88429828f27ac843903cfcfc801847c35eedb607..00a433d72c0a00e8e8ef796777bfa016679cbfe6 100644 --- a/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java +++ b/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java @@ -129,8 +129,7 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS result.setEmailSetting(EmailSetting.newBuilder().setEmail(user.email)); } if (user.entity != null) { - result.setEntitySetting( - EntitySetting.newBuilder().setEntityId(Integer.toString(user.entity))); + result.setEntitySetting(EntitySetting.newBuilder().setEntityId(user.entity)); } if (user.roles != null && !user.roles.isEmpty()) { result.addAllRoles(user.roles); diff --git a/src/main/java/org/caosdb/server/grpc/AuthInterceptor.java b/src/main/java/org/caosdb/server/grpc/AuthInterceptor.java index ce8a62210d3c8e849397600fc112cd3103f15df4..49f2facefa845a6deeed5f07bf4fde364891fcb6 100644 --- a/src/main/java/org/caosdb/server/grpc/AuthInterceptor.java +++ b/src/main/java/org/caosdb/server/grpc/AuthInterceptor.java @@ -87,6 +87,7 @@ public class AuthInterceptor implements ServerInterceptor { metadata.put(CookieSetter.SET_COOKIE, CookieSetter.EXPIRED_SESSION_COOKIE); return metadata; } + /** * A no-op listener. This class is used for failed authentications. We couldn't return a null * instead because the documentation of the {@link ServerInterceptor} explicitely forbids it. @@ -281,7 +282,7 @@ final class CookieSetter<ReqT, RespT> public void sendHeaders(Metadata headers) { setSessionCookies(headers); super.sendHeaders(headers); - }; + } private void setSessionCookies(Metadata headers) { // if authenticated as a normal user: generate and set session cookie. diff --git a/src/main/java/org/caosdb/server/grpc/EntityTransactionServiceImpl.java b/src/main/java/org/caosdb/server/grpc/EntityTransactionServiceImpl.java index 030b52544fec858ef3c62c531e9627baf5500551..3386676855dcaaae154a00700b83dc5e2852edff 100644 --- a/src/main/java/org/caosdb/server/grpc/EntityTransactionServiceImpl.java +++ b/src/main/java/org/caosdb/server/grpc/EntityTransactionServiceImpl.java @@ -1,8 +1,8 @@ /* * This file is a part of the CaosDB Project. * - * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com> - * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2021,2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2021,2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -18,7 +18,6 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. * */ - package org.caosdb.server.grpc; import io.grpc.stub.StreamObserver; @@ -47,11 +46,11 @@ import org.caosdb.api.entity.v1.RetrieveResponse; import org.caosdb.api.entity.v1.SelectQueryResult; import org.caosdb.api.entity.v1.TransactionRequest; import org.caosdb.api.entity.v1.TransactionRequest.WrappedRequestsCase; -import org.caosdb.api.entity.v1.TransactionResponse; import org.caosdb.api.entity.v1.UpdateRequest; import org.caosdb.api.entity.v1.UpdateResponse; import org.caosdb.server.CaosDBException; import org.caosdb.server.entity.DeleteEntity; +import org.caosdb.server.entity.EntityID; import org.caosdb.server.entity.EntityInterface; import org.caosdb.server.entity.FileProperties; import org.caosdb.server.entity.InsertEntity; @@ -68,6 +67,11 @@ import org.caosdb.server.transaction.UpdateACL; import org.caosdb.server.transaction.WriteTransaction; import org.caosdb.server.utils.ServerMessages; +/** + * Main entry point for the entity transaction service of the servers GRPC API. + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ public class EntityTransactionServiceImpl extends EntityTransactionServiceImplBase { // TODO(tf) let the clients define the time zone of the date time values which are being returned. @@ -135,15 +139,11 @@ public class EntityTransactionServiceImpl extends EntityTransactionServiceImplBa } else { // or ID retrieves. final String id = sub_request.getRetrieveRequest().getId(); if (!id.isBlank()) { - try { - final RetrieveEntity entity = new RetrieveEntity(grpcToCaosdb.getId(id)); - if (isFileDownload) { - entity.setFlag("download_files", "true"); - } - container.add(entity); - } catch (final NumberFormatException e) { - // We handle this after the retrieval + final RetrieveEntity entity = new RetrieveEntity(new EntityID(id)); + if (isFileDownload) { + entity.setFlag("download_files", "true"); } + container.add(entity); } } } @@ -193,32 +193,9 @@ public class EntityTransactionServiceImpl extends EntityTransactionServiceImplBa } } - // Add those entities which have not been retrieved because they have a string id - for (final TransactionRequest sub_request : request.getRequestsList()) { - final String id = sub_request.getRetrieveRequest().getId(); - if (!id.isBlank()) { - try { - grpcToCaosdb.getId(id); - } catch (final NumberFormatException e) { - // ID wasn't an integer - the server doesn't support string ids yet, so that entity - // cannot exist. - builder.addResponses( - TransactionResponse.newBuilder() - .setRetrieveResponse( - RetrieveResponse.newBuilder().setEntityResponse(entityDoesNotExist(id)))); - } - } - } return builder.build(); } - private EntityResponse entityDoesNotExist(final String id) { - return EntityResponse.newBuilder() - .addErrors(caosdbToGrpc.convert(ServerMessages.ENTITY_DOES_NOT_EXIST)) - .setEntity(Entity.newBuilder().setId(id)) - .build(); - } - private String getSRID() { return UUID.randomUUID().toString(); } @@ -295,29 +272,16 @@ public class EntityTransactionServiceImpl extends EntityTransactionServiceImplBa final UpdateRequest updateRequest = subRequest.getUpdateRequest(); final Entity updateEntity = updateRequest.getEntityRequest().getEntity(); - try { - final UpdateEntity entity = - new UpdateEntity( - grpcToCaosdb.getId(updateEntity.getId()), // ID is not handled by grpc convert - grpcToCaosdb.convert(updateEntity.getRole())); - grpcToCaosdb.convert(updateEntity, entity); - addFileUpload(container, entity, updateRequest.getEntityRequest()); - container.add(entity); - } catch (final NumberFormatException e) { - // ID wasn't an integer - return failedWriteDueToStringId(requests); - } + final UpdateEntity entity = + new UpdateEntity( + new EntityID(updateEntity.getId()), grpcToCaosdb.convert(updateEntity.getRole())); + grpcToCaosdb.convert(updateEntity, entity); + addFileUpload(container, entity, updateRequest.getEntityRequest()); + container.add(entity); break; case DELETE_REQUEST: final DeleteRequest deleteRequest = subRequest.getDeleteRequest(); - try { - final DeleteEntity entity = new DeleteEntity(grpcToCaosdb.getId(deleteRequest.getId())); - container.add(entity); - - } catch (final NumberFormatException e) { - // ID wasn't an integer - return failedWriteDueToStringId(requests); - } + container.add(new DeleteEntity(new EntityID(deleteRequest.getId()))); break; default: throw new CaosDBException( @@ -357,65 +321,6 @@ public class EntityTransactionServiceImpl extends EntityTransactionServiceImplBa return builder.build(); } - /** - * Handle a request which contains string id (which cannot be converted to integer ids) and return - * a response which has the "ENTITY_DOES_NOT_EXIST" error for all entities with affected ids. - * - * <p>This does not attempt to execute a single request. - * - * @param request - * @return - */ - private MultiTransactionResponse failedWriteDueToStringId(final MultiTransactionRequest request) { - final org.caosdb.api.entity.v1.MultiTransactionResponse.Builder builder = - MultiTransactionResponse.newBuilder(); - for (final TransactionRequest subRequest : request.getRequestsList()) { - final IdResponse.Builder idResponse = IdResponse.newBuilder(); - switch (subRequest.getWrappedRequestsCase()) { - case INSERT_REQUEST: - builder - .addResponsesBuilder() - .setInsertResponse(InsertResponse.newBuilder().setIdResponse(idResponse)); - - break; - case UPDATE_REQUEST: - final UpdateRequest updateRequest = subRequest.getUpdateRequest(); - final Entity updateEntity = updateRequest.getEntityRequest().getEntity(); - - idResponse.setId(updateEntity.getId()); - try { - grpcToCaosdb.getId(updateEntity.getId()); - } catch (final NumberFormatException e) { - // ID wasn't an integer - idResponse.addErrors(caosdbToGrpc.convert(ServerMessages.ENTITY_DOES_NOT_EXIST)); - } - builder - .addResponsesBuilder() - .setUpdateResponse(UpdateResponse.newBuilder().setIdResponse(idResponse)); - break; - case DELETE_REQUEST: - final DeleteRequest deleteRequest = subRequest.getDeleteRequest(); - idResponse.setId(deleteRequest.getId()); - try { - grpcToCaosdb.getId(deleteRequest.getId()); - } catch (final NumberFormatException e) { - // ID wasn't an integer - idResponse.addErrors(caosdbToGrpc.convert(ServerMessages.ENTITY_DOES_NOT_EXIST)); - } - builder - .addResponsesBuilder() - .setDeleteResponse(DeleteResponse.newBuilder().setIdResponse(idResponse)); - break; - default: - throw new CaosDBException( - "Cannot process a " - + subRequest.getWrappedRequestsCase().name() - + " in a write request."); - } - } - return builder.build(); - } - private void addFileUpload( final WritableContainer container, final EntityInterface entity, diff --git a/src/main/java/org/caosdb/server/grpc/GRPCServer.java b/src/main/java/org/caosdb/server/grpc/GRPCServer.java index 73356140b85fb09af93bdca034a69685ce8cd4eb..494ce94706eff75415264b175f58cdaf16a2a442 100644 --- a/src/main/java/org/caosdb/server/grpc/GRPCServer.java +++ b/src/main/java/org/caosdb/server/grpc/GRPCServer.java @@ -81,8 +81,11 @@ public class GRPCServer { * @throws IOException */ private SslContext buildSslContext() - throws NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException, - CertificateException, IOException { + throws NoSuchAlgorithmException, + UnrecoverableKeyException, + KeyStoreException, + CertificateException, + IOException { final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); final char[] password = @@ -112,7 +115,9 @@ public class GRPCServer { return builder.build(); } - /** @return A list of services which should be added to the gRPC end-point. */ + /** + * @return A list of services which should be added to the gRPC end-point. + */ private List<ServerServiceDefinition> getEnabledServices() { final List<ServerServiceDefinition> services = new LinkedList<>(); @@ -153,8 +158,11 @@ public class GRPCServer { * @throws IOException */ private Server buildServer(final int port, final boolean tls) - throws UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException, - CertificateException, IOException { + throws UnrecoverableKeyException, + NoSuchAlgorithmException, + KeyStoreException, + CertificateException, + IOException { final NettyServerBuilder builder = NettyServerBuilder.forPort(port); if (tls) { @@ -182,8 +190,12 @@ public class GRPCServer { * @throws UnrecoverableKeyException */ public static void startServer() - throws IOException, InterruptedException, KeyStoreException, NoSuchAlgorithmException, - CertificateException, UnrecoverableKeyException { + throws IOException, + InterruptedException, + KeyStoreException, + NoSuchAlgorithmException, + CertificateException, + UnrecoverableKeyException { boolean started = false; final String port_https_str = getServerProperty(ServerProperties.KEY_GRPC_SERVER_PORT_HTTPS); diff --git a/src/main/java/org/caosdb/server/grpc/GrpcToCaosDBConverters.java b/src/main/java/org/caosdb/server/grpc/GrpcToCaosDBConverters.java index 0430a795457152141473c2819997ebd1597028fd..587b89662456272d9a81aa910db3b1a962d67499 100644 --- a/src/main/java/org/caosdb/server/grpc/GrpcToCaosDBConverters.java +++ b/src/main/java/org/caosdb/server/grpc/GrpcToCaosDBConverters.java @@ -1,8 +1,8 @@ /* * This file is a part of the CaosDB Project. * - * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com> - * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -16,9 +16,7 @@ * * 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/>. - * */ - package org.caosdb.server.grpc; import java.util.ArrayList; @@ -64,12 +62,13 @@ import org.caosdb.server.permissions.EntityACLFactory; import org.caosdb.server.utils.EntityStatus; import org.caosdb.server.utils.ServerMessages; +/** + * Utility class for converting GRPC's native objects into our own CaosDB objects. + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ public class GrpcToCaosDBConverters { - public EntityID getId(final String id) { - return new EntityID(Integer.parseInt(id)); - } - public Role convert(final EntityRole role) { switch (role) { case ENTITY_ROLE_FILE: @@ -256,7 +255,7 @@ public class GrpcToCaosDBConverters { final Property result = new Property(new RetrieveEntity()); try { - result.setId(e.getId().isBlank() ? null : getId(e.getId())); + result.setId(e.getId().isBlank() ? null : new EntityID(e.getId())); } catch (final NumberFormatException exc) { result.addError(ServerMessages.ENTITY_DOES_NOT_EXIST); } @@ -314,7 +313,7 @@ public class GrpcToCaosDBConverters { new org.caosdb.server.entity.wrapper.Parent(new RetrieveEntity()); try { - result.setId(e.getId().isBlank() ? null : getId(e.getId())); + result.setId(e.getId().isBlank() ? null : new EntityID(e.getId())); } catch (final NumberFormatException exc) { result.addError(ServerMessages.ENTITY_DOES_NOT_EXIST); } @@ -336,7 +335,7 @@ public class GrpcToCaosDBConverters { private EntityInterface convert(EntityACL acl) { try { - EntityID id = getId(acl.getId()); + EntityID id = new EntityID(acl.getId()); UpdateEntity result = new UpdateEntity(id); result.setEntityACL(convertAcl(acl)); return result; diff --git a/src/main/java/org/caosdb/server/jobs/Job.java b/src/main/java/org/caosdb/server/jobs/Job.java index 9da74edd0db86f6d81a8636020afcdaf3faa9e09..7bb94635ed21c14c80c92d315539dd4fab5416ab 100644 --- a/src/main/java/org/caosdb/server/jobs/Job.java +++ b/src/main/java/org/caosdb/server/jobs/Job.java @@ -3,8 +3,8 @@ * * Copyright (C) 2018 Research Group Biomedical Physics, * Max-Planck-Institute for Dynamics and Self-Organization Göttingen - * Copyright (C) 2020-2021 IndiScale GmbH <info@indiscale.com> - * Copyright (C) 2020-2021 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2020-2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2020-2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -22,10 +22,10 @@ package org.caosdb.server.jobs; import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import org.apache.shiro.subject.Subject; @@ -33,9 +33,8 @@ import org.caosdb.server.CaosDBException; import org.caosdb.server.database.BackendTransaction; import org.caosdb.server.database.backend.transaction.GetIDByName; import org.caosdb.server.database.backend.transaction.IsSubType; -import org.caosdb.server.database.backend.transaction.RetrieveFullEntityTransaction; -import org.caosdb.server.database.backend.transaction.RetrieveParents; import org.caosdb.server.database.backend.transaction.RetrieveSparseEntity; +import org.caosdb.server.database.exceptions.EntityDoesNotExistException; import org.caosdb.server.database.exceptions.EntityWasNotUniqueException; import org.caosdb.server.database.exceptions.TransactionException; import org.caosdb.server.datatype.AbstractCollectionDatatype; @@ -44,13 +43,12 @@ import org.caosdb.server.datatype.ReferenceDatatype2; import org.caosdb.server.entity.EntityID; import org.caosdb.server.entity.EntityInterface; import org.caosdb.server.entity.Message; -import org.caosdb.server.entity.container.ParentContainer; +import org.caosdb.server.entity.Version; import org.caosdb.server.entity.container.TransactionContainer; import org.caosdb.server.entity.wrapper.Parent; import org.caosdb.server.jobs.core.JobFailureSeverity; import org.caosdb.server.transaction.Transaction; import org.caosdb.server.utils.EntityStatus; -import org.caosdb.server.utils.ServerMessages; import org.reflections.Reflections; /** @@ -73,7 +71,7 @@ public abstract class Job { public abstract JobTarget getTarget(); - protected <S, T> HashMap<S, T> getCache(final String name) { + protected <S, T> Map<S, T> getCache(final String name) { return getTransaction().getCache(name); } @@ -89,7 +87,7 @@ public abstract class Job { return getTransaction().getTransactor(); } - public <K extends BackendTransaction> K execute(final K t) { + protected <K extends BackendTransaction> K execute(final K t) { return getTransaction().execute(t, getTransaction().getAccess()); } @@ -185,8 +183,8 @@ public abstract class Job { if (targetParent.getId().equals(child.getId())) { return true; } - - } else if (targetParent.hasName()) { + } + if (targetParent.hasName()) { if (targetParent.getName().equals(child.getName())) { return true; } else { @@ -195,13 +193,7 @@ public abstract class Job { } // check direct parents of child - ParentContainer directParents; - if (child.hasParents()) { - directParents = child.getParents(); - } else { - directParents = resolve(child).getParents(); - } - for (final Parent directParent : directParents) { + for (final Parent directParent : child.getParents()) { EntityInterface resolvedDirectParent = null; if (directParent.hasId() && targetParent.hasId()) { if (directParent.getId().equals(targetParent.getId())) { @@ -231,78 +223,73 @@ public abstract class Job { } protected final boolean isValidSubType(final EntityID child, final EntityID parent) { - if (!"false".equals(getContainer().getFlags().get("cache"))) { - final HashMap<String, Boolean> isSubTypeCache = getCache("isSubType"); - final String key = child + "->" + parent; - final Boolean cached = isSubTypeCache.get(key); - if (cached == null) { - final Boolean toCache = isValidSubTypeNoCache(child, parent); - isSubTypeCache.put(key, toCache); - return toCache; - } else { - return cached; - } - } else { - return isValidSubTypeNoCache(child, parent); + Boolean result = null; + final Map<String, Boolean> isSubTypeCache = getCache("isSubType"); + + final String key = child + "->" + parent; + result = isSubTypeCache.get(key); + + if (result == null) { + result = isValidSubTypeNoCache(child, parent); + isSubTypeCache.put(key, result); } + + return result; } - protected final boolean isValidSubTypeNoCache(final EntityID child, final EntityID parent) { + private final boolean isValidSubTypeNoCache(final EntityID child, final EntityID parent) { return Objects.equals(child, parent) || execute(new IsSubType(child, parent)).isSubType(); } - protected final EntityInterface retrieveValidSparseEntityByName(final String name) - throws Message { - return retrieveValidSparseEntityById(retrieveValidIDByName(name), null); + private final EntityInterface retrieveValidLazyEntityByName(final String name, String version) { + EntityID id = null; + + final Map<String, EntityID> cache = getCache("validEntityByName"); + String key = name; + if (version != null && !version.equals("HEAD")) { + key += version; + } + + id = cache.get(key); + if (id == null) { + id = retrieveValidIDByName(name, version); + cache.put(key, id); + } + + return retrieveValidLazyEntityById(id, version); } - protected final EntityInterface retrieveValidSparseEntityById( - final EntityID id, final String version) throws Message { + private final EntityInterface retrieveValidLazyEntityById( + final EntityID id, final String version) { + EntityInterface result = null; - String resulting_version = version; - if (version == null || version.equals("HEAD")) { - // the targeted entity version is the entity after the transaction or the - // entity without a specific version. Thus we have to fetch the entity - // from the container if possible. - final EntityInterface ret = getEntityById(id); - if (ret != null) { - return ret; - } - } else if (version.startsWith("HEAD~")) { - final EntityInterface entById = getEntityById(id); - if (entById != null && entById.getEntityStatus() != EntityStatus.VALID) { - // if version is HEAD~{OFFSET} with {OFFSET} > 0 and the targeted entity is is to be - // updated, the actual offset has to be reduced by 1. HEAD always denotes the entity@HEAD - // *after* the successful transaction, so that it is consistent with subsequent retrieves. - final int offset = Integer.parseInt(version.substring(5)) - 1; - if (offset == 0) { - // special case HEAD~1 - resulting_version = "HEAD"; - } else { - resulting_version = new StringBuilder().append("HEAD~").append(offset).toString(); - } - } + final Map<String, EntityInterface> cache = getCache("validEntityById"); + String key = id.toString(); + if (version != null && !version.equals("HEAD")) { + key += version; } - final EntityInterface ret = - execute(new RetrieveSparseEntity(id, resulting_version)).getEntity(); - if (ret.getEntityStatus() == EntityStatus.NONEXISTENT) { - throw ServerMessages.ENTITY_DOES_NOT_EXIST; + result = cache.get(key); + if (result == null) { + result = retrieveValidLazyEntityByIdNoCache(id, version); + cache.put(key, result); } - return ret; - } - protected final EntityInterface retrieveValidEntity(final EntityID id) { - return execute(new RetrieveFullEntityTransaction(id)).getContainer().get(0); + return result; } - protected final EntityID retrieveValidIDByName(final String name) { - return execute(new GetIDByName(name)).getId(); + private final EntityInterface retrieveValidLazyEntityByIdNoCache( + final EntityID id, final String version) { + + final EntityInterface ret = execute(new RetrieveSparseEntity(id, version)).getEntity(); + if (ret.getEntityStatus() == EntityStatus.NONEXISTENT) { + throw new EntityDoesNotExistException(); + } + return new LazyEntityResolver(ret, getTransaction()); } - protected EntityInterface retrieveParentsOfValidEntity(final EntityInterface entity) { - execute(new RetrieveParents(entity)); - return entity; + protected final EntityID retrieveValidIDByName(final String name, String version) { + return execute(new GetIDByName(name, version)).getId(); } /** @@ -334,7 +321,7 @@ public abstract class Job { private static void scanJobClasspath() { if (allClasses == null || loadAlways == null) { allClasses = new HashMap<>(); - loadAlways = new ArrayList<>(); + loadAlways = new LinkedList<>(); Reflections jobPackage = new Reflections("org.caosdb.server.jobs.core"); Set<Class<? extends Job>> allClassesSet = jobPackage.getSubTypesOf(Job.class); allClassesSet.addAll(jobPackage.getSubTypesOf(FlagJob.class)); @@ -383,11 +370,11 @@ public abstract class Job { final EntityInterface entity, final Transaction<? extends TransactionContainer> transaction) { if (dt == null) { - return new ArrayList<Job>(); + return new LinkedList<Job>(); } if (dt instanceof ReferenceDatatype2) { return jobConfig.getConfiguredJobs( - EntityID.DEFAULT_DOMAIN, new EntityID(17), entity, transaction); + EntityID.DEFAULT_DOMAIN, new EntityID("17"), entity, transaction); } else if (dt instanceof AbstractCollectionDatatype) { final AbstractDatatype datatype = ((AbstractCollectionDatatype) dt).getDatatype(); return loadDataTypeSpecificJobs(datatype, entity, transaction); @@ -401,18 +388,22 @@ public abstract class Job { public List<Job> loadStandardJobs( final EntityInterface entity, final Transaction<? extends TransactionContainer> transaction) { - final ArrayList<Job> jobs = new ArrayList<>(); + final List<Job> jobs = new LinkedList<>(); // load permanent jobs - for (final Class<? extends Job> j : loadAlways) { - if (EntityJob.class.isAssignableFrom(j) - && j.getAnnotation(JobAnnotation.class).transaction().isInstance(transaction)) { - jobs.add(getJob(j, JobFailureSeverity.ERROR, entity, transaction)); + for (final Class<? extends Job> jobClass : loadAlways) { + if (EntityJob.class.isAssignableFrom(jobClass) + && jobClass.getAnnotation(JobAnnotation.class).transaction().isInstance(transaction)) { + Job j = getJob(jobClass, JobFailureSeverity.ERROR, entity, transaction); + if (j != null) { + jobs.add(j); + } } } // load general rules final List<Job> generalRules = - jobConfig.getConfiguredJobs(EntityID.DEFAULT_DOMAIN, new EntityID(0), entity, transaction); + jobConfig.getConfiguredJobs( + EntityID.DEFAULT_DOMAIN, new EntityID("0"), entity, transaction); if (generalRules != null) { jobs.addAll(generalRules); } @@ -541,10 +532,6 @@ public abstract class Job { + "]"; } - public void print() { - System.out.println(toString()); - } - public TransactionStage getTransactionStage() { return this.stage; } @@ -559,33 +546,80 @@ public abstract class Job { */ protected EntityInterface resolve(final EntityInterface entity) throws EntityWasNotUniqueException { - EntityInterface resolvedEntity = null; - if (!entity.hasId() && entity.hasName()) { - resolvedEntity = getEntityByName(entity.getName()); + return resolve(entity.getId(), entity.getName(), entity.getVersion()); + } + + protected EntityInterface resolve(EntityID id) { + return resolve(id, null, (String) null); + } + + protected EntityInterface resolve(EntityID id, String name, String versionId) + throws EntityDoesNotExistException, EntityWasNotUniqueException { + EntityInterface resolvedEntity = getEntityById(id); + + String resulting_version = versionId; + if (versionId == null || versionId.equals("HEAD")) { + resulting_version = null; + versionId = null; + } else if (versionId.startsWith("HEAD~")) { if (resolvedEntity == null) { - final EntityID eid = retrieveValidIDByName(entity.getName()); - entity.setId(eid); + resolvedEntity = getEntityByName(name); + } + if (resolvedEntity != null && resolvedEntity.getEntityStatus() == EntityStatus.QUALIFIED) { + // if versionId is HEAD~{OFFSET} with {OFFSET} > 0 and the targeted entity is is to be + // updated, the actual offset has to be reduced by 1. HEAD always denotes the entity@HEAD + // *after* the successful transaction, so that it is consistent with subsequent retrieves. + final int offset = Integer.parseInt(versionId.substring(5)) - 1; + if (offset == 0) { + // special case HEAD~1 + resulting_version = "HEAD"; + } else { + resulting_version = new StringBuilder().append("HEAD~").append(offset).toString(); + } } + resolvedEntity = null; + } else { + // We want another version, throw away current result. + resolvedEntity = null; } - if (entity.hasId()) { - // get entity from container - resolvedEntity = getEntityById(entity.getId()); - if (resolvedEntity == null && !entity.getId().isTemporary()) { - resolvedEntity = retrieveValidEntity(entity.getId()); + if (resolvedEntity == null && id != null) { + try { + resolvedEntity = retrieveValidLazyEntityById(id, resulting_version); + } catch (EntityDoesNotExistException e) { + // ignore here, try to find by name. } } + // Last resort: find by name ... + if (resolvedEntity == null && versionId == null) { + // ... from current transaction's container + resolvedEntity = getEntityByName(name); + } + + if (resolvedEntity == null && name != null) { + // ... from database + resolvedEntity = retrieveValidLazyEntityByName(name, resulting_version); + } + + if (resolvedEntity == null) { + throw new EntityDoesNotExistException(); + } return resolvedEntity; } + protected EntityInterface resolve(EntityID id, String name, Version version) { + String versionId = version != null ? version.getId() : null; + return resolve(id, name, versionId); + } + /** * Return those matching jobs which are annotated with the "loadAlways" attribute. * * @return A list with the jobs. */ public static List<Job> loadPermanentContainerJobs(final Transaction<?> transaction) { - final ArrayList<Job> jobs = new ArrayList<>(); + final List<Job> jobs = new LinkedList<>(); // load permanent jobs: ContainerJob classes with the correct transaction for (final Class<? extends Job> j : loadAlways) { if (ContainerJob.class.isAssignableFrom(j) @@ -595,4 +629,22 @@ public abstract class Job { } return jobs; } + + @Override + public boolean equals(Object obj) { + if (obj != null && obj instanceof Job) { + Job other = (Job) obj; + return other.getClass().equals(this.getClass()) + && Objects.equals(other.getEntity(), this.getEntity()) + && other.getFailureSeverity().equals(this.getFailureSeverity()); + } + return false; + } + + @Override + public int hashCode() { + return getClass().hashCode() + + (getEntity() == null ? 0 : getEntity().hashCode()) + + getFailureSeverity().hashCode(); + } } diff --git a/src/main/java/org/caosdb/server/jobs/JobConfig.java b/src/main/java/org/caosdb/server/jobs/JobConfig.java index 40917b36714ee95ef491a266580068a06f51703a..b2bb0d4a1b13a98dd68893c9f0cad7bc8192edba 100644 --- a/src/main/java/org/caosdb/server/jobs/JobConfig.java +++ b/src/main/java/org/caosdb/server/jobs/JobConfig.java @@ -1,8 +1,8 @@ /* * This file is a part of the CaosDB Project. * - * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com> - * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2021,2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2021,2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -141,8 +141,8 @@ public class JobConfig { "Could not parse the job rules. Lines of five comma-separated values expected"); } try { - final Integer domain = Integer.parseInt(row[0]); - final Integer entity = Integer.parseInt(row[1]); + final String domain = row[0]; + final String entity = row[1]; final String transaction = row[2]; final String job = row[3]; final JobFailureSeverity severity = JobFailureSeverity.valueOf(row[4]); @@ -158,7 +158,7 @@ public class JobConfig { } /** - * Factory method for the instanciation and configuration of jobs for a specific entity. + * Factory method for the instantiation and configuration of jobs for a specific entity. * * @param domain the domain of the rule * @param entity the entity of the rule (this might be a particular datatype or a role, like @@ -184,9 +184,13 @@ public class JobConfig { final List<Job> result = new LinkedList<>(); jobRules.forEach( config -> { - result.add( - Job.getJob( - (String) config[0], (JobFailureSeverity) config[1], target, transaction)); + Job j = + Job.getJob((String) config[0], (JobFailureSeverity) config[1], target, transaction); + if (j == null) { + throw new NullPointerException("Could not load job: " + config[0]); + } else { + result.add(j); + } }); return result; } diff --git a/src/main/java/org/caosdb/server/jobs/LazyEntityResolver.java b/src/main/java/org/caosdb/server/jobs/LazyEntityResolver.java new file mode 100644 index 0000000000000000000000000000000000000000..7088ce7d35903daf34a0e060241aa22d749988c0 --- /dev/null +++ b/src/main/java/org/caosdb/server/jobs/LazyEntityResolver.java @@ -0,0 +1,101 @@ +/* + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> + * + * 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/>. + */ +package org.caosdb.server.jobs; + +import org.caosdb.server.database.backend.transaction.RetrieveParents; +import org.caosdb.server.database.backend.transaction.RetrieveProperties; +import org.caosdb.server.entity.EntityInterface; +import org.caosdb.server.entity.container.ParentContainer; +import org.caosdb.server.entity.container.PropertyContainer; +import org.caosdb.server.entity.wrapper.EntityWrapper; +import org.caosdb.server.transaction.Transaction; + +/** + * Implementation of {@link EntityInterface} which retrieves the parents and properties only when + * requested via getProperties and getParents. + * + * <p>This is particularly useful for jobs sharing the same entity object between jobs which need + * different parts of it while reducing the logic of checking whether the entity is "only" sparse, + * or has been retrieved fully and so on. + * + * <p>However, when the entities parents or properties are being retrieved after the transaction's + * access has been released, an {@link IllegalStateException} will be thrown. + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ +public class LazyEntityResolver extends EntityWrapper { + + private Transaction<?> transaction; + private boolean propertiesResolved = false; + private boolean parentsResolved = false; + + /** + * Constructor. + * + * <p>Initialize this with a sparse entity. + */ + public LazyEntityResolver(EntityInterface entity, Transaction<?> transaction) { + super(entity); + this.transaction = transaction; + } + + public void resolveParents() { + if (parentsResolved) return; + parentsResolved = true; + transaction.execute(new RetrieveParents(getWrapped()), transaction.getAccess()); + } + + private void resolveProperties() { + if (propertiesResolved) return; + propertiesResolved = true; + transaction.execute(new RetrieveProperties(getWrapped()), transaction.getAccess()); + } + + public void resolveAll() { + resolveParents(); + resolveProperties(); + // release the transaction for the garbage collector + this.transaction = null; + } + + @Override + public ParentContainer getParents() { + resolveParents(); + return super.getParents(); + } + + @Override + public boolean hasParents() { + resolveParents(); + return super.hasParents(); + } + + @Override + public PropertyContainer getProperties() { + resolveProperties(); + return super.getProperties(); + } + + @Override + public boolean hasProperties() { + resolveProperties(); + return super.hasProperties(); + } +} diff --git a/src/main/java/org/caosdb/server/jobs/Schedule.java b/src/main/java/org/caosdb/server/jobs/Schedule.java index 57474da515418745ed0457962596dfce233bff48..1b7ac8f4b41118803e7d5a540be6a1a3ba57cfec 100644 --- a/src/main/java/org/caosdb/server/jobs/Schedule.java +++ b/src/main/java/org/caosdb/server/jobs/Schedule.java @@ -3,8 +3,8 @@ * * Copyright (C) 2018 Research Group Biomedical Physics, * Max-Planck-Institute for Dynamics and Self-Organization Göttingen - * Copyright (C) 2020-2021 IndiScale GmbH <info@indiscale.com> - * Copyright (C) 2020-2021 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2020-2021,2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2020-2021,2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -35,6 +35,8 @@ import org.caosdb.server.entity.EntityInterface; * <p>The Schedule class orders jobs by {@link TransactionStage} and also assures that jobs are * skipped when appropriate and to prevent that jobs run more than once (because sometimes they * trigger each other). + * + * @author Timm Fitschen <t.fitschen@indiscale.com> */ public class Schedule { @@ -44,20 +46,26 @@ public class Schedule { public List<ScheduledJob> addAll(final Collection<Job> jobs) { final List<ScheduledJob> result = new ArrayList<ScheduledJob>(jobs.size()); for (final Job j : jobs) { - result.add(add(j)); + ScheduledJob scheduledJob = add(j); + if (scheduledJob != null) { + result.add(scheduledJob); + } } return result; } public ScheduledJob add(final Job j) { - final ScheduledJob ret = new ScheduledJob(j); - List<ScheduledJob> jobs = jobLists.get(ret.getTransactionStage().ordinal()); + final ScheduledJob scheduled = new ScheduledJob(j); + List<ScheduledJob> jobs = jobLists.get(scheduled.getTransactionStage().ordinal()); if (jobs == null) { jobs = new CopyOnWriteArrayList<ScheduledJob>(); - jobLists.put(ret.getTransactionStage().ordinal(), jobs); + jobLists.put(scheduled.getTransactionStage().ordinal(), jobs); + } + if (!jobs.contains(scheduled)) { + jobs.add(scheduled); + return scheduled; } - jobs.add(ret); - return ret; + return null; } /** Run all Jobs from the specified {@link TransactionStage}. */ @@ -71,7 +79,7 @@ public class Schedule { } public void runJob(final ScheduledJob scheduledJob) { - if (scheduledJob.skip()) { + if (scheduledJob == null || scheduledJob.skip()) { return; } final ScheduledJob parent = this.running; diff --git a/src/main/java/org/caosdb/server/jobs/ScheduledJob.java b/src/main/java/org/caosdb/server/jobs/ScheduledJob.java index 2122b34de5f0b60db4c2b6d4c1c2358d3df02851..1754f19f5fdab5b32e19fcba7758481f0dba8948 100644 --- a/src/main/java/org/caosdb/server/jobs/ScheduledJob.java +++ b/src/main/java/org/caosdb/server/jobs/ScheduledJob.java @@ -1,8 +1,8 @@ /* * This file is a part of the CaosDB Project. * - * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com> - * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2020,2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2020,2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -28,7 +28,7 @@ import org.caosdb.server.entity.Message; * <p>It is mainly a means to have simplified interface for the Scheduler which also measures the * execution time of the job "from outside". * - * @author Timm Fitschen (t.fitschen@indiscale.com) + * @author Timm Fitschen <t.fitschen@indiscale.com> */ public class ScheduledJob { @@ -102,4 +102,18 @@ public class ScheduledJob { public Job getJob() { return job; } + + @Override + public boolean equals(Object obj) { + if (obj instanceof ScheduledJob) { + ScheduledJob other = (ScheduledJob) obj; + return this.job.equals(other.job); + } + return false; + } + + @Override + public int hashCode() { + return job.hashCode(); + } } diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckChildDependencyExistent.java b/src/main/java/org/caosdb/server/jobs/core/CheckChildDependencyExistent.java deleted file mode 100644 index d60db5a3ea2f58caefe240894920318e01efc8a6..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/jobs/core/CheckChildDependencyExistent.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * ** 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 - */ -package org.caosdb.server.jobs.core; - -import java.util.List; -import java.util.Objects; -import org.caosdb.server.database.backend.transaction.GetChildren; -import org.caosdb.server.entity.EntityID; -import org.caosdb.server.entity.EntityInterface; -import org.caosdb.server.jobs.EntityJob; -import org.caosdb.server.utils.EntityStatus; -import org.caosdb.server.utils.ServerMessages; - -/** - * Check whether any children of this entity do exist. There must not be any children left when an - * entity is to be deleted. If all children are to be deleted, too, the test passes. - * - * @author tf - */ -public class CheckChildDependencyExistent extends EntityJob { - - @Override - public final void run() { - if (getEntity().getDomain() == null - || Objects.equals(getEntity().getDomain(), EntityID.DEFAULT_DOMAIN)) { - - final List<EntityID> children = execute(new GetChildren(getEntity().getId())).getList(); - - // loop: - for (final EntityID id : children) { - final EntityInterface foreign = getEntityById(id); - if (foreign == null) { - // if the child is not in the container, the test fails. - getEntity().addError(ServerMessages.REQUIRED_BY_PERSISTENT_ENTITY); - getEntity().addInfo("Required by entity " + id + "."); - getEntity().setEntityStatus(EntityStatus.UNQUALIFIED); - } - - // // loop through all entities in the current container - // for (final EntityInterface e : getContainer()) { - // - // // if e is a child - // if (e.getId().equals(id)) { - // if (e.getEntityStatus() == EntityStatus.UNQUALIFIED) { - // getEntity().addError(ServerMessages.REQUIRED_BY_UNQUALIFIED); - // getEntity().addInfo("Required by entity " + id + "."); - // getEntity().setEntityStatus(EntityStatus.UNQUALIFIED); - // } else { - // // if the children are among the entities in the - // // container which is to be deleted, the test passes - // e.acceptObserver(new Observer() { - // @Override - // public boolean notifyObserver(final String evt, - // final Observable o) { - // if (evt == Entity.ENTITY_STATUS_CHANGED_EVENT && o == e) { - // if (e.getEntityStatus() == EntityStatus.UNQUALIFIED) { - // getEntity().addError( - // ServerMessages.REQUIRED_BY_UNQUALIFIED); - // getEntity().addInfo("Required by entity " + id + "."); - // getEntity().setEntityStatus(EntityStatus.UNQUALIFIED); - // return false; - // } - // } - // return true; - // } - // }); - // } - // continue loop; - // } - // } - - // // if the child is not in the container, the test fails. - // getEntity().addError(ServerMessages.REQUIRED_BY_PERSISTENT_ENTITY); - // getEntity().addInfo("Required by entity " + id + "."); - // getEntity().setEntityStatus(EntityStatus.UNQUALIFIED); - } - } - } -} diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckDatatypePresent.java b/src/main/java/org/caosdb/server/jobs/core/CheckDatatypePresent.java index 40e01d2636509774555f1514c75bb0bf16870539..b3a06ca09d7870da843a2f3ad295e019ab683c12 100644 --- a/src/main/java/org/caosdb/server/jobs/core/CheckDatatypePresent.java +++ b/src/main/java/org/caosdb/server/jobs/core/CheckDatatypePresent.java @@ -1,9 +1,10 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,8 +18,6 @@ * * 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 */ package org.caosdb.server.jobs.core; @@ -41,10 +40,11 @@ import org.caosdb.server.utils.EntityStatus; import org.caosdb.server.utils.ServerMessages; /** - * Check whether the entity has a data type. Assign the data type of the abstract property if - * necessary + * Check whether the entity has a data type. + * + * <p>Assign the data type of the abstract property if necessary. * - * @author tf + * @author Timm Fitschen <t.fitschen@indiscale.com> */ public final class CheckDatatypePresent extends EntityJob { @@ -115,44 +115,15 @@ public final class CheckDatatypePresent extends EntityJob { } private void checkReference2(final ReferenceDatatype2 datatype) throws Message { + EntityInterface datatypeEntity = resolve(datatype.getId(), datatype.getName(), (String) null); - if (datatype.getId() == null) { - // try and get from container... - final EntityInterface datatypeEntity = getEntityByName(datatype.getName()); - - // if the container carried a corresponding entity - if (datatypeEntity != null) { - assertAllowedToUse(datatypeEntity); - - // ... we set it as the datatypevalue - datatype.setEntity(datatypeEntity); - } else { - - // else try and get from database. - final EntityInterface validDatatypeEntity = - retrieveValidSparseEntityByName(datatype.getName()); - assertAllowedToUse(validDatatypeEntity); - datatype.setId(validDatatypeEntity.getId()); - } - } else if (datatype.getId().isTemporary()) { - final EntityInterface datatypeEntity = getEntityById(datatype.getId()); - - // if the container carried a corresponding entity - if (datatypeEntity != null) { - assertAllowedToUse(datatypeEntity); - // ... we set it as the datatype - datatype.setEntity(datatypeEntity); - } else { - - throw ServerMessages.UNKNOWN_DATATYPE; - } - } else { - - final EntityInterface validDatatypeEntity = - retrieveValidSparseEntityById(datatype.getId(), null); - assertAllowedToUse(validDatatypeEntity); - datatype.setEntity(validDatatypeEntity); + if (datatypeEntity == null) { + throw ServerMessages.UNKNOWN_DATATYPE; } + + // do the actual checking + assertAllowedToUse(datatypeEntity); + datatype.setEntity(datatypeEntity); } private void assertAllowedToUse(final EntityInterface datatype) { @@ -160,62 +131,39 @@ public final class CheckDatatypePresent extends EntityJob { } private void checkIfOverride() throws Message { - if (getEntity().hasId() && !getEntity().getId().isTemporary()) { - // get data type from database - final EntityInterface foreign = retrieveValidSparseEntityById(getEntity().getId(), null); - - if (foreign.hasDatatype() && !foreign.getDatatype().equals(getEntity().getDatatype())) { - // is override! - getEntity().setDatatypeOverride(true); - } - } else { - // get data type from container - EntityInterface abstractProperty = null; - if (getEntity().hasId()) { - abstractProperty = getEntityById(getEntity().getId()); - } else if (getEntity().hasName()) { - abstractProperty = getEntityByName(getEntity().getName()); - } - if (abstractProperty != null && abstractProperty.hasDatatype()) { - if (!getEntity().getDatatype().equals(abstractProperty.getDatatype())) { - // is override! - getEntity().setDatatypeOverride(true); - } - } + EntityInterface abstractProperty = resolve(getEntity()); + if (abstractProperty != null + && abstractProperty.hasDatatype() + && !abstractProperty.getDatatype().equals(getEntity().getDatatype())) { + // is override! + getEntity().setDatatypeOverride(true); } } + /** + * If this is a record type property or a concrete property, assign the data type of the + * corresponding abstract property. + */ private void inheritDatatypeFromAbstractEntity() throws Message { - // if this is a record type property or a concrete property, assign - // the data type of the corresponding abstract property. - if (getEntity().hasId() && !getEntity().getId().isTemporary()) { - // get from data base - final EntityInterface foreign = retrieveValidSparseEntityById(getEntity().getId(), null); - inheritDatatypeFromForeignEntity(foreign); - } else if (getEntity().hasId() && getEntity().getId().isTemporary()) { - // get from container - final EntityInterface foreign = getEntityById(getEntity().getId()); - inheritDatatypeFromForeignEntity(foreign); - } - } - - private void inheritDatatypeFromForeignEntity(final EntityInterface foreign) { - if (foreign != null && foreign.hasDatatype()) { - getEntity().setDatatype(foreign.getDatatype()); - } else if (foreign != null && foreign != getEntity() && foreign.getRole() == Role.RecordType) { - getEntity().setDatatype(ReferenceDatatype2.datatypeFactory(foreign.getId())); + EntityInterface abstractProperty = resolve(getEntity()); + if (abstractProperty != null && abstractProperty.hasDatatype()) { + getEntity().setDatatype(abstractProperty.getDatatype()); + } else if (abstractProperty != null + && abstractProperty != getEntity() + && abstractProperty.getRole() == Role.RecordType) { + getEntity().setDatatype(ReferenceDatatype2.datatypeFactory(abstractProperty.getId())); } } private void resolveId(final EntityInterface entity) { if (!entity.hasId() && entity.hasName()) { try { - entity.setId(retrieveValidIDByName(entity.getName())); - if (entity.getEntityStatus() != EntityStatus.UNQUALIFIED) { - entity.setEntityStatus(EntityStatus.VALID); + EntityInterface resolved = resolve(entity); + if (resolved == null) { + entity.addError(ServerMessages.ENTITY_DOES_NOT_EXIST); + } else { + entity.setId(resolved.getId()); } - } catch (final EntityDoesNotExistException exc) { - entity.addError(ServerMessages.ENTITY_DOES_NOT_EXIST); } catch (final EntityWasNotUniqueException exc) { entity.addError(ServerMessages.ENTITY_NAME_DUPLICATES); } @@ -227,14 +175,12 @@ public final class CheckDatatypePresent extends EntityJob { AbstractDatatype datatype = null; for (final EntityInterface parent : getEntity().getParents()) { - EntityInterface parentEntity = null; - if (!parent.getId().isTemporary()) { - parentEntity = retrieveValidSparseEntityById(parent.getId(), null); - } else { - parentEntity = getEntityById(parent.getId()); + EntityInterface parentEntity = resolve(parent); + if (!parentEntity.hasDatatype() && parentEntity.getEntityStatus() == EntityStatus.QUALIFIED) { runJobFromSchedule(parentEntity, CheckDatatypePresent.class); } - if (parentEntity.hasDatatype()) { + if (parentEntity.hasDatatype() + && parentEntity.getEntityStatus() != EntityStatus.UNQUALIFIED) { if (datatype != null && !parentEntity.getDatatype().equals(datatype)) { getEntity().addError(ServerMessages.DATATYPE_INHERITANCE_AMBIGUOUS); return; diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckReferenceDependencyExistent.java b/src/main/java/org/caosdb/server/jobs/core/CheckDependenciesBeforeDeletion.java similarity index 53% rename from src/main/java/org/caosdb/server/jobs/core/CheckReferenceDependencyExistent.java rename to src/main/java/org/caosdb/server/jobs/core/CheckDependenciesBeforeDeletion.java index 2edbc5a270db93312c73b8c960c615a161e8907b..ecbbf4af7fb4a81605d61a3d251b88c4f1d0917a 100644 --- a/src/main/java/org/caosdb/server/jobs/core/CheckReferenceDependencyExistent.java +++ b/src/main/java/org/caosdb/server/jobs/core/CheckDependenciesBeforeDeletion.java @@ -1,9 +1,10 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,8 +18,6 @@ * * 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 */ package org.caosdb.server.jobs.core; @@ -32,13 +31,13 @@ import org.caosdb.server.utils.EntityStatus; import org.caosdb.server.utils.ServerMessages; /** - * Check whether an entity is referenced by other entities. In the event that someone wants to - * delete this entity, this entity must not be referenced by any other entity which is not deleted - * along with this entity. + * Check whether an entity is referenced by other entities, is being used as a data type by other + * entities or has direct children which are not to be deleted along with this entity and add an + * appropriate error if so. * - * @author tf + * @author Timm Fitschen <t.fitschen@indiscale.com> */ -public class CheckReferenceDependencyExistent extends EntityJob { +public class CheckDependenciesBeforeDeletion extends EntityJob { @Override public final void run() { if (getEntity().getDomain() == null @@ -59,42 +58,6 @@ public class CheckReferenceDependencyExistent extends EntityJob { getEntity().addInfo("Required by entity " + id + "."); getEntity().setEntityStatus(EntityStatus.UNQUALIFIED); } - // for (final EntityInterface e : getContainer()) { - // if (e.getId().equals(id)) { - // // entity is in the container. - // if (e.getEntityStatus() == EntityStatus.UNQUALIFIED) { - // getEntity().addError(ServerMessages.REQUIRED_BY_UNQUALIFIED); - // getEntity().addInfo("Required by entity " + id + "."); - // getEntity().setEntityStatus(EntityStatus.UNQUALIFIED); - // } else { - // e.acceptObserver(new Observer() { - // @Override - // public boolean notifyObserver(final String evt, - // final Observable o) { - // if (e == o && evt == Entity.ENTITY_STATUS_CHANGED_EVENT) { - // if (e.getEntityStatus() == EntityStatus.UNQUALIFIED) { - // getEntity().addError( - // ServerMessages.REQUIRED_BY_UNQUALIFIED); - // getEntity().addInfo("Required by entity " + id + "."); - // getEntity().setEntityStatus(EntityStatus.UNQUALIFIED); - // return false; - // } - // } - // return true; - // } - // }); - // } - // - // continue loop; - // } - // } - - // // dependent entity is not in the container. That is, it will - // // not be deleted. Therefore, this entity cannot be deleted - // // either. - // getEntity().addError(ServerMessages.REQUIRED_BY_PERSISTENT_ENTITY); - // getEntity().addInfo("Required by entity " + id + "."); - // getEntity().setEntityStatus(EntityStatus.UNQUALIFIED); } } } diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckParOblPropPresent.java b/src/main/java/org/caosdb/server/jobs/core/CheckParOblPropPresent.java index e6ba9da430e6218e5c40fba968c309ed94152bf6..eba79831468b1f6799507ca0866a64e594997caa 100644 --- a/src/main/java/org/caosdb/server/jobs/core/CheckParOblPropPresent.java +++ b/src/main/java/org/caosdb/server/jobs/core/CheckParOblPropPresent.java @@ -93,7 +93,7 @@ public class CheckParOblPropPresent extends EntityJob { // loop over all properties of the entity for (final EntityInterface entityProperty : getEntity().getProperties()) { - if (isSubType(entityProperty, parentProperty)) { + if (isSubType(resolve(entityProperty), parentProperty)) { // continue outer loop means that we // found an entityProperty that // implements the obligatory diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckParValid.java b/src/main/java/org/caosdb/server/jobs/core/CheckParValid.java index 8895ae9adb4d12da2f7789a8798ca0fb9d183c05..fd235c21319d7b573db1cd42b23d9184aa7e56e6 100644 --- a/src/main/java/org/caosdb/server/jobs/core/CheckParValid.java +++ b/src/main/java/org/caosdb/server/jobs/core/CheckParValid.java @@ -1,9 +1,10 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,8 +18,6 @@ * * 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 */ package org.caosdb.server.jobs.core; @@ -42,7 +41,7 @@ import org.caosdb.server.utils.ServerMessages; /** * Check whether all parents of an entity are valid/qualified. * - * @author tf + * @author Timm Fitschen <t.fitschen@indiscale.com> */ @JobAnnotation(stage = TransactionStage.PRE_CHECK) public class CheckParValid extends EntityJob { @@ -60,73 +59,21 @@ public class CheckParValid extends EntityJob { if (!parent.hasId() && !parent.hasName()) { // The parent has neither an id nor a name. // Therefore it cannot be identified. - - throw ServerMessages.ENTITY_HAS_NO_NAME_OR_ID; - } - - if (parent.hasId()) { - // check parent by id - if (!parent.getId().isTemporary()) { - // id isn't temporary, i.e., parent is already in the database and - // can be retrieved by id - final EntityInterface foreign = retrieveValidSparseEntityById(parent.getId(), null); - // check permissions for this - // parentforeign.acceptObserver(o) - assertAllowedToUse(foreign); - parent.setAffiliation(getAffiliation(getEntity().getRole(), foreign.getRole())); - continue; - } else { - // id < 0 (parent is to be stored along with - // this entity) - - // get entity with corresponding (negative) id - // from container - final EntityInterface foreign = getEntityById(parent.getId()); - - // if the container carried a corresponding - // entity - if (foreign != null) { - assertAllowedToUse(foreign); - - parent.setAffiliation(getAffiliation(getEntity().getRole(), foreign.getRole())); - - // ... we can set it as the parent - parent.linkIdToEntity(foreign); - continue; - } - } + parent.addError(ServerMessages.ENTITY_HAS_NO_NAME_OR_ID); } - - // parent doesn't have an id. - if (parent.hasName()) { - if (getEntityByName(parent.getName()) != null) { - // get the parent entity from the container by its - // name - final EntityInterface foreign = getEntityByName(parent.getName()); - - assertAllowedToUse(foreign); - parent.setAffiliation(getAffiliation(getEntity().getRole(), foreign.getRole())); - parent.linkIdToEntity(foreign); - continue; - } else { - // check parent by name (parent is expected to be - // valid). This only works if the name is unique. - final EntityInterface foreign = retrieveValidSparseEntityByName(parent.getName()); - assertAllowedToUse(foreign); - parent.setAffiliation(getAffiliation(getEntity().getRole(), foreign.getRole())); - parent.setId(foreign.getId()); - continue; - } + try { + EntityInterface foreign = resolve(parent.getId(), parent.getName(), (String) null); + assertAllowedToUse(foreign); + parent.linkIdToEntity(foreign); + parent.setAffiliation(getAffiliation(getEntity().getRole(), foreign.getRole())); + } catch (EntityDoesNotExistException e) { + parent.addError(ServerMessages.ENTITY_DOES_NOT_EXIST); } - - parent.addError(ServerMessages.ENTITY_DOES_NOT_EXIST); } catch (final Message m) { parent.addError(m); } catch (AuthorizationException e) { parent.addError(ServerMessages.AUTHORIZATION_ERROR); parent.addInfo(e.getMessage()); - } catch (final EntityDoesNotExistException exc) { - parent.addError(ServerMessages.ENTITY_DOES_NOT_EXIST); } catch (final EntityWasNotUniqueException exc) { parent.addError(ServerMessages.ENTITY_NAME_DUPLICATES); } diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckPropValid.java b/src/main/java/org/caosdb/server/jobs/core/CheckPropValid.java index 23a92340f5befbfbe7ded2fe95d3244b5ff78594..bfd2bc3019d1d4ae6204ebbe5b5975932603ed76 100644 --- a/src/main/java/org/caosdb/server/jobs/core/CheckPropValid.java +++ b/src/main/java/org/caosdb/server/jobs/core/CheckPropValid.java @@ -1,9 +1,10 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,8 +18,6 @@ * * 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 */ package org.caosdb.server.jobs.core; @@ -41,7 +40,7 @@ import org.caosdb.server.utils.ServerMessages; /** * Check whether all properties of an entity are valid or qualified. * - * @author tf + * @author Timm Fitschen <t.fitschen@indiscale.com> */ @JobAnnotation(stage = TransactionStage.PRE_CHECK) public class CheckPropValid extends EntityJob { @@ -54,75 +53,21 @@ public class CheckPropValid extends EntityJob { if (property.getEntityStatus() == EntityStatus.QUALIFIED) { // this property is to be tested. - // does this property have an id at all? - if (property.hasId()) { - if (!property.getId().isTemporary()) { - - final EntityInterface abstractProperty = - retrieveValidSparseEntityById(property.getId(), null); - - assertAllowedToUse(abstractProperty); - - deriveOverrideStatus(property, abstractProperty); - continue; - } else { - // task here: find the corresponding abstract - // property (or rarely any other entity) in this - // container which has the same (negative) id. - - // fetch the abstractProperty from the container. - final EntityInterface abstractProperty = getEntityById(property.getId()); - - if (abstractProperty != null) { - assertAllowedToUse(abstractProperty); - - // link the id of the property to the id of - // the abstractProperty means. This has the - // effect that the property will have a - // valid id as soon as the abstractProperty - // has been inserted. - property.linkIdToEntity(abstractProperty); - deriveOverrideStatus(property, abstractProperty); - continue; - } else if (!property.hasName()) { - // an abstractProperty with this (negative) id - // had not been found in this container. - throw ENTITY_DOES_NOT_EXIST; - } - } - } - - if (property.hasName()) { - - // try and get it from the container - EntityInterface foreign = getEntityByName(property.getName()); - if (foreign != null) { - assertAllowedToUse(foreign); - - // link the id of the property to the id of - // the abstractProperty means. This has the - // effect that the property will have a - // valid id as soon as the abstractProperty - // has been inserted. - property.linkIdToEntity(foreign); - deriveOverrideStatus(property, foreign); - } else { - foreign = retrieveValidSparseEntityByName(property.getName()); - - assertAllowedToUse(foreign); - - property.setId(foreign.getId()); - - deriveOverrideStatus(property, foreign); - } - } - if (!property.hasName() && !property.hasId()) { // The property has neither an id nor a name. // Thus it cannot be identified. throw ServerMessages.ENTITY_HAS_NO_NAME_OR_ID; } + EntityInterface abstractProperty = + resolve(property.getId(), property.getName(), (String) null); + if (abstractProperty == null) { + property.addError(ENTITY_DOES_NOT_EXIST); + continue; + } + assertAllowedToUse(abstractProperty); + deriveOverrideStatus(property, abstractProperty); + property.linkIdToEntity(abstractProperty); } } catch (final Message m) { property.addError(m); diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckRefidIsaParRefid.java b/src/main/java/org/caosdb/server/jobs/core/CheckRefidIsaParRefid.java index 0b8ba45b1cae19812641ed733be7aacc2fb78575..7b0a9977680c378db98dca4d3e7ee31dba06e840 100644 --- a/src/main/java/org/caosdb/server/jobs/core/CheckRefidIsaParRefid.java +++ b/src/main/java/org/caosdb/server/jobs/core/CheckRefidIsaParRefid.java @@ -1,9 +1,10 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,8 +18,6 @@ * * 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 */ package org.caosdb.server.jobs.core; @@ -41,10 +40,11 @@ import org.caosdb.server.utils.Observer; import org.caosdb.server.utils.ServerMessages; /** - * Check if the referenced entity is in the scope of the data type. E.g. if the data type is - * 'Person' the referenced entity is to be a child of 'Person'. + * Check if the referenced entity is in the scope of the data type. + * + * <p>E.g. if the data type is 'Person' the referenced entity is to be a child of 'Person'. * - * @author tf + * @author Timm Fitschen <t.fitschen@indiscale.com> */ public class CheckRefidIsaParRefid extends EntityJob implements Observer { @@ -82,24 +82,8 @@ public class CheckRefidIsaParRefid extends EntityJob implements Observer { for (final IndexedSingleValue v : vals) { if (v != null && v.getWrapped() != null) { final ReferenceValue rv = (ReferenceValue) v.getWrapped(); - if (rv.getEntity() != null - && rv.getEntity().hasRole() - && rv.getEntity().getRole() == Role.File) { - } else if (rv.getId() != null - && rv.getId().isTemporary() - && getEntityById(rv.getId()) != null - && getEntityById(rv.getId()).getRole() == Role.File) { - } else if (rv.getId() == null - && rv.getName() != null - && getEntityByName(rv.getName()) != null - && getEntityByName(rv.getName()).getRole() == Role.File) { - } else if (rv.getId() != null - && !rv.getId().isTemporary() - && retrieveValidSparseEntityById(rv.getId(), rv.getVersion()).getRole() - == Role.File) { - } else if (rv.getName() != null - && retrieveValidSparseEntityByName(rv.getName()).getRole() == Role.File) { - } else { + EntityInterface referencedEntity = resolve(rv.getId(), rv.getName(), rv.getVersion()); + if (referencedEntity.getRole() != Role.File) { throw ServerMessages.REFERENCE_IS_NOT_ALLOWED_BY_DATATYPE; } } diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckRefidValid.java b/src/main/java/org/caosdb/server/jobs/core/CheckRefidValid.java index 12b545b8e6fd608e20851d37f34e809acfcd1544..86e95542aaa0e6f373521149d9f7376f09d3972c 100644 --- a/src/main/java/org/caosdb/server/jobs/core/CheckRefidValid.java +++ b/src/main/java/org/caosdb/server/jobs/core/CheckRefidValid.java @@ -1,11 +1,10 @@ /* - * ** 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 - * Copyright (C) 2020-2021 IndiScale GmbH <info@indiscale.com> - * Copyright (C) 2020-2021 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2020-2021,2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2020-2021,2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -19,8 +18,6 @@ * * 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 */ package org.caosdb.server.jobs.core; @@ -43,7 +40,7 @@ import org.caosdb.server.utils.ServerMessages; /** * Check whether a reference property is pointing to a valid entity. * - * @author tf + * @author Timm Fitschen <t.fitschen@indiscale.com> */ public class CheckRefidValid extends EntityJob implements Observer { @Override @@ -83,61 +80,12 @@ public class CheckRefidValid extends EntityJob implements Observer { } private void checkRefValue(final ReferenceValue ref) throws Message { - if (ref.getId() != null) { - if (!ref.getId().isTemporary()) { - final EntityInterface referencedValidEntity = - retrieveValidSparseEntityById(ref.getId(), ref.getVersion()); - assertAllowedToUse(referencedValidEntity); - - // link the entity as versioned entity iff the reference specified a version - ref.setEntity(referencedValidEntity, ref.getVersion() != null); - - } else { - - // is the referenced entity yet linked to this refid - // property? - if (ref.getEntity() == null) { - - // link the entity with the corresponding - // negative id to this reference object - final EntityInterface referencedEntity = getEntityById(ref.getId()); - if (referencedEntity != null) { - assertAllowedToUse(referencedEntity); - - // link the entity as versioned entity iff the reference specified a version - ref.setEntity(referencedEntity, ref.getVersion() != null); - } else { - throw ServerMessages.REFERENCED_ENTITY_DOES_NOT_EXIST; - } - } - ref.getEntity().acceptObserver(this); - checkRefEntity(ref); - } - } else if (ref.getName() != null) { - // is the referenced entity yet linked to this - // refid property? - if (ref.getEntity() == null) { - // the entity is in this container? - final EntityInterface referencedEntity = getEntityByName(ref.getName()); - - if (referencedEntity != null) { - assertAllowedToUse(referencedEntity); - - // link the entity as versioned entity iff the reference specified a version - ref.setEntity(referencedEntity, ref.getVersion() != null); - if (checkRefEntity(ref)) { - ref.getEntity().acceptObserver(this); - } - } else { - final EntityInterface referencedValidEntity = - retrieveValidSparseEntityByName(ref.getName()); - assertAllowedToUse(referencedValidEntity); - - // link the entity as versioned entity iff the reference specified a version - ref.setEntity(referencedValidEntity, ref.getVersion() != null); - } - } + EntityInterface referencedEntity = resolve(ref.getId(), ref.getName(), ref.getVersion()); + if (referencedEntity == null) { + throw ServerMessages.REFERENCED_ENTITY_DOES_NOT_EXIST; } + assertAllowedToUse(referencedEntity); + ref.setEntity(referencedEntity, ref.getVersion() != null); } private void assertAllowedToUse(final EntityInterface referencedEntity) { diff --git a/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java b/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java index a4c50f8893935d7bbd6091b970b32a8811a08d93..01b0ecb2274a85635dee3fe88f041e50178936fe 100644 --- a/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java +++ b/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java @@ -1,8 +1,8 @@ /* * This file is a part of the CaosDB Project. * - * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com> - * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2020,2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2020,2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -53,6 +53,7 @@ import org.caosdb.server.entity.container.TransactionContainer; import org.caosdb.server.entity.wrapper.Property; import org.caosdb.server.entity.xml.ToElementable; import org.caosdb.server.jobs.EntityJob; +import org.caosdb.server.jobs.LazyEntityResolver; import org.caosdb.server.permissions.EntityACI; import org.caosdb.server.permissions.EntityACL; import org.caosdb.server.query.Query; @@ -572,7 +573,7 @@ public abstract class EntityStateJob extends EntityJob { final String key = "transition" + refid.toString(); EntityInterface transition = getCached(key); if (transition == null) { - transition = retrieveValidEntity(refid); + transition = resolve(refid); putCache(key, transition); } result.add(new Transition(transition)); @@ -659,7 +660,7 @@ public abstract class EntityStateJob extends EntityJob { private EntityInterface retrieveStateEntity(final String stateName) throws Message { try { - return retrieveValidEntity(retrieveValidIDByName(stateName)); + return resolve(null, stateName, (String) null); } catch (final EntityDoesNotExistException e) { throw STATE_NOT_IN_STATE_MODEL; } @@ -667,7 +668,7 @@ public abstract class EntityStateJob extends EntityJob { private EntityInterface retrieveStateModelEntity(final String stateModel) throws Message { try { - return retrieveValidEntity(retrieveValidIDByName(stateModel)); + return resolve(null, stateModel, (String) null); } catch (final EntityDoesNotExistException e) { throw STATE_MODEL_NOT_FOUND; } @@ -676,7 +677,7 @@ public abstract class EntityStateJob extends EntityJob { protected EntityInterface getStateRecordType() throws Message { EntityInterface stateRecordType = getCached(STATE_RECORD_TYPE_NAME); if (stateRecordType == null) { - stateRecordType = retrieveValidSparseEntityByName(STATE_RECORD_TYPE_NAME); + stateRecordType = resolve(null, STATE_RECORD_TYPE_NAME, (String) null); putCache(STATE_RECORD_TYPE_NAME, stateRecordType); } return stateRecordType; @@ -797,7 +798,7 @@ public abstract class EntityStateJob extends EntityJob { EntityInterface stateEntity = getCached(key); if (stateEntity == null) { - stateEntity = retrieveValidEntity(refid.getId()); + stateEntity = resolve(refid.getId()); putCache(key, stateEntity); } @@ -834,7 +835,7 @@ public abstract class EntityStateJob extends EntityJob { getUser(), c); query.execute(getTransaction().getAccess()); - result = retrieveValidEntity(c.get(0).getId()); + result = resolve(c.get(0).getId()); putCache(key, result); return result; } @@ -852,6 +853,11 @@ public abstract class EntityStateJob extends EntityJob { if (value instanceof DeleteEntity) { throw new RuntimeException("Delete entity in cache. This is an implementation error."); } + if (value instanceof LazyEntityResolver) { + // resolve immediately, otherwise the access might be released when + // the object is being resolved. + ((LazyEntityResolver) value).resolveAll(); + } id_in_cache.add(value.getId()); cache.put(key, value); } diff --git a/src/main/java/org/caosdb/server/jobs/core/GenerateEntityId.java b/src/main/java/org/caosdb/server/jobs/core/GenerateEntityId.java new file mode 100644 index 0000000000000000000000000000000000000000..e3b21d9bdf67ad7a1ebe23ea1b77816e72b12de3 --- /dev/null +++ b/src/main/java/org/caosdb/server/jobs/core/GenerateEntityId.java @@ -0,0 +1,50 @@ +/* + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> + * + * 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/>. + * + */ +package org.caosdb.server.jobs.core; + +import org.caosdb.server.entity.EntityInterface; +import org.caosdb.server.entity.InsertEntity; +import org.caosdb.server.entity.Message; +import org.caosdb.server.jobs.EntityJob; +import org.caosdb.server.jobs.JobAnnotation; +import org.caosdb.server.jobs.TransactionStage; +import org.caosdb.server.transaction.WriteTransactionInterface; +import org.caosdb.server.utils.EntityStatus; + +/** + * Generates entity ids for newly inserted entities. + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ +@JobAnnotation(stage = TransactionStage.PRE_TRANSACTION) +public class GenerateEntityId extends EntityJob { + + @Override + protected void run() throws Message { + EntityInterface entity = getEntity(); + if (entity instanceof InsertEntity + && getTransaction() instanceof WriteTransactionInterface + && entity.getEntityStatus() == EntityStatus.QUALIFIED) { + String id = ((WriteTransactionInterface) getTransaction()).generateId(); + entity.setId(id); + } + } +} diff --git a/src/main/java/org/caosdb/server/jobs/core/Inheritance.java b/src/main/java/org/caosdb/server/jobs/core/Inheritance.java index a1bd800c7ca40b28d7b9ff27dd8f828ed112891f..70aea11281774230e90e586cad3e897426df8e83 100644 --- a/src/main/java/org/caosdb/server/jobs/core/Inheritance.java +++ b/src/main/java/org/caosdb/server/jobs/core/Inheritance.java @@ -1,11 +1,10 @@ /* - * ** 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 - * Copyright (C) 2019-2021 IndiScale GmbH <info@indiscale.com> - * Copyright (C) 2019-2021 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2019-2021,2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2019-2021,2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -19,20 +18,16 @@ * * 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 */ package org.caosdb.server.jobs.core; import java.util.ArrayList; import java.util.List; import org.caosdb.api.entity.v1.MessageCode; -import org.caosdb.server.database.backend.transaction.RetrieveFullEntityTransaction; import org.caosdb.server.entity.EntityInterface; import org.caosdb.server.entity.InsertEntity; import org.caosdb.server.entity.Message; import org.caosdb.server.entity.Message.MessageType; -import org.caosdb.server.entity.RetrieveEntity; import org.caosdb.server.entity.StatementStatus; import org.caosdb.server.entity.UpdateEntity; import org.caosdb.server.entity.wrapper.Property; @@ -88,12 +83,7 @@ public class Inheritance extends EntityJob { runJobFromSchedule(getEntity(), CheckParValid.class); // try to get the parent entity from the current transaction container - EntityInterface foreign = getEntityByName(parent.getName()); - if (foreign == null) { - // was not in container -> retrieve from database. - execute(new RetrieveFullEntityTransaction(parent)); - foreign = parent; - } + EntityInterface foreign = resolve(parent); collectInheritedProperties(transfer, foreign, inheritance); } catch (final IllegalArgumentException e) { @@ -136,26 +126,24 @@ public class Inheritance extends EntityJob { break propertyLoop; } - EntityInterface validProperty = new RetrieveEntity(property.getId()); + EntityInterface abstractProperty = null; if (getEntity().hasParents()) { outer: for (EntityInterface par : getEntity().getParents()) { - if (!par.hasProperties()) { - par = resolve(par); - } + par = resolve(par); for (final EntityInterface prop : par.getProperties()) { - if (validProperty.hasId() && validProperty.getId().equals(prop.getId())) { - validProperty = prop; + if ((property.hasId() && property.getId().equals(prop.getId())) + || property.hasName() && property.getName().equals(prop.getName())) { + abstractProperty = prop; break outer; } } } - } else { - execute(new RetrieveFullEntityTransaction(validProperty)); } - if (validProperty.getEntityStatus() == EntityStatus.VALID) { - collectInheritedProperties(transfer, validProperty, inheritance); + if (abstractProperty == null) { + abstractProperty = resolve(property); } + collectInheritedProperties(transfer, abstractProperty, inheritance); } catch (final IllegalArgumentException e) { property.addWarning(ILLEGAL_INHERITANCE_MODE); break propertyLoop; diff --git a/src/main/java/org/caosdb/server/jobs/core/NoCache.java b/src/main/java/org/caosdb/server/jobs/core/NoCache.java index e408330ee2639d1c5d7dc5f145848e292daa9b3b..78b54f6c7ecf97669889097241a9ee5f595a9416 100644 --- a/src/main/java/org/caosdb/server/jobs/core/NoCache.java +++ b/src/main/java/org/caosdb/server/jobs/core/NoCache.java @@ -1,9 +1,10 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,8 +18,6 @@ * * 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 */ package org.caosdb.server.jobs.core; @@ -26,6 +25,13 @@ import org.caosdb.server.jobs.FlagJob; import org.caosdb.server.jobs.JobAnnotation; import org.caosdb.server.jobs.TransactionStage; +/** + * Turn off caching, just for the current transaction. + * + * <p>If the caching is disabled globally this has no effect. + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ @JobAnnotation( flag = "cache", defaultValue = "true", @@ -37,6 +43,6 @@ public class NoCache extends FlagJob { @Override protected void job(final String value) { - getTransaction().getAccess().setUseCache(Boolean.valueOf(value)); + getTransaction().setUseCache(Boolean.valueOf(value)); } } diff --git a/src/main/java/org/caosdb/server/jobs/core/ProcessNameProperties.java b/src/main/java/org/caosdb/server/jobs/core/ProcessNameProperties.java index 7196f04edc99cf1269603e820975a96703639965..ecac050455b385d2ecf27bb4ff5d39d46c941508 100644 --- a/src/main/java/org/caosdb/server/jobs/core/ProcessNameProperties.java +++ b/src/main/java/org/caosdb/server/jobs/core/ProcessNameProperties.java @@ -1,9 +1,10 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,17 +18,16 @@ * * 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 */ package org.caosdb.server.jobs.core; import static org.caosdb.server.entity.MagicTypes.NAME; -import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; +import java.util.LinkedList; import java.util.Set; +import org.caosdb.server.database.exceptions.EntityDoesNotExistException; import org.caosdb.server.datatype.AbstractDatatype; import org.caosdb.server.entity.EntityID; import org.caosdb.server.entity.EntityInterface; @@ -41,9 +41,13 @@ import org.caosdb.server.utils.EntityStatus; import org.caosdb.server.utils.ServerMessages; /** - * To be called after CheckPropValid and Inheritance. + * Checks if a property is a subproperty of "name" and flags the entity accordingly. + * + * <p>This enables a different behavior in the search. * - * @author tf + * <p>To be called after CheckPropValid and Inheritance. + * + * @author Timm Fitschen <t.fitschen@indiscale.com> */ @JobAnnotation(stage = TransactionStage.PRE_TRANSACTION) public class ProcessNameProperties extends EntityJob { @@ -133,21 +137,22 @@ public class ProcessNameProperties extends EntityJob { } private Collection<Object> getNearestValidParents(final Property prop) { - if (prop.hasId() && !prop.getId().isTemporary()) { - final ArrayList<Object> ret = new ArrayList<Object>(); - if (prop.hasParents()) { - for (final Parent par : prop.getParents()) { + if (prop.hasParents()) { + final Collection<Object> ret = new LinkedList<Object>(); + for (final Parent par : prop.getParents()) { + if (par.hasId()) { ret.add(par.getId()); } - } else { - ret.add(prop.getId()); } return ret; } else if (prop.hasId()) { - // property is new -> get valid parents of any depth - final EntityInterface propertyEntity = getEntityById(prop.getId()); - if (propertyEntity != null) { - return getNearestValidParents(propertyEntity); + try { + EntityInterface propertyEntity = resolve(prop.getId()); + if (propertyEntity != null) { + return getNearestValidParents(propertyEntity); + } + } catch (EntityDoesNotExistException e) { + // return null } return null; } else { diff --git a/src/main/java/org/caosdb/server/jobs/core/ResolveNames.java b/src/main/java/org/caosdb/server/jobs/core/ResolveNames.java index 5ab722c7176e89485e2bd40dfd515a77bc09e38b..e353375baaaa11c59d7e67c7e39c8cd5a8eb38ee 100644 --- a/src/main/java/org/caosdb/server/jobs/core/ResolveNames.java +++ b/src/main/java/org/caosdb/server/jobs/core/ResolveNames.java @@ -1,9 +1,10 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,14 +18,13 @@ * * 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 */ package org.caosdb.server.jobs.core; import java.util.ArrayList; import java.util.List; import org.caosdb.server.database.backend.transaction.GetIDByName; +import org.caosdb.server.database.backend.transaction.RetrieveSparseEntity; import org.caosdb.server.database.exceptions.EntityDoesNotExistException; import org.caosdb.server.entity.Entity; import org.caosdb.server.entity.EntityID; @@ -35,6 +35,18 @@ import org.caosdb.server.jobs.ContainerJob; import org.caosdb.server.utils.EntityStatus; import org.caosdb.server.utils.ServerMessages; +/** + * Resolve the names to ids for all entities in the current retrieval. + * + * <p>If an entity has a name which looks like an id: Check whether that is an existing id first. + * + * <p>Otherwise, search via the name. + * + * <p>It there are multiple matching entities, add all of them to the current container. The clients + * may handle the ambiguity. + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ public class ResolveNames extends ContainerJob { @Override @@ -45,6 +57,20 @@ public class ResolveNames extends ContainerJob { public void resolve(final TransactionContainer container) { final ArrayList<Entity> add = new ArrayList<Entity>(); for (final EntityInterface e : container) { + if (!e.hasId() && e.hasName() && getTransaction().matchIdPattern(e.getName())) { + try { + EntityInterface valid = + execute( + new RetrieveSparseEntity( + new EntityID(e.getName()), + e.hasVersion() ? e.getVersion().getId() : null)) + .getEntity(); + e.setId(valid.getId()); + } catch (final EntityDoesNotExistException exc) { + // Definitely not an existing id. + } + } + if (e.hasName() && !e.hasId()) { try { final List<EntityID> c = execute(new GetIDByName(e.getName(), false)).getList(); diff --git a/src/main/java/org/caosdb/server/jobs/core/UniqueName.java b/src/main/java/org/caosdb/server/jobs/core/UniqueName.java index 86e6c37e35ec91db647e14e30896abb6b884555b..079afdd9a922fa5758e5f4eb6bec80538338db21 100644 --- a/src/main/java/org/caosdb/server/jobs/core/UniqueName.java +++ b/src/main/java/org/caosdb/server/jobs/core/UniqueName.java @@ -40,7 +40,7 @@ public class UniqueName extends FlagJob { // check against data base try { - final EntityID foreign = retrieveValidIDByName(entity.getName()); + final EntityID foreign = retrieveValidIDByName(entity.getName(), null); if (entity.hasId() && !foreign.equals(entity.getId())) { throw new EntityWasNotUniqueException(); } diff --git a/src/main/java/org/caosdb/server/query/CQLLexer.g4 b/src/main/java/org/caosdb/server/query/CQLLexer.g4 index be6dcfa99d2f9c48cba8a0f7fccb7f97b277e01d..f21caf26f0f19d0433b1cdef2d5975d472791e99 100644 --- a/src/main/java/org/caosdb/server/query/CQLLexer.g4 +++ b/src/main/java/org/caosdb/server/query/CQLLexer.g4 @@ -433,7 +433,7 @@ WHITE_SPACE_f: /** */ WHITE_SPACE: - [ \t\n\r]+ + WHITE_SPACE_f ; /** */ @@ -529,9 +529,9 @@ COLON: /** Matches signed and unsigned numbers with decimal points, also numbers in scientific notation. */ DECIMAL_NUMBER: - ((HYPHEN_f | PLUS ) WHITE_SPACE_f?)? - ( NUM_f? DOT NUM_f WHITE_SPACE_f? E_NOTATION_f? - | NUM_f WHITE_SPACE_f? E_NOTATION_f + ((HYPHEN_f | PLUS ) WHITE_SPACE_f?)? + ( NUM_f? DOT NUM_f (WHITE_SPACE_f? E_NOTATION_f)? + | NUM_f (WHITE_SPACE_f? E_NOTATION_f)? ) ; diff --git a/src/main/java/org/caosdb/server/query/IDFilter.java b/src/main/java/org/caosdb/server/query/IDFilter.java index ce01f5f242f914c6f6f812f0875332f7ca004c07..8011139796abff42c0e8109621c849d3e6328137 100644 --- a/src/main/java/org/caosdb/server/query/IDFilter.java +++ b/src/main/java/org/caosdb/server/query/IDFilter.java @@ -1,9 +1,10 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,8 +18,6 @@ * * 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 */ package org.caosdb.server.query; @@ -32,24 +31,21 @@ import java.sql.SQLException; import org.caosdb.server.query.Query.QueryException; import org.jdom2.Element; +/** + * Applies an ID filter to the query's current result set. + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ public class IDFilter implements EntityFilterInterface { private final String operator; private final String value; private final String aggregate; - private final Integer vInt; public IDFilter(final String o, final String v, final String a) { this.operator = o; this.value = v; this.aggregate = a; - Integer i; - try { - i = Integer.valueOf(v); - } catch (final NumberFormatException e) { - i = null; - } - this.vInt = i; } @Override @@ -79,7 +75,7 @@ public class IDFilter implements EntityFilterInterface { if (getValue() == null) { callIDFilter.setNull(4, INTEGER); } else { - callIDFilter.setInt(4, this.vInt); + callIDFilter.setString(4, this.getValue()); } // aggregate diff --git a/src/main/java/org/caosdb/server/query/POV.java b/src/main/java/org/caosdb/server/query/POV.java index ebc35a16b1495accf8cd4a114dd688c2b2529c1b..6ee5f9d0122aa417c357e71ef6d6ea0516007215 100644 --- a/src/main/java/org/caosdb/server/query/POV.java +++ b/src/main/java/org/caosdb/server/query/POV.java @@ -1,9 +1,10 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,8 +18,6 @@ * * 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 */ package org.caosdb.server.query; @@ -50,6 +49,11 @@ import org.jdom2.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * Applies a POV (Property-operator-value) filter to the query's current result set. + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ public class POV implements EntityFilterInterface { public static final Pattern NUMBER_PATTERN = Pattern.compile( @@ -383,8 +387,8 @@ public class POV implements EntityFilterInterface { query.getConnection().prepareCall("call initPOVRefidsTable(?,?)")) { // stmt = this.connection.prepareCall("call initPOV(?,?,?,?,?)"); // initPOVRefidsTable(in vInt INT, in vText VARCHAR(255)) - if (this.vInt != null && this.vInt > 0) { - stmt.setInt(1, this.vInt); + if (this.value != null) { + stmt.setString(1, this.value); } else { stmt.setNull(1, Types.INTEGER); } diff --git a/src/main/java/org/caosdb/server/query/Query.java b/src/main/java/org/caosdb/server/query/Query.java index abaca4365e54c21ee2ba8cf8bf7727fe746c7277..53cd7bf04bccf03eab9422e591750849bfa48728 100644 --- a/src/main/java/org/caosdb/server/query/Query.java +++ b/src/main/java/org/caosdb/server/query/Query.java @@ -3,8 +3,8 @@ * * Copyright (C) 2018 Research Group Biomedical Physics, * Max-Planck-Institute for Dynamics and Self-Organization Göttingen - * Copyright (C) 2019-2021 IndiScale GmbH <info@indiscale.com> - * Copyright (C) 2019-2021 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2019-2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2019-2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -73,7 +73,6 @@ import org.caosdb.server.query.CQLParser.CqContext; import org.caosdb.server.query.CQLParsingErrorListener.ParsingError; import org.caosdb.server.transaction.TransactionInterface; import org.caosdb.server.transaction.WriteTransaction; -import org.caosdb.server.utils.ResultSetIterator; import org.jdom2.Element; import org.slf4j.Logger; @@ -218,22 +217,22 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac /** A data class for storing triplets of (Entity ID, version hash, ACL string) */ public static class IdVersionAclTriplet { - public IdVersionAclTriplet(final Integer id, final String version, final String acl) { + public IdVersionAclTriplet(final String id, final String version, final String acl) { this.id = id; this.version = version; this.acl = acl; } - public Integer id; + public String id; public String version; public String acl; @Override public String toString() { if (version == null) { - return Integer.toString(id); + return id; } - return Integer.toString(id) + "@" + version; + return id + "@" + version; } @Override @@ -256,6 +255,14 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac public int hashCode() { return toString().hashCode(); } + + public boolean isInternal() { + try { + return Integer.parseInt(id) < 100; + } catch (NumberFormatException e) { + return false; + } + } } private final boolean filterEntitiesWithoutRetrievePermisions = @@ -280,6 +287,7 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac private final ArrayList<ToElementable> messages = new ArrayList<>(); private Access access; private boolean versioned = false; + /** * This key-value cache stores lists of of (id, version hash, acl string) triplets. Those values * are the result sets of queries. The keys are created such that they are different for different @@ -294,8 +302,10 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac */ private static ICacheAccess<String, Serializable> cache = Cache.getCache("HIGH_LEVEL_QUERY_CACHE"); + /** Cached=true means that the results of this query have actually been pulled from the cache. */ private boolean cached = false; + /** * Cachable=false means that the results of this query cannot be used for the cache, because the * query evaluation contains complex permission checking which could only be cached on a per-user @@ -327,7 +337,8 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac this.container = container; this.query = query; this.user = user; - }; + } + ; /** * Fill the initially empty resultSet with all entities which match the name, or the id. Then @@ -412,12 +423,19 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac final Map<EntityID, String> queryTemplates = getQueryTemplates(query, resultSet); final PreparedStatement removeQTStmt = - query.getConnection().prepareStatement("DELETE FROM `" + resultSet + "` WHERE id=?"); + query + .getConnection() + .prepareStatement( + "DELETE FROM `" + + resultSet + + "` WHERE EXISTS (SELECT 1 FROM entity_ids AS eids WHERE eids.id=? AND eids.internal_id=`" + + resultSet + + "`.id)"); // Run thru all QTs found... for (final Entry<EntityID, String> q : queryTemplates.entrySet()) { // ... remove the QT from resultSet... - removeQTStmt.setInt(1, q.getKey().toInteger()); + removeQTStmt.setString(1, q.getKey().toString()); removeQTStmt.execute(); // ... check for RETRIEVE:ENTITY permission... @@ -468,12 +486,12 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac query .getConnection() .prepareCall( - "SELECT q.id, q.definition FROM query_template_def AS q INNER JOIN `" + "SELECT (SELECT eids.id FROM entity_ids AS eids WHERE eids.internal_id = q.id) as id, q.definition FROM query_template_def AS q INNER JOIN `" + resultSet + "` AS r ON (r.id=q.id);"); rs = stmt.executeQuery(); while (rs.next()) { - ret.put(new EntityID(rs.getInt("id")), rs.getString("definition")); + ret.put(new EntityID(rs.getString("id")), rs.getString("definition")); } return ret; } finally { @@ -625,7 +643,7 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac if (this.entity != null) { return sourceStrategy(initQuery(versioned)); } else if (this.role == Role.ENTITY && this.filter == null) { - return "entities"; + return "entity_ids"; } else { return targetStrategy(initQuery(versioned)); } @@ -644,29 +662,27 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac */ private String generateSelectStatementForResultSet( final String resultSetTableName, final boolean versioned) { - // TODO remove the entities.role part when - // https://gitlab.indiscale.com/caosdb/src/caosdb-server/-/issues/245 is resolved - if (resultSetTableName.equals("entities")) { + if (resultSetTableName.equals("entity_ids")) { final String baseStatement = - "SELECT entities.id, entity_acl.acl FROM entities INNER JOIN entity_acl ON entity_acl.id=entities.acl WHERE entities.role!='DOMAIN'"; + "SELECT entities.id AS internal_id, (SELECT id FROM entity_ids WHERE internal_id = entities.id) as id, entity_acl.acl FROM entities INNER JOIN entity_acl ON entity_acl.id=entities.acl"; if (!versioned) { return baseStatement + ";"; } // if versioned, the statement is surrounded with another SELECT and JOIN return ("SELECT id, acl, version FROM (" + baseStatement - + ") AS tmp JOIN entity_version ON entity_version.entity_id=tmp.id;"); + + ") AS tmp JOIN entity_version ON entity_version.entity_id=tmp.internal_id;"); } else { if (!versioned) { - return (" SELECT tmp.id, entity_acl.acl FROM " + return (" SELECT (SELECT id FROM entity_ids WHERE internal_id = tmp.id) AS id, entity_acl.acl FROM " + " (SELECT results.id AS id, entities.acl AS acl_id FROM `" + resultSetTableName - + "` AS results JOIN entities ON results.id=entities.id WHERE entities.role!='DOMAIN') AS tmp" + + "` AS results JOIN entities ON results.id=entities.id) AS tmp" + " JOIN entity_acl ON entity_acl.id=tmp.acl_id") + ";"; } // if versioned, the statement is surrounded with another SELECT and JOIN - return ("SELECT tmp2.id, acl, version FROM( SELECT tmp.id, entity_acl.acl, tmp._iversion AS _iversion FROM " + return ("SELECT (SELECT id FROM entity_ids WHERE internal_id = tmp2.id) AS id, acl, version FROM (SELECT tmp.id, entity_acl.acl, tmp._iversion AS _iversion FROM " + " (SELECT results.id AS id, entities.acl AS acl_id, results._iversion AS _iversion FROM `" + resultSetTableName + "` AS results JOIN entities ON results.id=entities.id) AS tmp" @@ -692,10 +708,14 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac finishResultSet = finish.executeQuery(); final List<IdVersionAclTriplet> rs = new LinkedList<>(); while (finishResultSet.next()) { + final String id = finishResultSet.getString("id"); + if (finishResultSet.wasNull()) { + continue; + } final String version = versioned ? finishResultSet.getString("version") : null; final String acl = finishResultSet.getString("acl"); - rs.add(new IdVersionAclTriplet(finishResultSet.getInt("id"), version, acl)); + rs.add(new IdVersionAclTriplet(id, version, acl)); } return rs; } catch (final SQLException e) { @@ -724,6 +744,7 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac this.resultSet = getCached(getCacheKey(false)); } } + /** Store the content of `resultSet` member in the high level query cache. */ private void storeResultInCache() { // Decide whether user specific cache needs to be used or not @@ -735,6 +756,7 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac cacheItem(getCacheKey(false), this.resultSet); } } + /** Fill entities from `resultSet` into `container`. */ private void fillContainerWithResult() { if (this.container != null && (this.type == Type.FIND || this.type == Type.SELECT)) { @@ -786,6 +808,7 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac } return this; } + /** Remove all cached queries from the cache. */ public static void clearCache() { cacheETag = UUID.randomUUID().toString(); @@ -798,7 +821,7 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac final List<IdVersionAclTriplet> filtered = new ArrayList<>(); for (final IdVersionAclTriplet triplet : resultSet) { - if (triplet.id >= 100) { + if (!triplet.isInternal()) { filtered.add(triplet); } } @@ -908,11 +931,11 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac + "left join entity_acl on entity_n_acl.acl=entity_acl.id;"); final ResultSet entitiesRS = stmt.executeQuery(query); final ResultSetIterator entitiesWithACL = new ResultSetIterator(entitiesRS); - final List<Integer> toBeDeleted = collectIdsWithoutPermission(entitiesWithACL); + final List<String> toBeDeleted = collectIdsWithoutPermission(entitiesWithACL); try (final PreparedStatement pstmt = this.getConnection().prepareStatement("DELETE FROM `" + tabname + "` WHERE id = ?")) { - for (final Integer id : toBeDeleted) { - pstmt.setInt(1, id); + for (final String id : toBeDeleted) { + pstmt.setString(1, id); pstmt.execute(); } } @@ -931,8 +954,8 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac */ private List<IdVersionAclTriplet> filterEntitiesWithoutRetrievePermission( final List<IdVersionAclTriplet> resultSet) { - final List<Integer> toBeDeleted = collectIdsWithoutPermission(resultSet.iterator()); - final List<IdVersionAclTriplet> filtered = new ArrayList<>(); + final List<String> toBeDeleted = collectIdsWithoutPermission(resultSet.iterator()); + final List<IdVersionAclTriplet> filtered = new LinkedList<>(); for (final IdVersionAclTriplet triplet : resultSet) { if (-1 == toBeDeleted.indexOf(triplet.id)) { filtered.add(triplet); @@ -940,6 +963,7 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac } return filtered; } + /** * Creates a list with IDs of those entities that do not have sufficient RETRIEVE permission * @@ -947,9 +971,9 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac * triplets. * @return compiled list */ - private List<Integer> collectIdsWithoutPermission(Iterator<IdVersionAclTriplet> entityIterator) { - final HashMap<String, Boolean> acl_cache = new HashMap<String, Boolean>(); - final List<Integer> toBeDeleted = new LinkedList<Integer>(); + private List<String> collectIdsWithoutPermission(Iterator<IdVersionAclTriplet> entityIterator) { + final Map<String, Boolean> acl_cache = new HashMap<>(); + final List<String> toBeDeleted = new LinkedList<>(); while (entityIterator.hasNext()) { final long t1 = System.currentTimeMillis(); @@ -999,7 +1023,9 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac return this.access; } - /** @return the number of entities in the resultset. Might be updated by the filters. */ + /** + * @return the number of entities in the resultset. Might be updated by the filters. + */ @Override public int getTargetSetCount() { return this.targetSetCount; diff --git a/src/main/java/org/caosdb/server/utils/ResultSetIterator.java b/src/main/java/org/caosdb/server/query/ResultSetIterator.java similarity index 95% rename from src/main/java/org/caosdb/server/utils/ResultSetIterator.java rename to src/main/java/org/caosdb/server/query/ResultSetIterator.java index 71429f1a704299555ab1d86be0695e3104be828d..a26c1f41867e96722dd4ba6451344b689d0299e0 100644 --- a/src/main/java/org/caosdb/server/utils/ResultSetIterator.java +++ b/src/main/java/org/caosdb/server/query/ResultSetIterator.java @@ -1,4 +1,4 @@ -package org.caosdb.server.utils; +package org.caosdb.server.query; import static org.caosdb.server.database.backend.implementation.MySQL.DatabaseUtils.bytes2UTF8; @@ -35,7 +35,8 @@ public class ResultSetIterator implements Iterator<IdVersionAclTriplet> { this.cursorHasMoved = true; } return this.currentIsValid; - }; + } + ; public IdVersionAclTriplet next() { if (!this.cursorHasMoved) { @@ -50,7 +51,7 @@ public class ResultSetIterator implements Iterator<IdVersionAclTriplet> { throw new NoSuchElementException(); } try { - final Integer id = resultSet.getInt("id"); + final String id = resultSet.getString("id"); final String acl_str = bytes2UTF8(resultSet.getBytes("ACL")); return new IdVersionAclTriplet(id, "", acl_str); } catch (SQLException e) { diff --git a/src/main/java/org/caosdb/server/resource/AbstractCaosDBServerResource.java b/src/main/java/org/caosdb/server/resource/AbstractCaosDBServerResource.java index 44ebfa22d08c975031f5bf5bfca325d6b706af24..de56fff3207cd554d23badcc63dbd8e5130e6e81 100644 --- a/src/main/java/org/caosdb/server/resource/AbstractCaosDBServerResource.java +++ b/src/main/java/org/caosdb/server/resource/AbstractCaosDBServerResource.java @@ -243,8 +243,12 @@ public abstract class AbstractCaosDBServerResource extends ServerResource { } protected abstract Representation httpGetInChildClass() - throws ConnectionException, IOException, SQLException, CaosDBException, - NoSuchAlgorithmException, Exception; + throws ConnectionException, + IOException, + SQLException, + CaosDBException, + NoSuchAlgorithmException, + Exception; @Post public Representation httpPost(final Representation entity) { @@ -277,8 +281,12 @@ public abstract class AbstractCaosDBServerResource extends ServerResource { } protected Representation httpDeleteInChildClass() - throws ConnectionException, SQLException, CaosDBException, IOException, - NoSuchAlgorithmException, Exception { + throws ConnectionException, + SQLException, + CaosDBException, + IOException, + NoSuchAlgorithmException, + Exception { getResponse().setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); return null; } @@ -293,8 +301,14 @@ public abstract class AbstractCaosDBServerResource extends ServerResource { } protected Representation httpPostInChildClass(final Representation entity) - throws ConnectionException, SQLException, CaosDBException, IOException, - NoSuchAlgorithmException, xmlNotWellFormedException, JDOMException, Exception { + throws ConnectionException, + SQLException, + CaosDBException, + IOException, + NoSuchAlgorithmException, + xmlNotWellFormedException, + JDOMException, + Exception { getResponse().setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); return null; } diff --git a/src/main/java/org/caosdb/server/resource/AuthenticationResource.java b/src/main/java/org/caosdb/server/resource/AuthenticationResource.java index ed60d7906b1cda003ed2a84f0e22200049d96c25..4f53ba12e958a33d694357372afba274fcfe4151 100644 --- a/src/main/java/org/caosdb/server/resource/AuthenticationResource.java +++ b/src/main/java/org/caosdb/server/resource/AuthenticationResource.java @@ -52,8 +52,12 @@ public class AuthenticationResource extends AbstractCaosDBServerResource { /** Returns single "Login" element. XSLT will create a form. */ @Override protected Representation httpGetInChildClass() - throws ConnectionException, IOException, SQLException, CaosDBException, - NoSuchAlgorithmException, Exception { + throws ConnectionException, + IOException, + SQLException, + CaosDBException, + NoSuchAlgorithmException, + Exception { final Document doc = new Document(); final Element rootElem = generateRootElement(); doc.setRootElement(rootElem); @@ -66,8 +70,12 @@ public class AuthenticationResource extends AbstractCaosDBServerResource { @Override protected Representation httpDeleteInChildClass() - throws ConnectionException, SQLException, CaosDBException, IOException, - NoSuchAlgorithmException, Exception { + throws ConnectionException, + SQLException, + CaosDBException, + IOException, + NoSuchAlgorithmException, + Exception { final Subject user = SecurityUtils.getSubject(); if (user.isAuthenticated()) { user.logout(); diff --git a/src/main/java/org/caosdb/server/resource/DefaultResource.java b/src/main/java/org/caosdb/server/resource/DefaultResource.java index 488cd667c8832e4b90277e07144c65cbb7e4036d..39fbf47dcd4cde1d96642a0112b58c3285dec2c9 100644 --- a/src/main/java/org/caosdb/server/resource/DefaultResource.java +++ b/src/main/java/org/caosdb/server/resource/DefaultResource.java @@ -43,8 +43,12 @@ public class DefaultResource extends AbstractCaosDBServerResource { @Override protected Representation httpGetInChildClass() - throws ConnectionException, IOException, SQLException, CaosDBException, - NoSuchAlgorithmException, Exception { + throws ConnectionException, + IOException, + SQLException, + CaosDBException, + NoSuchAlgorithmException, + Exception { final Document doc = new org.jdom2.Document(); final Element root = generateRootElement(); if (this.responseBody != null) { diff --git a/src/main/java/org/caosdb/server/resource/LogoutResource.java b/src/main/java/org/caosdb/server/resource/LogoutResource.java index 1659f751a4710fbf99fc473b586cc3b180c8a62c..f17bd0c65119bc21183a0d7165a62688e60f2d25 100644 --- a/src/main/java/org/caosdb/server/resource/LogoutResource.java +++ b/src/main/java/org/caosdb/server/resource/LogoutResource.java @@ -33,8 +33,12 @@ public class LogoutResource extends AuthenticationResource { @Override protected Representation httpGetInChildClass() - throws ConnectionException, IOException, SQLException, CaosDBException, - NoSuchAlgorithmException, Exception { + throws ConnectionException, + IOException, + SQLException, + CaosDBException, + NoSuchAlgorithmException, + Exception { return super.httpDeleteInChildClass(); } } diff --git a/src/main/java/org/caosdb/server/resource/PermissionRulesResource.java b/src/main/java/org/caosdb/server/resource/PermissionRulesResource.java index 8d2aa1720aaefcdbaccee18c6a1626a912319b6a..afdcb0d60f077ebcfc2dfcc0e99093289a6d621f 100644 --- a/src/main/java/org/caosdb/server/resource/PermissionRulesResource.java +++ b/src/main/java/org/caosdb/server/resource/PermissionRulesResource.java @@ -44,8 +44,12 @@ public class PermissionRulesResource extends AbstractCaosDBServerResource { @Override protected Representation httpGetInChildClass() - throws ConnectionException, IOException, SQLException, CaosDBException, - NoSuchAlgorithmException, Exception { + throws ConnectionException, + IOException, + SQLException, + CaosDBException, + NoSuchAlgorithmException, + Exception { final String role = getRequestedItems()[0]; getUser().checkPermission(ACMPermissions.PERMISSION_RETRIEVE_ROLE_PERMISSIONS(role)); diff --git a/src/main/java/org/caosdb/server/resource/RolesResource.java b/src/main/java/org/caosdb/server/resource/RolesResource.java index ad1544da3a326f1c16e0b01238985d10e93b0581..73dee43e1f6b49d2c73cf44fe80510bb704d4f0d 100644 --- a/src/main/java/org/caosdb/server/resource/RolesResource.java +++ b/src/main/java/org/caosdb/server/resource/RolesResource.java @@ -46,8 +46,12 @@ public class RolesResource extends AbstractCaosDBServerResource { @Override protected Representation httpGetInChildClass() - throws ConnectionException, IOException, SQLException, CaosDBException, - NoSuchAlgorithmException, Exception { + throws ConnectionException, + IOException, + SQLException, + CaosDBException, + NoSuchAlgorithmException, + Exception { final Element root = generateRootElement(); final Document document = new Document(); @@ -74,8 +78,12 @@ public class RolesResource extends AbstractCaosDBServerResource { @Override protected Representation httpDeleteInChildClass() - throws ConnectionException, SQLException, CaosDBException, IOException, - NoSuchAlgorithmException, Exception { + throws ConnectionException, + SQLException, + CaosDBException, + IOException, + NoSuchAlgorithmException, + Exception { if (getRequestedItems().length > 0) { final String name = getRequestedItems()[0]; if (name != null) { @@ -97,8 +105,14 @@ public class RolesResource extends AbstractCaosDBServerResource { @Override protected Representation httpPostInChildClass(final Representation entity) - throws ConnectionException, SQLException, CaosDBException, IOException, - NoSuchAlgorithmException, xmlNotWellFormedException, JDOMException, Exception { + throws ConnectionException, + SQLException, + CaosDBException, + IOException, + NoSuchAlgorithmException, + xmlNotWellFormedException, + JDOMException, + Exception { String name = null; String description = null; diff --git a/src/main/java/org/caosdb/server/resource/ServerLogsResource.java b/src/main/java/org/caosdb/server/resource/ServerLogsResource.java index f304f10062209d62855a0bff21e0c8cad54e1962..acc1833dc5158d23e72f06f15cf1ddd037185043 100644 --- a/src/main/java/org/caosdb/server/resource/ServerLogsResource.java +++ b/src/main/java/org/caosdb/server/resource/ServerLogsResource.java @@ -41,8 +41,12 @@ public class ServerLogsResource extends AbstractCaosDBServerResource { @Override protected Representation httpGetInChildClass() - throws ConnectionException, IOException, SQLException, CaosDBException, - NoSuchAlgorithmException, Exception { + throws ConnectionException, + IOException, + SQLException, + CaosDBException, + NoSuchAlgorithmException, + Exception { Level level = null; String message = null; diff --git a/src/main/java/org/caosdb/server/resource/UserResource.java b/src/main/java/org/caosdb/server/resource/UserResource.java index de07331e9637549285d71ca2fd910916cd13316a..ea8e8a264542474cccf74d20cf070342cb43d717 100644 --- a/src/main/java/org/caosdb/server/resource/UserResource.java +++ b/src/main/java/org/caosdb/server/resource/UserResource.java @@ -1,9 +1,10 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,8 +18,6 @@ * * 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 */ package org.caosdb.server.resource; @@ -48,14 +47,18 @@ import org.restlet.representation.Representation; /** * This class handles requests for Users. * - * @author Timm Fitschen + * @author Timm Fitschen <t.fitschen@indiscale.com> */ public class UserResource extends AbstractCaosDBServerResource { @Override protected Representation httpGetInChildClass() - throws ConnectionException, IOException, SQLException, CaosDBException, - NoSuchAlgorithmException, Exception { + throws ConnectionException, + IOException, + SQLException, + CaosDBException, + NoSuchAlgorithmException, + Exception { final Document doc = new Document(); final Element rootElem = generateRootElement(); @@ -104,12 +107,12 @@ public class UserResource extends AbstractCaosDBServerResource { form.getFirstValue("status") != null ? UserStatus.valueOf(form.getFirstValue("status").toUpperCase()) : null; - Integer userEntity = null; + String userEntity = null; if (form.getFirst("entity") != null) { if (form.getFirstValue("entity").isEmpty()) { - userEntity = 0; + userEntity = ""; } else { - userEntity = Integer.parseInt(form.getFirstValue("entity")); + userEntity = form.getFirstValue("entity"); } } @@ -150,9 +153,9 @@ public class UserResource extends AbstractCaosDBServerResource { "status", CaosDBServer.getServerProperty(ServerProperties.KEY_NEW_USER_DEFAULT_ACTIVITY)) .toUpperCase()); - Integer userEntity = null; + String userEntity = null; if (form.getFirst("entity") != null) { - userEntity = Integer.parseInt(form.getFirstValue("entity")); + userEntity = form.getFirstValue("entity"); } final InsertUserTransaction t = @@ -177,8 +180,12 @@ public class UserResource extends AbstractCaosDBServerResource { @Override protected Representation httpDeleteInChildClass() - throws ConnectionException, SQLException, CaosDBException, IOException, - NoSuchAlgorithmException, Exception { + throws ConnectionException, + SQLException, + CaosDBException, + IOException, + NoSuchAlgorithmException, + Exception { final Document doc = new Document(); final Element rootElem = generateRootElement(); diff --git a/src/main/java/org/caosdb/server/resource/UserRolesResource.java b/src/main/java/org/caosdb/server/resource/UserRolesResource.java index 4eb7f6143038bb6e4b0c3fda4646e2730acfea04..72fc906a54b753166a4f5358dfda31ba0dff953e 100644 --- a/src/main/java/org/caosdb/server/resource/UserRolesResource.java +++ b/src/main/java/org/caosdb/server/resource/UserRolesResource.java @@ -45,8 +45,12 @@ public class UserRolesResource extends AbstractCaosDBServerResource { @Override protected Representation httpGetInChildClass() - throws ConnectionException, IOException, SQLException, CaosDBException, - NoSuchAlgorithmException, Exception { + throws ConnectionException, + IOException, + SQLException, + CaosDBException, + NoSuchAlgorithmException, + Exception { final String user = getRequestedItems()[0]; final String realm = (getRequestAttributes().get("realm") != null diff --git a/src/main/java/org/caosdb/server/resource/transaction/EntityResource.java b/src/main/java/org/caosdb/server/resource/transaction/EntityResource.java index 4cd7b2fcbd4ae9e9dd3d047555194172c4224d38..833043885ecb0843483417c97cc7569ae316e8ef 100644 --- a/src/main/java/org/caosdb/server/resource/transaction/EntityResource.java +++ b/src/main/java/org/caosdb/server/resource/transaction/EntityResource.java @@ -3,8 +3,8 @@ * * Copyright (C) 2018 Research Group Biomedical Physics, * Max-Planck-Institute for Dynamics and Self-Organization Göttingen - * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com> - * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2021,2023 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2021,2023 IndiScale GmbH <info@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -53,11 +53,16 @@ import org.restlet.representation.Representation; * * <p>The GET requests (Retrieval) is handled in the superclass {@link RetrieveEntityResource}. * - * @author Timm Fitschen (t.fitschen@indiscale.com) + * @author Timm Fitschen <t.fitschen@indiscale.com> */ public class EntityResource extends RetrieveEntityResource { - /** Handle entity deletions (DELETE requests). */ + /** + * Handle entity deletions (DELETE requests). + * + * <p>Note: The list of entity "specifier" is treated strictly as a list of entity ids. You cannot + * delete an entity by name. + */ @Override protected final Representation httpDeleteInChildClass() throws Exception { @@ -67,18 +72,14 @@ public class EntityResource extends RetrieveEntityResource { for (final String item : getRequestedItems()) { final String[] elem = item.split("@", 1); - Integer id = null; + String id = null; String version = null; - try { - id = Integer.parseInt(elem[0]); - } catch (final NumberFormatException e) { - // pass - } + id = elem[0]; if (elem.length > 1) { version = elem[1]; } - if (id != null && id > 0) { + if (id != null) { container.add(new DeleteEntity(new EntityID(id), version)); } } @@ -129,8 +130,12 @@ public class EntityResource extends RetrieveEntityResource { * requests which also contain file blobs. */ private Element parseMultipartEntity(final WritableContainer container) - throws FileUploadException, IOException, Message, xmlNotWellFormedException, - NoSuchAlgorithmException, CaosDBException { + throws FileUploadException, + IOException, + Message, + xmlNotWellFormedException, + NoSuchAlgorithmException, + CaosDBException { final DiskFileItemFactory factory = new DiskFileItemFactory(); factory.setSizeThreshold(1000240); final RestletFileUpload upload = new RestletFileUpload(factory); diff --git a/src/main/java/org/caosdb/server/resource/transaction/RetrieveEntityResource.java b/src/main/java/org/caosdb/server/resource/transaction/RetrieveEntityResource.java index 6527376d71ba31718394f887b7335615d80db5c6..d5feea9a0fe815004230e697ff0fa38b74fc72b8 100644 --- a/src/main/java/org/caosdb/server/resource/transaction/RetrieveEntityResource.java +++ b/src/main/java/org/caosdb/server/resource/transaction/RetrieveEntityResource.java @@ -3,8 +3,8 @@ * * Copyright (C) 2018 Research Group Biomedical Physics, * Max-Planck-Institute for Dynamics and Self-Organization Göttingen - * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com> - * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2021,2023 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2021,2023 IndiScale GmbH <info@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -26,8 +26,8 @@ import java.security.NoSuchAlgorithmException; import java.sql.SQLException; import org.caosdb.server.CaosDBException; import org.caosdb.server.database.backend.implementation.MySQL.ConnectionException; -import org.caosdb.server.entity.EntityID; import org.caosdb.server.entity.container.RetrieveContainer; +import org.caosdb.server.jobs.core.ResolveNames; import org.caosdb.server.resource.AbstractCaosDBServerResource; import org.caosdb.server.transaction.Retrieve; import org.jdom2.Document; @@ -38,44 +38,40 @@ import org.restlet.representation.Representation; * Handles GET requests for different subclasses which all have in common that they retrieve * Entities (plus other information in some cases). * - * @author Timm Fitschen (t.fitschen@indiscale.com) + * @author Timm Fitschen <t.fitschen@indiscale.com> */ public abstract class RetrieveEntityResource extends AbstractCaosDBServerResource { /** - * Parse the segment which specifies the entities which are to be retrieved + * Parse the segment which specifies the entities which are to be retrieved. * - * @param container + * <p>The segments are being treated as names here. The {@link ResolveNames} job is responsible + * for detecting whether the name is actually an id. */ protected void handleRetrieveContainer(final RetrieveContainer container) { for (final String item : getRequestedItems()) { final String[] elem = item.split("@", 2); - Integer id = null; - String name = null; + String id = null; String version = null; - try { - id = Integer.parseInt(elem[0]); - } catch (final NumberFormatException e) { - name = elem[0]; - } + id = elem[0]; if (elem.length > 1) { version = elem[1]; } - if (id != null) { - container.add(new EntityID(id), version); - } else { - container.add(name); - } + container.add(null, id, version); } } /** Handle the GET request. */ @Override protected final Representation httpGetInChildClass() - throws ConnectionException, IOException, SQLException, CaosDBException, - NoSuchAlgorithmException, Exception { + throws ConnectionException, + IOException, + SQLException, + CaosDBException, + NoSuchAlgorithmException, + Exception { final RetrieveContainer entityContainer = new RetrieveContainer(getUser(), getTimestamp(), getSRID(), getFlags()); diff --git a/src/main/java/org/caosdb/server/scripting/ServerSideScriptingCaller.java b/src/main/java/org/caosdb/server/scripting/ServerSideScriptingCaller.java index edb6b39a4e92466e5a3e5a489669d366bbe8ce37..9a710e25fad549e5d4c14bb623378a2e6aaff806 100644 --- a/src/main/java/org/caosdb/server/scripting/ServerSideScriptingCaller.java +++ b/src/main/java/org/caosdb/server/scripting/ServerSideScriptingCaller.java @@ -253,7 +253,9 @@ public class ServerSideScriptingCaller { if (pwd.exists()) FileUtils.forceDelete(pwd); } - /** @fixme Should be injected into environment instead. Will be changed in v0.4 of SSS-API */ + /** + * @fixme Should be injected into environment instead. Will be changed in v0.4 of SSS-API + */ String[] injectAuthToken(String[] commandLine) { String[] newCommandLine = new String[commandLine.length + 1]; newCommandLine[0] = commandLine[0]; diff --git a/src/main/java/org/caosdb/server/transaction/EntityTransactionInterface.java b/src/main/java/org/caosdb/server/transaction/EntityTransactionInterface.java new file mode 100644 index 0000000000000000000000000000000000000000..9d96c4e7499728ca11c348ac1875d94ece100a72 --- /dev/null +++ b/src/main/java/org/caosdb/server/transaction/EntityTransactionInterface.java @@ -0,0 +1,37 @@ +/* + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> + * + * 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/>. + */ +package org.caosdb.server.transaction; + +import org.caosdb.server.entity.EntityID; + +/** + * This interface is implemented by all transactions of entities (as opposed to transaction which + * only concern users or other kinds of transactions). + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ +public interface EntityTransactionInterface extends TransactionInterface { + + public boolean matchIdPattern(String id); + + public default boolean matchIdPattern(EntityID id) { + return id != null && matchIdPattern(id.toString()); + } +} diff --git a/src/main/java/org/caosdb/server/transaction/InsertUserTransaction.java b/src/main/java/org/caosdb/server/transaction/InsertUserTransaction.java index f3f4c7f5fd583ff0ff16d4fd1ce583394258f35b..3174dabf4e3d6a581f27aaf59cb8ffd866401bf5 100644 --- a/src/main/java/org/caosdb/server/transaction/InsertUserTransaction.java +++ b/src/main/java/org/caosdb/server/transaction/InsertUserTransaction.java @@ -50,7 +50,7 @@ public class InsertUserTransaction extends AccessControlTransaction { final String password, final String email, final UserStatus status, - final Integer entity) { + final String entity) { this(new ProtoUser(), password); this.user.realm = UserSources.getInternalRealm().getName(); this.user.name = username; diff --git a/src/main/java/org/caosdb/server/transaction/ListRolesTransaction.java b/src/main/java/org/caosdb/server/transaction/ListRolesTransaction.java index ed3cd3cc5a154e82946e76f9934ee8608fe6980c..f0b0db78eeff6c04ca466dbd6f65f8350fbab783 100644 --- a/src/main/java/org/caosdb/server/transaction/ListRolesTransaction.java +++ b/src/main/java/org/caosdb/server/transaction/ListRolesTransaction.java @@ -39,9 +39,7 @@ public class ListRolesTransaction extends AccessControlTransaction { protected void transaction() throws Exception { Subject currentUser = SecurityUtils.getSubject(); roles = - execute(new ListRoles(), getAccess()) - .getRoles() - .stream() + execute(new ListRoles(), getAccess()).getRoles().stream() .filter( role -> currentUser.isPermitted( diff --git a/src/main/java/org/caosdb/server/transaction/ListUsersTransaction.java b/src/main/java/org/caosdb/server/transaction/ListUsersTransaction.java index 2ade4e8595f159d1fc6996c1e04913f4195ecd97..fe9b9c172d2c6b4361790d30590650ae0233a2ad 100644 --- a/src/main/java/org/caosdb/server/transaction/ListUsersTransaction.java +++ b/src/main/java/org/caosdb/server/transaction/ListUsersTransaction.java @@ -37,9 +37,7 @@ public class ListUsersTransaction extends AccessControlTransaction { protected void transaction() throws Exception { Subject currentUser = SecurityUtils.getSubject(); users = - execute(new ListUsers(), getAccess()) - .getUsers() - .stream() + execute(new ListUsers(), getAccess()).getUsers().stream() .filter( user -> currentUser.isPermitted( diff --git a/src/main/java/org/caosdb/server/transaction/RetrieveACL.java b/src/main/java/org/caosdb/server/transaction/RetrieveACL.java index ac0a2896451a43ca10985bd38787ad4f847a05cc..b5a4a489c60269dbae906bba1942f8e9525eb710 100644 --- a/src/main/java/org/caosdb/server/transaction/RetrieveACL.java +++ b/src/main/java/org/caosdb/server/transaction/RetrieveACL.java @@ -40,7 +40,7 @@ public class RetrieveACL extends Transaction<TransactionContainer> { new TransactionContainer( SecurityUtils.getSubject(), System.currentTimeMillis(), UUID.randomUUID().toString())); for (String strId : idList) { - getContainer().add(new RetrieveEntity(new EntityID(Integer.parseInt(strId)))); + getContainer().add(new RetrieveEntity(new EntityID(strId))); } } diff --git a/src/main/java/org/caosdb/server/transaction/RetrieveUserTransaction.java b/src/main/java/org/caosdb/server/transaction/RetrieveUserTransaction.java index eb24fb24db4f1b6a50a279cfcd9e158003a70948..72a1d61929e692e551a53013ac1aabb1a4f28f5a 100644 --- a/src/main/java/org/caosdb/server/transaction/RetrieveUserTransaction.java +++ b/src/main/java/org/caosdb/server/transaction/RetrieveUserTransaction.java @@ -77,7 +77,7 @@ public class RetrieveUserTransaction extends AccessControlTransaction { ret.setAttribute("email", user.email); } if (user.entity != null) { - ret.setAttribute("entity", Integer.toString(user.entity)); + ret.setAttribute("entity", user.entity); } if (user.status != null) { ret.setAttribute("status", user.status.toString()); diff --git a/src/main/java/org/caosdb/server/transaction/Transaction.java b/src/main/java/org/caosdb/server/transaction/Transaction.java index 5fc06f5ec82066d3515cbf524e64beb001ba0fcd..69dde0ea3602a650e52dadcdbc0107ff7cf12ded 100644 --- a/src/main/java/org/caosdb/server/transaction/Transaction.java +++ b/src/main/java/org/caosdb/server/transaction/Transaction.java @@ -3,8 +3,8 @@ * * Copyright (C) 2018 Research Group Biomedical Physics, * Max-Planck-Institute for Dynamics and Self-Organization Göttingen - * Copyright (C) 2019-2021 IndiScale GmbH <info@indiscale.com> - * Copyright (C) 2019-2021 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2019-2021,2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2019-2021,2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -21,8 +21,12 @@ */ package org.caosdb.server.transaction; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Set; import org.apache.shiro.subject.Subject; import org.caosdb.datetime.UTCDateTime; import org.caosdb.server.accessControl.Principal; @@ -31,6 +35,7 @@ import org.caosdb.server.database.access.Access; import org.caosdb.server.database.backend.transaction.InsertTransactionHistory; import org.caosdb.server.database.exceptions.TransactionException; import org.caosdb.server.database.misc.TransactionBenchmark; +import org.caosdb.server.entity.EntityIdRegistry; import org.caosdb.server.entity.EntityInterface; import org.caosdb.server.entity.Message; import org.caosdb.server.entity.container.TransactionContainer; @@ -47,9 +52,89 @@ import org.caosdb.server.permissions.EntityACL; import org.caosdb.server.utils.AbstractObservable; import org.caosdb.server.utils.Info; import org.caosdb.server.utils.Observer; +import org.caosdb.server.utils.UseCacheResource; +import org.caosdb.server.utils.UseCacheResourceDelegate; +/** + * The NoOpCache is used when the caching is diabled (globally or for the current transaction. + * + * <p>It results in much simpler code when the callers do not need to check whether the caching is + * active all the time. + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ +@SuppressWarnings("rawtypes") +class NoOpCache implements Map { + + @Override + public int size() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean isEmpty() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean containsKey(Object key) { + return false; + } + + @Override + public boolean containsValue(Object value) { + return false; + } + + @Override + public Object get(Object key) { + return null; + } + + @Override + public Object put(Object key, Object value) { + return null; + } + + @Override + public Object remove(Object key) { + return null; + } + + @Override + public void putAll(Map m) {} + + @Override + public void clear() {} + + @Override + public Set keySet() { + return Collections.EMPTY_SET; + } + + @Override + public Collection values() { + return Collections.EMPTY_LIST; + } + + @Override + public Set entrySet() { + return Collections.EMPTY_SET; + } +} + +/** + * Abstract base implementation for all EntityTransactions (Transaction involving entities). + * + * <p>Handles caching, benchmark timing, the access to the back end, generates and checks entity + * ids, holds the state of the transaction and triggers the scheduler to run the jobs. + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ public abstract class Transaction<C extends TransactionContainer> extends AbstractObservable - implements TransactionInterface { + implements EntityTransactionInterface, UseCacheResource { @Override public TransactionBenchmark getTransactionBenchmark() { @@ -59,6 +144,8 @@ public abstract class Transaction<C extends TransactionContainer> extends Abstra private static final DatabaseAccessManager monitor = DatabaseAccessManager.getInstance(); public static final String CLEAN_UP = "TransactionCleanUp"; + + protected EntityIdRegistry idRegistry = null; private final C container; private Access access = null; private final Schedule schedule = new Schedule(); @@ -270,61 +357,104 @@ public abstract class Transaction<C extends TransactionContainer> extends Abstra } } - /** @see {@link #execute()} */ + /** + * @see {@link #execute()} + */ protected void rollBack() { this.schedule.runJobs(TransactionStage.ROLL_BACK); } - /** @see {@link #execute()} */ + /** + * @see {@link #execute()} + */ protected abstract void init() throws Exception; - /** @see {@link #execute()} */ + /** + * @see {@link #execute()} + */ protected abstract void preCheck() throws InterruptedException, Exception; - /** @see {@link #execute()} */ + /** + * @see {@link #execute()} + */ protected final void check() { this.schedule.runJobs(TransactionStage.CHECK); } - /** @see {@link #execute()} */ + /** + * @see {@link #execute()} + */ protected abstract void postCheck(); - /** @see {@link #execute()} */ + /** + * @see {@link #execute()} + */ protected abstract void preTransaction() throws InterruptedException; - /** @see {@link #execute()} */ + /** + * @see {@link #execute()} + */ protected abstract void transaction() throws Exception; - /** @see {@link #execute()} */ + /** + * @see {@link #execute()} + */ protected abstract void postTransaction() throws Exception; - /** @see {@link #execute()} */ + /** + * @see {@link #execute()} + */ protected abstract void cleanUp(); - /** @see {@link #execute()} */ + /** + * @see {@link #execute()} + */ protected void commit() throws Exception {} - public boolean useCache() { - return getContainer().getFlags() != null - && !getContainer().getFlags().containsKey("disableCache"); - } - public final Access getAccess() { return this.access; } protected void setAccess(final Access a) { this.access = a; + if (access != null) { + this.access.setUseCacheResourceDelegate(useCacheResourceDelegate); + } } @SuppressWarnings("unchecked") - public <S, T> HashMap<S, T> getCache(final String name) { + public <S, T> Map<S, T> getCache(final String name) { + if (!useCache()) { + return NO_OP_CACHE; + } if (!this.caches.containsKey(name)) { this.caches.put(name, new HashMap<S, T>()); } return this.caches.get(name); } + private static final NoOpCache NO_OP_CACHE = new NoOpCache(); + @SuppressWarnings("rawtypes") - private final HashMap<String, HashMap> caches = new HashMap<>(); + private final Map<String, Map> caches = new HashMap<>(); + + private final UseCacheResource useCacheResourceDelegate = new UseCacheResourceDelegate(); + + @Override + public UseCacheResource getUseCacheResourceDelegate() { + return this.useCacheResourceDelegate.getUseCacheResourceDelegate(); + } + + @Override + public void setUseCacheResourceDelegate(UseCacheResource delegate) { + this.useCacheResourceDelegate.setUseCacheResourceDelegate(delegate); + } + + @Override + public boolean matchIdPattern(String id) { + if (this.idRegistry == null) { + this.idRegistry = new EntityIdRegistry(this); + } + return idRegistry.matchIdPattern(id); + } } diff --git a/src/main/java/org/caosdb/server/transaction/UpdateACL.java b/src/main/java/org/caosdb/server/transaction/UpdateACL.java index 84c73080550d8899f4f1a5156bebb2a44c39df6a..289b5d53a14c642b4ac85bd9c02dd5c69484fa91 100644 --- a/src/main/java/org/caosdb/server/transaction/UpdateACL.java +++ b/src/main/java/org/caosdb/server/transaction/UpdateACL.java @@ -143,4 +143,10 @@ public class UpdateACL extends Transaction<TransactionContainer> public String getSRID() { return getContainer().getRequestId(); } + + @Override + public String generateId() { + throw new UnsupportedOperationException( + "This is not implemented on purpose. This exception indicates a usage error of this UpdateACL class."); + } } diff --git a/src/main/java/org/caosdb/server/transaction/UpdateUserTransaction.java b/src/main/java/org/caosdb/server/transaction/UpdateUserTransaction.java index 6808968f20574242cfdaa9b87793d7a4ad4b6914..7730d82b2fc2fb7ff3d34430ea3d1b60ffb9c16e 100644 --- a/src/main/java/org/caosdb/server/transaction/UpdateUserTransaction.java +++ b/src/main/java/org/caosdb/server/transaction/UpdateUserTransaction.java @@ -63,7 +63,7 @@ public class UpdateUserTransaction extends AccessControlTransaction { final String username, final UserStatus status, final String email, - final Integer entity, + final String entity, final String password) { this.user = new ProtoUser(); this.user.realm = @@ -177,7 +177,7 @@ public class UpdateUserTransaction extends AccessControlTransaction { ACMPermissions.PERMISSION_UPDATE_USER_ENTITY(this.user.realm, this.user.name)); isToBeUpdated = true; - if (this.user.entity.equals(0)) { + if (this.user.entity.isEmpty()) { // this means that the entity is to be reset. this.user.entity = null; } else { diff --git a/src/main/java/org/caosdb/server/transaction/WriteTransaction.java b/src/main/java/org/caosdb/server/transaction/WriteTransaction.java index 47b7a53c71ddebb7c1b062c2c8599cf44eabe039..b780d3d68f20cb6973c902cbe92fbd4fd46d51c1 100644 --- a/src/main/java/org/caosdb/server/transaction/WriteTransaction.java +++ b/src/main/java/org/caosdb/server/transaction/WriteTransaction.java @@ -3,8 +3,8 @@ * * Copyright (C) 2018 Research Group Biomedical Physics, * Max-Planck-Institute for Dynamics and Self-Organization Göttingen - * Copyright (C) 2019-2022 IndiScale GmbH <info@indiscale.com> - * Copyright (C) 2019-2022 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2019-2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2019-2023 Timm Fitschen <t.fitschen@indiscale.com> * Copyright (C) 2022 Daniel Hornung <d.hornung@indiscale.com> * * This program is free software: you can redistribute it and/or modify @@ -40,6 +40,7 @@ import org.caosdb.server.database.backend.transaction.UpdateEntityTransaction; import org.caosdb.server.database.misc.RollBackHandler; import org.caosdb.server.entity.DeleteEntity; import org.caosdb.server.entity.EntityID; +import org.caosdb.server.entity.EntityIdRegistry; import org.caosdb.server.entity.EntityInterface; import org.caosdb.server.entity.FileProperties; import org.caosdb.server.entity.InsertEntity; @@ -593,4 +594,13 @@ public class WriteTransaction extends Transaction<WritableContainer> public String getSRID() { return getContainer().getRequestId(); } + + @Override + public String generateId() { + if (this.idRegistry == null) { + this.idRegistry = new EntityIdRegistry(this); + } + + return this.idRegistry.generate(); + } } diff --git a/src/main/java/org/caosdb/server/transaction/WriteTransactionInterface.java b/src/main/java/org/caosdb/server/transaction/WriteTransactionInterface.java index 4e2938e18d061cbd1a7575baa59322356731859a..c39d2c14a7dc625b1aa8bf0529feaeec2c4ca8b3 100644 --- a/src/main/java/org/caosdb/server/transaction/WriteTransactionInterface.java +++ b/src/main/java/org/caosdb/server/transaction/WriteTransactionInterface.java @@ -10,4 +10,6 @@ public interface WriteTransactionInterface extends TransactionInterface { public Subject getTransactor(); public String getSRID(); + + public String generateId(); } diff --git a/src/main/java/org/caosdb/server/utils/AbstractObservable.java b/src/main/java/org/caosdb/server/utils/AbstractObservable.java index abc7f0a0ae1cdcbeff613c2778637de97a507c57..1bb4fc472a90c05d656f0d4250299801b73f07a2 100644 --- a/src/main/java/org/caosdb/server/utils/AbstractObservable.java +++ b/src/main/java/org/caosdb/server/utils/AbstractObservable.java @@ -37,7 +37,9 @@ public abstract class AbstractObservable implements Observable { return this.observers.add(o); } - /** @param e A String denoting the notification event. */ + /** + * @param e A String denoting the notification event. + */ @Override public void notifyObservers(final String e) { if (this.observers != null) { diff --git a/src/main/java/org/caosdb/server/utils/UseCacheResource.java b/src/main/java/org/caosdb/server/utils/UseCacheResource.java new file mode 100644 index 0000000000000000000000000000000000000000..89057f51c59f0f99a3eed3346ec18ab29ada74c0 --- /dev/null +++ b/src/main/java/org/caosdb/server/utils/UseCacheResource.java @@ -0,0 +1,41 @@ +/* + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> + * + * 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/>. + */ +package org.caosdb.server.utils; + +/** + * Interface which indicates that a class can turn on and off the caching of the current transaction + * and inform the caller about the state of the transactions's caching. + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ +public interface UseCacheResource { + + public default boolean useCache() { + return getUseCacheResourceDelegate().useCache(); + } + + public default void setUseCache(boolean useCache) { + getUseCacheResourceDelegate().setUseCache(useCache); + } + + public void setUseCacheResourceDelegate(UseCacheResource delegate); + + public UseCacheResource getUseCacheResourceDelegate(); +} diff --git a/src/main/java/org/caosdb/server/utils/UseCacheResourceDelegate.java b/src/main/java/org/caosdb/server/utils/UseCacheResourceDelegate.java new file mode 100644 index 0000000000000000000000000000000000000000..c537f0a813fe149b7338909f0dc95e8f3728dec1 --- /dev/null +++ b/src/main/java/org/caosdb/server/utils/UseCacheResourceDelegate.java @@ -0,0 +1,70 @@ +/* + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> + * + * 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/>. + */ +package org.caosdb.server.utils; + +import org.caosdb.server.CaosDBServer; + +/** + * Basic implementation of the {@link UseCacheResource} interface. + * + * <p>This implementation uses a Delegator Pattern to link different UseCacheResources together. + * This is uses to delegate the responsibility to the transaction as the final Object of the + * delegation. + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ +public class UseCacheResourceDelegate implements UseCacheResource { + private boolean useCache = CaosDBServer.useCache(); + private UseCacheResource delegate = null; + + @Override + public void setUseCache(boolean useCache) { + if (this.delegate != null) { + this.delegate.setUseCache(useCache); + } else { + this.useCache = useCache; + } + } + + @Override + public boolean useCache() { + if (delegate != null) { + return delegate.useCache(); + } + return CaosDBServer.useCache() && useCache; + } + + @Override + public void setUseCacheResourceDelegate(UseCacheResource delegate) { + if (delegate == this) { + this.delegate = null; + } else { + this.delegate = delegate; + } + } + + @Override + public UseCacheResource getUseCacheResourceDelegate() { + if (this.delegate != null) { + return this.delegate; + } + return this; + } +} diff --git a/src/test/java/org/caosdb/server/caching/TestCaching.java b/src/test/java/org/caosdb/server/caching/TestCaching.java index 1aba2ff6f16cea7a1f62e84e50e2cc9797114e27..8ff05660afde6e67d65e94011b662f09bdda9294 100644 --- a/src/test/java/org/caosdb/server/caching/TestCaching.java +++ b/src/test/java/org/caosdb/server/caching/TestCaching.java @@ -1,3 +1,22 @@ +/* + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> + * + * 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/>. + */ package org.caosdb.server.caching; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -17,6 +36,7 @@ public class TestCaching { @BeforeAll public static void init() throws IOException { CaosDBServer.initServerProperties(); + CaosDBServer.initCaching(); JCSCacheHelper.init(); } diff --git a/src/test/java/org/caosdb/server/caching/TestNoCaching.java b/src/test/java/org/caosdb/server/caching/TestNoCaching.java index 4fc73adf89f9355d1ff6addcd0f71fd9eda4e653..7eeeee71a909a106ad93b8c440ad895e366cca5f 100644 --- a/src/test/java/org/caosdb/server/caching/TestNoCaching.java +++ b/src/test/java/org/caosdb/server/caching/TestNoCaching.java @@ -1,3 +1,22 @@ +/* + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> + * + * 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/>. + */ package org.caosdb.server.caching; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -17,6 +36,7 @@ public class TestNoCaching { public static void init() throws IOException { CaosDBServer.initServerProperties(); CaosDBServer.setProperty(ServerProperties.KEY_CACHE_DISABLE, "TRUE"); + CaosDBServer.initCaching(); JCSCacheHelper.init(); } diff --git a/src/test/java/org/caosdb/server/database/backend/implementation/MySQL/InsertTest.java b/src/test/java/org/caosdb/server/database/backend/implementation/MySQL/InsertTest.java index 03bd185c8884822f89f8ff323a1c32125b66f88b..feee1360bf7fa0ae865422b31d8a2ad2bcf64ebe 100644 --- a/src/test/java/org/caosdb/server/database/backend/implementation/MySQL/InsertTest.java +++ b/src/test/java/org/caosdb/server/database/backend/implementation/MySQL/InsertTest.java @@ -1,9 +1,10 @@ /* - * ** 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 + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,8 +18,6 @@ * * 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 */ package org.caosdb.server.database.backend.implementation.MySQL; @@ -50,7 +49,7 @@ public class InsertTest { private Deque<EntityID> registerReplacementIds(int count) { Deque<EntityID> replacementIds = new ArrayDeque<>(); for (int i = 1; i < count + 1; i++) { - replacementIds.add(new EntityID(-i)); + replacementIds.add(new EntityID(Integer.toString(-i))); } return replacementIds; } @@ -63,70 +62,70 @@ public class InsertTest { @Test public void testTransformation1() throws Exception { final Entity r = new InsertEntity("Test", Role.Record); - final Property p1 = new Property(new RetrieveEntity(new EntityID(1))); + final Property p1 = new Property(new RetrieveEntity(new EntityID("1"))); p1.setRole("Property"); p1.setValue(new GenericValue("V1")); p1.setDatatype("TEXT"); p1.setStatementStatus(StatementStatus.FIX); r.addProperty(p1); - final Property p2 = new Property(new RetrieveEntity(new EntityID(2))); + final Property p2 = new Property(new RetrieveEntity(new EntityID("2"))); p2.setRole("Property"); p2.setValue(new GenericValue("V2")); p2.setDatatype("TEXT"); p2.setStatementStatus(StatementStatus.RECOMMENDED); r.addProperty(p2); - final Property p3 = new Property(new RetrieveEntity(new EntityID(3))); + final Property p3 = new Property(new RetrieveEntity(new EntityID("3"))); p3.setRole("Property"); p3.setValue(new GenericValue("V3")); p3.setDatatype("TEXT"); p3.setStatementStatus(StatementStatus.OBLIGATORY); p2.addProperty(p3); - final Property p4 = new Property(new RetrieveEntity(new EntityID(4))); + final Property p4 = new Property(new RetrieveEntity(new EntityID("4"))); p4.setRole("Property"); p4.setValue(new GenericValue("V4")); p4.setDatatype("TEXT"); p4.setStatementStatus(StatementStatus.OBLIGATORY); r.addProperty(p4); - final Property p5 = new Property(new RetrieveEntity(new EntityID(5))); + final Property p5 = new Property(new RetrieveEntity(new EntityID("5"))); p5.setRole("Property"); p5.setValue(new GenericValue("V5")); p5.setDatatype("TEXT"); p5.setStatementStatus(StatementStatus.OBLIGATORY); p4.addProperty(p5); - final Property p6 = new Property(new RetrieveEntity(new EntityID(6))); + final Property p6 = new Property(new RetrieveEntity(new EntityID("6"))); p6.setRole("Property"); p6.setValue(new GenericValue("V6")); p6.setDatatype("TEXT"); p6.setStatementStatus(StatementStatus.OBLIGATORY); p5.addProperty(p6); - final Property p7 = new Property(new RetrieveEntity(new EntityID(7))); + final Property p7 = new Property(new RetrieveEntity(new EntityID("7"))); p7.setRole("Property"); p7.setValue(new GenericValue("V7")); p7.setDatatype("TEXT"); p7.setStatementStatus(StatementStatus.OBLIGATORY); r.addProperty(p7); - final Property p8 = new Property(new RetrieveEntity(new EntityID(8))); + final Property p8 = new Property(new RetrieveEntity(new EntityID("8"))); p8.setRole("Property"); p8.setValue(new GenericValue("V8")); p8.setDatatype("TEXT"); p8.setStatementStatus(StatementStatus.OBLIGATORY); p7.addProperty(p8); - final Property p9 = new Property(new RetrieveEntity(new EntityID(9))); + final Property p9 = new Property(new RetrieveEntity(new EntityID("9"))); p9.setRole("Property"); p9.setValue(new GenericValue("V9")); p9.setDatatype("TEXT"); p9.setStatementStatus(StatementStatus.OBLIGATORY); p8.addProperty(p9); - final Property p10 = new Property(new RetrieveEntity(new EntityID(10))); + final Property p10 = new Property(new RetrieveEntity(new EntityID("10"))); p10.setRole("Property"); p10.setValue(new GenericValue("V10")); p10.setDatatype("TEXT"); @@ -143,57 +142,57 @@ public class InsertTest { assertEquals(7, stage1Inserts.size()); assertFalse(stage1Inserts.get(0) instanceof Replacement); - assertEquals(1, stage1Inserts.get(0).getId().toInteger()); + assertEquals("1", stage1Inserts.get(0).getId().toString()); assertEquals("V1", ((SingleValue) stage1Inserts.get(0).getValue()).toDatabaseString()); assertFalse(stage1Inserts.get(1) instanceof Replacement); - assertEquals(2, stage1Inserts.get(1).getId().toInteger()); + assertEquals("2", stage1Inserts.get(1).getId().toString()); assertEquals("V2", ((SingleValue) stage1Inserts.get(1).getValue()).toDatabaseString()); assertFalse(stage1Inserts.get(2) instanceof Replacement); - assertEquals(4, stage1Inserts.get(2).getId().toInteger()); + assertEquals("4", stage1Inserts.get(2).getId().toString()); assertEquals("V4", ((SingleValue) stage1Inserts.get(2).getValue()).toDatabaseString()); assertTrue(stage1Inserts.get(3) instanceof Replacement); - assertEquals(-1, stage1Inserts.get(3).getId().toInteger()); + assertEquals("-1", stage1Inserts.get(3).getId().toString()); assertEquals("V5", ((SingleValue) stage1Inserts.get(3).getValue()).toDatabaseString()); assertFalse(stage1Inserts.get(4) instanceof Replacement); - assertEquals(7, stage1Inserts.get(4).getId().toInteger()); + assertEquals("7", stage1Inserts.get(4).getId().toString()); assertEquals("V7", ((SingleValue) stage1Inserts.get(4).getValue()).toDatabaseString()); assertTrue(stage1Inserts.get(5) instanceof Replacement); - assertEquals(-2, stage1Inserts.get(5).getId().toInteger()); + assertEquals("-2", stage1Inserts.get(5).getId().toString()); assertEquals("V8", ((SingleValue) stage1Inserts.get(5).getValue()).toDatabaseString()); assertTrue(stage1Inserts.get(6) instanceof Replacement); - assertEquals(-3, stage1Inserts.get(6).getId().toInteger()); + assertEquals("-3", stage1Inserts.get(6).getId().toString()); assertEquals("V9", ((SingleValue) stage1Inserts.get(6).getValue()).toDatabaseString()); assertEquals(6, stage2Inserts.size()); - assertEquals(new EntityID(3), stage2Inserts.get(0).getId()); + assertEquals(new EntityID("3"), stage2Inserts.get(0).getId()); assertEquals("V3", ((SingleValue) stage2Inserts.get(0).getValue()).toDatabaseString()); - assertEquals(new EntityID(2), stage2Inserts.get(0).getDomain()); + assertEquals(new EntityID("2"), stage2Inserts.get(0).getDomain()); - assertEquals(new EntityID(5), stage2Inserts.get(1).getId()); + assertEquals(new EntityID("5"), stage2Inserts.get(1).getId()); assertEquals("V5", ((SingleValue) stage2Inserts.get(1).getValue()).toDatabaseString()); - assertEquals(new EntityID(4), stage2Inserts.get(1).getDomain()); + assertEquals(new EntityID("4"), stage2Inserts.get(1).getDomain()); - assertEquals(new EntityID(6), stage2Inserts.get(2).getId()); + assertEquals(new EntityID("6"), stage2Inserts.get(2).getId()); assertEquals("V6", ((SingleValue) stage2Inserts.get(2).getValue()).toDatabaseString()); - assertEquals(new EntityID(-1), stage2Inserts.get(2).getDomain()); + assertEquals(new EntityID("-1"), stage2Inserts.get(2).getDomain()); - assertEquals(new EntityID(8), stage2Inserts.get(3).getId()); + assertEquals(new EntityID("8"), stage2Inserts.get(3).getId()); assertEquals("V8", ((SingleValue) stage2Inserts.get(3).getValue()).toDatabaseString()); - assertEquals(new EntityID(7), stage2Inserts.get(3).getDomain()); + assertEquals(new EntityID("7"), stage2Inserts.get(3).getDomain()); - assertEquals(new EntityID(9), stage2Inserts.get(4).getId()); + assertEquals(new EntityID("9"), stage2Inserts.get(4).getId()); assertEquals("V9", ((SingleValue) stage2Inserts.get(4).getValue()).toDatabaseString()); - assertEquals(new EntityID(-2), stage2Inserts.get(4).getDomain()); + assertEquals(new EntityID("-2"), stage2Inserts.get(4).getDomain()); - assertEquals(new EntityID(10), stage2Inserts.get(5).getId()); + assertEquals(new EntityID("10"), stage2Inserts.get(5).getId()); assertEquals("V10", ((SingleValue) stage2Inserts.get(5).getValue()).toDatabaseString()); - assertEquals(new EntityID(-3), stage2Inserts.get(5).getDomain()); + assertEquals(new EntityID("-3"), stage2Inserts.get(5).getDomain()); } /** @@ -205,21 +204,21 @@ public class InsertTest { @Test public void testTransformation2() throws Exception { final Entity r = new InsertEntity("Test", Role.Record); - final Property p1 = new Property(new RetrieveEntity(new EntityID(1))); + final Property p1 = new Property(new RetrieveEntity(new EntityID("1"))); p1.setRole("Property"); p1.setValue(new GenericValue("V1-1")); p1.setDatatype("TEXT"); p1.setStatementStatus(StatementStatus.FIX); r.addProperty(p1); - final Property p2 = new Property(new RetrieveEntity(new EntityID(1))); + final Property p2 = new Property(new RetrieveEntity(new EntityID("1"))); p2.setRole("Property"); p2.setValue(new GenericValue("V1-2")); p2.setDatatype("TEXT"); p2.setStatementStatus(StatementStatus.FIX); r.addProperty(p2); - final Property subp = new Property(new RetrieveEntity(new EntityID(2))); + final Property subp = new Property(new RetrieveEntity(new EntityID("2"))); subp.setRole("Property"); subp.setValue(new GenericValue("V2")); subp.setDatatype("TEXT"); @@ -235,21 +234,21 @@ public class InsertTest { assertEquals(3, stage1Inserts.size()); assertFalse(stage1Inserts.get(0) instanceof Replacement); - assertEquals(new EntityID(1), stage1Inserts.get(0).getId()); + assertEquals(new EntityID("1"), stage1Inserts.get(0).getId()); assertEquals("V1-1", ((SingleValue) stage1Inserts.get(0).getValue()).toDatabaseString()); assertTrue(stage1Inserts.get(1) instanceof Replacement); assertEquals("V1-2", ((SingleValue) stage1Inserts.get(1).getValue()).toDatabaseString()); - assertEquals(new EntityID(-1), stage1Inserts.get(1).getId()); + assertEquals(new EntityID("-1"), stage1Inserts.get(1).getId()); assertFalse(stage1Inserts.get(2) instanceof Replacement); - assertEquals(new EntityID(-1), stage1Inserts.get(2).getId()); + assertEquals(new EntityID("-1"), stage1Inserts.get(2).getId()); assertEquals("1", ((SingleValue) stage1Inserts.get(2).getValue()).toDatabaseString()); assertEquals(1, stage2Inserts.size()); assertFalse(stage2Inserts.get(0) instanceof Replacement); - assertEquals(new EntityID(2), stage2Inserts.get(0).getId()); + assertEquals(new EntityID("2"), stage2Inserts.get(0).getId()); assertEquals("V2", ((SingleValue) stage2Inserts.get(0).getValue()).toDatabaseString()); } @@ -262,25 +261,25 @@ public class InsertTest { @Test public void testTransformation3() throws Exception { final Entity r = new InsertEntity("Test", Role.Record); - final Property p1 = new Property(new RetrieveEntity(new EntityID(1))); + final Property p1 = new Property(new RetrieveEntity(new EntityID("1"))); p1.setRole("Property"); p1.setDatatype("TEXT"); p1.setStatementStatus(StatementStatus.FIX); r.addProperty(p1); - final Property p2 = new Property(new RetrieveEntity(new EntityID(1))); + final Property p2 = new Property(new RetrieveEntity(new EntityID("1"))); p2.setRole("Property"); p2.setDatatype("TEXT"); p2.setStatementStatus(StatementStatus.FIX); r.addProperty(p2); - final Property p3 = new Property(new RetrieveEntity(new EntityID(1))); + final Property p3 = new Property(new RetrieveEntity(new EntityID("1"))); p3.setRole("Property"); p3.setDatatype("TEXT"); p3.setStatementStatus(StatementStatus.FIX); r.addProperty(p3); - final Property sub1 = new Property(new RetrieveEntity(new EntityID(2))); + final Property sub1 = new Property(new RetrieveEntity(new EntityID("2"))); sub1.setRole("Property"); sub1.setDatatype("TEXT"); sub1.setValue(new GenericValue("V1")); @@ -295,25 +294,25 @@ public class InsertTest { assertEquals(4, stage1Inserts.size()); assertTrue(stage1Inserts.get(0) instanceof Replacement); - assertEquals(new EntityID(-1), stage1Inserts.get(0).getId()); + assertEquals(new EntityID("-1"), stage1Inserts.get(0).getId()); assertEquals(null, stage1Inserts.get(0).getValue()); assertFalse(stage1Inserts.get(1) instanceof Replacement); - assertEquals(new EntityID(-1), stage1Inserts.get(1).getId()); - assertEquals(1, ((ReferenceValue) stage1Inserts.get(1).getValue()).getId().toInteger()); + assertEquals(new EntityID("-1"), stage1Inserts.get(1).getId()); + assertEquals("1", ((ReferenceValue) stage1Inserts.get(1).getValue()).getId().toString()); assertFalse(stage1Inserts.get(2) instanceof Replacement); - assertEquals(new EntityID(1), stage1Inserts.get(2).getId()); + assertEquals(new EntityID("1"), stage1Inserts.get(2).getId()); assertEquals(null, stage1Inserts.get(2).getValue()); assertFalse(stage1Inserts.get(3) instanceof Replacement); - assertEquals(new EntityID(1), stage1Inserts.get(3).getId()); + assertEquals(new EntityID("1"), stage1Inserts.get(3).getId()); assertEquals(null, stage1Inserts.get(3).getValue()); assertEquals(1, stage2Inserts.size()); - assertEquals(new EntityID(2), stage2Inserts.get(0).getId()); + assertEquals(new EntityID("2"), stage2Inserts.get(0).getId()); assertEquals("V1", ((SingleValue) stage2Inserts.get(0).getValue()).toDatabaseString()); - assertEquals(new EntityID(-1), stage2Inserts.get(0).getDomain()); + assertEquals(new EntityID("-1"), stage2Inserts.get(0).getDomain()); } /** @@ -325,14 +324,14 @@ public class InsertTest { public void testTransformation4() throws Exception { final Entity r = new InsertEntity("Test", Role.Record); - final Property p1 = new Property(new RetrieveEntity(new EntityID(1))); + final Property p1 = new Property(new RetrieveEntity(new EntityID("1"))); p1.setRole("Property"); p1.setDatatype("TEXT"); p1.setValue(new GenericValue("V1")); p1.setStatementStatus(StatementStatus.FIX); r.addProperty(p1); - final Property p2 = new Property(new RetrieveEntity(new EntityID(2))); + final Property p2 = new Property(new RetrieveEntity(new EntityID("2"))); p2.setRole("Property"); p2.setDatatype("LIST<TEXT>"); p2.setStatementStatus(StatementStatus.FIX); @@ -343,7 +342,7 @@ public class InsertTest { p2.setValue(vals); r.addProperty(p2); - final Property p3 = new Property(new RetrieveEntity(new EntityID(3))); + final Property p3 = new Property(new RetrieveEntity(new EntityID("3"))); p3.setRole("Property"); p3.setDatatype("TEXT"); p3.setValue(new GenericValue("V3")); @@ -359,19 +358,19 @@ public class InsertTest { assertEquals(4, stage1Inserts.size()); assertFalse(stage1Inserts.get(0) instanceof Replacement); - assertEquals(new EntityID(1), stage1Inserts.get(0).getId()); + assertEquals(new EntityID("1"), stage1Inserts.get(0).getId()); assertEquals("V1", ((SingleValue) stage1Inserts.get(0).getValue()).toDatabaseString()); assertTrue(stage1Inserts.get(1) instanceof Replacement); - assertEquals(new EntityID(-1), stage1Inserts.get(1).getId()); + assertEquals(new EntityID("-1"), stage1Inserts.get(1).getId()); assertTrue(stage1Inserts.get(1).getValue() instanceof CollectionValue); assertFalse(stage1Inserts.get(2) instanceof Replacement); - assertEquals(new EntityID(-1), stage1Inserts.get(2).getId()); - assertEquals(2, ((ReferenceValue) stage1Inserts.get(2).getValue()).getId().toInteger()); + assertEquals(new EntityID("-1"), stage1Inserts.get(2).getId()); + assertEquals("2", ((ReferenceValue) stage1Inserts.get(2).getValue()).getId().toString()); assertFalse(stage1Inserts.get(3) instanceof Replacement); - assertEquals(new EntityID(3), stage1Inserts.get(3).getId()); + assertEquals(new EntityID("3"), stage1Inserts.get(3).getId()); assertEquals("V3", ((SingleValue) stage1Inserts.get(3).getValue()).toDatabaseString()); assertEquals(0, stage2Inserts.size()); @@ -381,7 +380,7 @@ public class InsertTest { @Test public void testTransformation5() { final Entity r = new InsertEntity("Test", Role.RecordType); - final Property p1 = new Property(new RetrieveEntity(new EntityID(1))); + final Property p1 = new Property(new RetrieveEntity(new EntityID("1"))); p1.setRole("Property"); p1.setDatatype("TEXT"); p1.setDescription("desc1"); @@ -391,7 +390,7 @@ public class InsertTest { p1.setStatementStatus(StatementStatus.RECOMMENDED); r.addProperty(p1); - final Property p2 = new Property(new RetrieveEntity(new EntityID(2))); + final Property p2 = new Property(new RetrieveEntity(new EntityID("2"))); p2.setRole("Property"); p2.setDatatype("TEXT"); p2.setDescription("desc2"); @@ -401,7 +400,7 @@ public class InsertTest { p2.setStatementStatus(StatementStatus.RECOMMENDED); r.addProperty(p2); - final Property p21 = new Property(new RetrieveEntity(new EntityID(1))); + final Property p21 = new Property(new RetrieveEntity(new EntityID("1"))); p21.setRole("Property"); p21.setDatatype("TEXT"); p21.setDescription("desc21"); @@ -411,7 +410,7 @@ public class InsertTest { p21.setStatementStatus(StatementStatus.FIX); p2.addProperty(p21); - final Property p22 = new Property(new RetrieveEntity(new EntityID(2))); + final Property p22 = new Property(new RetrieveEntity(new EntityID("2"))); p22.setRole("Property"); p22.setDatatype("TEXT"); p22.setDescription("desc22"); @@ -421,7 +420,7 @@ public class InsertTest { p22.setStatementStatus(StatementStatus.FIX); p2.addProperty(p22); - final Property p3 = new Property(new RetrieveEntity(new EntityID(3))); + final Property p3 = new Property(new RetrieveEntity(new EntityID("3"))); p3.setRole("Property"); p3.setDatatype("TEXT"); p3.setDescription("desc3"); @@ -431,7 +430,7 @@ public class InsertTest { p3.setStatementStatus(StatementStatus.RECOMMENDED); r.addProperty(p3); - final Property p31 = new Property(new RetrieveEntity(new EntityID(1))); + final Property p31 = new Property(new RetrieveEntity(new EntityID("1"))); p31.setRole("Property"); p31.setDatatype("TEXT"); p31.setDescription("desc31"); @@ -441,7 +440,7 @@ public class InsertTest { p31.setStatementStatus(StatementStatus.FIX); p3.addProperty(p31); - final Property p32 = new Property(new RetrieveEntity(new EntityID(2))); + final Property p32 = new Property(new RetrieveEntity(new EntityID("2"))); p32.setRole("Property"); p32.setDatatype("TEXT"); p32.setDescription("desc32"); @@ -451,7 +450,7 @@ public class InsertTest { p32.setStatementStatus(StatementStatus.FIX); p3.addProperty(p32); - final Property p321 = new Property(new RetrieveEntity(new EntityID(1))); + final Property p321 = new Property(new RetrieveEntity(new EntityID("1"))); p321.setRole("Property"); p321.setDatatype("TEXT"); p321.setDescription("desc321"); @@ -461,7 +460,7 @@ public class InsertTest { p321.setStatementStatus(StatementStatus.FIX); p32.addProperty(p321); - final Property p322 = new Property(new RetrieveEntity(new EntityID(2))); + final Property p322 = new Property(new RetrieveEntity(new EntityID("2"))); p322.setRole("Property"); p322.setDatatype("TEXT"); p322.setDescription("desc322"); @@ -471,7 +470,7 @@ public class InsertTest { p322.setStatementStatus(StatementStatus.FIX); p32.addProperty(p322); - final Property p323 = new Property(new RetrieveEntity(new EntityID(3))); + final Property p323 = new Property(new RetrieveEntity(new EntityID("3"))); p323.setRole("Property"); p323.setDatatype("TEXT"); p323.setDescription("desc323"); @@ -481,7 +480,7 @@ public class InsertTest { p323.setStatementStatus(StatementStatus.FIX); p32.addProperty(p323); - final Property p33 = new Property(new RetrieveEntity(new EntityID(3))); + final Property p33 = new Property(new RetrieveEntity(new EntityID("3"))); p33.setRole("Property"); p33.setDatatype("TEXT"); p33.setDescription("desc33"); diff --git a/src/test/java/org/caosdb/server/database/backend/transaction/RetrieveFullEntityTest.java b/src/test/java/org/caosdb/server/database/backend/transaction/RetrieveFullEntityTest.java index 72c84d1f7b262a854a257c8ae764d4e3da9b8e80..cfd8fcc9ba39cdf68dbf43be77de30f81c4fbec9 100644 --- a/src/test/java/org/caosdb/server/database/backend/transaction/RetrieveFullEntityTest.java +++ b/src/test/java/org/caosdb/server/database/backend/transaction/RetrieveFullEntityTest.java @@ -1,9 +1,8 @@ /* - * ** header v3.0 * This file is a part of the CaosDB Project. * - * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com> - * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2020,2023 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2020,2023 IndiScale GmbH <info@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,8 +16,6 @@ * * 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 */ package org.caosdb.server.database.backend.transaction; @@ -43,32 +40,33 @@ public class RetrieveFullEntityTest { @Test public void testRetrieveSubEntities() { final RetrieveFullEntityTransaction r = - new RetrieveFullEntityTransaction(new EntityID(0)) { + new RetrieveFullEntityTransaction(new EntityID("0")) { /** Mock-up */ @Override public void retrieveFullEntity( final EntityInterface e, final List<Selection> selections) { // The id of the referenced window - assertEquals(new EntityID(1234), e.getId()); + assertEquals(new EntityID("1234"), e.getId()); // The level of selectors has been reduced by 1 assertEquals("description", selections.get(0).getSelector()); e.setDescription("A heart-shaped window."); - }; + } + ; }; - final Property window = new Property(new RetrieveEntity(new EntityID(2345))); + final Property window = new Property(new RetrieveEntity(new EntityID("2345"))); window.setName("Window"); window.setDatatype("Window"); - window.setValue(new ReferenceValue(1234)); + window.setValue(new ReferenceValue("1234")); - final Entity house = new RetrieveEntity(new EntityID(3456)); + final Entity house = new RetrieveEntity(new EntityID("3456")); house.addProperty(window); final ReferenceValue value = - (ReferenceValue) house.getProperties().getEntityById(new EntityID(2345)).getValue(); - assertEquals(new EntityID(1234), value.getId()); + (ReferenceValue) house.getProperties().getEntityById(new EntityID("2345")).getValue(); + assertEquals(new EntityID("1234"), value.getId()); assertNull(value.getEntity()); final List<Selection> selections = new ArrayList<>(); @@ -76,7 +74,7 @@ public class RetrieveFullEntityTest { r.retrieveSubEntities(house, selections); - assertEquals(new EntityID(1234), value.getId()); + assertEquals(new EntityID("1234"), value.getId()); assertNotNull(value.getEntity()); assertEquals("A heart-shaped window.", value.getEntity().getDescription()); } diff --git a/src/test/java/org/caosdb/server/entity/container/PropertyContainerTest.java b/src/test/java/org/caosdb/server/entity/container/PropertyContainerTest.java index a8ccdbdc9f7a9522020332e01cba96a1667d70b6..f8fb0217adb0cc35cbc51d90b96f76d5f7b3592e 100644 --- a/src/test/java/org/caosdb/server/entity/container/PropertyContainerTest.java +++ b/src/test/java/org/caosdb/server/entity/container/PropertyContainerTest.java @@ -1,9 +1,8 @@ /* - * ** header v3.0 * This file is a part of the CaosDB Project. * - * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com> - * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2020,2023 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2020,2023 IndiScale GmbH <info@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,8 +16,6 @@ * * 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 */ package org.caosdb.server.entity.container; @@ -48,7 +45,7 @@ public class PropertyContainerTest { @BeforeAll public static void setup() { - window = new RetrieveEntity(new EntityID(1234)); + window = new RetrieveEntity(new EntityID("1234")); windowHeight = new Property(new RetrieveEntity("window.height", Role.Property)); window.addProperty(windowHeight); windowHeight.setValue(new GenericValue("windowHeight")); @@ -59,7 +56,7 @@ public class PropertyContainerTest { houseHeight = new Property(new RetrieveEntity("height", Role.Property)); houseHeight.setValue(new GenericValue("houseHeight")); house.addProperty(houseHeight); - windowProperty = new Property(new RetrieveEntity(new EntityID(2345))); + windowProperty = new Property(new RetrieveEntity(new EntityID("2345"))); windowProperty.setName("window"); windowProperty.setValue(new ReferenceValue(window.getId())); house.addProperty(windowProperty); diff --git a/src/test/java/org/caosdb/server/entity/xml/PropertyToElementStrategyTest.java b/src/test/java/org/caosdb/server/entity/xml/PropertyToElementStrategyTest.java index a2c9676b9759fc55fb0d1e089808ab50d016288e..bab174c88ed595ec4b79b1cf47b1cf2dbb0cbc95 100644 --- a/src/test/java/org/caosdb/server/entity/xml/PropertyToElementStrategyTest.java +++ b/src/test/java/org/caosdb/server/entity/xml/PropertyToElementStrategyTest.java @@ -1,9 +1,8 @@ /* - * ** header v3.0 * This file is a part of the CaosDB Project. * - * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com> - * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2020,2023 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2020,2023 IndiScale GmbH <info@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,8 +16,6 @@ * * 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 */ package org.caosdb.server.entity.xml; @@ -66,7 +63,7 @@ public class PropertyToElementStrategyTest { @BeforeEach public void setup() { - window = new RetrieveEntity(new EntityID(1234), Role.Record); + window = new RetrieveEntity(new EntityID("1234"), Role.Record); windowHeight = new Property(new RetrieveEntity("height", Role.Property)); window.addProperty(windowHeight); windowHeight.setValue(new GenericValue("windowHeight")); @@ -77,7 +74,7 @@ public class PropertyToElementStrategyTest { houseHeight = new Property(new RetrieveEntity("height", Role.Property)); houseHeight.setValue(new GenericValue("houseHeight")); house.addProperty(houseHeight); - windowProperty = new Property(new RetrieveEntity(new EntityID(2345))); + windowProperty = new Property(new RetrieveEntity(new EntityID("2345"))); windowProperty.setName("window"); windowProperty.setDatatype("window"); windowProperty.setValue(new ReferenceValue(window.getId())); diff --git a/src/test/java/org/caosdb/server/grpc/CaosDBToGrpcConvertersTest.java b/src/test/java/org/caosdb/server/grpc/CaosDBToGrpcConvertersTest.java index d46c187324b76e179ef8d562cd5a150ab7968a45..ad85d3d4c00727134440d0f4eaa1a424fb550372 100644 --- a/src/test/java/org/caosdb/server/grpc/CaosDBToGrpcConvertersTest.java +++ b/src/test/java/org/caosdb/server/grpc/CaosDBToGrpcConvertersTest.java @@ -1,3 +1,22 @@ +/* + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> + * + * 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/>. + */ package org.caosdb.server.grpc; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -74,7 +93,7 @@ public class CaosDBToGrpcConvertersTest { RetrieveEntity entity = new RetrieveEntity(); // must be printed - entity.setId(new EntityID(1234)); + entity.setId(new EntityID("1234")); entity.addInfo("info"); entity.addWarning(new Message("warning")); entity.addError(new Message("error")); @@ -136,7 +155,7 @@ public class CaosDBToGrpcConvertersTest { RetrieveEntity person1 = new RetrieveEntity(); person1.addProperty(fullName1); - ReferenceValue val1 = new ReferenceValue(new EntityID(1234)); + ReferenceValue val1 = new ReferenceValue(new EntityID("1234")); val1.setEntity(person1, false); col.add(val1); @@ -147,7 +166,7 @@ public class CaosDBToGrpcConvertersTest { RetrieveEntity person2 = new RetrieveEntity(); person2.addProperty(fullName2); - ReferenceValue val2 = new ReferenceValue(new EntityID(1234)); + ReferenceValue val2 = new ReferenceValue(new EntityID("1234")); val2.setEntity(person2, false); col.add(val2); p.setValue(col); diff --git a/src/test/java/org/caosdb/server/jobs/JobConfigTest.java b/src/test/java/org/caosdb/server/jobs/JobConfigTest.java index e176c2111189fb52ca9946e10ae5ec705549b6c8..c097d0a76ec5444881e9dd58bb735015b9fe8db1 100644 --- a/src/test/java/org/caosdb/server/jobs/JobConfigTest.java +++ b/src/test/java/org/caosdb/server/jobs/JobConfigTest.java @@ -1,3 +1,22 @@ +/* + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> + * + * 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/>. + */ package org.caosdb.server.jobs; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -26,7 +45,7 @@ public class JobConfigTest { final JobConfig jobConfig = JobConfig.getInstance(); assertEquals("Retrieve", jobConfig.getTransactionType(new RetrieveEntity("test"))); assertEquals("Insert", jobConfig.getTransactionType(new InsertEntity("test", Role.Record))); - assertEquals("Delete", jobConfig.getTransactionType(new DeleteEntity(new EntityID(1234)))); + assertEquals("Delete", jobConfig.getTransactionType(new DeleteEntity(new EntityID("1234")))); assertEquals("Update", jobConfig.getTransactionType(new UpdateEntity(new Element("Record")))); } } diff --git a/src/test/java/org/caosdb/server/query/TestCQL.java b/src/test/java/org/caosdb/server/query/TestCQL.java index 8d7a7e8fb219f286d6cdaffc4b28240a7b2897d5..61a87b7f5764a8da26bc7bb6ccd8f06a320302f1 100644 --- a/src/test/java/org/caosdb/server/query/TestCQL.java +++ b/src/test/java/org/caosdb/server/query/TestCQL.java @@ -36,6 +36,7 @@ import java.util.regex.Matcher; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.Vocabulary; import org.antlr.v4.runtime.tree.ParseTree; import org.caosdb.server.CaosDBServer; import org.caosdb.server.database.access.Access; @@ -3546,7 +3547,10 @@ public class TestCQL { */ @Test public void TestTicket147i() - throws InterruptedException, SQLException, ConnectionException, QueryException, + throws InterruptedException, + SQLException, + ConnectionException, + QueryException, TransactionException { CQLLexer lexer; lexer = new CQLLexer(CharStreams.fromString(this.ticket147i)); @@ -6754,6 +6758,7 @@ public class TestCQL { // must not throw ParsingException new Query(this.queryIssue134).parse(); } + /** * Space before special character unit * @@ -7103,4 +7108,32 @@ public class TestCQL { assertTrue(conj.getFilters().get(0) instanceof POV); assertTrue(conj.getFilters().get(1) instanceof POV); } + + @Test + public void testDecimalNumber() { + // This should always be DEC, WHITESPACE, AND repeat + final String text = + "1.2123e+3 AND 1.21234E+3 AND 1.21234E-3 AND 1.21234E3 AND 16.0 AND 1.2 AND -1.2 AND +1.2 AND 1.2 AND - 1.2 AND + 1.2 AND 2e-323 AND 2E-323 AND 2E- 323 AND 2 e -323 AND + 1.2123e+323"; + CQLLexer lexer = new CQLLexer(CharStreams.fromString(text)); + final CommonTokenStream tokens = new CommonTokenStream(lexer); + Vocabulary vocab = lexer.getVocabulary(); + + final CQLParser parser = new CQLParser(tokens); + final CqContext sfq = parser.cq(); + int no = 0; + for (final Token t : tokens.getTokens()) { + if (no % 3 == 0) { + assertEquals(vocab.getSymbolicName(t.getType()), "DECIMAL_NUMBER"); + } + if (no % 3 == 1) { + if (vocab.getSymbolicName(t.getType()) != "EOF") { + assertEquals(vocab.getSymbolicName(t.getType()), "WHITE_SPACE"); + } + } + if (no % 3 == 2) { + assertEquals(vocab.getSymbolicName(t.getType()), "AND"); + } + no = no + 1; + } + } } diff --git a/src/test/java/org/caosdb/server/resource/TestAbstractCaosDBServerResource.java b/src/test/java/org/caosdb/server/resource/TestAbstractCaosDBServerResource.java index ca4fbb0954f469055c2da3d0bf00b03cdc9dd563..4da4ba5cb4959787f5aaf1bf12e47140fdf4ee00 100644 --- a/src/test/java/org/caosdb/server/resource/TestAbstractCaosDBServerResource.java +++ b/src/test/java/org/caosdb/server/resource/TestAbstractCaosDBServerResource.java @@ -95,8 +95,12 @@ public class TestAbstractCaosDBServerResource { @Override protected Representation httpGetInChildClass() - throws ConnectionException, IOException, SQLException, CaosDBException, - NoSuchAlgorithmException, Exception { + throws ConnectionException, + IOException, + SQLException, + CaosDBException, + NoSuchAlgorithmException, + Exception { // TODO Auto-generated method stub return null; } diff --git a/src/test/java/org/caosdb/server/resource/TestScriptingResource.java b/src/test/java/org/caosdb/server/resource/TestScriptingResource.java index 31d4a384c4a2a7e12b48c99073119cb7a38e3acc..3ce449ae2eea5ab5b4487ee4c14c22692423c8aa 100644 --- a/src/test/java/org/caosdb/server/resource/TestScriptingResource.java +++ b/src/test/java/org/caosdb/server/resource/TestScriptingResource.java @@ -178,12 +178,12 @@ public class TestScriptingResource { return 0; } return -1; - }; + } @Override public Element generateRootElement(ServerSideScriptingCaller caller) { return new Element("OK"); - }; + } @Override public Object generateAuthToken(String purpose) { diff --git a/src/test/java/org/caosdb/server/transaction/RetrieveTest.java b/src/test/java/org/caosdb/server/transaction/RetrieveTest.java index a3ac40e1bf1e0fe6489a74f45d6c253b903b4478..95a019d46d9325759a3ad032ec285e98c9e51210 100644 --- a/src/test/java/org/caosdb/server/transaction/RetrieveTest.java +++ b/src/test/java/org/caosdb/server/transaction/RetrieveTest.java @@ -1,3 +1,22 @@ +/* + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2023 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> + * + * 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/>. + */ package org.caosdb.server.transaction; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -62,7 +81,7 @@ public class RetrieveTest { public void testMissingRetrievePermission() { Subject subject = SecurityUtils.getSubject(); subject.login(AnonymousAuthenticationToken.getInstance()); - EntityInterface entity = new RetrieveEntity(new EntityID(1234)); + EntityInterface entity = new RetrieveEntity(new EntityID("1234")); EntityACLFactory fac = new EntityACLFactory(); fac.deny(AnonymousAuthenticationToken.PRINCIPAL, "RETRIEVE:ENTITY"); entity.setEntityACL(fac.create()); diff --git a/src/test/java/org/caosdb/server/transaction/UpdateTest.java b/src/test/java/org/caosdb/server/transaction/UpdateTest.java index a0891a3eb34e1759100c78916d0fb250de149ef8..9a138106f4313f06ac2457728e3b5bd218470fa5 100644 --- a/src/test/java/org/caosdb/server/transaction/UpdateTest.java +++ b/src/test/java/org/caosdb/server/transaction/UpdateTest.java @@ -3,8 +3,9 @@ * * Copyright (C) 2018 Research Group Biomedical Physics, * Max-Planck-Institute for Dynamics and Self-Organization Göttingen - * Copyright (C) 2022 Indiscale GmbH <info@indiscale.com> + * Copyright (C) 2022,2023 Indiscale GmbH <info@indiscale.com> * Copyright (C) 2022 Daniel Hornung <d.hornung@indiscale.com> + * Copyright (C) 2023 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -75,11 +76,11 @@ public class UpdateTest { @Test public void testDeriveUpdate_SameProperty() throws NoSuchAlgorithmException, IOException, CaosDBException { - final Entity newEntity = new RetrieveEntity(new EntityID(1234)); - final Property newProperty = new Property(new RetrieveEntity(new EntityID(1))); + final Entity newEntity = new RetrieveEntity(new EntityID("1234")); + final Property newProperty = new Property(new RetrieveEntity(new EntityID("1"))); newEntity.addProperty(newProperty); - final Property oldProperty = new Property(new RetrieveEntity(new EntityID(1))); - final Entity oldEntity = new RetrieveEntity(new EntityID(1234)); + final Property oldProperty = new Property(new RetrieveEntity(new EntityID("1"))); + final Entity oldEntity = new RetrieveEntity(new EntityID("1234")); oldEntity.addProperty(oldProperty); new WriteTransaction(null).deriveUpdate(newEntity, oldEntity); @@ -89,13 +90,13 @@ public class UpdateTest { @Test public void testDeriveUpdate_AnotherProperty() throws NoSuchAlgorithmException, IOException, CaosDBException { - final Entity newEntity = new RetrieveEntity(new EntityID(1234)); - final Property newProperty = new Property(new RetrieveEntity(new EntityID(1))); - final Property newProperty2 = new Property(new RetrieveEntity(new EntityID(2))); + final Entity newEntity = new RetrieveEntity(new EntityID("1234")); + final Property newProperty = new Property(new RetrieveEntity(new EntityID("1"))); + final Property newProperty2 = new Property(new RetrieveEntity(new EntityID("2"))); newEntity.addProperty(newProperty); newEntity.addProperty(newProperty2); - final Property oldProperty = new Property(new RetrieveEntity(new EntityID(1))); - final Entity oldEntity = new RetrieveEntity(new EntityID(1234)); + final Property oldProperty = new Property(new RetrieveEntity(new EntityID("1"))); + final Entity oldEntity = new RetrieveEntity(new EntityID("1234")); oldEntity.addProperty(oldProperty); new WriteTransaction(null).deriveUpdate(newEntity, oldEntity); @@ -107,15 +108,15 @@ public class UpdateTest { @Test public void testDeriveUpdate_SameUnit() throws NoSuchAlgorithmException, IOException, CaosDBException { - final EntityInterface magicUnit = new RetrieveEntity(new EntityID(1234)); + final EntityInterface magicUnit = new RetrieveEntity(new EntityID("1234")); magicUnit.setName("Unit"); - magicUnit.setId(new EntityID(24)); + magicUnit.setId(new EntityID("24")); magicUnit.setDatatype("TEXT"); - final Entity newEntity = new RetrieveEntity(new EntityID(1234)); - final Property newProperty = new Property(new RetrieveEntity(new EntityID(1))); + final Entity newEntity = new RetrieveEntity(new EntityID("1234")); + final Property newProperty = new Property(new RetrieveEntity(new EntityID("1"))); - final Property newUnit = new Property(new RetrieveEntity(new EntityID(5))); + final Property newUnit = new Property(new RetrieveEntity(new EntityID("5"))); newUnit.setName(magicUnit.getName()); newUnit.setId(magicUnit.getId()); newUnit.setDatatype(magicUnit.getDatatype()); @@ -126,10 +127,10 @@ public class UpdateTest { newEntity.addProperty(newProperty); - final Entity oldEntity = new RetrieveEntity(new EntityID(1234)); - final Property oldProperty = new Property(new RetrieveEntity(new EntityID(1))); + final Entity oldEntity = new RetrieveEntity(new EntityID("1234")); + final Property oldProperty = new Property(new RetrieveEntity(new EntityID("1"))); - final Property oldUnit = new Property(new RetrieveEntity(new EntityID(5))); + final Property oldUnit = new Property(new RetrieveEntity(new EntityID("5"))); oldUnit.setName(magicUnit.getName()); oldUnit.setId(magicUnit.getId()); oldUnit.setDatatype(magicUnit.getDatatype()); @@ -149,15 +150,15 @@ public class UpdateTest { @Test public void testDeriveUpdate_DifferentUnit() throws NoSuchAlgorithmException, IOException, CaosDBException { - final EntityInterface magicUnit = new RetrieveEntity(new EntityID(1234)); + final EntityInterface magicUnit = new RetrieveEntity(new EntityID("1234")); magicUnit.setName("Unit"); - magicUnit.setId(new EntityID(24)); + magicUnit.setId(new EntityID("24")); magicUnit.setDatatype("TEXT"); - final Entity newEntity = new RetrieveEntity(new EntityID(1234)); - final Property newProperty = new Property(new RetrieveEntity(new EntityID(1))); + final Entity newEntity = new RetrieveEntity(new EntityID("1234")); + final Property newProperty = new Property(new RetrieveEntity(new EntityID("1"))); - final Property newUnit = new Property(new RetrieveEntity(new EntityID(5))); + final Property newUnit = new Property(new RetrieveEntity(new EntityID("5"))); newUnit.setName(magicUnit.getName()); newUnit.setId(magicUnit.getId()); newUnit.setDatatype(magicUnit.getDatatype()); @@ -168,10 +169,10 @@ public class UpdateTest { newEntity.addProperty(newProperty); - final Entity oldEntity = new RetrieveEntity(new EntityID(1234)); - final Property oldProperty = new Property(new RetrieveEntity(new EntityID(1))); + final Entity oldEntity = new RetrieveEntity(new EntityID("1234")); + final Property oldProperty = new Property(new RetrieveEntity(new EntityID("1"))); - final Property oldUnit = new Property(new RetrieveEntity(new EntityID(5))); + final Property oldUnit = new Property(new RetrieveEntity(new EntityID("5"))); oldUnit.setName(magicUnit.getName()); oldUnit.setId(magicUnit.getId()); oldUnit.setDatatype(magicUnit.getDatatype()); @@ -192,10 +193,10 @@ public class UpdateTest { throws NoSuchAlgorithmException, CaosDBException, IOException { final Entity newEntity = new UpdateEntity(); - final Property newProperty = new Property(new UpdateEntity(new EntityID(1))); + final Property newProperty = new Property(new UpdateEntity(new EntityID("1"))); newProperty.setDatatype("List<Person>"); CollectionValue newValue = new CollectionValue(); - newValue.add(new ReferenceValue(1234)); + newValue.add(new ReferenceValue("1234")); newValue.add(null); newValue.add(new GenericValue(2345)); newValue.add(new GenericValue(3465)); @@ -206,14 +207,14 @@ public class UpdateTest { // old entity represents the stored entity. final Entity oldEntity = new UpdateEntity(); - final Property oldProperty = new Property(new UpdateEntity(new EntityID(1))); + final Property oldProperty = new Property(new UpdateEntity(new EntityID("1"))); oldProperty.setDatatype("List<Person>"); CollectionValue oldValue = new CollectionValue(); // Values are shuffled but have the correct index oldValue.add(1, null); oldValue.add(3, new GenericValue(3465)); - oldValue.add(2, new ReferenceValue(2345)); - oldValue.add(0, new ReferenceValue(1234)); + oldValue.add(2, new ReferenceValue("2345")); + oldValue.add(0, new ReferenceValue("1234")); oldProperty.setValue(oldValue); oldEntity.addProperty(oldProperty); @@ -245,8 +246,8 @@ public class UpdateTest { // Values are shuffled but have the correct index oldValue2.add(0, null); oldValue2.add(1, new GenericValue(3465)); - oldValue2.add(2, new ReferenceValue(2345)); - oldValue2.add(3, new ReferenceValue(1234)); + oldValue2.add(2, new ReferenceValue("2345")); + oldValue2.add(3, new ReferenceValue("1234")); oldValue2.add(4, null); oldProperty.setValue(oldValue2); @@ -266,8 +267,8 @@ public class UpdateTest { public void testDeriveUpdate_UpdateList() throws NoSuchAlgorithmException, CaosDBException, IOException { // @review Florian Spreckelsen 2022-03-15 - final Property oldProperty = new Property(new UpdateEntity(new EntityID(1))); - final Property newProperty = new Property(new UpdateEntity(new EntityID(1))); + final Property oldProperty = new Property(new UpdateEntity(new EntityID("1"))); + final Property newProperty = new Property(new UpdateEntity(new EntityID("1"))); oldProperty.setDatatype("List<Person>"); newProperty.setDatatype("List<Person>"); final Entity oldEntity = new UpdateEntity(); diff --git a/src/test/java/org/caosdb/server/utils/FileUtilsTest.java b/src/test/java/org/caosdb/server/utils/FileUtilsTest.java index e05a3c7f6f082f2432137737c5937e67ff75d4ef..36851c4eacc7d78c4fcfd033f16c53f5e086b49b 100644 --- a/src/test/java/org/caosdb/server/utils/FileUtilsTest.java +++ b/src/test/java/org/caosdb/server/utils/FileUtilsTest.java @@ -573,7 +573,10 @@ public class FileUtilsTest { @Test public void testFileSystemConsistencyCheck() - throws NoSuchAlgorithmException, IOException, TransactionException, InterruptedException, + throws NoSuchAlgorithmException, + IOException, + TransactionException, + InterruptedException, Message { final String rootPath = "./testFileSystemConsistencyCheck/"; final File root = new File(rootPath);