diff --git a/CHANGELOG.md b/CHANGELOG.md index fc59b37a94bb93437462f7dd061305c5e1a46e7f..8077b4075f814ed34577e68cba4de2fdeed0b192 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed +* Text user interface (CaosDBTerminal). + ### Fixed * Bug: When the user password is updated the user is deactivated. diff --git a/pom.xml b/pom.xml index 3e0fd96a209bc97fe89a827e97377bbd019da40d..4c4435934c97633ecadf7bc1ca2d2b6917198bd6 100644 --- a/pom.xml +++ b/pom.xml @@ -117,11 +117,6 @@ <artifactId>reflections</artifactId> <version>0.9.11</version> </dependency> - <dependency> - <groupId>com.googlecode.lanterna</groupId> - <artifactId>lanterna</artifactId> - <version>2.1.9</version> - </dependency> <dependency> <groupId>org.antlr</groupId> <artifactId>antlr4</artifactId> diff --git a/src/main/java/org/caosdb/server/CaosDBServer.java b/src/main/java/org/caosdb/server/CaosDBServer.java index 996e1480a8158dd27e62e3af072a725698a3e5f3..4a69f24d4d905f5d8fea360c291dfc1522b414fc 100644 --- a/src/main/java/org/caosdb/server/CaosDBServer.java +++ b/src/main/java/org/caosdb/server/CaosDBServer.java @@ -78,13 +78,9 @@ import org.caosdb.server.resource.Webinterface; import org.caosdb.server.resource.WebinterfaceBuildNumber; import org.caosdb.server.resource.transaction.EntityNamesResource; import org.caosdb.server.resource.transaction.EntityResource; -import org.caosdb.server.terminal.CaosDBTerminal; -import org.caosdb.server.terminal.StatsPanel; -import org.caosdb.server.terminal.SystemErrPanel; import org.caosdb.server.transaction.ChecksumUpdater; import org.caosdb.server.utils.FileUtils; import org.caosdb.server.utils.Initialization; -import org.caosdb.server.utils.NullPrintStream; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; @@ -116,7 +112,6 @@ import org.slf4j.LoggerFactory; public class CaosDBServer extends Application { private static Logger logger = LoggerFactory.getLogger(CaosDBServer.class); - private static boolean START_GUI = true; private static Properties SERVER_PROPERTIES = null; private static Component component = null; private static ArrayList<Runnable> postShutdownHooks = new ArrayList<Runnable>(); @@ -146,7 +141,7 @@ public class CaosDBServer extends Application { public static void main(final String[] args) throws SecurityException, FileNotFoundException, IOException { try { - init(args); + parseArguments(args); initScheduler(); initServerProperties(); initTimeZone(); @@ -161,16 +156,22 @@ public class CaosDBServer extends Application { } } - private static void init(final String[] args) { - // Important change: - // Make silent the default option - START_GUI = false; + /** + * Parse the command line arguments. + * + * <ul> + * <li>"nobackend": flag to run caosdb without any backend (for testing purposes) + * <li>"insecure": flag to start only a http server (no https server) + * </ul> + * + * <p>Both flags are only available in the debug mode which is controlled by the `caosdb.debug` + * JVM Property. + * + * @param args + */ + private static void parseArguments(final String[] args) { for (final String s : args) { - if (s.equals("silent")) { - START_GUI = false; - } else if (s.equals("gui")) { - START_GUI = true; - } else if (s.equals("nobackend")) { + if (s.equals("nobackend")) { START_BACKEND = false; } else if (s.equals("insecure")) { INSECURE = true; @@ -354,32 +355,6 @@ public class CaosDBServer extends Application { } } - public static void initGUI() throws InterruptedException { - if (START_GUI) { - final CaosDBTerminal caosDBTerminal = new CaosDBTerminal(); - caosDBTerminal.setName("CaosDBTerminal"); - caosDBTerminal.start(); - - addPreShutdownHook( - new Runnable() { - - @Override - public void run() { - caosDBTerminal.shutDown(); - SystemErrPanel.close(); - } - }); - // wait until the terminal is initialized. - Thread.sleep(1000); - - // add Benchmark - StatsPanel.addStat("TransactionBenchmark", TransactionBenchmark.getRootInstance()); - } else { - logger.info("NO GUI"); - System.setOut(new NullPrintStream()); - } - } - private static void initDatatypes(final Access access) throws Exception { final RetrieveDatatypes t = new RetrieveDatatypes(); t.setAccess(access); diff --git a/src/main/java/org/caosdb/server/database/BackendTransaction.java b/src/main/java/org/caosdb/server/database/BackendTransaction.java index 5bf978343d72272309d4f4ab0f039770419c87b8..61aebf42a2dfd65fb9ed1c84459064e3aa274c06 100644 --- a/src/main/java/org/caosdb/server/database/BackendTransaction.java +++ b/src/main/java/org/caosdb/server/database/BackendTransaction.java @@ -129,6 +129,8 @@ import org.caosdb.server.database.backend.interfaces.UpdateUserImpl; import org.caosdb.server.database.backend.interfaces.UpdateUserRolesImpl; import org.caosdb.server.database.exceptions.TransactionException; import org.caosdb.server.database.misc.TransactionBenchmark; +import org.caosdb.server.entity.EntityInterface; +import org.caosdb.server.entity.RetrieveEntity; import org.caosdb.server.utils.UndoHandler; import org.caosdb.server.utils.Undoable; @@ -295,4 +297,9 @@ public abstract class BackendTransaction implements Undoable { this.benchmark.addMeasurement(o, time); } } + + /** Return the type of transaction of an entity, e.g. "Retrieve" for a {@link RetrieveEntity}. */ + public String getTransactionType(EntityInterface e) { + return e.getClass().getSimpleName().replace("Entity", ""); + } } diff --git a/src/main/java/org/caosdb/server/database/DatabaseAccessManager.java b/src/main/java/org/caosdb/server/database/DatabaseAccessManager.java new file mode 100644 index 0000000000000000000000000000000000000000..196a9fb2b4418d81ced4b174482f8f6a5dce8ce8 --- /dev/null +++ b/src/main/java/org/caosdb/server/database/DatabaseAccessManager.java @@ -0,0 +1,331 @@ +/* + * ** 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) 2021 Indiscale GmbH <info@indiscale.com> + * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2021 Daniel Hornung <d.hornung@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/>. + * + * ** end header + */ +package org.caosdb.server.database; + +import java.util.concurrent.Semaphore; +import java.util.concurrent.locks.ReentrantLock; +import org.caosdb.server.database.access.Access; +import org.caosdb.server.database.access.AccessControlAccess; +import org.caosdb.server.database.access.InfoAccess; +import org.caosdb.server.database.access.InitAccess; +import org.caosdb.server.database.access.TransactionAccess; +import org.caosdb.server.transaction.AccessControlTransaction; +import org.caosdb.server.transaction.TransactionInterface; +import org.caosdb.server.transaction.WriteTransactionInterface; +import org.caosdb.server.utils.Info; +import org.caosdb.server.utils.Initialization; +import org.caosdb.server.utils.Releasable; + +/** + * Acquire and release read access. {@link DatabaseAccessManager} uses this class for managing + * access to the back-end during processing updates, inserts, deletions and retrievals. Read access + * will be granted immediately for every thread that requests it, unless a thread requests that read + * access be blocked (usually by requesting write access). If this happens, all threads that already + * have read access will proceed and release their read access as usual but no NEW read permits will + * be granted. + * + * <p>This is a blockable {@link Semaphore}. Any number of threads may acquire a permit unless it is + * blocked. The blocking thread (requesting a {@link WriteAccessLock}) waits until all threads have + * released their permits. + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ +class ReadAccessSemaphore extends Semaphore implements Releasable { + + private static final long serialVersionUID = 4384921156838881337L; + private int acquired = 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() { + // this is a fair semaphore with no initial permit. + super(1, true); + } + + /** + * Acquire a read access permit if and only if it has not been blocked via block(). If read access + * is currently blocked, the thread waits until the unblock() method is invoked by any thread. + */ + @Override + public void acquire() throws InterruptedException { + super.acquire(); // Protect the next few lines + if (this.acquired == 0) { + this.writersBlock.acquire(); + } + this.acquired++; + super.release(); + } + + /** + * Release a read access permit. + * + * <p>If this is the last remaining acquired permit, also release the general writersBlock. + */ + @Override + public void release() { + this.acquired--; + if (this.acquired <= 0) { // Last permit: release + this.acquired = 0; + if (this.writersBlock.availablePermits() <= 0) { + this.writersBlock.release(); + } + } + } + + /** + * Acquire the permit to block further read access, if no thread currently has acquired read + * access and no thread currently has a block. + * + * <p>Consequences of calling this method are: + * + * <ul> + * <li>Further read access permits are blocked for any thread. + * <li>Every thread that has a read access already can proceed. The current thread waits until + * all threads have released their read access. + * </ul> + * + * <p>If another thread has invoked this method before, the current thread waits until the + * unblock() method is called. + * + * @throws InterruptedException + */ + public void block() throws InterruptedException { + super.reducePermits(1); + this.writersBlock.acquire(); + } + + /** + * Unblock read access. + * + * <p>This method releases the writersBlock introduced by calling the block() method. + */ + public void unblock() { + if (this.writersBlock.availablePermits() <= 0) { + this.writersBlock.release(); + } + super.release(); + } + + public int waitingAquireAccess() { + return getQueueLength(); + } +} + +/** + * Acquire and release write access. DatabaseAccessManager uses this class for managing access to + * entities while processing updates, inserts, deletions and retrievals. Write access will be + * granted to one and only one thread if no other thread already holds read or write access permits. + * The write access seat has to be reserved before acquiring the lock. + * + * <p>The flow is as follows: + * + * <p> + * + * <pre> + * No new read + * access possible, + * Read access Read access wait for running read + * possible possible threads to fininish + * + * +------+ +----------+ +----------+ + * +--> | no | ----> | reserved | ----> | acquired | --+ + * | | Lock | +----------+ +----------+ | + * | +------+ \ / | + * | (1 seat together) | + * +-----------------------------------------------------+ + * release() + * </pre> + * + * @author Timm Fitschen + */ +class WriteAccessLock extends ReentrantLock implements Releasable { + + private static final long serialVersionUID = 833147084787201103L; + private ReadAccessSemaphore readSem = null; + private Thread reservedBy = null; + + public WriteAccessLock(final ReadAccessSemaphore readSem) { + super(); + this.readSem = readSem; + } + + /** + * Reserve the seat for the next write access. While a write access is reserved but not yet + * acquired, all read access may still be granted. When a write access has been already granted or + * another reservation is active, the thread waits until the write access has been released. + * + * @throws InterruptedException + */ + public void reserve() throws InterruptedException { + super.lock(); + this.reservedBy = Thread.currentThread(); + } + + /** + * Lock the write access seat. This method returns once all current read permits have been + * released. + */ + @Override + public void lockInterruptibly() throws InterruptedException { + if (!super.isHeldByCurrentThread()) { + super.lock(); + } + this.readSem.block(); // Wait until all current read permits have been released. + } + + @Override + public void unlock() { + if (super.isHeldByCurrentThread()) { + this.readSem.unblock(); + this.reservedBy = null; + super.unlock(); + } + } + + @Override + public void release() { + unlock(); + } + + public Thread whoHasReservedAccess() { + return this.reservedBy; + } +} + +/** + * Manages the read and write access to the database. + * + * @author tf + */ +public class DatabaseAccessManager { + + private DatabaseAccessManager() {} + + private static final DatabaseAccessManager instance = new DatabaseAccessManager(); + private final ReadAccessSemaphore readAccess = new ReadAccessSemaphore(); + private final WriteAccessLock writeAccess = new WriteAccessLock(this.readAccess); + + public static DatabaseAccessManager getInstance() { + return instance; + } + + /** + * Return the thread which has successfully reserved write access. This is the thread which will + * be the next to actually acquire write access. + * + * @return + */ + public static Thread whoHasReservedWriteAccess() { + return instance.writeAccess.whoHasReservedAccess(); + } + + /** + * Acquire read access. This method returns the Access object as soon as there are no active write + * permits. + * + * <p>The returned Access object can be used to read in the data base back-end. + * + * <p>Read access can be acquired parallel to other threads having read access or while another + * thread has <em>reserved</em> write access. As soon as any thread has requested to + * <em>acquire</em> write access, all other threads have to wait. + * + * @param t the {@link TransactionInterface} which requests the read access + * @return {@link Access} object which holds and abstract away all connection details. + * @throws InterruptedException + */ + public Access acquireReadAccess(final TransactionInterface t) throws InterruptedException { + this.readAccess.acquire(); + return new TransactionAccess(t, this.readAccess); + } + + /** + * Reserve write access. This method returns the Access object as soon as there is no other + * reserved or acquired write access. + * + * <p>The returned Access object can be used to read in the data base back-end. + * + * <p>The reservation has no effect on granting of read access permits, but only one thread may at + * any time reserve or acquire write access. + * + * @param wt - the {@link WriteTransactionInterface} which request the reservation of the write + * access. + * @return {@link Access} object which holds and abstract away all connection details. + * @throws InterruptedException + */ + public Access reserveWriteAccess(final WriteTransactionInterface wt) throws InterruptedException { + this.writeAccess.reserve(); + return new TransactionAccess(wt, this.writeAccess); + } + + /** + * Acquire write access. This method returns the Access object as soon as all already acquired + * read access permits have been released. When the write access is acquired, no other access can + * be acquired (read or write). + * + * <p>The returned Access object can be used to read and write in the data base back-end. + * + * @param wt - the {@link WriteTransactionInterface} which request the acquisition of the write + * access. + * @return {@link Access} object which holds and abstract away all connection details. + * @throws InterruptedException + */ + public Access acquireWriteAccess(final WriteTransactionInterface wt) throws InterruptedException { + this.writeAccess.lockInterruptibly(); + return wt.getAccess(); + } + + /** + * Special access to be used by {@link Info}. + * + * @param i + * @return + */ + public static Access getInfoAccess(final Info i) { + return new InfoAccess(i); + } + + /** + * Special access to be used during the {@link Initialization} of the caosdb server. + * + * @param initialization + * @return + */ + public static Access getInitAccess(final Initialization initialization) { + return new InitAccess(initialization); + } + + /** + * Special access to be used for {@link AccessControlTransaction}s, mainly authentication and + * authorization. + * + * @param t + * @return + */ + public static Access getAccountAccess(final AccessControlTransaction t) { + return new AccessControlAccess(t); + } +} diff --git a/src/main/java/org/caosdb/server/database/DatabaseMonitor.java b/src/main/java/org/caosdb/server/database/DatabaseMonitor.java deleted file mode 100644 index ed98d1d6d88e3e797ce022c0880640d9a9128a0d..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/database/DatabaseMonitor.java +++ /dev/null @@ -1,330 +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; - -import java.util.Iterator; -import java.util.LinkedList; -import java.util.concurrent.Semaphore; -import java.util.concurrent.locks.ReentrantLock; -import org.caosdb.server.database.access.Access; -import org.caosdb.server.database.access.AccessControlAccess; -import org.caosdb.server.database.access.InfoAccess; -import org.caosdb.server.database.access.InitAccess; -import org.caosdb.server.database.access.TransactionAccess; -import org.caosdb.server.entity.container.TransactionContainer; -import org.caosdb.server.transaction.AccessControlTransaction; -import org.caosdb.server.transaction.TransactionInterface; -import org.caosdb.server.transaction.WriteTransaction; -import org.caosdb.server.utils.Info; -import org.caosdb.server.utils.Initialization; -import org.caosdb.server.utils.Observable; -import org.caosdb.server.utils.Observer; -import org.caosdb.server.utils.Releasable; - -/** - * Acquire and release weak access. DatabaseMonitor uses this class for managing access to entities - * during processing updates, inserts, deletions and retrievals. Weak access will be granted - * immediately for every thread that requests it, unless one or more threads requested for blocking - * the weak access (usually due to requesting for strong access). If this happens, all threads that - * already have weak access will proceed and release their weak access as usual but no NEW permits - * will be granted. - * - * @author Timm Fitschen - */ -class WeakAccessSemaphore extends Semaphore implements Observable, Releasable { - - private static final long serialVersionUID = -262226321839837533L; - private int acquired = 0; // how many thread have weak access - Semaphore block = new Semaphore(1, true); - - public WeakAccessSemaphore() { - // this is a fair semaphore with no initial permit. - super(1, true); - } - - /** - * Acquires a weak access permit if and only if it has not been blocked via block(). If the - * WeakAccess is currently blocked, the thread wait until the unblock() method is invoked by any - * thread. - */ - @Override - public void acquire() throws InterruptedException { - super.acquire(); - if (this.acquired == 0) { - this.block.acquire(); - } - this.acquired++; - notifyObservers(null); - super.release(); - } - - /** Releases a weak access permit. */ - @Override - public void release() { - this.acquired--; - notifyObservers(null); - if (this.acquired <= 0) { - this.acquired = 0; - if (this.block.availablePermits() <= 0) { - this.block.release(); - } - } - } - - /** - * Acquires the permit of a block of WeakAccess if no thread currently has a WeakAccess and no - * thread currently has a block. I.e. it blocks the further permission of weak access for any - * thread. Every thread that has a weak access yet can proceed. The current thread waits until any - * thread has released its weak access. If another thread has invoked this method yet the current - * thread waits until the unblock() method is called. - * - * @throws InterruptedException - */ - public void block() throws InterruptedException { - super.reducePermits(1); - this.block.acquire(); - } - - /** - * Unblock WeakAccess. - * - * @throws InterruptedException - */ - public void unblock() { - if (this.block.availablePermits() <= 0) { - this.block.release(); - } - super.release(); - } - - public int waitingAquireAccess() { - return getQueueLength(); - } - - public int acquiredAccess() { - return this.acquired; - } - - LinkedList<Observer> observers = new LinkedList<Observer>(); - - @Override - public boolean acceptObserver(final Observer o) { - return this.observers.add(o); - } - - @Override - public void notifyObservers(final String e) { - final Iterator<Observer> it = this.observers.iterator(); - while (it.hasNext()) { - final Observer o = it.next(); - if (!o.notifyObserver(e, this)) { - it.remove(); - } - } - } -} - -/** - * Acquire and release strong access. DatabaseMonitor uses this class for managing access to - * entities during processing updates, inserts, deletions and retrievals. Strong access will be - * granted to one and only one thread if no other thread yet holds weak or strong access permits. - * The strong access has to be allocated before requesting it to be permitted. See below. - * - * @author Timm Fitschen - */ -class StrongAccessLock extends ReentrantLock implements Observable, Releasable { - - private static final long serialVersionUID = -262226321839837533L; - private WeakAccessSemaphore wa = null; - private Thread allocator = null; - private Thread acquirer = null; - - public StrongAccessLock(final WeakAccessSemaphore wa) { - super(); - this.wa = wa; - } - - /** - * Allocates the strong access permits. While a strong access is allocated but not yet acquired - * any weak access may still be granted. When a strong access is yet granted or another allocation - * is still active, the thread waits until the strong access has been released. - * - * @throws InterruptedException - */ - public void allocate() throws InterruptedException { - super.lock(); - this.allocator = Thread.currentThread(); - notifyObservers(null); - } - - @Override - public void lockInterruptibly() throws InterruptedException { - if (!super.isHeldByCurrentThread()) { - super.lock(); - } - this.acquirer = Thread.currentThread(); - notifyObservers(null); - this.wa.block(); - } - - @Override - public void unlock() { - if (super.isHeldByCurrentThread()) { - this.wa.unblock(); - this.allocator = null; - this.acquirer = null; - notifyObservers(null); - super.unlock(); - } - } - - @Override - public void release() { - unlock(); - } - - LinkedList<Observer> observers = new LinkedList<Observer>(); - - @Override - public boolean acceptObserver(final Observer o) { - return this.observers.add(o); - } - - @Override - public void notifyObservers(final String e) { - final Iterator<Observer> it = this.observers.iterator(); - while (it.hasNext()) { - final Observer o = it.next(); - if (!o.notifyObserver(e, this)) { - it.remove(); - } - } - } - - public Thread whoHasAllocatedAccess() { - return this.allocator; - } - - public Thread whoHasAcquiredAccess() { - return this.acquirer; - } - - public int waitingAllocateAccess() { - return getQueueLength(); - } -} - -/** - * Manages the read and write access to the database. - * - * @author tf - */ -public class DatabaseMonitor { - - private DatabaseMonitor() {} - - private static final DatabaseMonitor instance = new DatabaseMonitor(); - private final WeakAccessSemaphore wa = new WeakAccessSemaphore(); - private final StrongAccessLock sa = new StrongAccessLock(this.wa); - - public static DatabaseMonitor getInstance() { - return instance; - } - - public void acquireWeakAccess() throws InterruptedException { - this.wa.acquire(); - } - - public void releaseWeakAccess() { - this.wa.release(); - } - - public void allocateStrongAccess() throws InterruptedException { - this.sa.allocate(); - } - - public void acquireStrongAccess() throws InterruptedException { - this.sa.lockInterruptibly(); - } - - public void releaseStrongAccess() { - this.sa.unlock(); - } - - public static final void acceptWeakAccessObserver(final Observer o) { - instance.wa.acceptObserver(o); - } - - public static final void acceptStrongAccessObserver(final Observer o) { - instance.sa.acceptObserver(o); - } - - public static int waitingAllocateStrongAccess() { - return instance.sa.waitingAllocateAccess(); - } - - public static int waitingAcquireWeakAccess() { - return instance.wa.waitingAquireAccess(); - } - - public static int acquiredWeakAccess() { - return instance.wa.acquiredAccess(); - } - - public static Thread whoHasAllocatedStrongAccess() { - return instance.sa.whoHasAllocatedAccess(); - } - - public static Thread whoHasAcquiredStrongAccess() { - return instance.sa.whoHasAcquiredAccess(); - } - - public Access acquiredWeakAccess(final TransactionInterface t) { - acquiredWeakAccess(); - return new TransactionAccess(t, this.wa); - } - - public Access allocateStrongAccess(final WriteTransaction<? extends TransactionContainer> wt) - throws InterruptedException { - allocateStrongAccess(); - return new TransactionAccess(wt, this.sa); - } - - public Access acquireStrongAccess(final WriteTransaction<? extends TransactionContainer> wt) - throws InterruptedException { - acquireStrongAccess(); - return wt.getAccess(); - } - - public static Access getInfoAccess(final Info i) { - return new InfoAccess(i); - } - - public static Access getInitAccess(final Initialization initialization) { - return new InitAccess(initialization); - } - - public static Access getAccountAccess(final AccessControlTransaction t) { - return new AccessControlAccess(t); - } -} diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLHelper.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLHelper.java index 2ad36235bd16e1cecde9908d334f643ebc127e54..b1f445e2b8ad2d79b756e1dd662296d8e6c9a49c 100644 --- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLHelper.java +++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLHelper.java @@ -55,7 +55,7 @@ public class MySQLHelper implements DBHelper { * * <p>In the database, this adds a row to the transaction table with SRID, user and timestamp. */ - public void initTransaction(Connection connection, WriteTransaction<?> transaction) + public void initTransaction(Connection connection, WriteTransaction transaction) throws SQLException { try (CallableStatement call = connection.prepareCall("CALL set_transaction(?,?,?,?,?)")) { @@ -89,7 +89,7 @@ public class MySQLHelper implements DBHelper { } else if (transaction instanceof WriteTransaction) { connection.setReadOnly(false); connection.setAutoCommit(false); - initTransaction(connection, (WriteTransaction<?>) transaction); + initTransaction(connection, (WriteTransaction) transaction); } else { connection.setReadOnly(false); connection.setAutoCommit(true); diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/DeleteEntity.java b/src/main/java/org/caosdb/server/database/backend/transaction/DeleteEntityTransaction.java similarity index 92% rename from src/main/java/org/caosdb/server/database/backend/transaction/DeleteEntity.java rename to src/main/java/org/caosdb/server/database/backend/transaction/DeleteEntityTransaction.java index 7f3d3435e1d4913d99ea0c9b7b064da85e12ac02..f8ebcd2af6ff73dd11f1b845d7e5aef58ff925f3 100644 --- a/src/main/java/org/caosdb/server/database/backend/transaction/DeleteEntity.java +++ b/src/main/java/org/caosdb/server/database/backend/transaction/DeleteEntityTransaction.java @@ -27,11 +27,11 @@ import org.caosdb.server.database.exceptions.TransactionException; import org.caosdb.server.entity.EntityInterface; import org.caosdb.server.entity.container.TransactionContainer; -public class DeleteEntity extends BackendTransaction { +public class DeleteEntityTransaction extends BackendTransaction { private final TransactionContainer container; - public DeleteEntity(final TransactionContainer container) { + public DeleteEntityTransaction(final TransactionContainer container) { this.container = container; } diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/InsertEntity.java b/src/main/java/org/caosdb/server/database/backend/transaction/InsertEntityTransaction.java similarity index 94% rename from src/main/java/org/caosdb/server/database/backend/transaction/InsertEntity.java rename to src/main/java/org/caosdb/server/database/backend/transaction/InsertEntityTransaction.java index 42439aa54e06e2556cb7b814f1c9056afb35f9d7..9c86ba33bf47b8051bc365bb32a6f36d0342ff92 100644 --- a/src/main/java/org/caosdb/server/database/backend/transaction/InsertEntity.java +++ b/src/main/java/org/caosdb/server/database/backend/transaction/InsertEntityTransaction.java @@ -27,11 +27,11 @@ import org.caosdb.server.entity.EntityInterface; import org.caosdb.server.entity.container.TransactionContainer; import org.caosdb.server.utils.EntityStatus; -public class InsertEntity extends BackendTransaction { +public class InsertEntityTransaction extends BackendTransaction { private final TransactionContainer container; - public InsertEntity(final TransactionContainer container) { + public InsertEntityTransaction(final TransactionContainer container) { this.container = container; } diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/InsertTransactionHistory.java b/src/main/java/org/caosdb/server/database/backend/transaction/InsertTransactionHistory.java index db7393b04f0afe3c15622dfb750cf31d42050521..9b798de4279de8034168c4ecfbc1738c70370a65 100644 --- a/src/main/java/org/caosdb/server/database/backend/transaction/InsertTransactionHistory.java +++ b/src/main/java/org/caosdb/server/database/backend/transaction/InsertTransactionHistory.java @@ -30,22 +30,24 @@ import org.caosdb.server.entity.EntityInterface; import org.caosdb.server.entity.container.TransactionContainer; import org.caosdb.server.utils.EntityStatus; +/** + * Record the current transaction in the entities transaction history. + * + * @author Timm Fitschen (t.fitschen@indiscale.com) + */ public class InsertTransactionHistory extends BackendTransaction { private final TransactionContainer container; - private final String transaction; private final UTCDateTime datetime; private final String user; private final String realm; public InsertTransactionHistory( final TransactionContainer container, - final String transaction, final String realm, final String user, final UTCDateTime timestamp) { this.container = container; - this.transaction = transaction; this.user = user; this.datetime = timestamp; this.realm = realm; @@ -60,7 +62,7 @@ public class InsertTransactionHistory extends BackendTransaction { || e.getEntityStatus() == EntityStatus.VALID) { t.execute( - this.transaction, + getTransactionType(e), this.realm, this.user, this.datetime.getUTCSeconds(), diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveFullEntity.java b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveFullEntityTransaction.java similarity index 96% rename from src/main/java/org/caosdb/server/database/backend/transaction/RetrieveFullEntity.java rename to src/main/java/org/caosdb/server/database/backend/transaction/RetrieveFullEntityTransaction.java index 17a0b2e035447eeda095be6357f959ef6af0d9a4..6bac0ff187fe2a08d2d0c1c7450ac954bc023d5e 100644 --- a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveFullEntity.java +++ b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveFullEntityTransaction.java @@ -53,21 +53,21 @@ import org.caosdb.server.utils.EntityStatus; * * @author Timm Fitschen <t.fitschen@indiscale.com> */ -public class RetrieveFullEntity extends BackendTransaction { +public class RetrieveFullEntityTransaction extends BackendTransaction { private final Container<? extends EntityInterface> container; - public RetrieveFullEntity(final EntityInterface entity) { + public RetrieveFullEntityTransaction(final EntityInterface entity) { final Container<EntityInterface> c = new Container<>(); c.add(entity); this.container = c; } - public RetrieveFullEntity(final Container<? extends EntityInterface> container) { + public RetrieveFullEntityTransaction(final Container<? extends EntityInterface> container) { this.container = container; } - public RetrieveFullEntity(Integer id) { + public RetrieveFullEntityTransaction(Integer id) { this(new RetrieveEntity(id)); } diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/RuleLoader.java b/src/main/java/org/caosdb/server/database/backend/transaction/RuleLoader.java index 2bc6a92a2449a8ba2d42119c0ba743129bcee2ac..c73023bd4429db19ca0a968aa99b657f0d0cbea4 100644 --- a/src/main/java/org/caosdb/server/database/backend/transaction/RuleLoader.java +++ b/src/main/java/org/caosdb/server/database/backend/transaction/RuleLoader.java @@ -33,9 +33,16 @@ import org.caosdb.server.database.exceptions.TransactionException; import org.caosdb.server.database.proto.Rule; import org.caosdb.server.entity.EntityInterface; import org.caosdb.server.entity.container.TransactionContainer; +import org.caosdb.server.entity.wrapper.Property; import org.caosdb.server.jobs.Job; import org.caosdb.server.transaction.Transaction; +/** + * Load transaction rules (e.g. "Referenced entities must always exist") and their configuration + * from the back-end. + * + * @author Timm Fitschen (t.fitschen@indiscale.com) + */ public class RuleLoader extends CacheableBackendTransaction<String, ArrayList<Rule>> { private static final ICacheAccess<String, ArrayList<Rule>> cache = @@ -45,6 +52,7 @@ public class RuleLoader extends CacheableBackendTransaction<String, ArrayList<Ru private final Integer entity; private final Integer domain; private ArrayList<Job> jobs; + private String transactionType; public RuleLoader( final Integer domain, @@ -55,18 +63,17 @@ public class RuleLoader extends CacheableBackendTransaction<String, ArrayList<Ru this.domain = domain; this.entity = entity; this.e = e; + if (e instanceof Property) { + this.transactionType = getTransactionType(((Property) e).getDomainEntity()); + } else { + this.transactionType = getTransactionType(e); + } this.transaction = transaction; } @Override protected String getKey() { - return "<" - + this.domain - + "," - + this.entity - + "," - + this.transaction.getClass().getSimpleName() - + ">"; + return "<" + this.domain + "," + this.entity + "," + this.transactionType + ">"; } public ArrayList<Job> getJobs() { @@ -76,7 +83,7 @@ public class RuleLoader extends CacheableBackendTransaction<String, ArrayList<Ru @Override public ArrayList<Rule> executeNoCache() throws TransactionException { final RuleLoaderImpl t = getImplementation(RuleLoaderImpl.class); - return t.executeNoCache(this.domain, this.entity, this.transaction.getClass().getSimpleName()); + return t.executeNoCache(this.domain, this.entity, this.transactionType); } @Override diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/UpdateEntity.java b/src/main/java/org/caosdb/server/database/backend/transaction/UpdateEntityTransaction.java similarity index 93% rename from src/main/java/org/caosdb/server/database/backend/transaction/UpdateEntity.java rename to src/main/java/org/caosdb/server/database/backend/transaction/UpdateEntityTransaction.java index 2bf965575884f29640a80ec3d3ab284b7779c8f2..275947abd9b42a2424141bc28111889859fb1475 100644 --- a/src/main/java/org/caosdb/server/database/backend/transaction/UpdateEntity.java +++ b/src/main/java/org/caosdb/server/database/backend/transaction/UpdateEntityTransaction.java @@ -28,11 +28,11 @@ import org.caosdb.server.entity.EntityInterface; import org.caosdb.server.entity.container.TransactionContainer; import org.caosdb.server.utils.EntityStatus; -public class UpdateEntity extends BackendTransaction { +public class UpdateEntityTransaction extends BackendTransaction { private final TransactionContainer container; - public UpdateEntity(final TransactionContainer container) { + public UpdateEntityTransaction(final TransactionContainer container) { this.container = container; } diff --git a/src/main/java/org/caosdb/server/entity/FileProperties.java b/src/main/java/org/caosdb/server/entity/FileProperties.java index 0c10288e0cbaa6a4f55a2959f72ac09db9d6f394..c2c2c10fae44d4d84af56a33a052f5ed589debed 100644 --- a/src/main/java/org/caosdb/server/entity/FileProperties.java +++ b/src/main/java/org/caosdb/server/entity/FileProperties.java @@ -289,4 +289,8 @@ public class FileProperties { public void removeOnCleanUp(final String tempPath) { this.tempPath = tempPath; } + + public boolean hasTmpIdentifier() { + return this.tmpIdentifyer != null; + } } diff --git a/src/main/java/org/caosdb/server/entity/InsertEntity.java b/src/main/java/org/caosdb/server/entity/InsertEntity.java index 56eeb12b1c2e7b9d5259a4d418e87c79e8068f51..4f7c33015e6ba70fb3d062ff4b32c27ebd80b8a8 100644 --- a/src/main/java/org/caosdb/server/entity/InsertEntity.java +++ b/src/main/java/org/caosdb/server/entity/InsertEntity.java @@ -29,4 +29,8 @@ public class InsertEntity extends WritableEntity { public InsertEntity(final Element element) { super(element); } + + public InsertEntity(String name, Role role) { + super(name, role); + } } diff --git a/src/main/java/org/caosdb/server/entity/WritableEntity.java b/src/main/java/org/caosdb/server/entity/WritableEntity.java index d9dc3edc4fc1fa886370bafb5c40ce0e732b3e4b..ab8ccbb995430e267e84d1f2a235c0d928e6762d 100644 --- a/src/main/java/org/caosdb/server/entity/WritableEntity.java +++ b/src/main/java/org/caosdb/server/entity/WritableEntity.java @@ -29,4 +29,8 @@ public class WritableEntity extends Entity { public WritableEntity(final Element element) { super(element); } + + public WritableEntity(String name, Role role) { + super(name, role); + } } diff --git a/src/main/java/org/caosdb/server/entity/container/DeleteContainer.java b/src/main/java/org/caosdb/server/entity/container/DeleteContainer.java deleted file mode 100644 index b0c2f21ff3b0e0dd96e24f0ef042a29e843c54f0..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/entity/container/DeleteContainer.java +++ /dev/null @@ -1,50 +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.entity.container; - -import java.util.HashMap; -import org.apache.shiro.subject.Subject; -import org.caosdb.server.entity.DeleteEntity; - -public class DeleteContainer extends EntityByIdContainer { - - private static final long serialVersionUID = -1279198934417710461L; - - public DeleteContainer( - final Subject user, - final Long timestamp, - final String srid, - final HashMap<String, String> flags) { - super(user, timestamp, srid, flags); - } - - @Override - public void add(final int id) { - add(new DeleteEntity(id)); - } - - @Override - public void add(int id, String version) { - add(new DeleteEntity(id, version)); - } -} diff --git a/src/main/java/org/caosdb/server/entity/container/InsertContainer.java b/src/main/java/org/caosdb/server/entity/container/InsertContainer.java deleted file mode 100644 index 99128a8a56758e67ce1529785de0151b1fe624d4..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/entity/container/InsertContainer.java +++ /dev/null @@ -1,46 +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.entity.container; - -import java.util.HashMap; -import org.apache.shiro.subject.Subject; -import org.caosdb.server.entity.InsertEntity; -import org.jdom2.Element; - -public class InsertContainer extends WritableContainer { - - public InsertContainer( - final Subject user, - final Long timestamp, - final String srid, - final HashMap<String, String> flags) { - super(user, timestamp, srid, flags); - } - - private static final long serialVersionUID = 8896630069350477274L; - - @Override - public void add(final Element entity) { - add(new InsertEntity(entity)); - } -} diff --git a/src/main/java/org/caosdb/server/entity/container/UpdateContainer.java b/src/main/java/org/caosdb/server/entity/container/UpdateContainer.java deleted file mode 100644 index 1c2a807b7759ff680d30ed0f1f9f14d5952e98f8..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/entity/container/UpdateContainer.java +++ /dev/null @@ -1,46 +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.entity.container; - -import java.util.HashMap; -import org.apache.shiro.subject.Subject; -import org.caosdb.server.entity.UpdateEntity; -import org.jdom2.Element; - -public class UpdateContainer extends WritableContainer { - - private static final long serialVersionUID = 8489287672264669883L; - - public UpdateContainer( - final Subject user, - final Long timestamp, - final String srid, - final HashMap<String, String> flags) { - super(user, timestamp, srid, flags); - } - - @Override - public void add(final Element entity) { - add(new UpdateEntity(entity)); - } -} diff --git a/src/main/java/org/caosdb/server/entity/container/WritableContainer.java b/src/main/java/org/caosdb/server/entity/container/WritableContainer.java index 2f9148651c890a0e7188acb035bf8fc55609834b..5fdb8c39ec7f01280278bec0a1f2928bfed7f5c2 100644 --- a/src/main/java/org/caosdb/server/entity/container/WritableContainer.java +++ b/src/main/java/org/caosdb/server/entity/container/WritableContainer.java @@ -4,6 +4,8 @@ * * Copyright (C) 2018 Research Group Biomedical Physics, * Max-Planck-Institute for Dynamics and Self-Organization Göttingen + * Copyright (C) 2021 Indiscale GmbH <info@indiscale.com> + * Copyright (C) 2021 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 @@ -23,12 +25,12 @@ package org.caosdb.server.entity.container; import java.util.HashMap; +import org.apache.shiro.SecurityUtils; import org.apache.shiro.subject.Subject; import org.caosdb.server.entity.FileProperties; -import org.jdom2.Element; -public abstract class WritableContainer extends TransactionContainer { - private static final long serialVersionUID = 3011242254959617091L; +public class WritableContainer extends TransactionContainer { + private static final long serialVersionUID = -4097777313518959519L; public WritableContainer( final Subject user, @@ -38,7 +40,9 @@ public abstract class WritableContainer extends TransactionContainer { super(user, timestamp, srid, flags); } - public abstract void add(final Element entity); + public WritableContainer() { + this(SecurityUtils.getSubject(), System.currentTimeMillis(), null, null); + } @Override public void addFile(final String uploadId, final FileProperties fileProperties) { diff --git a/src/main/java/org/caosdb/server/jobs/Job.java b/src/main/java/org/caosdb/server/jobs/Job.java index 5d8dd5d98a00960561c6b97f8e1f4fa118eafd0b..28dc512b669eb6f1df0cfd24e32e2051f734b697 100644 --- a/src/main/java/org/caosdb/server/jobs/Job.java +++ b/src/main/java/org/caosdb/server/jobs/Job.java @@ -37,7 +37,7 @@ 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.RetrieveFullEntity; +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.backend.transaction.RuleLoader; @@ -218,7 +218,7 @@ public abstract class Job extends AbstractObservable implements Observer { } protected final EntityInterface retrieveValidEntity(Integer id) { - return execute(new RetrieveFullEntity(id)).getContainer().get(0); + return execute(new RetrieveFullEntityTransaction(id)).getContainer().get(0); } protected final Integer retrieveValidIDByName(final String name) { diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckEntityACLRoles.java b/src/main/java/org/caosdb/server/jobs/core/CheckEntityACLRoles.java index e2dd63b1d733d737e09a6b4e86f3fe4ae4bd0e30..037b38b90c94827265d47876e722686d5d030cc0 100644 --- a/src/main/java/org/caosdb/server/jobs/core/CheckEntityACLRoles.java +++ b/src/main/java/org/caosdb/server/jobs/core/CheckEntityACLRoles.java @@ -26,10 +26,10 @@ import org.caosdb.server.CaosDBServer; import org.caosdb.server.ServerProperties; import org.caosdb.server.accessControl.AuthenticationUtils; import org.caosdb.server.entity.EntityInterface; +import org.caosdb.server.entity.InsertEntity; +import org.caosdb.server.entity.UpdateEntity; import org.caosdb.server.jobs.ContainerJob; import org.caosdb.server.permissions.EntityACI; -import org.caosdb.server.transaction.Insert; -import org.caosdb.server.transaction.Update; import org.caosdb.server.utils.EntityStatus; import org.caosdb.server.utils.ServerMessages; @@ -37,12 +37,9 @@ public class CheckEntityACLRoles extends ContainerJob { @Override protected void run() { - if (!(getTransaction() instanceof Update || getTransaction() instanceof Insert)) { - return; - } - for (final EntityInterface entity : getContainer()) { - if (entity.getEntityACL() != null) { + if ((entity instanceof UpdateEntity || entity instanceof InsertEntity) + && entity.getEntityACL() != null) { for (final EntityACI aci : entity.getEntityACL().getRules()) { if (!AuthenticationUtils.isResponsibleAgentExistent(aci.getResponsibleAgent())) { if (CaosDBServer.getServerProperty(ServerProperties.KEY_CHECK_ENTITY_ACL_ROLES_MODE) 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 c592eca81394e4a92582fd3d2286b5ac0507dcce..7aca2bac3dd3709006497f9ce8f556f267c4ca81 100644 --- a/src/main/java/org/caosdb/server/jobs/core/CheckParOblPropPresent.java +++ b/src/main/java/org/caosdb/server/jobs/core/CheckParOblPropPresent.java @@ -22,7 +22,7 @@ */ package org.caosdb.server.jobs.core; -import org.caosdb.server.database.backend.transaction.RetrieveFullEntity; +import org.caosdb.server.database.backend.transaction.RetrieveFullEntityTransaction; import org.caosdb.server.entity.EntityInterface; import org.caosdb.server.entity.StatementStatus; import org.caosdb.server.jobs.EntityJob; @@ -48,7 +48,7 @@ public class CheckParOblPropPresent extends EntityJob { runJobFromSchedule(getEntity(), CheckParValid.class); if (getEntity().hasParents()) { - execute(new RetrieveFullEntity(getEntity().getParents())); + execute(new RetrieveFullEntityTransaction(getEntity().getParents())); } // inherit properties 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 086787e8bd4752ccf0a25f8684afa5a5c4a33c82..d49bec09c909c5e884eae80619a5911a50b1f333 100644 --- a/src/main/java/org/caosdb/server/jobs/core/Inheritance.java +++ b/src/main/java/org/caosdb/server/jobs/core/Inheritance.java @@ -24,16 +24,16 @@ package org.caosdb.server.jobs.core; import java.util.ArrayList; import java.util.List; -import org.caosdb.server.database.backend.transaction.RetrieveFullEntity; +import org.caosdb.server.database.backend.transaction.RetrieveFullEntityTransaction; import org.caosdb.server.entity.Entity; 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.StatementStatus; +import org.caosdb.server.entity.UpdateEntity; import org.caosdb.server.entity.wrapper.Property; import org.caosdb.server.jobs.EntityJob; -import org.caosdb.server.transaction.Insert; -import org.caosdb.server.transaction.Update; import org.caosdb.server.utils.EntityStatus; /** @@ -63,7 +63,7 @@ public class Inheritance extends EntityJob { @Override protected void run() { - if (getTransaction() instanceof Insert || getTransaction() instanceof Update) { + if (getEntity() instanceof InsertEntity || getEntity() instanceof UpdateEntity) { if (getEntity().hasParents()) { final ArrayList<EntityInterface> transfer = new ArrayList<EntityInterface>(); parentLoop: @@ -87,7 +87,7 @@ public class Inheritance extends EntityJob { EntityInterface foreign = getEntityByName(parent.getName()); if (foreign == null) { // was not in container -> retrieve from database. - execute(new RetrieveFullEntity(parent)); + execute(new RetrieveFullEntityTransaction(parent)); foreign = parent; } @@ -144,7 +144,7 @@ public class Inheritance extends EntityJob { } } } else { - execute(new RetrieveFullEntity(validProperty)); + execute(new RetrieveFullEntityTransaction(validProperty)); } if (validProperty.getEntityStatus() == EntityStatus.VALID) { collectInheritedProperties(transfer, validProperty, inheritance); diff --git a/src/main/java/org/caosdb/server/jobs/core/InsertFilesInDir.java b/src/main/java/org/caosdb/server/jobs/core/InsertFilesInDir.java index ac7a9b8389d4baeda012d0abe97d627ece638c87..35ce49297291accd41a7c293fc909cc0a27f9317 100644 --- a/src/main/java/org/caosdb/server/jobs/core/InsertFilesInDir.java +++ b/src/main/java/org/caosdb/server/jobs/core/InsertFilesInDir.java @@ -4,6 +4,8 @@ * * Copyright (C) 2018 Research Group Biomedical Physics, * Max-Planck-Institute for Dynamics and Self-Organization Göttingen + * Copyright (C) 2021 Indiscale GmbH <info@indiscale.com> + * Copyright (C) 2021 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 @@ -34,16 +36,19 @@ import org.caosdb.server.FileSystem; import org.caosdb.server.ServerProperties; import org.caosdb.server.database.exceptions.TransactionException; import org.caosdb.server.database.misc.RollBackHandler; -import org.caosdb.server.entity.Entity; +import org.caosdb.server.entity.EntityInterface; import org.caosdb.server.entity.FileProperties; +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.Role; import org.caosdb.server.jobs.FlagJob; import org.caosdb.server.jobs.Job; import org.caosdb.server.jobs.JobAnnotation; import org.caosdb.server.jobs.JobExecutionTime; import org.caosdb.server.transaction.Retrieve; +import org.caosdb.server.transaction.WriteTransactionInterface; import org.caosdb.server.utils.EntityStatus; import org.caosdb.server.utils.FileUtils; import org.caosdb.server.utils.Undoable; @@ -208,7 +213,7 @@ public class InsertFilesInDir extends FlagJob { } else { i++; final String targetPath = root + sub.getName(); - final Entity newFileEntity = new Entity(sub.getName(), Role.File); + final EntityInterface newFileEntity = createInsertFileEntity(sub.getName()); final long size = sub.length(); final FileProperties fp = new FileProperties(null, targetPath, size); newFileEntity.setFileProperties(fp); @@ -253,6 +258,22 @@ public class InsertFilesInDir extends FlagJob { return i; } + /** + * Create a new InsertEntity (if this is an actual run) or a new RetrieveEntity (in dry-run mode) + * with {@link Role.File}. + * + * @param name the file name + * @return new File entity + */ + private EntityInterface createInsertFileEntity(String name) { + if (getTransaction() instanceof WriteTransactionInterface) { + return new InsertEntity(name, Role.File); + } + EntityInterface result = new RetrieveEntity(name); + result.setRole(Role.File); + return result; + } + boolean isExcluded(File f) throws IOException { return this.exclude != null && this.exclude.matcher(f.getCanonicalPath()).find(); } @@ -317,7 +338,7 @@ public class InsertFilesInDir extends FlagJob { return true; } - private void loadJobs(final Entity e) { + private void loadJobs(final EntityInterface e) { final List<Job> loadJobs = loadJobs(e, getTransaction()); getTransaction().getSchedule().addAll(loadJobs); } diff --git a/src/main/java/org/caosdb/server/query/Backreference.java b/src/main/java/org/caosdb/server/query/Backreference.java index 1fe26d97a5c7e8386328140a512f124fd405e7fc..fec64b19b761418a4da148ca4adff9d9145960af 100644 --- a/src/main/java/org/caosdb/server/query/Backreference.java +++ b/src/main/java/org/caosdb/server/query/Backreference.java @@ -130,6 +130,7 @@ public class Backreference implements EntityFilterInterface, QueryInterface { callApplyBackRef.setNull(2, VARCHAR); } if (this.propertiesTable != null) { // propertiesTable + getQuery().filterEntitiesWithoutRetrievePermission(this.propertiesTable); callApplyBackRef.setString(3, this.propertiesTable); this.statistics.put("propertiesTable", this.propertiesTable); this.statistics.put( @@ -139,6 +140,7 @@ public class Backreference implements EntityFilterInterface, QueryInterface { callApplyBackRef.setNull(3, VARCHAR); } if (this.entitiesTable != null) { // entitiesTable + getQuery().filterEntitiesWithoutRetrievePermission(this.entitiesTable); callApplyBackRef.setString(4, this.entitiesTable); this.statistics.put("entitiesTable", this.entitiesTable); this.statistics.put( diff --git a/src/main/java/org/caosdb/server/resource/EntityOwnerResource.java b/src/main/java/org/caosdb/server/resource/EntityOwnerResource.java index f20a4ab042bd996ef7e7d004e9246935cf034b64..41f92a5631d9cabb04ee5498373848b516885424 100644 --- a/src/main/java/org/caosdb/server/resource/EntityOwnerResource.java +++ b/src/main/java/org/caosdb/server/resource/EntityOwnerResource.java @@ -4,6 +4,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> * * 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,20 +24,26 @@ */ package org.caosdb.server.resource; +import org.caosdb.server.entity.EntityInterface; import org.caosdb.server.entity.container.RetrieveContainer; -import org.caosdb.server.resource.transaction.EntityResource; -import org.caosdb.server.resource.transaction.handlers.RequestHandler; -import org.caosdb.server.resource.transaction.handlers.RetriveOwnerRequestHandler; +import org.caosdb.server.permissions.EntityPermission; +import org.caosdb.server.resource.transaction.RetrieveEntityResource; -public class EntityOwnerResource extends EntityResource { - - public EntityOwnerResource() { - // only retrieval is allowed - super(true, false, false, false); - } +/** + * Resource which returns the entity with the owner information attached to it. + * + * <p>Note: 'Owners' are all roles with the {@link EntityPermission#EDIT_ACL} + * + * @author Timm Fitschen (t.fitschen@indiscale.com) + */ +public class EntityOwnerResource extends RetrieveEntityResource { @Override - protected RequestHandler<RetrieveContainer> getGetRequestHandler() { - return new RetriveOwnerRequestHandler(); + protected void handleRetrieveContainer(RetrieveContainer container) { + super.handleRetrieveContainer(container); + for (final EntityInterface e : container) { + // with this flag, the owner info will be loaded as well. + e.setFlag("owner", null); + } } } diff --git a/src/main/java/org/caosdb/server/resource/EntityPermissionsResource.java b/src/main/java/org/caosdb/server/resource/EntityPermissionsResource.java index ffce6fe18378ec2586276cd69124cfaeaf8f29fc..1f5ee5f7d8a28a235de02bbc33f826da32af0615 100644 --- a/src/main/java/org/caosdb/server/resource/EntityPermissionsResource.java +++ b/src/main/java/org/caosdb/server/resource/EntityPermissionsResource.java @@ -4,6 +4,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> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -23,19 +25,20 @@ package org.caosdb.server.resource; import org.caosdb.server.entity.container.RetrieveContainer; -import org.caosdb.server.resource.transaction.EntityResource; -import org.caosdb.server.resource.transaction.handlers.RequestHandler; -import org.caosdb.server.resource.transaction.handlers.RetrieveGlobalEntityPermissionsHandler; +import org.caosdb.server.permissions.EntityPermission; +import org.caosdb.server.resource.transaction.RetrieveEntityResource; -public class EntityPermissionsResource extends EntityResource { - - public EntityPermissionsResource() { - // only retrieval is allowed - super(true, false, false, false); - } +/** + * Resource which appends the entity permissions (esp. the global ones) to the normal entity + * response. + * + * @author Timm Fitschen (t.fitschen@indiscale.com) + */ +public class EntityPermissionsResource extends RetrieveEntityResource { @Override - protected RequestHandler<RetrieveContainer> getGetRequestHandler() { - return new RetrieveGlobalEntityPermissionsHandler(); + protected void handleRetrieveContainer(RetrieveContainer container) { + super.handleRetrieveContainer(container); + container.addMessage(EntityPermission.getAllEntityPermissions()); } } diff --git a/src/main/java/org/caosdb/server/resource/transaction/EntityNamesResource.java b/src/main/java/org/caosdb/server/resource/transaction/EntityNamesResource.java index 0b3f8b7aa4f4a7724b9b6646e7ecb72330293980..0c66485ea3d62286d633d9580c1362ba33435095 100644 --- a/src/main/java/org/caosdb/server/resource/transaction/EntityNamesResource.java +++ b/src/main/java/org/caosdb/server/resource/transaction/EntityNamesResource.java @@ -1,18 +1,41 @@ +/* + * ** 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) 2021 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2021 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 + * 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.resource.transaction; import org.caosdb.server.entity.container.RetrieveContainer; -import org.caosdb.server.resource.transaction.handlers.GetNamesRequestHandler; -import org.caosdb.server.resource.transaction.handlers.RequestHandler; -public class EntityNamesResource extends EntityResource { - - public EntityNamesResource() { - // only get is allowed - super(true, false, false, false); - } +/** + * Resource which returns only the names of entities. + * + * @author Timm Fitschen (t.fitschen@indiscale.com) + */ +public class EntityNamesResource extends RetrieveEntityResource { @Override - protected RequestHandler<RetrieveContainer> getGetRequestHandler() { - return new GetNamesRequestHandler(); + protected void handleRetrieveContainer(RetrieveContainer container) { + super.handleRetrieveContainer(container); + container.getFlags().put("names", 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 12659362e0a9a82e8cbb95e73e4d6ac6c2d27c6c..704fcfb9aa92e56ad963365d763f70fa7feb470e 100644 --- a/src/main/java/org/caosdb/server/resource/transaction/EntityResource.java +++ b/src/main/java/org/caosdb/server/resource/transaction/EntityResource.java @@ -4,6 +4,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> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -24,182 +26,167 @@ package org.caosdb.server.resource.transaction; import java.io.IOException; import java.security.NoSuchAlgorithmException; -import java.sql.SQLException; +import java.util.List; +import org.apache.commons.fileupload.FileItemIterator; +import org.apache.commons.fileupload.FileItemStream; +import org.apache.commons.fileupload.FileUploadException; +import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.caosdb.server.CaosDBException; +import org.caosdb.server.FileSystem; import org.caosdb.server.database.backend.implementation.MySQL.ConnectionException; -import org.caosdb.server.entity.container.DeleteContainer; -import org.caosdb.server.entity.container.InsertContainer; -import org.caosdb.server.entity.container.RetrieveContainer; -import org.caosdb.server.entity.container.UpdateContainer; -import org.caosdb.server.resource.AbstractCaosDBServerResource; -import org.caosdb.server.resource.transaction.handlers.FileUploadHandler; -import org.caosdb.server.resource.transaction.handlers.RequestHandler; -import org.caosdb.server.resource.transaction.handlers.SimpleDeleteRequestHandler; -import org.caosdb.server.resource.transaction.handlers.SimpleGetRequestHandler; -import org.caosdb.server.resource.transaction.handlers.SimpleWriteHandler; -import org.caosdb.server.transaction.Delete; -import org.caosdb.server.transaction.Insert; -import org.caosdb.server.transaction.Retrieve; -import org.caosdb.server.transaction.Update; +import org.caosdb.server.entity.DeleteEntity; +import org.caosdb.server.entity.FileProperties; +import org.caosdb.server.entity.InsertEntity; +import org.caosdb.server.entity.Message; +import org.caosdb.server.entity.UpdateEntity; +import org.caosdb.server.entity.container.WritableContainer; +import org.caosdb.server.transaction.WriteTransaction; +import org.caosdb.server.transaction.WriteTransactionInterface; +import org.caosdb.server.utils.ServerMessages; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.JDOMException; import org.restlet.data.MediaType; -import org.restlet.data.Status; +import org.restlet.ext.fileupload.RestletFileUpload; import org.restlet.representation.Representation; -public class EntityResource extends AbstractCaosDBServerResource { - - private final boolean get; - private final boolean post; - private final boolean put; - private final boolean delete; - - public EntityResource( - final boolean get, final boolean post, final boolean put, final boolean delete) { - this.get = get; - this.post = post; - this.put = put; - this.delete = delete; - } - - public EntityResource() { - this(true, true, true, true); - } - - protected RequestHandler<RetrieveContainer> getGetRequestHandler() { - return new SimpleGetRequestHandler(); - } - - protected RequestHandler<DeleteContainer> getDeleteRequestHandler() { - return new SimpleDeleteRequestHandler(); - } - - protected RequestHandler<InsertContainer> getPostRequestHandler() { - if (getRequest().getEntity().getMediaType() != null - && getRequest().getEntity().getMediaType().equals(MediaType.MULTIPART_FORM_DATA, true)) { - return new FileUploadHandler<InsertContainer>(); - } else { - return new SimpleWriteHandler<InsertContainer>(); - } - } - - protected RequestHandler<UpdateContainer> getPutRequestHandler() { - if (getRequest().getEntity().getMediaType() != null - && getRequest().getEntity().getMediaType().equals(MediaType.MULTIPART_FORM_DATA, true)) { - return new FileUploadHandler<UpdateContainer>(); - } else { - return new SimpleWriteHandler<UpdateContainer>(); - } - } - - @Override - protected final Representation httpGetInChildClass() - throws ConnectionException, IOException, SQLException, CaosDBException, - NoSuchAlgorithmException, Exception { - - final long t1 = System.currentTimeMillis(); - if (!this.get) { - getResponse().setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - return null; - } - final RetrieveContainer entityContainer = - new RetrieveContainer(getUser(), getTimestamp(), getSRID(), getFlags()); - final Document doc = new Document(); - - getGetRequestHandler().handle(this, entityContainer); - - final long t2 = System.currentTimeMillis(); - entityContainer - .getTransactionBenchmark() - .addMeasurement(getClass().getSimpleName() + ".httpGetInChildClass#handle", t2 - t1); - - final Retrieve retrieve = new Retrieve(entityContainer); - retrieve.execute(); - - final long t3 = System.currentTimeMillis(); - entityContainer - .getTransactionBenchmark() - .addMeasurement( - getClass().getSimpleName() + ".httpGetInChildClass#retrieve.execute", t3 - t2); - - final Element rootElem = generateRootElement(); - entityContainer.addToElement(rootElem); - doc.setRootElement(rootElem); - - final long t4 = System.currentTimeMillis(); - entityContainer - .getTransactionBenchmark() - .addMeasurement( - getClass().getSimpleName() + ".httpGetInChildClass#element_handling", t4 - t3); - - return ok(doc); - } +/** + * Handles POST, PUT, and DELETE requests for Entities. + * + * <p>The GET requests (Retrieval) is handled in the superclass {@link RetrieveEntityResource}. + * + * @author Timm Fitschen (t.fitschen@indiscale.com) + */ +public class EntityResource extends RetrieveEntityResource { + /** Handle entity deletions (DELETE requests). */ @Override protected final Representation httpDeleteInChildClass() throws Exception { - if (!this.delete) { - getResponse().setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - return null; - } - final DeleteContainer entityContainer = - new DeleteContainer(getUser(), getTimestamp(), getSRID(), getFlags()); + final WritableContainer container = + new WritableContainer(getUser(), getTimestamp(), getSRID(), getFlags()); final Document doc = new Document(); - getDeleteRequestHandler().handle(this, entityContainer); + for (final String item : getRequestedItems()) { + String[] elem = item.split("@", 1); + Integer id = null; + String version = null; + try { + id = Integer.parseInt(elem[0]); + } catch (NumberFormatException e) { + // pass + } + if (elem.length > 1) { + version = elem[1]; + } + + if (id != null) { + container.add(new DeleteEntity(id, version)); + } + } - final Delete delete = new Delete(entityContainer); + final WriteTransactionInterface delete = new WriteTransaction(container); delete.execute(); final Element rootElem = generateRootElement(); - entityContainer.addToElement(rootElem); + container.addToElement(rootElem); doc.setRootElement(rootElem); return ok(doc); } + /** Handle entity insertions (POST requests). */ @Override protected final Representation httpPostInChildClass(final Representation entity) throws xmlNotWellFormedException, Exception { - if (!this.post) { - getResponse().setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - return null; - } + final WritableContainer entityContainer = + new WritableContainer(getUser(), getTimestamp(), getSRID(), getFlags()); - final InsertContainer entityContainer = - new InsertContainer(getUser(), getTimestamp(), getSRID(), getFlags()); - final Document doc = new Document(); + List<Element> insertEntities; + if (entity.getMediaType() != null + && entity.getMediaType().equals(MediaType.MULTIPART_FORM_DATA, true)) { + insertEntities = parseMultipartEntity(entityContainer).getChildren(); + } else { + insertEntities = parseEntity(entity).getRootElement().getChildren(); + } - getPostRequestHandler().handle(this, entityContainer); + for (final Element e : insertEntities) { + entityContainer.add(new InsertEntity(e)); + } - final Insert insert = new Insert(entityContainer); + final WriteTransactionInterface insert = new WriteTransaction(entityContainer); insert.execute(); final Element rootElem = generateRootElement(); entityContainer.addToElement(rootElem); + final Document doc = new Document(); doc.setRootElement(rootElem); return ok(doc); } + /** + * Parse the body of requests with content type "multipart/form-data". This is specific for + * requests which also contain file blobs. + */ + private Element parseMultipartEntity(WritableContainer container) + throws FileUploadException, IOException, Message, xmlNotWellFormedException, + NoSuchAlgorithmException, CaosDBException { + final DiskFileItemFactory factory = new DiskFileItemFactory(); + factory.setSizeThreshold(1000240); + final RestletFileUpload upload = new RestletFileUpload(factory); + final FileItemIterator iter = upload.getItemIterator(getRequest().getEntity()); + + FileItemStream item = iter.next(); + + final Element element; + // First part of the multi-part form data entity must be the xml + // representation of the files. + // Name of the form field: FileRepresentation + if (item.isFormField()) { + if (item.getFieldName().equals("FileRepresentation")) { + element = parseEntity(item.openStream()).getRootElement(); + } else { + throw ServerMessages.NO_FILE_REPRESENTATION_SUBMITTED; + } + } else { + throw ServerMessages.FORM_CONTAINS_UNSUPPORTED_CONTENT; + } + + // Get files, store to tmp dir. + while (iter.hasNext()) { + item = iter.next(); + final FileProperties file = FileSystem.upload(item, getSRID()); + container.addFile(item.getName(), file); + } + + // transform xml elements to entities and add them to the container. + return element; + } + + /** Handle entity updates (PUT requests). */ @Override protected final Representation httpPutInChildClass(final Representation entity) throws ConnectionException, JDOMException, Exception, xmlNotWellFormedException { - if (!this.put) { - getResponse().setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); - return null; - } - - final UpdateContainer entityContainer = - new UpdateContainer(getUser(), getTimestamp(), getSRID(), getFlags()); + final WritableContainer entityContainer = + new WritableContainer(getUser(), getTimestamp(), getSRID(), getFlags()); final Document doc = new Document(); - getPutRequestHandler().handle(this, entityContainer); + List<Element> updateEntities; + if (getRequest().getEntity().getMediaType() != null + && getRequest().getEntity().getMediaType().equals(MediaType.MULTIPART_FORM_DATA, true)) { + updateEntities = parseMultipartEntity(entityContainer).getChildren(); + } else { + updateEntities = parseEntity(getRequest().getEntity()).getRootElement().getChildren(); + } + + for (final Element e : updateEntities) { + entityContainer.add(new UpdateEntity(e)); + } - final Update update = new Update(entityContainer); + final WriteTransactionInterface update = new WriteTransaction(entityContainer); update.execute(); final Element rootElem = generateRootElement(); diff --git a/src/main/java/org/caosdb/server/resource/transaction/RetrieveEntityResource.java b/src/main/java/org/caosdb/server/resource/transaction/RetrieveEntityResource.java new file mode 100644 index 0000000000000000000000000000000000000000..5eb9db72f69f8f054dbb6f02b970d09e09ff366c --- /dev/null +++ b/src/main/java/org/caosdb/server/resource/transaction/RetrieveEntityResource.java @@ -0,0 +1,110 @@ +/* + * ** 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) 2021 Timm Fitschen <t.fitschen@indiscale.com> + * Copyright (C) 2021 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 + * 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.resource.transaction; + +import java.io.IOException; +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.container.RetrieveContainer; +import org.caosdb.server.resource.AbstractCaosDBServerResource; +import org.caosdb.server.transaction.Retrieve; +import org.jdom2.Document; +import org.jdom2.Element; +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) + */ +public abstract class RetrieveEntityResource extends AbstractCaosDBServerResource { + + /** + * Parse the segment which specifies the entities which are to be retrieved + * + * @param container + */ + protected void handleRetrieveContainer(RetrieveContainer container) { + + for (final String item : getRequestedItems()) { + String[] elem = item.split("@", 2); + Integer id = null; + String name = null; + String version = null; + try { + id = Integer.parseInt(elem[0]); + } catch (NumberFormatException e) { + name = elem[0]; + } + if (elem.length > 1) { + version = elem[1]; + } + + if (id != null) { + container.add(id, version); + } else { + container.add(name); + } + } + } + + /** Handle the GET request. */ + @Override + protected final Representation httpGetInChildClass() + throws ConnectionException, IOException, SQLException, CaosDBException, + NoSuchAlgorithmException, Exception { + + final RetrieveContainer entityContainer = + new RetrieveContainer(getUser(), getTimestamp(), getSRID(), getFlags()); + + handleRetrieveContainer(entityContainer); + + final long t2 = System.currentTimeMillis(); + final Retrieve retrieve = new Retrieve(entityContainer); + retrieve.execute(); + + final long t3 = System.currentTimeMillis(); + entityContainer + .getTransactionBenchmark() + .addMeasurement( + getClass().getSimpleName() + ".httpGetInChildClass#retrieve.execute", t3 - t2); + + final Element rootElem = generateRootElement(); + entityContainer.addToElement(rootElem); + final Document doc = new Document(); + doc.setRootElement(rootElem); + + final long t4 = System.currentTimeMillis(); + entityContainer + .getTransactionBenchmark() + .addMeasurement( + getClass().getSimpleName() + ".httpGetInChildClass#element_handling", t4 - t3); + + return ok(doc); + } +} diff --git a/src/main/java/org/caosdb/server/resource/transaction/handlers/FileUploadHandler.java b/src/main/java/org/caosdb/server/resource/transaction/handlers/FileUploadHandler.java deleted file mode 100644 index ec5c5bc4d144af9ce99f7b27915a52420c331bb5..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/resource/transaction/handlers/FileUploadHandler.java +++ /dev/null @@ -1,81 +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.resource.transaction.handlers; - -import java.io.IOException; -import org.apache.commons.fileupload.FileItemIterator; -import org.apache.commons.fileupload.FileItemStream; -import org.apache.commons.fileupload.FileUploadException; -import org.apache.commons.fileupload.disk.DiskFileItemFactory; -import org.caosdb.server.FileSystem; -import org.caosdb.server.entity.FileProperties; -import org.caosdb.server.entity.container.WritableContainer; -import org.caosdb.server.resource.transaction.EntityResource; -import org.caosdb.server.utils.ServerMessages; -import org.jdom2.Element; -import org.restlet.data.MediaType; -import org.restlet.ext.fileupload.RestletFileUpload; - -public class FileUploadHandler<T extends WritableContainer> extends SimpleWriteHandler<T> { - - @Override - public void handle(final EntityResource t, final T container) throws Exception { - if (!t.getRequest().getEntity().getMediaType().equals(MediaType.MULTIPART_FORM_DATA, true)) { - throw new Exception("Wrong MEDIATYPE"); - } - final DiskFileItemFactory factory = new DiskFileItemFactory(); - factory.setSizeThreshold(1000240); - final RestletFileUpload upload = new RestletFileUpload(factory); - final FileItemIterator iter = upload.getItemIterator(t.getRequest().getEntity()); - - FileItemStream item = iter.next(); - - final Element element; - // First part of the multi-part form data entity must be the xml - // representation of the files. - // Name of the form field: FileRepresentation - if (item.isFormField()) { - if (item.getFieldName().equals("FileRepresentation")) { - element = t.parseEntity(item.openStream()).getRootElement(); - } else { - throw ServerMessages.NO_FILE_REPRESENTATION_SUBMITTED; - } - } else { - throw ServerMessages.FORM_CONTAINS_UNSUPPORTED_CONTENT; - } - - // Get files, store to tmp dir. - while (iter.hasNext()) { - item = iter.next(); - try { - final FileProperties file = FileSystem.upload(item, t.getSRID()); - container.addFile(item.getName(), file); - } catch (final IOException e) { - throw new FileUploadException(); - } - } - - // transform xml elements to entities and add them to the container. - processEntities(container, element); - } -} diff --git a/src/main/java/org/caosdb/server/resource/transaction/handlers/GetNamesRequestHandler.java b/src/main/java/org/caosdb/server/resource/transaction/handlers/GetNamesRequestHandler.java deleted file mode 100644 index c327c6833d281c07148c59be689ddfac25b33f4d..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/resource/transaction/handlers/GetNamesRequestHandler.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.caosdb.server.resource.transaction.handlers; - -import org.caosdb.server.entity.container.RetrieveContainer; -import org.caosdb.server.resource.transaction.EntityResource; - -public class GetNamesRequestHandler extends RequestHandler<RetrieveContainer> { - - /** Adds the `names` flag to the RetrieveContainer which triggers the RetrieveAllNames job. */ - @Override - public void handle(EntityResource t, RetrieveContainer container) throws Exception { - container.getFlags().put("names", null); - } -} diff --git a/src/main/java/org/caosdb/server/resource/transaction/handlers/RequestHandler.java b/src/main/java/org/caosdb/server/resource/transaction/handlers/RequestHandler.java deleted file mode 100644 index fc49cc518db12e1f750716372f94b808dcb3874b..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/resource/transaction/handlers/RequestHandler.java +++ /dev/null @@ -1,31 +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.resource.transaction.handlers; - -import org.caosdb.server.entity.container.TransactionContainer; -import org.caosdb.server.resource.transaction.EntityResource; - -public abstract class RequestHandler<T extends TransactionContainer> { - - public abstract void handle(final EntityResource t, T container) throws Exception; -} diff --git a/src/main/java/org/caosdb/server/resource/transaction/handlers/RetrieveGlobalEntityPermissionsHandler.java b/src/main/java/org/caosdb/server/resource/transaction/handlers/RetrieveGlobalEntityPermissionsHandler.java deleted file mode 100644 index 6801e804a8bcb721e2c7170ad857126579cbfb46..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/resource/transaction/handlers/RetrieveGlobalEntityPermissionsHandler.java +++ /dev/null @@ -1,36 +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.resource.transaction.handlers; - -import org.caosdb.server.entity.container.RetrieveContainer; -import org.caosdb.server.permissions.EntityPermission; -import org.caosdb.server.resource.transaction.EntityResource; - -public class RetrieveGlobalEntityPermissionsHandler extends SimpleGetRequestHandler { - - @Override - public void handle(final EntityResource t, final RetrieveContainer container) throws Exception { - super.handle(t, container); - container.addMessage(EntityPermission.getAllEntityPermissions()); - } -} diff --git a/src/main/java/org/caosdb/server/resource/transaction/handlers/RetriveOwnerRequestHandler.java b/src/main/java/org/caosdb/server/resource/transaction/handlers/RetriveOwnerRequestHandler.java deleted file mode 100644 index 7b2d698cb4673ba7e4ed30fc81fc249e89632734..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/resource/transaction/handlers/RetriveOwnerRequestHandler.java +++ /dev/null @@ -1,38 +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.resource.transaction.handlers; - -import org.caosdb.server.entity.EntityInterface; -import org.caosdb.server.entity.container.RetrieveContainer; -import org.caosdb.server.resource.transaction.EntityResource; - -public class RetriveOwnerRequestHandler extends SimpleGetRequestHandler { - - @Override - public void handle(final EntityResource t, final RetrieveContainer container) throws Exception { - super.handle(t, container); - for (final EntityInterface e : container) { - e.setFlag("owner", null); - } - } -} diff --git a/src/main/java/org/caosdb/server/resource/transaction/handlers/SimpleDeleteRequestHandler.java b/src/main/java/org/caosdb/server/resource/transaction/handlers/SimpleDeleteRequestHandler.java deleted file mode 100644 index 34c1a3836290f4e90fde5a1ec10a2818b7b63fae..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/resource/transaction/handlers/SimpleDeleteRequestHandler.java +++ /dev/null @@ -1,55 +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 - * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com> - * Copyright (C) 2020 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/>. - * - * ** end header - */ -package org.caosdb.server.resource.transaction.handlers; - -import org.caosdb.server.entity.container.DeleteContainer; -import org.caosdb.server.resource.transaction.EntityResource; - -public class SimpleDeleteRequestHandler extends RequestHandler<DeleteContainer> { - - @Override - public void handle(final EntityResource t, final DeleteContainer container) throws Exception { - // TODO a lot of code duplication, see SimpleGetRequestHandle#handle. - // However, this is about to be changed again when string ids are - // introduced, anyways. So we just leave it. - for (final String item : t.getRequestedItems()) { - String[] elem = item.split("@", 1); - Integer id = null; - String version = null; - try { - id = Integer.parseInt(elem[0]); - } catch (NumberFormatException e) { - // pass - } - if (elem.length > 1) { - version = elem[1]; - } - - if (id != null) { - container.add(id, version); - } - } - } -} diff --git a/src/main/java/org/caosdb/server/resource/transaction/handlers/SimpleGetRequestHandler.java b/src/main/java/org/caosdb/server/resource/transaction/handlers/SimpleGetRequestHandler.java deleted file mode 100644 index b80d9c219e2cbda1e012c9c73993b5ea427c6ccf..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/resource/transaction/handlers/SimpleGetRequestHandler.java +++ /dev/null @@ -1,53 +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.resource.transaction.handlers; - -import org.caosdb.server.entity.container.RetrieveContainer; -import org.caosdb.server.resource.transaction.EntityResource; - -public class SimpleGetRequestHandler extends RequestHandler<RetrieveContainer> { - - @Override - public void handle(final EntityResource t, final RetrieveContainer container) throws Exception { - for (final String item : t.getRequestedItems()) { - String[] elem = item.split("@", 2); - Integer id = null; - String name = null; - String version = null; - try { - id = Integer.parseInt(elem[0]); - } catch (NumberFormatException e) { - name = elem[0]; - } - if (elem.length > 1) { - version = elem[1]; - } - - if (id != null) { - container.add(id, version); - } else { - container.add(name); - } - } - } -} diff --git a/src/main/java/org/caosdb/server/resource/transaction/handlers/SimpleWriteHandler.java b/src/main/java/org/caosdb/server/resource/transaction/handlers/SimpleWriteHandler.java deleted file mode 100644 index feb784da2f1ab9dcc09942a108bcce6bd073e128..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/resource/transaction/handlers/SimpleWriteHandler.java +++ /dev/null @@ -1,58 +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.resource.transaction.handlers; - -import java.util.List; -import org.caosdb.server.entity.container.WritableContainer; -import org.caosdb.server.resource.transaction.EntityResource; -import org.jdom2.Element; - -public class SimpleWriteHandler<T extends WritableContainer> extends RequestHandler<T> { - - /** - * Transform child elements of parameter 'element' to entities and add them to the container. Iff - * parameter 'tag' is not null, only elements with that tag name will be transformed into - * entities. The others will be ignored. - * - * @param container - * @param element - * @param tag - * @throws Exception - */ - protected void processEntities(final WritableContainer container, final Element element) - throws Exception { - - final List<Element> insertEntities; - insertEntities = element.getChildren(); - - for (final Element e : insertEntities) { - container.add(e); - } - } - - @Override - public void handle(final EntityResource t, final T container) throws Exception { - final Element element = t.parseEntity(t.getRequest().getEntity()).getRootElement(); - processEntities(container, element); - } -} diff --git a/src/main/java/org/caosdb/server/terminal/CaosDBTerminal.java b/src/main/java/org/caosdb/server/terminal/CaosDBTerminal.java deleted file mode 100644 index 96bd6315b7e32c03273a8b65bd489bfc061e0ef1..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/terminal/CaosDBTerminal.java +++ /dev/null @@ -1,72 +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.terminal; - -import com.googlecode.lanterna.TerminalFacade; -import com.googlecode.lanterna.gui.GUIScreen; -import com.googlecode.lanterna.gui.GUIScreen.Position; -import com.googlecode.lanterna.screen.Screen; -import com.googlecode.lanterna.terminal.Terminal; -import com.googlecode.lanterna.terminal.text.UnixTerminal; -import java.nio.charset.Charset; - -/** - * @deprecated Soon to be removed - * @author Timm Fitschen (t.fitschen@indiscale.com) - */ -@Deprecated -public class CaosDBTerminal extends Thread { - - public CaosDBTerminal() { - if ((this.terminal = - TerminalFacade.createTerminal(System.in, System.out, Charset.forName("UTF8"))) - instanceof UnixTerminal) { - this.terminal = - new UnixTerminal( - System.in, - System.out, - Charset.forName("UTF8"), - null, - UnixTerminal.Behaviour.CTRL_C_KILLS_APPLICATION); - } - this.screen = new Screen(this.terminal); - this.guiScreen = new GUIScreen(this.screen); - } - - private Terminal terminal = null; - private Screen screen = null; - private GUIScreen guiScreen = null; - - @Override - public void run() { - this.guiScreen.getScreen().startScreen(); - final MainWindow m = new MainWindow(); - - this.guiScreen.showWindow(m, Position.FULL_SCREEN); - this.guiScreen.getScreen().stopScreen(); - } - - public void shutDown() { - this.guiScreen.getActiveWindow().close(); - } -} diff --git a/src/main/java/org/caosdb/server/terminal/DatabaseAccessPanel.java b/src/main/java/org/caosdb/server/terminal/DatabaseAccessPanel.java deleted file mode 100644 index 6877049605888682bd003f8382a931661e79602c..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/terminal/DatabaseAccessPanel.java +++ /dev/null @@ -1,84 +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.terminal; - -import com.googlecode.lanterna.gui.component.Label; -import com.googlecode.lanterna.gui.component.Panel; -import com.googlecode.lanterna.gui.component.Table; -import org.caosdb.server.database.DatabaseMonitor; -import org.caosdb.server.utils.Observable; -import org.caosdb.server.utils.Observer; - -public final class DatabaseAccessPanel extends Panel implements Observer { - - private static final DatabaseAccessPanel instance = new DatabaseAccessPanel(); - private final Table table = new Table(2); - private final Label label1 = new Label(); - private final Label label2 = new Label(); - private final Label label3 = new Label(); - private final Label label4 = new Label(); - private final Label label5 = new Label(); - - public static final DatabaseAccessPanel getInstance() { - return instance; - } - - private DatabaseAccessPanel() { - super(); - DatabaseMonitor.acceptWeakAccessObserver(this); - DatabaseMonitor.acceptStrongAccessObserver(this); - - this.table.addRow(new Label("Weak access, acquired:"), this.label1); - this.table.addRow(new Label("Weak access, waiting:"), this.label2); - this.table.addRow(new Label("Strong access, allocated:"), this.label3); - this.table.addRow(new Label("Strong access, acquired:"), this.label4); - this.table.addRow(new Label("Strong access, waiting:"), this.label5); - addComponent(this.table); - - notifyObserver(null, null); - } - - @Override - public boolean notifyObserver(final String e, final Observable o) { - try { - synchronized (this) { - if (getParent() != null) { - this.label1.setText(Integer.toString(DatabaseMonitor.acquiredWeakAccess())); - this.label2.setText(Integer.toString(DatabaseMonitor.waitingAcquireWeakAccess())); - this.label3.setText( - DatabaseMonitor.whoHasAllocatedStrongAccess() != null - ? DatabaseMonitor.whoHasAllocatedStrongAccess().getName() - : "None"); - this.label4.setText( - DatabaseMonitor.whoHasAcquiredStrongAccess() != null - ? DatabaseMonitor.whoHasAcquiredStrongAccess().getName() - : "None"); - this.label5.setText(Integer.toString(DatabaseMonitor.waitingAllocateStrongAccess())); - } - } - } catch (final Exception exc) { - exc.printStackTrace(); - } - return true; - } -} diff --git a/src/main/java/org/caosdb/server/terminal/EntitiesPanel.java b/src/main/java/org/caosdb/server/terminal/EntitiesPanel.java deleted file mode 100644 index b2f7489de66eeae9771e911b45e7d4546cb4f5b9..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/terminal/EntitiesPanel.java +++ /dev/null @@ -1,85 +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.terminal; - -import com.googlecode.lanterna.gui.component.Label; -import com.googlecode.lanterna.gui.component.Panel; -import com.googlecode.lanterna.gui.component.Table; -import org.caosdb.server.utils.Info; - -public final class EntitiesPanel extends Panel implements StatComponent { - - private static final EntitiesPanel instance = new EntitiesPanel(); - private boolean active = false; - private final Table table = new Table(2); - private final Label label1 = new Label(); - private final Label label2 = new Label(); - private final Label label3 = new Label(); - private final Label label4 = new Label(); - - public static final EntitiesPanel getInstance() { - return instance; - } - - private EntitiesPanel() { - super(); - - this.table.addRow(new Label("RecordTypes:"), this.label2); - this.table.addRow(new Label("Properties:"), this.label3); - this.table.addRow(new Label("Records:"), this.label4); - this.table.addRow(new Label("Files:"), this.label1); - addComponent(this.table); - - // init INFO - Info.getInstance().notifyObserver(null, null); - - // adds itself to StatsPanel - StatsPanel.addStat("Entities", this); - start(); - } - - @Override - public void start() { - this.active = true; - update(); - } - - @Override - public void stop() { - this.active = false; - } - - @Override - public void update() { - if (this.active) { - try { - this.label1.setText(Info.getFilesCount().toString()); - this.label2.setText(Info.getRecordTypesCount().toString()); - this.label3.setText(Info.getPropertiesCount().toString()); - this.label4.setText(Info.getRecordsCount().toString()); - } catch (final Exception e) { - e.printStackTrace(); - } - } - } -} diff --git a/src/main/java/org/caosdb/server/terminal/MainWindow.java b/src/main/java/org/caosdb/server/terminal/MainWindow.java deleted file mode 100644 index aa22f12b23bed00dfb46db8c7cd140fb05551eec..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/terminal/MainWindow.java +++ /dev/null @@ -1,174 +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.terminal; - -import com.googlecode.lanterna.gui.Action; -import com.googlecode.lanterna.gui.Border; -import com.googlecode.lanterna.gui.Window; -import com.googlecode.lanterna.gui.component.Button; -import com.googlecode.lanterna.gui.component.Label; -import com.googlecode.lanterna.gui.component.Panel; -import com.googlecode.lanterna.gui.dialog.DialogButtons; -import com.googlecode.lanterna.gui.dialog.DialogResult; -import com.googlecode.lanterna.gui.dialog.MessageBox; -import com.googlecode.lanterna.gui.layout.LinearLayout; -import com.googlecode.lanterna.input.Key; -import com.googlecode.lanterna.input.Key.Kind; -import org.caosdb.server.utils.Observer; - -public class MainWindow extends Window { - - // A panel at the top of the window which contains buttons to switch - // between several data panels which are to be shown in the dataPanel. - final Panel dataSelectorPanel = new Panel(new Border.Invisible(), Panel.Orientation.HORISONTAL); - - // A panel right below the dataSelectorPanel. Here the interesting - // data is to be show. The contained panels can be made visible or - // invisible via the buttons in the dataSelectorPanel. - final Panel dataPanel = new Panel(new Border.Bevel(true), Panel.Orientation.HORISONTAL); - - public final synchronized void setVisibleDataPanel(final Panel visibleDataPanel) { - this.dataPanel.removeAllComponents(); - this.dataPanel.addComponent(visibleDataPanel); - } - - public MainWindow() { - super("Caos DB"); - addComponent(this.dataSelectorPanel, LinearLayout.MAXIMIZES_HORIZONTALLY); - addComponent( - this.dataPanel, LinearLayout.MAXIMIZES_HORIZONTALLY, LinearLayout.MAXIMIZES_VERTICALLY); - - // initializing stream output panels. These pipe the output of - // System.out and System.err to a TextArea. - final SystemOutPanel systemOutPanel = SystemOutPanel.getInstance(); - final Thread systemOutThread = new Thread(systemOutPanel); - systemOutThread.setName("systemOutPanel"); - - final SystemErrPanel systemErrPanel = SystemErrPanel.getInstance(); - final Thread systemErrThread = new Thread(systemErrPanel); - systemErrThread.setName("systemErrPanel"); - - systemOutThread.start(); - systemErrThread.start(); - - // initializing DatabaseAccessPanel that shows which thread is accessing - // the database and how many threads are waiting for access. - final DatabaseAccessPanel databaseAccessPanel = DatabaseAccessPanel.getInstance(); - - // init StatsPanel - StatsPanel.getPanel(); - - // init entities stats - EntitiesPanel.getInstance(); - - // add all initialized panels to the main window - addDataPanel("System.out", systemOutPanel); - addDataPanel("System.err", systemErrPanel); - addDataPanel("DB Access", databaseAccessPanel); - addDataPanel("Misc", StatsPanel.getPanel()); - - // add a welcome panel - this.dataPanel.addComponent( - new Label("Welcome. This is CaosDB - An Open Scientific DataBase.")); - - // add shutdown button - this.dataSelectorPanel.addComponent(new ShutdownButton()); - - // set focus on the dataSelectorPanel - setFocus(this.dataSelectorPanel.nextFocus(null)); - } - - /** - * Add a panel to the dataPanel. A button is created within the dataSelectorPanel which shows the - * name of the panel - * - * @param name - * @param dataPanel - */ - private void addDataPanel(final String name, final Panel dataPanel) { - this.dataSelectorPanel.addComponent(new DataPanelSelectorButton(name, dataPanel)); - } - - /** - * A Button that causes the dataPanel to show a certain panel. - * - * @author tf - */ - class DataPanelSelectorButton extends Button { - public DataPanelSelectorButton(final String name, final Panel panel) { - super( - name, - new Action() { - - @Override - public void doAction() { - // show panel in dataPanel - setVisibleDataPanel(panel); - - // update panel if necessary - if (panel instanceof Observer) { - ((Observer) panel).notifyObserver(null, null); - } - - // move focus to panel - setFocus(panel.nextFocus(null)); - } - }); - } - } - - @Override - public void onKeyPressed(final Key key) { - if (key.getKind() == Kind.Escape) { - setFocus(this.dataSelectorPanel.nextFocus(null)); - } else { - super.onKeyPressed(key); - } - } - - /** - * A button that causes the server to shut down when pressed. - * - * @author tf - */ - class ShutdownButton extends Button { - public ShutdownButton() { - super( - "Shutdown Server", - new Action() { - @Override - public void doAction() { - final DialogResult result = - MessageBox.showMessageBox( - getOwner(), - "Server shutdown", - "Select [OK] to shut down the server now.", - DialogButtons.OK_CANCEL); - if (result == DialogResult.OK) { - System.exit(0); - } - } - }); - } - } -} diff --git a/src/main/java/org/caosdb/server/terminal/OutputStreamPanel.java b/src/main/java/org/caosdb/server/terminal/OutputStreamPanel.java deleted file mode 100644 index 50284f8a88e7d30596cc901be24108f3ea201d0a..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/terminal/OutputStreamPanel.java +++ /dev/null @@ -1,151 +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.terminal; - -import com.googlecode.lanterna.gui.Action; -import com.googlecode.lanterna.gui.component.Button; -import com.googlecode.lanterna.gui.component.Panel; -import com.googlecode.lanterna.gui.component.TextArea; -import com.googlecode.lanterna.gui.layout.LinearLayout; -import com.googlecode.lanterna.input.Key; -import com.googlecode.lanterna.input.Key.Kind; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.PipedInputStream; -import java.io.PipedOutputStream; -import java.io.PrintStream; - -class PipedPrintStream extends PrintStream { - private final TextArea out; - int lines = 0; - - public PipedPrintStream(final TextArea out) { - super(new NullOutputStream()); - this.out = out; - } - - @Override - public void println(final String x) { - if (open) { - synchronized (this) { - out.appendLine(x); - if (lines >= 4000) { - out.removeLine(0); - } else { - lines++; - } - } - } - } - - @Override - public void print(final String x) {} - - private final boolean open = false; -} - -class NullOutputStream extends OutputStream { - - @Override - public void write(final int b) throws IOException {} -} - -public abstract class OutputStreamPanel extends Panel implements Runnable { - - private final TextArea logArea = new TextArea(); - private BufferedReader reader = null; - protected final PipedOutputStream panelOutputStream = new PipedOutputStream(); - - protected final PipedOutputStream getPanelOutputStream() { - return panelOutputStream; - } - - Button toogle = - new Button( - "on/off", - new Action() { - - @Override - public void doAction() { - toogle(); - } - }); - - private boolean on = true; - - void toogle() { - on = !on; - } - - public OutputStreamPanel() { - super(); - // addComponent(toogle); - addComponent(logArea, LinearLayout.MAXIMIZES_HORIZONTALLY, LinearLayout.MAXIMIZES_VERTICALLY); - } - - protected void init() throws IOException { - final PipedInputStream pIn = new PipedInputStream(panelOutputStream); - reader = new BufferedReader(new InputStreamReader(pIn)); - } - - @Override - public final void run() { - int lines = 0; - while (true) { - while (on) { - try { - if (reader.ready()) { - logArea.appendLine(reader.readLine()); - if (lines >= 4000) { - logArea.removeLine(0); - } else { - lines++; - } - try { - logArea.keyboardInteraction(new Key(Kind.End)); - } catch (final NullPointerException e) { - // this usually happens when the logArea is updated - // but - // not painted (while not being an active panel) - } - } else { - Thread.sleep(1000); - } - - } catch (final Exception e2) { - e2.printStackTrace(); - } - } - } - } - - @Override - protected void finalize() throws Throwable { - if (reader != null) { - reader.close(); - } - super.finalize(); - } -} diff --git a/src/main/java/org/caosdb/server/terminal/StatComponent.java b/src/main/java/org/caosdb/server/terminal/StatComponent.java deleted file mode 100644 index 90a9ac1125425bd23415abf3e4186f00695b9b2f..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/terminal/StatComponent.java +++ /dev/null @@ -1,34 +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.terminal; - -import com.googlecode.lanterna.gui.Component; - -public interface StatComponent extends Component { - - public void start(); - - public void stop(); - - public void update(); -} diff --git a/src/main/java/org/caosdb/server/terminal/StatLabel.java b/src/main/java/org/caosdb/server/terminal/StatLabel.java deleted file mode 100644 index 73a03ac31ed8874fc936807393701995130eaf7f..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/terminal/StatLabel.java +++ /dev/null @@ -1,71 +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.terminal; - -import com.googlecode.lanterna.gui.component.Label; -import org.caosdb.server.utils.Observable; -import org.caosdb.server.utils.Observer; - -public class StatLabel extends Label implements Observer, StatComponent { - - boolean active = false; - Object obj; - String name; - - @Override - public boolean notifyObserver(final String e, final Observable o) { - update(); - return true; - } - - public StatLabel(final Object obj) { - this(null, obj); - } - - public StatLabel(final String name, final Object obj) { - this.name = name; - this.obj = obj; - if (obj instanceof Observable) { - ((Observable) obj).acceptObserver(this); - } - start(); - update(); - } - - @Override - public void stop() { - this.active = false; - } - - @Override - public void start() { - this.active = true; - } - - @Override - public void update() { - if (this.active) { - setText((this.name == null ? "" : this.name + ": ") + this.obj.toString()); - } - } -} diff --git a/src/main/java/org/caosdb/server/terminal/StatTable.java b/src/main/java/org/caosdb/server/terminal/StatTable.java deleted file mode 100644 index e7234cdb8000a7c4dcc98463c3e9ca6cc8c9f19c..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/terminal/StatTable.java +++ /dev/null @@ -1,56 +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.terminal; - -import com.googlecode.lanterna.gui.Component; -import com.googlecode.lanterna.gui.component.Panel; - -public class StatTable extends Panel implements StatComponent { - - @Override - public void start() { - for (final Component c : components()) { - if (c instanceof StatComponent) { - ((StatComponent) c).start(); - } - } - } - - @Override - public void stop() { - for (final Component c : components()) { - if (c instanceof StatComponent) { - ((StatComponent) c).stop(); - } - } - } - - @Override - public void update() { - for (final Component c : components()) { - if (c instanceof StatComponent) { - ((StatComponent) c).update(); - } - } - } -} diff --git a/src/main/java/org/caosdb/server/terminal/StatsPanel.java b/src/main/java/org/caosdb/server/terminal/StatsPanel.java deleted file mode 100644 index 3b41ac6a9c4ae7366e85cffedbd71f83b30c6438..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/terminal/StatsPanel.java +++ /dev/null @@ -1,106 +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.terminal; - -import com.googlecode.lanterna.gui.Action; -import com.googlecode.lanterna.gui.Border; -import com.googlecode.lanterna.gui.component.Button; -import com.googlecode.lanterna.gui.component.Panel; -import com.googlecode.lanterna.gui.layout.LinearLayout; -import java.util.HashMap; - -public class StatsPanel extends Panel { - - private static StatsPanel instance = null; - - HashMap<String, StatTable> stats = new HashMap<String, StatTable>(); - Panel dataPanel = new Panel("Data", new Border.Bevel(true), Panel.Orientation.VERTICAL); - Panel selectorPanel = new Panel("Selector", new Border.Bevel(true), Panel.Orientation.VERTICAL); - StatComponent current = null; - - private StatsPanel() { - super(Panel.Orientation.HORISONTAL); - addComponent(this.selectorPanel, LinearLayout.MAXIMIZES_VERTICALLY); - addComponent( - this.dataPanel, LinearLayout.MAXIMIZES_VERTICALLY, LinearLayout.MAXIMIZES_HORIZONTALLY); - } - - public static void addStat(final String name, final Object obj) { - if (instance != null) { - instance.addStatComponent(name, new StatLabel(obj)); - } - } - - public static void addStat(final String name, final StatComponent statComponent) { - if (instance != null) { - instance.addStatComponent(name, statComponent); - } - } - - private void addStatComponent(final String name, final StatComponent statComponent) { - final StatTable c = this.stats.get(name); - if (c != null) { - c.addComponent(statComponent); - } else { - final StatTable table = new StatTable(); - table.addComponent(statComponent); - this.selectorPanel.addComponent(new SelectorButton(name, table)); - this.stats.put(name, table); - } - } - - private void setVisibleDataPanel(final StatComponent panel) { - this.dataPanel.removeAllComponents(); - - if (this.current != null) { - this.current.stop(); - } - this.current = panel; - this.current.start(); - this.current.update(); - - this.dataPanel.addComponent(this.current); - } - - class SelectorButton extends Button { - public SelectorButton(final String name, final StatComponent panel) { - super( - name, - new Action() { - - @Override - public void doAction() { - // show panel in dataPanel - setVisibleDataPanel(panel); - } - }); - } - } - - public static Panel getPanel() { - if (instance == null) { - instance = new StatsPanel(); - } - return instance; - } -} diff --git a/src/main/java/org/caosdb/server/terminal/SystemErrPanel.java b/src/main/java/org/caosdb/server/terminal/SystemErrPanel.java deleted file mode 100644 index 9bf800fdb3c755cd8ea2b2a55119cf4e093fde09..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/terminal/SystemErrPanel.java +++ /dev/null @@ -1,53 +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.terminal; - -import com.googlecode.lanterna.gui.component.Label; -import java.io.IOException; -import java.io.PrintStream; - -public final class SystemErrPanel extends OutputStreamPanel { - - private static final SystemErrPanel instance = new SystemErrPanel(); - private PrintStream err; - - public static final SystemErrPanel getInstance() { - return instance; - } - - private SystemErrPanel() { - super(); - try { - init(); - this.err = System.err; - final PrintStream newErr = new PrintStream(getPanelOutputStream()); - System.setErr(newErr); - } catch (final IOException e) { - addComponent(new Label("Sorry, could not initialize this SystemErrPanel")); - } - } - - public static void close() { - System.setErr(instance.err); - } -} diff --git a/src/main/java/org/caosdb/server/terminal/SystemOutPanel.java b/src/main/java/org/caosdb/server/terminal/SystemOutPanel.java deleted file mode 100644 index b34cdaf97d8088a97ea66932d9abc96d6e1589be..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/terminal/SystemOutPanel.java +++ /dev/null @@ -1,46 +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.terminal; - -import com.googlecode.lanterna.gui.component.Label; -import java.io.IOException; -import java.io.PrintStream; - -public final class SystemOutPanel extends OutputStreamPanel { - - private static final SystemOutPanel instance = new SystemOutPanel(); - - public static final SystemOutPanel getInstance() { - return instance; - } - - private SystemOutPanel() { - super(); - try { - init(); - System.setOut(new PrintStream(getPanelOutputStream())); - } catch (final IOException e) { - addComponent(new Label("Sorry, could not initialize this SystemOutPanel")); - } - } -} diff --git a/src/main/java/org/caosdb/server/transaction/AccessControlTransaction.java b/src/main/java/org/caosdb/server/transaction/AccessControlTransaction.java index 0a2d1363427d9e21504b421c88e7605af59634ac..4897cd65620577e36a98f2db5e5530a42b3f43d0 100644 --- a/src/main/java/org/caosdb/server/transaction/AccessControlTransaction.java +++ b/src/main/java/org/caosdb/server/transaction/AccessControlTransaction.java @@ -22,7 +22,7 @@ */ package org.caosdb.server.transaction; -import org.caosdb.server.database.DatabaseMonitor; +import org.caosdb.server.database.DatabaseAccessManager; import org.caosdb.server.database.access.Access; import org.caosdb.server.database.misc.RollBackHandler; import org.caosdb.server.entity.Message; @@ -33,7 +33,7 @@ public abstract class AccessControlTransaction implements TransactionInterface { @Override public final void execute() throws Exception { - this.access = DatabaseMonitor.getAccountAccess(this); + this.access = DatabaseAccessManager.getAccountAccess(this); try { transaction(); diff --git a/src/main/java/org/caosdb/server/transaction/ChecksumUpdater.java b/src/main/java/org/caosdb/server/transaction/ChecksumUpdater.java index b9d350719f13b5bd26e44f7d2b959a428adf5793..56fc07da22d9fac5bfbe713d1a9cd36b2d25a9ba 100644 --- a/src/main/java/org/caosdb/server/transaction/ChecksumUpdater.java +++ b/src/main/java/org/caosdb/server/transaction/ChecksumUpdater.java @@ -25,7 +25,7 @@ package org.caosdb.server.transaction; import java.io.IOException; import java.security.NoSuchAlgorithmException; import org.caosdb.server.CaosDBException; -import org.caosdb.server.database.DatabaseMonitor; +import org.caosdb.server.database.DatabaseAccessManager; import org.caosdb.server.database.access.Access; import org.caosdb.server.database.backend.transaction.GetUpdateableChecksums; import org.caosdb.server.database.backend.transaction.RetrieveSparseEntity; @@ -34,7 +34,7 @@ import org.caosdb.server.database.exceptions.TransactionException; import org.caosdb.server.entity.EntityInterface; import org.caosdb.server.entity.FileProperties; import org.caosdb.server.entity.Message; -import org.caosdb.server.entity.container.TransactionContainer; +import org.caosdb.server.entity.container.WritableContainer; import org.caosdb.server.utils.FileUtils; /** @@ -43,14 +43,15 @@ import org.caosdb.server.utils.FileUtils; * * @author tf */ -public class ChecksumUpdater extends WriteTransaction<TransactionContainer> implements Runnable { +public class ChecksumUpdater extends WriteTransaction + implements Runnable, WriteTransactionInterface { private Boolean running = false; private static final ChecksumUpdater instance = new ChecksumUpdater(); private ChecksumUpdater() { - super(new TransactionContainer()); + super(new WritableContainer()); } /** Retrieves all file without a checksum, calculates one and stores it to the database */ @@ -76,14 +77,14 @@ public class ChecksumUpdater extends WriteTransaction<TransactionContainer> impl Access strongAccess = null; try { - strongAccess = DatabaseMonitor.getInstance().allocateStrongAccess(this); + strongAccess = DatabaseAccessManager.getInstance().reserveWriteAccess(this); if (wasChangedInTheMeantime(fileEntity, strongAccess, lastModified)) { continue; } fileEntity.getFileProperties().setChecksum(checksum); - DatabaseMonitor.getInstance().acquireStrongAccess(this); + DatabaseAccessManager.getInstance().acquireWriteAccess(this); // update execute(new SetFileChecksum(fileEntity), strongAccess); @@ -130,8 +131,8 @@ public class ChecksumUpdater extends WriteTransaction<TransactionContainer> impl return null; } - private EntityInterface getNextUpdateableFileEntity() { - final Access weakAccess = DatabaseMonitor.getInstance().acquiredWeakAccess(this); + private EntityInterface getNextUpdateableFileEntity() throws InterruptedException { + final Access weakAccess = DatabaseAccessManager.getInstance().acquireReadAccess(this); try { synchronized (instance.running) { diff --git a/src/main/java/org/caosdb/server/transaction/Delete.java b/src/main/java/org/caosdb/server/transaction/Delete.java deleted file mode 100644 index 44e68b3afe6225f286ab76329b699f0cf0f10670..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/transaction/Delete.java +++ /dev/null @@ -1,116 +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.transaction; - -import org.apache.shiro.authz.AuthorizationException; -import org.caosdb.server.database.access.Access; -import org.caosdb.server.database.backend.transaction.DeleteEntity; -import org.caosdb.server.database.backend.transaction.RetrieveFullEntity; -import org.caosdb.server.entity.EntityInterface; -import org.caosdb.server.entity.container.DeleteContainer; -import org.caosdb.server.entity.container.TransactionContainer; -import org.caosdb.server.jobs.JobExecutionTime; -import org.caosdb.server.permissions.EntityPermission; -import org.caosdb.server.utils.EntityStatus; -import org.caosdb.server.utils.ServerMessages; - -public class Delete extends WriteTransaction<DeleteContainer> { - - public Delete(final DeleteContainer container) { - super(container); - } - - @Override - protected void init() throws Exception {} - - @Override - protected void preCheck() throws InterruptedException, Exception { - // allocate strong (write) access. Only one thread can do this - // at a time. But weak access can still be acquired by other - // thread until the allocated strong access is actually - // acquired. - setAccess(getMonitor().allocateStrongAccess(this)); - - // retrieve all entities which are to be deleted. - execute(new RetrieveFullEntity(getContainer()), getAccess()); - - for (final EntityInterface e : getContainer()) { - if (e.getEntityStatus() == EntityStatus.NONEXISTENT) { - e.addError(ServerMessages.ENTITY_DOES_NOT_EXIST); - continue; - } - - // check permissions - try { - e.checkPermission(EntityPermission.DELETE); - } catch (final AuthorizationException exc) { - e.setEntityStatus(EntityStatus.UNQUALIFIED); - e.addError(ServerMessages.AUTHORIZATION_ERROR); - e.addInfo(exc.getMessage()); - } - - // no standard entities are to be deleted. - if (e.hasId() && e.getId() < 100) { - e.setEntityStatus(EntityStatus.UNQUALIFIED); - e.addInfo("This entity cannot be deleted"); - } - } - - // make schedule of existing entities which are to be deleted. - makeSchedule(); - getSchedule().runJobs(JobExecutionTime.INIT); - } - - @Override - protected void postCheck() {} - - @Override - protected void postTransaction() { - // set entityStatus to DELETED and add deletion info message - if (getContainer().getStatus().ordinal() >= EntityStatus.QUALIFIED.ordinal()) { - for (final EntityInterface entity : getContainer()) { - if (entity.getEntityStatus() == EntityStatus.VALID) { - entity.setEntityStatus(EntityStatus.DELETED); - entity.addInfo(ServerMessages.ENTITY_HAS_BEEN_DELETED_SUCCESSFULLY); - } - } - } - } - - @Override - public void transaction() throws Exception { - // delete entities from database - delete(getContainer(), getAccess()); - } - - private void delete(final TransactionContainer container, final Access access) throws Exception { - if (container.getStatus().ordinal() >= EntityStatus.QUALIFIED.ordinal()) { - execute(new DeleteEntity(container), access); - } - } - - @Override - public boolean logHistory() { - return true; - } -} diff --git a/src/main/java/org/caosdb/server/transaction/FileStorageConsistencyCheck.java b/src/main/java/org/caosdb/server/transaction/FileStorageConsistencyCheck.java index 5d2a04b1e9f45360f47a93889f638eab23fbd512..ed93812d22eaad6bf448fa1dede2d92cea1d50a6 100644 --- a/src/main/java/org/caosdb/server/transaction/FileStorageConsistencyCheck.java +++ b/src/main/java/org/caosdb/server/transaction/FileStorageConsistencyCheck.java @@ -29,7 +29,7 @@ import java.util.List; import java.util.Map.Entry; import java.util.TimeZone; import org.caosdb.datetime.UTCDateTime; -import org.caosdb.server.database.DatabaseMonitor; +import org.caosdb.server.database.DatabaseAccessManager; import org.caosdb.server.database.access.Access; import org.caosdb.server.database.backend.transaction.FileConsistencyCheck; import org.caosdb.server.database.backend.transaction.GetFileIterator; @@ -64,8 +64,8 @@ public class FileStorageConsistencyCheck extends Thread @Override public void run() { - this.access = DatabaseMonitor.getInstance().acquiredWeakAccess(this); try { + this.access = DatabaseAccessManager.getInstance().acquireReadAccess(this); // test all files in file system. final Iterator<String> iterator = @@ -73,12 +73,12 @@ public class FileStorageConsistencyCheck extends Thread this.ts = System.currentTimeMillis(); while (iterator != null && iterator.hasNext()) { - if (DatabaseMonitor.whoHasAllocatedStrongAccess() != null) { + if (DatabaseAccessManager.whoHasReservedWriteAccess() != null) { // there is a thread waiting to write. pause this one and // apply for a new weak access which will be granted when // the write thread is ready. this.access.release(); - this.access = DatabaseMonitor.getInstance().acquiredWeakAccess(this); + this.access = DatabaseAccessManager.getInstance().acquireReadAccess(this); } final String path = iterator.next(); diff --git a/src/main/java/org/caosdb/server/transaction/Insert.java b/src/main/java/org/caosdb/server/transaction/Insert.java deleted file mode 100644 index d37f7ce516f36252466555ea18fe249f2730fea1..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/transaction/Insert.java +++ /dev/null @@ -1,113 +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.transaction; - -import org.apache.shiro.SecurityUtils; -import org.caosdb.server.database.access.Access; -import org.caosdb.server.database.backend.transaction.InsertEntity; -import org.caosdb.server.entity.EntityInterface; -import org.caosdb.server.entity.FileProperties; -import org.caosdb.server.entity.container.InsertContainer; -import org.caosdb.server.entity.container.TransactionContainer; -import org.caosdb.server.permissions.EntityACL; -import org.caosdb.server.utils.EntityStatus; -import org.caosdb.server.utils.ServerMessages; - -public class Insert extends WriteTransaction<InsertContainer> { - - public Insert(final InsertContainer container) { - super(container); - } - - @Override - protected void init() throws Exception { - // allocate strong (write) access. Only one thread can do this - // at a time. But weak access can still be acquired by other - // thread until the allocated strong access is actually - // acquired. - setAccess(getMonitor().allocateStrongAccess(this)); - - // make schedule for all parsed entities; - makeSchedule(); - - // dereference files (file upload only) - for (final EntityInterface entity : getContainer()) { - if (entity.hasFileProperties() && !entity.getFileProperties().isPickupable()) { - if (entity.getFileProperties().getTmpIdentifyer() != null) { - final FileProperties f = - getContainer().getFiles().get(entity.getFileProperties().getTmpIdentifyer()); - if (f != null) { - entity.getFileProperties().setFile(f.getFile()); - if (f.getThumbnail() != null) { - entity.getFileProperties().setThumbnail(f.getThumbnail()); - } else { - final FileProperties thumbnail = - getContainer() - .getFiles() - .get(entity.getFileProperties().getTmpIdentifyer() + ".thumbnail"); - if (thumbnail != null) { - entity.getFileProperties().setThumbnail(thumbnail.getFile()); - } - } - } else { - entity.addError(ServerMessages.FILE_HAS_NOT_BEEN_UPLOAED); - entity.setEntityStatus(EntityStatus.UNQUALIFIED); - } - } - } - } - } - - @Override - protected void preCheck() throws InterruptedException { - for (final EntityInterface entity : getContainer()) { - // set default EntityACL if none present - if (entity.getEntityACL() == null) { - entity.setEntityACL(EntityACL.getOwnerACLFor(SecurityUtils.getSubject())); - } - } - } - - @Override - protected void postCheck() {} - - @Override - protected void postTransaction() {} - - @Override - public void transaction() throws Exception { - // write new entities to database - insert(getContainer(), getAccess()); - } - - public void insert(final TransactionContainer container, final Access access) throws Exception { - if (container.getStatus().ordinal() >= EntityStatus.QUALIFIED.ordinal()) { - execute(new InsertEntity(container), access); - } - } - - @Override - public boolean logHistory() { - return true; - } -} diff --git a/src/main/java/org/caosdb/server/transaction/InsertLogRecordTransaction.java b/src/main/java/org/caosdb/server/transaction/InsertLogRecordTransaction.java index be01d93086e65694313c0d97c6b1603aef6a6bc7..c1128135b8620ebfe08328cd4901e9c0e6062053 100644 --- a/src/main/java/org/caosdb/server/transaction/InsertLogRecordTransaction.java +++ b/src/main/java/org/caosdb/server/transaction/InsertLogRecordTransaction.java @@ -24,7 +24,7 @@ package org.caosdb.server.transaction; import java.util.List; import java.util.logging.LogRecord; -import org.caosdb.server.database.DatabaseMonitor; +import org.caosdb.server.database.DatabaseAccessManager; import org.caosdb.server.database.access.Access; import org.caosdb.server.database.backend.transaction.InsertLogRecord; @@ -38,7 +38,7 @@ public class InsertLogRecordTransaction implements TransactionInterface { @Override public void execute() throws Exception { - final Access access = DatabaseMonitor.getInstance().acquiredWeakAccess(this); + final Access access = DatabaseAccessManager.getInstance().acquireReadAccess(this); try { execute(new InsertLogRecord(this.toBeFlushed), access); } finally { diff --git a/src/main/java/org/caosdb/server/transaction/Retrieve.java b/src/main/java/org/caosdb/server/transaction/Retrieve.java index 5b01657a9c0df55f2b93fe83532b074543debff9..fd7bae8a82e95554d527135f76124d397c23f0c4 100644 --- a/src/main/java/org/caosdb/server/transaction/Retrieve.java +++ b/src/main/java/org/caosdb/server/transaction/Retrieve.java @@ -24,7 +24,7 @@ package org.caosdb.server.transaction; import org.apache.shiro.authz.AuthorizationException; import org.caosdb.server.database.access.Access; -import org.caosdb.server.database.backend.transaction.RetrieveFullEntity; +import org.caosdb.server.database.backend.transaction.RetrieveFullEntityTransaction; import org.caosdb.server.entity.EntityInterface; import org.caosdb.server.entity.container.RetrieveContainer; import org.caosdb.server.entity.xml.SetFieldStrategy; @@ -47,7 +47,7 @@ public class Retrieve extends Transaction<RetrieveContainer> { @Override protected void init() throws Exception { // acquire weak access - setAccess(getMonitor().acquiredWeakAccess(this)); + setAccess(getAccessManager().acquireReadAccess(this)); // resolve names final ResolveNames r = new ResolveNames(); @@ -137,7 +137,7 @@ public class Retrieve extends Transaction<RetrieveContainer> { private void retrieveFullEntities(final RetrieveContainer container, final Access access) throws Exception { - execute(new RetrieveFullEntity(container), access); + execute(new RetrieveFullEntityTransaction(container), access); } @Override diff --git a/src/main/java/org/caosdb/server/transaction/RetrieveLogRecordTransaction.java b/src/main/java/org/caosdb/server/transaction/RetrieveLogRecordTransaction.java index 49d850dbcb672767da33a7c855063b04e1099f29..7e6c527865566795c397377409b1fc63020ca1dd 100644 --- a/src/main/java/org/caosdb/server/transaction/RetrieveLogRecordTransaction.java +++ b/src/main/java/org/caosdb/server/transaction/RetrieveLogRecordTransaction.java @@ -27,7 +27,7 @@ import java.util.logging.Level; import java.util.logging.LogRecord; import org.apache.shiro.SecurityUtils; import org.caosdb.server.accessControl.ACMPermissions; -import org.caosdb.server.database.DatabaseMonitor; +import org.caosdb.server.database.DatabaseAccessManager; import org.caosdb.server.database.access.Access; import org.caosdb.server.database.backend.transaction.RetrieveLogRecord; @@ -60,7 +60,7 @@ public class RetrieveLogRecordTransaction implements TransactionInterface { @Override public void execute() throws Exception { SecurityUtils.getSubject().checkPermission(ACMPermissions.PERMISSION_RETRIEVE_SERVERLOGS); - final Access access = DatabaseMonitor.getInstance().acquiredWeakAccess(this); + final Access access = DatabaseAccessManager.getInstance().acquireReadAccess(this); try { this.logRecords = execute(new RetrieveLogRecord(this.logger, this.level, this.message), access) diff --git a/src/main/java/org/caosdb/server/transaction/RetrieveSparseEntityByPath.java b/src/main/java/org/caosdb/server/transaction/RetrieveSparseEntityByPath.java index d82985fe8060b3919d48c17a6bbd2994fc92c5bf..520787105388d8f1408c2d3b05090c761c8f687c 100644 --- a/src/main/java/org/caosdb/server/transaction/RetrieveSparseEntityByPath.java +++ b/src/main/java/org/caosdb/server/transaction/RetrieveSparseEntityByPath.java @@ -41,7 +41,7 @@ public class RetrieveSparseEntityByPath extends Transaction<TransactionContainer @Override protected void init() throws Exception { // acquire weak access - setAccess(getMonitor().acquiredWeakAccess(this)); + setAccess(getAccessManager().acquireReadAccess(this)); } @Override diff --git a/src/main/java/org/caosdb/server/transaction/Transaction.java b/src/main/java/org/caosdb/server/transaction/Transaction.java index 0235b8a25cd1d7df9f923023b1be2d5597038b0e..c2bd7e0feb62fa31dadd5580990350b829ff4591 100644 --- a/src/main/java/org/caosdb/server/transaction/Transaction.java +++ b/src/main/java/org/caosdb/server/transaction/Transaction.java @@ -29,7 +29,7 @@ import java.util.List; import org.apache.shiro.subject.Subject; import org.caosdb.datetime.UTCDateTime; import org.caosdb.server.accessControl.Principal; -import org.caosdb.server.database.DatabaseMonitor; +import org.caosdb.server.database.DatabaseAccessManager; import org.caosdb.server.database.access.Access; import org.caosdb.server.database.backend.transaction.InsertTransactionHistory; import org.caosdb.server.database.exceptions.TransactionException; @@ -61,7 +61,7 @@ public abstract class Transaction<C extends TransactionContainer> extends Abstra return getContainer().getTransactionBenchmark(); } - private static final DatabaseMonitor monitor = DatabaseMonitor.getInstance(); + private static final DatabaseAccessManager monitor = DatabaseAccessManager.getInstance(); public static final String CLEAN_UP = "TransactionCleanUp"; private final C container; @@ -77,7 +77,7 @@ public abstract class Transaction<C extends TransactionContainer> extends Abstra if (o != null) acceptObserver(o); } - public static DatabaseMonitor getMonitor() { + public static DatabaseAccessManager getAccessManager() { return monitor; } @@ -231,8 +231,7 @@ public abstract class Transaction<C extends TransactionContainer> extends Abstra String realm = ((Principal) getTransactor().getPrincipal()).getRealm(); String username = ((Principal) getTransactor().getPrincipal()).getUsername(); execute( - new InsertTransactionHistory( - getContainer(), this.getClass().getSimpleName(), realm, username, getTimestamp()), + new InsertTransactionHistory(getContainer(), realm, username, getTimestamp()), getAccess()); } } diff --git a/src/main/java/org/caosdb/server/transaction/Update.java b/src/main/java/org/caosdb/server/transaction/Update.java deleted file mode 100644 index f5a9a7ffd8b32e4b8d220ea6bba44731b51047ef..0000000000000000000000000000000000000000 --- a/src/main/java/org/caosdb/server/transaction/Update.java +++ /dev/null @@ -1,397 +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.transaction; - -import com.google.common.base.Objects; -import java.io.IOException; -import java.security.NoSuchAlgorithmException; -import java.util.HashSet; -import java.util.Set; -import org.apache.shiro.authz.AuthorizationException; -import org.caosdb.server.CaosDBException; -import org.caosdb.server.database.access.Access; -import org.caosdb.server.database.backend.transaction.RetrieveFullEntity; -import org.caosdb.server.database.backend.transaction.UpdateEntity; -import org.caosdb.server.entity.Entity; -import org.caosdb.server.entity.EntityInterface; -import org.caosdb.server.entity.FileProperties; -import org.caosdb.server.entity.RetrieveEntity; -import org.caosdb.server.entity.container.TransactionContainer; -import org.caosdb.server.entity.container.UpdateContainer; -import org.caosdb.server.entity.wrapper.Parent; -import org.caosdb.server.entity.wrapper.Property; -import org.caosdb.server.permissions.EntityPermission; -import org.caosdb.server.permissions.Permission; -import org.caosdb.server.utils.EntityStatus; -import org.caosdb.server.utils.ServerMessages; - -public class Update extends WriteTransaction<UpdateContainer> { - - public Update(final UpdateContainer container) { - super(container); - } - - @Override - protected void preCheck() throws Exception {} - - @Override - protected void init() throws Exception { - // collect all ids of the entities which are to be updated. - final TransactionContainer oldContainer = new TransactionContainer(); - for (final EntityInterface entity : getContainer()) { - // entity has no id -> it cannot be updated. - if (!entity.hasId()) { - entity.addError(ServerMessages.ENTITY_HAS_NO_ID); - entity.setEntityStatus(EntityStatus.UNQUALIFIED); - continue; - } - - // add entity with this id to the updateContainer - if (entity.getEntityStatus() == EntityStatus.QUALIFIED) { - final Entity oldEntity = new RetrieveEntity(entity.getId()); - oldContainer.add(oldEntity); - } - } - - // allocate strong (write) access. Only one thread can do this - // at a time. But weak access can still be acquired by other - // thread until the allocated strong access is actually - // acquired. - setAccess(getMonitor().allocateStrongAccess(this)); - - // retrieve a container which contains all id of those entities - // which are to be updated. - execute(new RetrieveFullEntity(oldContainer), getAccess()); - - // Check if any updates are to be processed. - for (final EntityInterface newEntity : getContainer()) { - if (newEntity.getEntityStatus() == EntityStatus.QUALIFIED) { - innerLoop: - for (final EntityInterface oldEntity : oldContainer) { - if (oldEntity.getId().equals(newEntity.getId())) { - if (oldEntity.getEntityStatus() == EntityStatus.NONEXISTENT) { - newEntity.addError(ServerMessages.ENTITY_DOES_NOT_EXIST); - newEntity.setEntityStatus(EntityStatus.UNQUALIFIED); - } else { - // dereference files (upload only) - if (newEntity.hasFileProperties() && !newEntity.getFileProperties().isPickupable()) { - - if (newEntity.getFileProperties().getTmpIdentifyer() != null) { - // get file by tmpIdentifier - final FileProperties f = - getContainer() - .getFiles() - .get(newEntity.getFileProperties().getTmpIdentifyer()); - - // is it there? - if (f != null) { - newEntity.getFileProperties().setFile(f.getFile()); - - // has it a thumbnail? - if (f.getThumbnail() != null) { - newEntity.getFileProperties().setThumbnail(f.getThumbnail()); - } else { - final FileProperties thumbnail = - getContainer() - .getFiles() - .get(newEntity.getFileProperties().getTmpIdentifyer() + ".thumbnail"); - if (thumbnail != null) { - newEntity.getFileProperties().setThumbnail(thumbnail.getFile()); - } else { - newEntity.addWarning(ServerMessages.THUMBNAIL_HAS_NOT_BEEN_UPLOAED); - } - } - } else { - newEntity.addError(ServerMessages.FILE_HAS_NOT_BEEN_UPLOAED); - newEntity.setEntityStatus(EntityStatus.UNQUALIFIED); - } - - } else { - // in case no file has been uploaded, - // the file is expected to stay - // unchanged. Therefore we let the file - // object point to the original file in - // the file system. - newEntity - .getFileProperties() - .setFile(oldEntity.getFileProperties().retrieveFromFileSystem()); - } - } - - try { - checkPermissions(newEntity, deriveUpdate(newEntity, oldEntity)); - } catch (final AuthorizationException exc) { - newEntity.setEntityStatus(EntityStatus.UNQUALIFIED); - newEntity.addError(ServerMessages.AUTHORIZATION_ERROR); - newEntity.addInfo(exc.getMessage()); - } - } - break innerLoop; - } - } - } - } - - // make schedule of those entities which are to be updated. - makeSchedule(); - } - - @Override - protected void postCheck() {} - - @Override - protected void postTransaction() {} - - @Override - public void transaction() throws Exception { - // write new entities to database - update(getContainer(), getAccess()); - } - - private void update(final TransactionContainer container, final Access access) throws Exception { - if (container.getStatus().ordinal() >= EntityStatus.QUALIFIED.ordinal()) { - execute(new UpdateEntity(container), access); - } - } - - /** Check if the user has all permissions */ - public void checkPermissions(final EntityInterface entity, final Set<Permission> permissions) { - for (final Permission p : permissions) { - entity.checkPermission(p); - } - } - - /** - * The entity is set to VALID iff there are no updates to be processed. The entity is set to - * QUALIFIED otherwise. - * - * @param newEntity - * @param oldEntity - * @throws CaosDBException - * @throws IOException - * @throws NoSuchAlgorithmException - */ - public static HashSet<Permission> deriveUpdate( - final EntityInterface newEntity, final EntityInterface oldEntity) - throws NoSuchAlgorithmException, IOException, CaosDBException { - final HashSet<Permission> needPermissions = new HashSet<>(); - boolean updatetable = false; - - // new acl? - if (newEntity.hasEntityACL() && !newEntity.getEntityACL().equals(oldEntity.getEntityACL())) { - oldEntity.checkPermission(EntityPermission.EDIT_ACL); - if (!newEntity - .getEntityACL() - .getPriorityEntityACL() - .equals(oldEntity.getEntityACL().getPriorityEntityACL())) { - // priority acl is to be changed? - oldEntity.checkPermission(Permission.EDIT_PRIORITY_ACL); - } - updatetable = true; - } else if (!newEntity.hasEntityACL()) { - newEntity.setEntityACL(oldEntity.getEntityACL()); - } - - // new query template definition? - if (!Objects.equal( - newEntity.getQueryTemplateDefinition(), oldEntity.getQueryTemplateDefinition())) { - needPermissions.add(EntityPermission.UPDATE_QUERY_TEMPLATE_DEFINITION); - updatetable = true; - } - - // new datatype? - if (newEntity.hasDatatype() - && oldEntity.hasDatatype() - && !newEntity.getDatatype().equals(oldEntity.getDatatype()) - || newEntity.hasDatatype() ^ oldEntity.hasDatatype()) { - needPermissions.add(EntityPermission.UPDATE_DATA_TYPE); - updatetable = true; - } - - // entity role - if (newEntity.hasRole() - && oldEntity.hasRole() - && !newEntity.getRole().equals(oldEntity.getRole()) - || newEntity.hasRole() ^ oldEntity.hasRole()) { - needPermissions.add(EntityPermission.UPDATE_ROLE); - updatetable = true; - } - - // entity value - if (newEntity.hasValue() - && oldEntity.hasValue() - && !newEntity.getValue().equals(oldEntity.getValue()) - || newEntity.hasValue() ^ oldEntity.hasValue()) { - needPermissions.add(EntityPermission.UPDATE_VALUE); - updatetable = true; - } - - // entity name - if (newEntity.hasName() - && oldEntity.hasName() - && !newEntity.getName().equals(oldEntity.getName()) - || newEntity.hasName() ^ oldEntity.hasName()) { - needPermissions.add(EntityPermission.UPDATE_NAME); - updatetable = true; - } - - // entity description - if (newEntity.hasDescription() - && oldEntity.hasDescription() - && !newEntity.getDescription().equals(oldEntity.getDescription()) - || newEntity.hasDescription() ^ oldEntity.hasDescription()) { - needPermissions.add(EntityPermission.UPDATE_DESCRIPTION); - updatetable = true; - } - - // file properties - if (newEntity.hasFileProperties() || oldEntity.hasFileProperties()) { - if (newEntity.hasFileProperties() && !oldEntity.hasFileProperties()) { - // add a file - needPermissions.add(EntityPermission.UPDATE_ADD_FILE); - updatetable = true; - } else if (!newEntity.hasFileProperties() && oldEntity.hasFileProperties()) { - // remove a file - needPermissions.add(EntityPermission.UPDATE_REMOVE_FILE); - updatetable = true; - } else { - // change file - final FileProperties newFile = newEntity.getFileProperties(); - final FileProperties oldFile = oldEntity.getFileProperties(); - - // file path - if (newFile.hasPath() && oldFile.hasPath() && !newFile.getPath().equals(oldFile.getPath()) - || newFile.hasPath() ^ oldFile.hasPath()) { - // this means, the location of the file is to be changed - needPermissions.add(EntityPermission.UPDATE_MOVE_FILE); - updatetable = true; - } - - // change actual file (different byte code!) - if (!oldFile.retrieveFromFileSystem().equals(newFile.getFile())) { - // CHANGE A FILE is like REMOVE AND ADD - needPermissions.add(EntityPermission.UPDATE_REMOVE_FILE); - needPermissions.add(EntityPermission.UPDATE_ADD_FILE); - updatetable = true; - } - } - } - - // properties - outerLoop: - for (final EntityInterface newProperty : newEntity.getProperties()) { - - // find corresponding oldProperty for this new property and make a - // diff. - if (newProperty.hasId()) { - for (final EntityInterface oldProperty : oldEntity.getProperties()) { - if (newProperty.getId().equals(oldProperty.getId())) { - // do not check again. - oldEntity.getProperties().remove(oldProperty); - - if (((Property) oldProperty).getPIdx() != ((Property) newProperty).getPIdx()) { - // change order of properties - needPermissions.add(EntityPermission.UPDATE_ADD_PROPERTY); - needPermissions.add(EntityPermission.UPDATE_REMOVE_PROPERTY); - updatetable = true; - } - - deriveUpdate(newProperty, oldProperty); - if (newProperty.getEntityStatus() == EntityStatus.QUALIFIED) { - needPermissions.add(EntityPermission.UPDATE_ADD_PROPERTY); - needPermissions.add(EntityPermission.UPDATE_REMOVE_PROPERTY); - updatetable = true; - } - - continue outerLoop; - } - } - } else { - newProperty.setEntityStatus(EntityStatus.UNQUALIFIED); - newProperty.addError(ServerMessages.ENTITY_HAS_NO_ID); - newProperty.addInfo("On updates, allways specify the id not just the name."); - newEntity.addError(ServerMessages.ENTITY_HAS_UNQUALIFIED_PROPERTIES); - newEntity.setEntityStatus(EntityStatus.UNQUALIFIED); - return needPermissions; - } - - // no corresponding property found -> this property is new. - needPermissions.add(EntityPermission.UPDATE_ADD_PROPERTY); - updatetable = true; - } - - // some old properties left (and not matched with new ones) -> there are - // properties to be deleted. - if (!oldEntity.getProperties().isEmpty()) { - needPermissions.add(EntityPermission.UPDATE_REMOVE_PROPERTY); - updatetable = true; - } - - // update parents - outerLoop: - for (final Parent newParent : newEntity.getParents()) { - - // find corresponding oldParent - if (newParent.hasId()) { - for (final Parent oldParent : oldEntity.getParents()) { - if (oldParent.getId().equals(newParent.getId())) { - // still there! do not check this one again - oldEntity.getParents().remove(oldParent); - continue outerLoop; - } - } - } else { - newParent.setEntityStatus(EntityStatus.UNQUALIFIED); - newParent.addError(ServerMessages.ENTITY_HAS_NO_ID); - newParent.addInfo("On updates, allways specify the id not just the name."); - newEntity.addError(ServerMessages.ENTITY_HAS_UNQUALIFIED_PROPERTIES); - newEntity.setEntityStatus(EntityStatus.UNQUALIFIED); - return needPermissions; - } - - // no corresponding parent found -> this parent is new. - needPermissions.add(EntityPermission.UPDATE_ADD_PARENT); - updatetable = true; - } - - // some old parents left (and not matched with new ones) -> there are - // parents to be deleted. - if (!oldEntity.getParents().isEmpty()) { - needPermissions.add(EntityPermission.UPDATE_REMOVE_PARENT); - updatetable = true; - } - - // nothing to be updated - if (!updatetable) { - newEntity.setEntityStatus(EntityStatus.VALID); - newEntity.addInfo("Nothing to be updated."); - } - - return needPermissions; - } - - @Override - public boolean logHistory() { - return true; - } -} diff --git a/src/main/java/org/caosdb/server/transaction/WriteTransaction.java b/src/main/java/org/caosdb/server/transaction/WriteTransaction.java index 7f5fd3741d2575c1c5c7679b2917737bb9069528..abda78b6feeb0de7104de5365877b28d474058fe 100644 --- a/src/main/java/org/caosdb/server/transaction/WriteTransaction.java +++ b/src/main/java/org/caosdb/server/transaction/WriteTransaction.java @@ -4,6 +4,8 @@ * * Copyright (C) 2018 Research Group Biomedical Physics, * Max-Planck-Institute for Dynamics and Self-Organization Göttingen + * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2021 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,14 +24,53 @@ */ package org.caosdb.server.transaction; +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.authz.AuthorizationException; +import org.caosdb.server.CaosDBException; +import org.caosdb.server.database.BackendTransaction; +import org.caosdb.server.database.access.Access; +import org.caosdb.server.database.backend.transaction.DeleteEntityTransaction; +import org.caosdb.server.database.backend.transaction.InsertEntityTransaction; +import org.caosdb.server.database.backend.transaction.RetrieveFullEntityTransaction; +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.EntityInterface; import org.caosdb.server.entity.FileProperties; +import org.caosdb.server.entity.InsertEntity; +import org.caosdb.server.entity.RetrieveEntity; +import org.caosdb.server.entity.UpdateEntity; import org.caosdb.server.entity.container.TransactionContainer; +import org.caosdb.server.entity.container.WritableContainer; +import org.caosdb.server.entity.wrapper.Parent; +import org.caosdb.server.entity.wrapper.Property; +import org.caosdb.server.jobs.Schedule; +import org.caosdb.server.permissions.EntityACL; +import org.caosdb.server.permissions.EntityPermission; +import org.caosdb.server.permissions.Permission; import org.caosdb.server.query.Query; +import org.caosdb.server.utils.EntityStatus; +import org.caosdb.server.utils.ServerMessages; -public abstract class WriteTransaction<C extends TransactionContainer> extends Transaction<C> { +/** + * This class is responsible for inserting, updating and deleting entities which are held in the + * {@link TransactionContainer}. + * + * <p>This class initializes and runs the {@link Schedule} of {@link Job}s, calls the {@link + * BackendTransaction}s for each entity, and handles exceptions, roll-back (if necessary) and clean + * up afterwards. + * + * @author Timm Fitschen <t.fitschen@indiscale.com> + */ +public class WriteTransaction extends Transaction<WritableContainer> + implements WriteTransactionInterface { - protected WriteTransaction(final C container) { + public WriteTransaction(final WritableContainer container) { super(container); } @@ -37,7 +78,7 @@ public abstract class WriteTransaction<C extends TransactionContainer> extends T protected final void preTransaction() throws InterruptedException { // acquire strong access. No other thread can have access until // it this strong access is released. - setAccess(getMonitor().acquireStrongAccess(this)); + setAccess(getAccessManager().acquireWriteAccess(this)); } @Override @@ -53,6 +94,18 @@ public abstract class WriteTransaction<C extends TransactionContainer> extends T super.rollBack(); } + private void update(final TransactionContainer container, final Access access) throws Exception { + execute(new UpdateEntityTransaction(container), access); + } + + private void insert(final TransactionContainer container, final Access access) throws Exception { + execute(new InsertEntityTransaction(container), access); + } + + private void delete(final TransactionContainer container, final Access access) throws Exception { + execute(new DeleteEntityTransaction(container), access); + } + @Override protected final void cleanUp() { // release strong access. Other threads can acquire or allocate @@ -67,6 +120,437 @@ public abstract class WriteTransaction<C extends TransactionContainer> extends T } } + @Override + public boolean logHistory() { + return true; + } + + @Override + protected void init() throws Exception { + // collect all ids of the entities which are to be updated. + final TransactionContainer oldContainer = new TransactionContainer(); + // collect all ids of the entities which are to be deleted. + final TransactionContainer deleteContainer = new TransactionContainer(); + for (final EntityInterface entity : getContainer()) { + if (entity instanceof UpdateEntity) { + // entity has no id -> it cannot be updated. + if (!entity.hasId()) { + entity.addError(ServerMessages.ENTITY_HAS_NO_ID); + entity.setEntityStatus(EntityStatus.UNQUALIFIED); + continue; + } + + // add entity with this id to the updateContainer + if (entity.getEntityStatus() == EntityStatus.QUALIFIED) { + final EntityInterface oldEntity = new RetrieveEntity(entity.getId()); + oldContainer.add(oldEntity); + } + } else if (entity instanceof DeleteEntity) { + deleteContainer.add(entity); + } + } + + // Reserve write access. Only one thread can do this at a time. But read access can still be + // acquired by other threads until the reserved write access is actually acquired. + setAccess(getAccessManager().reserveWriteAccess(this)); + + // Retrieve a container which contains all IDs of those entities + // which are to be updated. + execute(new RetrieveFullEntityTransaction(oldContainer), getAccess()); + + // Retrieve all entities which are to be deleted. + execute(new RetrieveFullEntityTransaction(deleteContainer), getAccess()); + + // Check if any updates are to be processed. + for (final EntityInterface entity : getContainer()) { + if (entity instanceof UpdateEntity && entity.getEntityStatus() == EntityStatus.QUALIFIED) { + innerLoop: + for (final EntityInterface oldEntity : oldContainer) { + if (oldEntity.getId().equals(entity.getId())) { + if (oldEntity.getEntityStatus() == EntityStatus.NONEXISTENT) { + entity.addError(ServerMessages.ENTITY_DOES_NOT_EXIST); + entity.setEntityStatus(EntityStatus.UNQUALIFIED); + } else { + // dereference files (upload only) + if (entity.hasFileProperties() + && entity.getFileProperties().hasTmpIdentifier() + && !entity.getFileProperties().isPickupable()) { + + // get file by tmpIdentifier + final FileProperties f = + getContainer().getFiles().get(entity.getFileProperties().getTmpIdentifyer()); + + // is it there? + if (f != null) { + entity.getFileProperties().setFile(f.getFile()); + + // has it a thumbnail? + if (f.getThumbnail() != null) { + entity.getFileProperties().setThumbnail(f.getThumbnail()); + } else { + final FileProperties thumbnail = + getContainer() + .getFiles() + .get(entity.getFileProperties().getTmpIdentifyer() + ".thumbnail"); + if (thumbnail != null) { + entity.getFileProperties().setThumbnail(thumbnail.getFile()); + } else { + entity.addWarning(ServerMessages.THUMBNAIL_HAS_NOT_BEEN_UPLOAED); + } + } + } else { + entity.addError(ServerMessages.FILE_HAS_NOT_BEEN_UPLOAED); + entity.setEntityStatus(EntityStatus.UNQUALIFIED); + } + + } else if (entity.hasFileProperties() + && !entity.getFileProperties().hasTmpIdentifier()) { + // in case no file has been uploaded, + // the file is expected to stay + // unchanged. Therefore we let the file + // object point to the original file in + // the file system. + entity + .getFileProperties() + .setFile(oldEntity.getFileProperties().retrieveFromFileSystem()); + } + + try { + checkPermissions(entity, deriveUpdate(entity, oldEntity)); + } catch (final AuthorizationException exc) { + entity.setEntityStatus(EntityStatus.UNQUALIFIED); + entity.addError(ServerMessages.AUTHORIZATION_ERROR); + entity.addInfo(exc.getMessage()); + } + } + break innerLoop; + } + } + } else if (entity instanceof DeleteEntity) { + if (entity.getEntityStatus() == EntityStatus.NONEXISTENT) { + entity.addError(ServerMessages.ENTITY_DOES_NOT_EXIST); + continue; + } + + // check permissions + try { + entity.checkPermission(EntityPermission.DELETE); + } catch (final AuthorizationException exc) { + entity.setEntityStatus(EntityStatus.UNQUALIFIED); + entity.addError(ServerMessages.AUTHORIZATION_ERROR); + entity.addInfo(exc.getMessage()); + } + + // no standard entities are to be deleted. + if (entity.hasId() && entity.getId() < 100) { + entity.setEntityStatus(EntityStatus.UNQUALIFIED); + entity.addInfo("This entity cannot be deleted"); + } + + } else if (entity instanceof InsertEntity + && entity.hasFileProperties() + && entity.getFileProperties().hasTmpIdentifier() + && !entity.getFileProperties().isPickupable()) { + // dereference files (file upload only) + final FileProperties f = + getContainer().getFiles().get(entity.getFileProperties().getTmpIdentifyer()); + if (f != null) { + entity.getFileProperties().setFile(f.getFile()); + if (f.getThumbnail() != null) { + entity.getFileProperties().setThumbnail(f.getThumbnail()); + } else { + final FileProperties thumbnail = + getContainer() + .getFiles() + .get(entity.getFileProperties().getTmpIdentifyer() + ".thumbnail"); + if (thumbnail != null) { + entity.getFileProperties().setThumbnail(thumbnail.getFile()); + } else { + entity.addWarning(ServerMessages.THUMBNAIL_HAS_NOT_BEEN_UPLOAED); + } + } + } else { + entity.addError(ServerMessages.FILE_HAS_NOT_BEEN_UPLOAED); + entity.setEntityStatus(EntityStatus.UNQUALIFIED); + } + } + } + + makeSchedule(); + } + + /** Check if the user has all permissions */ + public static void checkPermissions( + final EntityInterface entity, final Set<Permission> permissions) { + for (final Permission p : permissions) { + entity.checkPermission(p); + } + } + + @Override + protected void preCheck() throws InterruptedException, Exception { + for (final EntityInterface entity : getContainer()) { + // set default EntityACL if none present + if (entity.getEntityACL() == null) { + entity.setEntityACL(EntityACL.getOwnerACLFor(SecurityUtils.getSubject())); + } + } + } + + @Override + protected void postCheck() {} + + @Override + protected void transaction() throws Exception { + if (getContainer().getStatus().ordinal() >= EntityStatus.QUALIFIED.ordinal()) { + + // split up the TransactionContainer into three containers, one for each + // type of writing transaction. + TransactionContainer inserts = new TransactionContainer(); + TransactionContainer updates = new TransactionContainer(); + TransactionContainer deletes = new TransactionContainer(); + for (EntityInterface entity : getContainer()) { + if (entity instanceof InsertEntity) { + inserts.add(entity); + } else if (entity instanceof UpdateEntity) { + updates.add(entity); + } else if (entity instanceof DeleteEntity) { + deletes.add(entity); + } + } + + // The order is crucial: 1) Update-entities may reference new entities which + // have to be inserted first. 2) Update-Entities which (originally) + // reference old entities which are to be deleted, have be updated before + // the others can be deleted. + insert(inserts, getAccess()); + update(updates, getAccess()); + delete(deletes, getAccess()); + } + } + + @Override + protected void postTransaction() throws Exception { + // set entityStatus to DELETED and add deletion info message for deleted entities. + if (getContainer().getStatus().ordinal() >= EntityStatus.QUALIFIED.ordinal()) { + for (final EntityInterface entity : getContainer()) { + if (entity instanceof DeleteEntity && entity.getEntityStatus() == EntityStatus.VALID) { + entity.setEntityStatus(EntityStatus.DELETED); + entity.addInfo(ServerMessages.ENTITY_HAS_BEEN_DELETED_SUCCESSFULLY); + } + } + } + } + + /** + * The entity is set to VALID iff there are no updates to be processed. The entity is set to + * QUALIFIED otherwise. + * + * @param newEntity + * @param oldEntity + * @throws CaosDBException + * @throws IOException + * @throws NoSuchAlgorithmException + */ + public static HashSet<Permission> deriveUpdate( + final EntityInterface newEntity, final EntityInterface oldEntity) + throws NoSuchAlgorithmException, IOException, CaosDBException { + final HashSet<Permission> needPermissions = new HashSet<>(); + boolean updatetable = false; + + // new acl? + if (newEntity.hasEntityACL() && !newEntity.getEntityACL().equals(oldEntity.getEntityACL())) { + oldEntity.checkPermission(EntityPermission.EDIT_ACL); + if (!newEntity + .getEntityACL() + .getPriorityEntityACL() + .equals(oldEntity.getEntityACL().getPriorityEntityACL())) { + // priority acl is to be changed? + oldEntity.checkPermission(Permission.EDIT_PRIORITY_ACL); + } + updatetable = true; + } else if (!newEntity.hasEntityACL()) { + newEntity.setEntityACL(oldEntity.getEntityACL()); + } + + // new query template definition? + if (!Objects.equals( + newEntity.getQueryTemplateDefinition(), oldEntity.getQueryTemplateDefinition())) { + needPermissions.add(EntityPermission.UPDATE_QUERY_TEMPLATE_DEFINITION); + updatetable = true; + } + + // new datatype? + if (newEntity.hasDatatype() + && oldEntity.hasDatatype() + && !newEntity.getDatatype().equals(oldEntity.getDatatype()) + || newEntity.hasDatatype() ^ oldEntity.hasDatatype()) { + needPermissions.add(EntityPermission.UPDATE_DATA_TYPE); + updatetable = true; + } + + // entity role + if (newEntity.hasRole() + && oldEntity.hasRole() + && !newEntity.getRole().equals(oldEntity.getRole()) + || newEntity.hasRole() ^ oldEntity.hasRole()) { + needPermissions.add(EntityPermission.UPDATE_ROLE); + updatetable = true; + } + + // entity value + if (newEntity.hasValue() + && oldEntity.hasValue() + && !newEntity.getValue().equals(oldEntity.getValue()) + || newEntity.hasValue() ^ oldEntity.hasValue()) { + needPermissions.add(EntityPermission.UPDATE_VALUE); + updatetable = true; + } + + // entity name + if (newEntity.hasName() + && oldEntity.hasName() + && !newEntity.getName().equals(oldEntity.getName()) + || newEntity.hasName() ^ oldEntity.hasName()) { + needPermissions.add(EntityPermission.UPDATE_NAME); + updatetable = true; + } + + // entity description + if (newEntity.hasDescription() + && oldEntity.hasDescription() + && !newEntity.getDescription().equals(oldEntity.getDescription()) + || newEntity.hasDescription() ^ oldEntity.hasDescription()) { + needPermissions.add(EntityPermission.UPDATE_DESCRIPTION); + updatetable = true; + } + + // file properties + if (newEntity.hasFileProperties() || oldEntity.hasFileProperties()) { + if (newEntity.hasFileProperties() && !oldEntity.hasFileProperties()) { + // add a file + needPermissions.add(EntityPermission.UPDATE_ADD_FILE); + updatetable = true; + } else if (!newEntity.hasFileProperties() && oldEntity.hasFileProperties()) { + // remove a file + needPermissions.add(EntityPermission.UPDATE_REMOVE_FILE); + updatetable = true; + } else { + // change file + final FileProperties newFile = newEntity.getFileProperties(); + final FileProperties oldFile = oldEntity.getFileProperties(); + + // file path + if (newFile.hasPath() && oldFile.hasPath() && !newFile.getPath().equals(oldFile.getPath()) + || newFile.hasPath() ^ oldFile.hasPath()) { + // this means, the location of the file is to be changed + needPermissions.add(EntityPermission.UPDATE_MOVE_FILE); + updatetable = true; + } + + // change actual file (different byte code!) + if (!oldFile.retrieveFromFileSystem().equals(newFile.getFile())) { + // CHANGE A FILE is like REMOVE AND ADD + needPermissions.add(EntityPermission.UPDATE_REMOVE_FILE); + needPermissions.add(EntityPermission.UPDATE_ADD_FILE); + updatetable = true; + } + } + } + + // properties + outerLoop: + for (final EntityInterface newProperty : newEntity.getProperties()) { + + // find corresponding oldProperty for this new property and make a + // diff. + if (newProperty.hasId()) { + for (final EntityInterface oldProperty : oldEntity.getProperties()) { + if (newProperty.getId().equals(oldProperty.getId())) { + // do not check again. + oldEntity.getProperties().remove(oldProperty); + + if (((Property) oldProperty).getPIdx() != ((Property) newProperty).getPIdx()) { + // change order of properties + needPermissions.add(EntityPermission.UPDATE_ADD_PROPERTY); + needPermissions.add(EntityPermission.UPDATE_REMOVE_PROPERTY); + updatetable = true; + } + + deriveUpdate(newProperty, oldProperty); + if (newProperty.getEntityStatus() == EntityStatus.QUALIFIED) { + needPermissions.add(EntityPermission.UPDATE_ADD_PROPERTY); + needPermissions.add(EntityPermission.UPDATE_REMOVE_PROPERTY); + updatetable = true; + } + + continue outerLoop; + } + } + } else { + newProperty.setEntityStatus(EntityStatus.UNQUALIFIED); + newProperty.addError(ServerMessages.ENTITY_HAS_NO_ID); + newProperty.addInfo("On updates, allways specify the id not just the name."); + newEntity.addError(ServerMessages.ENTITY_HAS_UNQUALIFIED_PROPERTIES); + newEntity.setEntityStatus(EntityStatus.UNQUALIFIED); + return needPermissions; + } + + // no corresponding property found -> this property is new. + needPermissions.add(EntityPermission.UPDATE_ADD_PROPERTY); + updatetable = true; + } + + // some old properties left (and not matched with new ones) -> there are + // properties to be deleted. + if (!oldEntity.getProperties().isEmpty()) { + needPermissions.add(EntityPermission.UPDATE_REMOVE_PROPERTY); + updatetable = true; + } + + // update parents + outerLoop: + for (final Parent newParent : newEntity.getParents()) { + + // find corresponding oldParent + if (newParent.hasId()) { + for (final Parent oldParent : oldEntity.getParents()) { + if (oldParent.getId().equals(newParent.getId())) { + // still there! do not check this one again + oldEntity.getParents().remove(oldParent); + continue outerLoop; + } + } + } else { + newParent.setEntityStatus(EntityStatus.UNQUALIFIED); + newParent.addError(ServerMessages.ENTITY_HAS_NO_ID); + newParent.addInfo("On updates, allways specify the id not just the name."); + newEntity.addError(ServerMessages.ENTITY_HAS_UNQUALIFIED_PROPERTIES); + newEntity.setEntityStatus(EntityStatus.UNQUALIFIED); + return needPermissions; + } + + // no corresponding parent found -> this parent is new. + needPermissions.add(EntityPermission.UPDATE_ADD_PARENT); + updatetable = true; + } + + // some old parents left (and not matched with new ones) -> there are + // parents to be deleted. + if (!oldEntity.getParents().isEmpty()) { + needPermissions.add(EntityPermission.UPDATE_REMOVE_PARENT); + updatetable = true; + } + + // nothing to be updated + if (!updatetable) { + newEntity.setEntityStatus(EntityStatus.VALID); + newEntity.addInfo("Nothing to be updated."); + } + + return needPermissions; + } + public String getSRID() { return getContainer().getRequestId(); } diff --git a/src/main/java/org/caosdb/server/transaction/WriteTransactionInterface.java b/src/main/java/org/caosdb/server/transaction/WriteTransactionInterface.java new file mode 100644 index 0000000000000000000000000000000000000000..165acb408776a720c6887d31b550cf57e3d6fa0c --- /dev/null +++ b/src/main/java/org/caosdb/server/transaction/WriteTransactionInterface.java @@ -0,0 +1,8 @@ +package org.caosdb.server.transaction; + +import org.caosdb.server.database.access.Access; + +public interface WriteTransactionInterface extends TransactionInterface { + + public Access getAccess(); +} diff --git a/src/main/java/org/caosdb/server/utils/EntityStatus.java b/src/main/java/org/caosdb/server/utils/EntityStatus.java index ef8bdc101c61e216c9aa448d667b59dfa5107672..d6658ef12535bca15a6af7b1d16a4d3d8c0b5e58 100644 --- a/src/main/java/org/caosdb/server/utils/EntityStatus.java +++ b/src/main/java/org/caosdb/server/utils/EntityStatus.java @@ -4,6 +4,8 @@ * * Copyright (C) 2018 Research Group Biomedical Physics, * Max-Planck-Institute for Dynamics and Self-Organization Göttingen + * Copyright (C) 2021 Indiscale GmbH <info@indiscale.com> + * Copyright (C) 2021 Daniel Hornung <d.hornung@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 @@ -23,17 +25,24 @@ package org.caosdb.server.utils; /** - * IGNORE - This instance must be ignored during further processing. <br> - * VALID - This instance has a final ID and has been synchronized with the database. <br> - * WRITTEN - This instance has been written successfully to the database. It has a final ID. <br> - * QUALIFIED - This instance has a provisional or final ID, represents a well-formed entity, and any - * referenced entities (by a reference property) have status QUALIFIED or VALID. QUALIFIED - This - * instance has been checked and is not qualified to be processed further (i.e. to be inserted, - * updated, deleted) DELETED - This instance has been deleted recently. CORRUPT - This instance has - * been retrieved from the database, but though something turned out to be wrong with it. - * NONEXISTENT - This instance has been called (via id or something) but it doesn't exist. - * - * @author Timm Fitschen + * The order of the EntityStatus values matters, in that everything later than "QUALIFIED" also + * counts as qualified. + * + * <p> + * + * <ul> + * <li>IGNORE - This instance must be ignored during further processing. + * <li>UNQUALIFIED - This instance has been checked and is not qualified to be processed further + * (i.e. to be inserted, updated, deleted) + * <li>DELETED - This instance has been deleted recently. + * <li>NONEXISTENT - This instance has been called (via id or something) but it doesn't exist. + * <li>QUALIFIED - This instance has a provisional or final ID, represents a well-formed entity, + * and any referenced entities (by a reference property) have status QUALIFIED or VALID. Every + * status after this one also counts as QUALIFIED. + * <li>VALID - This instance has a final ID and has been synchronized with the database. + * </ul> + * + * @author Timm Fitschen <t.fitschen@indiscale.com> */ public enum EntityStatus { IGNORE, diff --git a/src/main/java/org/caosdb/server/utils/Info.java b/src/main/java/org/caosdb/server/utils/Info.java index 19f792cf625765924f02a7b734f2e57849584ffc..18ee09828006c0394dec315ca77483f8298ba86b 100644 --- a/src/main/java/org/caosdb/server/utils/Info.java +++ b/src/main/java/org/caosdb/server/utils/Info.java @@ -30,7 +30,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.caosdb.server.CaosDBServer; import org.caosdb.server.FileSystem; -import org.caosdb.server.database.DatabaseMonitor; +import org.caosdb.server.database.DatabaseAccessManager; import org.caosdb.server.database.access.Access; import org.caosdb.server.database.backend.transaction.GetInfo; import org.caosdb.server.database.backend.transaction.SyncStats; @@ -58,7 +58,7 @@ public class Info extends AbstractObservable implements Observer, TransactionInt } private Info() { - this.access = DatabaseMonitor.getInfoAccess(this); + this.access = DatabaseAccessManager.getInfoAccess(this); try { syncDatabase(); } catch (final Exception exc) { diff --git a/src/main/java/org/caosdb/server/utils/Initialization.java b/src/main/java/org/caosdb/server/utils/Initialization.java index baae53804f57433aa58da1ae1b9c58060c3db160..cb1a36307928a72b9da95b48737196e569c43346 100644 --- a/src/main/java/org/caosdb/server/utils/Initialization.java +++ b/src/main/java/org/caosdb/server/utils/Initialization.java @@ -24,7 +24,7 @@ */ package org.caosdb.server.utils; -import org.caosdb.server.database.DatabaseMonitor; +import org.caosdb.server.database.DatabaseAccessManager; import org.caosdb.server.database.access.Access; import org.caosdb.server.transaction.TransactionInterface; @@ -34,7 +34,7 @@ public final class Initialization implements TransactionInterface, AutoCloseable private static final Initialization instance = new Initialization(); private Initialization() { - this.access = DatabaseMonitor.getInitAccess(this); + this.access = DatabaseAccessManager.getInitAccess(this); } public static final Initialization setUp() { diff --git a/src/test/java/org/caosdb/server/database/DatabaseMonitorTest.java b/src/test/java/org/caosdb/server/database/DatabaseAccessManagerTest.java similarity index 52% rename from src/test/java/org/caosdb/server/database/DatabaseMonitorTest.java rename to src/test/java/org/caosdb/server/database/DatabaseAccessManagerTest.java index 554baec079ed17d332dff47f0afb57405913dc33..8e08131f3b18d9e4a495301ee570683facb4d9c5 100644 --- a/src/test/java/org/caosdb/server/database/DatabaseMonitorTest.java +++ b/src/test/java/org/caosdb/server/database/DatabaseAccessManagerTest.java @@ -25,61 +25,61 @@ package org.caosdb.server.database; import org.junit.Assert; import org.junit.Test; -public class DatabaseMonitorTest { - public static final WeakAccessSemaphore wa = new WeakAccessSemaphore(); - public static final StrongAccessLock sa = new StrongAccessLock(wa); +public class DatabaseAccessManagerTest { + public static final ReadAccessSemaphore readAccess = new ReadAccessSemaphore(); + public static final WriteAccessLock writeAccess = new WriteAccessLock(readAccess); /** - * Two read-, two write-threads. The read-threads request weak access, the write-threads request - * strong access.<br> - * The read-thread rt1 is started and gets weak access. Then the write-thread wt1 starts and - * allocates strong access.<br> - * A second read-thread rt2 starts and also gets weak access. A second write thread wt2 starts and - * requests allocation of strong access. It has to wait until wt2 releases it. <br> - * wt1 acquires strong access as soon as both read-threads released their weak-accesss.<br> + * Two read-, two write-threads. The read-threads request read access, the write-threads request + * write access.<br> + * The read-thread rt1 is started and gets read access. Then the write-thread wt1 starts and + * reserves write access.<br> + * A second read-thread rt2 starts and also gets read access. A second write thread wt2 starts and + * requests allocation of write access. It has to wait until wt2 releases it. <br> + * wt1 acquires write access as soon as both read-threads released their read-accesss.<br> * * @throws InterruptedException */ @Test public void test1() throws InterruptedException { // first invoke a read thread - final ReadThread rt1 = new ReadThread(); - rt1.start(); // paused, has acquired weak access now + final ReadThread rt1 = new ReadThread("rt1"); + rt1.start(); // paused, has acquired read access now // invoke a write thread - final WriteThread wt1 = new WriteThread(); - wt1.start(); // paused, has allocated strong access now + final WriteThread wt1 = new WriteThread("wt1"); + wt1.start(); // paused, has reserved write access now synchronized (this) { - this.wait(1000); + this.wait(500); } // waiting means any processing Assert.assertEquals(wt1.getState(), Thread.State.WAITING); - final ReadThread rt2 = new ReadThread(); - rt2.start(); // paused, has acquired a second weak access now + final ReadThread rt2 = new ReadThread("rt2"); + rt2.start(); // paused, has acquired a second read access now synchronized (this) { - this.wait(1000); + this.wait(500); } synchronized (rt2) { rt2.notify(); } synchronized (this) { - this.wait(1000); + this.wait(500); } - // rt2 was processed while wt1 has allocated but not yet acquired strong - // access. rt2 terminated after releasing its weak access. + // rt2 was processed while wt1 has reserved but not yet acquired write + // access. rt2 terminated after releasing its read access. Assert.assertEquals(rt2.getState(), Thread.State.TERMINATED); - final WriteThread wt2 = new WriteThread(); - wt2.start(); // wt2 immediatelly allocates strong access and is block + final WriteThread wt2 = new WriteThread("wt2"); + wt2.start(); // wt2 immediatelly reserves write access and is block // since wt1 already yields it. synchronized (this) { - this.wait(1000); + this.wait(500); } - // Assert.assertEquals(wt2.getState(), Thread.State.BLOCKED); + Assert.assertEquals(wt2.getState(), Thread.State.WAITING); - // wt1 request strong access. + // wt1 request write access. synchronized (wt1) { wt1.notify(); } @@ -92,10 +92,10 @@ public class DatabaseMonitorTest { rt1.notify(); } synchronized (this) { - this.wait(1000); + this.wait(500); } - // rt1 was notified an terminated, releasing the weak acccess. - // so wt1 acquires strong access and pauses the second time. + // rt1 was notified an terminated, releasing the read acccess. + // so wt1 acquires write access and pauses the second time. Assert.assertEquals(rt1.getState(), Thread.State.TERMINATED); synchronized (wt1) { @@ -104,16 +104,16 @@ public class DatabaseMonitorTest { synchronized (this) { this.wait(1000); } - // wt2 allocates strong access as wt1 released it now. + // wt2 reserves write access as wt1 released it now. Assert.assertEquals(wt1.getState(), Thread.State.TERMINATED); Assert.assertEquals(wt2.getState(), Thread.State.WAITING); - // while wt2 has not yet acquired strong access, rt3 acquires weak + // while wt2 has not yet acquired write access, rt3 acquires read // access - final ReadThread rt3 = new ReadThread(); + final ReadThread rt3 = new ReadThread("rt3"); rt3.start(); synchronized (this) { - this.wait(1000); + this.wait(500); } Assert.assertEquals(rt3.getState(), Thread.State.WAITING); @@ -121,24 +121,23 @@ public class DatabaseMonitorTest { wt2.notify(); } synchronized (this) { - this.wait(1000); + this.wait(500); } - // Assert.assertEquals(wt2.getState(), Thread.State.BLOCKED); + Assert.assertEquals(wt2.getState(), Thread.State.WAITING); synchronized (rt3) { rt3.notify(); } synchronized (this) { - this.wait(1000); + this.wait(500); } Assert.assertEquals(rt3.getState(), Thread.State.TERMINATED); - Assert.assertEquals(wt2.getState(), Thread.State.WAITING); synchronized (wt2) { wt2.notify(); } synchronized (this) { - this.wait(1000); + this.wait(500); } Assert.assertEquals(wt2.getState(), Thread.State.TERMINATED); } @@ -147,34 +146,35 @@ public class DatabaseMonitorTest { public void test2() throws InterruptedException { // start a write-thread - final WriteThread wt1 = new WriteThread(); + final WriteThread wt1 = new WriteThread("wt1"); wt1.start(); // start another write-thread. It is blocked until wt1 releases the - // strong access. - final WriteThread wt2 = new WriteThread(); + // write access. + final WriteThread wt2 = new WriteThread("wt2"); wt2.start(); synchronized (this) { - this.wait(1000); + this.wait(500); } - // and interrupt wt1 after allocating, but before acquiring the strong + // and interrupt wt1 after allocating, but before acquiring the write // access. wt1.interrupt(); synchronized (this) { - this.wait(1000); + this.wait(500); } synchronized (wt2) { wt2.notify(); } // read access should still be blocked. - final ReadThread rt1 = new ReadThread(); + final ReadThread rt1 = new ReadThread("rt1"); rt1.start(); synchronized (this) { - this.wait(1000); + this.wait(500); } + Assert.assertEquals(rt1.getState(), Thread.State.WAITING); synchronized (wt2) { wt2.notify(); } @@ -184,23 +184,29 @@ public class DatabaseMonitorTest { @Override public void run() { try { - System.out.println("T" + currentThread().getId() + " request allocation sa"); - sa.allocate(); - System.out.println("T" + currentThread().getId() + " allocates sa"); - pause(); - sa.lockInterruptibly(); - System.out.println("T" + currentThread().getId() + " acquires sa"); - pause(); - sa.unlock(); - System.out.println("T" + currentThread().getId() + " releases sa"); + System.out.println(currentThread().getName() + " request to reserve write access"); + writeAccess.reserve(); + System.out.println(currentThread().getName() + " has reserved write access "); + process("reserved write access"); + writeAccess.lockInterruptibly(); + System.out.println(currentThread().getName() + " acquires write access"); + process("acquired write access"); + writeAccess.unlock(); + System.out.println(currentThread().getName() + " releases write access"); } catch (final InterruptedException e) { - System.out.println("T" + currentThread().getId() + " was interrupted"); - sa.unlock(); + System.out.println(currentThread().getName() + " was interrupted"); + writeAccess.unlock(); } } - private synchronized void pause() throws InterruptedException { + private synchronized void process(String access) throws InterruptedException { + System.out.println(currentThread().getName() + " processes with " + access); this.wait(); + System.out.println(currentThread().getName() + " is ready with " + access); + } + + public WriteThread(String name) { + super(name); } } @@ -208,19 +214,25 @@ public class DatabaseMonitorTest { @Override public void run() { try { - System.out.println("T" + currentThread().getId() + " requests wa"); - wa.acquire(); - System.out.println("T" + currentThread().getId() + " acquires wa"); + System.out.println(currentThread().getName() + " requests read access"); + readAccess.acquire(); + System.out.println(currentThread().getName() + " acquires read access"); pause(); - wa.release(); - System.out.println("T" + currentThread().getId() + " releases wa"); + readAccess.release(); + System.out.println(currentThread().getName() + " releases read access"); } catch (final InterruptedException e) { e.printStackTrace(); } } private synchronized void pause() throws InterruptedException { + System.out.println(currentThread().getName() + " waits "); this.wait(); + System.out.println(currentThread().getName() + " goes on"); + } + + public ReadThread(String name) { + super(name); } } } diff --git a/src/test/java/org/caosdb/server/database/backend/transaction/BackendTransactionTest.java b/src/test/java/org/caosdb/server/database/backend/transaction/BackendTransactionTest.java new file mode 100644 index 0000000000000000000000000000000000000000..93402bf0d06c487451d9219b4487f301eb855f49 --- /dev/null +++ b/src/test/java/org/caosdb/server/database/backend/transaction/BackendTransactionTest.java @@ -0,0 +1,24 @@ +package org.caosdb.server.database.backend.transaction; + +import static org.junit.Assert.assertEquals; + +import org.caosdb.server.database.BackendTransaction; +import org.caosdb.server.entity.DeleteEntity; +import org.caosdb.server.entity.InsertEntity; +import org.caosdb.server.entity.RetrieveEntity; +import org.caosdb.server.entity.Role; +import org.caosdb.server.entity.UpdateEntity; +import org.jdom2.Element; +import org.junit.Test; + +public class BackendTransactionTest { + + @Test + public void testGetTransactionType() { + BackendTransaction transaction = new InsertTransactionHistory(null, null, null, null); + assertEquals("Retrieve", transaction.getTransactionType(new RetrieveEntity("test"))); + assertEquals("Insert", transaction.getTransactionType(new InsertEntity("test", Role.Record))); + assertEquals("Delete", transaction.getTransactionType(new DeleteEntity(1234))); + assertEquals("Update", transaction.getTransactionType(new UpdateEntity(new Element("Record")))); + } +} 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 52b4c951d939f097258ec40ed16b6fd99784f9bd..4b6429b73946110b0fd173de9c53a3b30fd83e18 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 @@ -40,8 +40,8 @@ public class RetrieveFullEntityTest { @Test public void testRetrieveSubEntities() { - RetrieveFullEntity r = - new RetrieveFullEntity(0) { + RetrieveFullEntityTransaction r = + new RetrieveFullEntityTransaction(0) { /** Mock-up */ @Override diff --git a/src/test/java/org/caosdb/server/transaction/UpdateTest.java b/src/test/java/org/caosdb/server/transaction/UpdateTest.java index 6b0e7b3f988f18142c6ebdb535212cd7454233f0..d22fbea8982b1f8d88c9ccdf069a0c8a816add25 100644 --- a/src/test/java/org/caosdb/server/transaction/UpdateTest.java +++ b/src/test/java/org/caosdb/server/transaction/UpdateTest.java @@ -44,7 +44,7 @@ public class UpdateTest { throws NoSuchAlgorithmException, IOException, CaosDBException { final Entity newEntity = new Entity("Name"); final Entity oldEntity = new Entity("Name"); - Update.deriveUpdate(newEntity, oldEntity); + WriteTransaction.deriveUpdate(newEntity, oldEntity); assertEquals(newEntity.getEntityStatus(), EntityStatus.VALID); } @@ -53,7 +53,7 @@ public class UpdateTest { throws NoSuchAlgorithmException, IOException, CaosDBException { final Entity newEntity = new Entity("NewName"); final Entity oldEntity = new Entity("OldName"); - Update.deriveUpdate(newEntity, oldEntity); + WriteTransaction.deriveUpdate(newEntity, oldEntity); assertEquals(newEntity.getEntityStatus(), EntityStatus.QUALIFIED); } @@ -67,7 +67,7 @@ public class UpdateTest { final Entity oldEntity = new Entity(); oldEntity.addProperty(oldProperty); - Update.deriveUpdate(newEntity, oldEntity); + WriteTransaction.deriveUpdate(newEntity, oldEntity); assertEquals(newEntity.getEntityStatus(), VALID); } @@ -83,7 +83,7 @@ public class UpdateTest { final Entity oldEntity = new Entity(); oldEntity.addProperty(oldProperty); - Update.deriveUpdate(newEntity, oldEntity); + WriteTransaction.deriveUpdate(newEntity, oldEntity); assertEquals(newEntity.getEntityStatus(), QUALIFIED); assertEquals(newProperty.getEntityStatus(), VALID); assertEquals(newProperty2.getEntityStatus(), QUALIFIED); @@ -124,7 +124,7 @@ public class UpdateTest { oldEntity.addProperty(oldProperty); - Update.deriveUpdate(newEntity, oldEntity); + WriteTransaction.deriveUpdate(newEntity, oldEntity); assertEquals(newUnit.getEntityStatus(), VALID); assertEquals(newProperty.getEntityStatus(), VALID); assertEquals(newEntity.getEntityStatus(), VALID); @@ -165,7 +165,7 @@ public class UpdateTest { oldEntity.addProperty(oldProperty); - Update.deriveUpdate(newEntity, oldEntity); + WriteTransaction.deriveUpdate(newEntity, oldEntity); assertEquals(newEntity.getEntityStatus(), QUALIFIED); assertEquals(newProperty.getEntityStatus(), QUALIFIED); }