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);
   }