diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 7161569a7e43017869d9bb0025c35388db6c3623..1802b5bb9fe8cd2f2a21643ee44a380ed8621c37 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -59,7 +59,7 @@ trigger_build:
       -F "variables[SERVER]=$CI_COMMIT_REF_NAME"
       -F "variables[TriggerdBy]=SERVER"
       -F "variables[TriggerdByHash]=$CI_COMMIT_SHORT_SHA"
-      -F ref=master https://gitlab.indiscale.com/api/v4/projects/14/trigger/pipeline
+      -F ref=dev https://gitlab.indiscale.com/api/v4/projects/14/trigger/pipeline
 
 # Build a docker image in which tests for this repository can run
 build-testenv:
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000000000000000000000000000000000000..4d8a87996586347cb32457bfbb06e524f380098b
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,42 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [Unreleased]
+
+### Added
+
+- Scripting is simplified by adding a `home` directory, of which a copy is
+  created for each called script and set as the `HOME` environment variable.
+
+### Changed
+
+- 
+
+
+### Deprecated
+
+- 
+
+
+### Fixed
+
+- #46 - Server-side scripting failed as an unprivileged user because the was no
+  writable home directory.
+
+- NaN Double Values (see #41)
+
+
+
+### Security (in case of vulnerabilities)
+
+## [0.1.0] - 2018-10-09
+
+Tag `v0.1` - Commit 3b17b49
+
+### Added
+
+- everything
diff --git a/caosdb-webui b/caosdb-webui
index 8c43e0ed13d4e2baf34b80ac2e98d0494e3272a3..b4950db910523029c3b1f9a268e84576b2584fbf 160000
--- a/caosdb-webui
+++ b/caosdb-webui
@@ -1 +1 @@
-Subproject commit 8c43e0ed13d4e2baf34b80ac2e98d0494e3272a3
+Subproject commit b4950db910523029c3b1f9a268e84576b2584fbf
diff --git a/conf/core/cache.ccf b/conf/core/cache.ccf
index 87d1338a5cf70da9b551744bc154190965d4bb46..672c798d126e9c759e0c7a59a81702733aa4e7b3 100644
--- a/conf/core/cache.ccf
+++ b/conf/core/cache.ccf
@@ -1,3 +1,35 @@
+# default caching options
 jcs.default.cacheattributes=org.apache.commons.jcs.engine.CompositeCacheAttributes
 jcs.default.cacheattributes.MaxObjects=1000
-jcs.default.cacheattributes.MemoryCacheName=org.apache.commons.jcs.engine.memory.lru.LRUMemoryCache
\ No newline at end of file
+jcs.default.elementattributes=org.apache.commons.jcs.engine.ElementAttributes
+
+# Caching of the backend transactions
+jcs.region.BACKEND_UserRoles.cacheattributes.MaxObjects=100
+jcs.region.BACKEND_Users.cacheattributes.MaxObjects=100
+jcs.region.BACKEND_PermissionRules.cacheattributes.MaxObjects=1000
+
+jcs.region.BACKEND_SparseEntities.cacheattributes.MaxObjects=1000
+jcs.region.BACKEND_EntityProperties.cacheattributes.MaxObjects=1000
+jcs.region.BACKEND_EntityParents.cacheattributes.MaxObjects=1000
+jcs.region.BACKEND_SparseFileRecordsByPath.cacheattributes.MaxObjects=1000
+
+jcs.region.BACKEND_JobRules.cacheattributes.MaxObjects=100
+
+
+# PAM UserSource Caching: Cached Items expire after 60 seconds if they are not requested (idle) and after 600 seconds max.
+# PAM_UnixUserGroups
+jcs.region.PAM_UnixUserGroups.elementattributes.IsEternal=false
+jcs.region.PAM_UnixUserGroups.elementattributes.MaxLife=600
+jcs.region.PAM_UnixUserGroups.elementattributes.IdleTime=60
+
+# PAM_UnixUserExists
+jcs.region.PAM_UnixUserExists.elementattributes.IsEternal=false
+jcs.region.PAM_UnixUserExists.elementattributes.MaxLife=600
+jcs.region.PAM_UnixUserExists.elementattributes.IdleTime=60
+
+# PAM_Authentication
+jcs.region.PAM_Authentication.elementattributes.IsEternal=false
+jcs.region.PAM_Authentication.elementattributes.MaxLife=600
+jcs.region.PAM_Authentication.elementattributes.IdleTime=60
+
+
diff --git a/conf/core/log4j2-default.properties b/conf/core/log4j2-default.properties
index ab7aed268e1e1e1a4088796d68b9e065086b5958..b1697a73fe6703850865406e1441947614d00f47 100644
--- a/conf/core/log4j2-default.properties
+++ b/conf/core/log4j2-default.properties
@@ -12,6 +12,11 @@ appender.stderr.name = stderr
 appender.stderr.layout.type = PatternLayout
 appender.stderr.layout.pattern = [%d{yy-MMM-dd HH:mm:ss:SSS}] [%p] [%c{1}:%L] - %m%n
 appender.stderr.target = SYSTEM_ERR
+appender.stderr.filter.threshold.type = ThresholdFilter
+appender.stderr.filter.threshold.level = INFO
+appender.stderr.filter.threshold.onMatch = ACCEPT
+appender.stderr.filter.threshold.onMismatch = DENY
+
 
 # ${LOG_DIR}/request_errors/...log
 appender.request_errors.type = RollingRandomAccessFile
diff --git a/conf/core/server.conf b/conf/core/server.conf
index 8e71c561705fda384a2a87b2b45c3aa8ccfb8ea4..882c4453f1dd2f11eb36bf6a534e0ba26b249c62 100644
--- a/conf/core/server.conf
+++ b/conf/core/server.conf
@@ -3,6 +3,7 @@ SERVER_OWNER=
 SERVER_NAME=CaosDB Server
 SERVER_SIDE_SCRIPTING_BIN_DIR=./scripting/bin/
 SERVER_SIDE_SCRIPTING_WORKING_DIR=./scripting/working/
+SERVER_SIDE_SCRIPTING_HOME_DIR=./scripting/home/
 FILE_SYSTEM_ROOT=./CaosDBFileSystem/FileSystemRoot/
 DROP_OFF_BOX=./CaosDBFileSystem/DropOffBox/
 TMP_FILES=./CaosDBFileSystem/TMP/
@@ -49,13 +50,6 @@ BUGTRACKER_URI=
 TRANSACTION_BENCHMARK_ENABLED=true
 CACHE_CONF_LOC=./conf/core/cache.ccf
 
-RULES_CACHE_CAPACITY=100
-SPARSE_ENTITY_CACHE_CAPACITY=1000
-PROPERTIES_CACHE_CAPACITY=1000
-PARENTS_CACHE_CAPACITY=1000
-USER_ACCOUNT_CACHE_CAPACITY=100
-GROUP_CACHE_CAPACITY=100
-
 INSERT_FILES_IN_DIR_ALLOWED_DIRS=
 
 SUDO_PASSWORD=
@@ -69,4 +63,4 @@ CERTIFICATES_KEY_PASSWORD=
 CERTIFICATES_KEY_STORE_PATH=
 CERTIFICATES_KEY_STORE_PASSWORD=
 
-WEBUI_HTTP_HEADER_CACHE_MAX_AGE=28800
\ No newline at end of file
+WEBUI_HTTP_HEADER_CACHE_MAX_AGE=28800
diff --git a/doc/devel/Benchmarking.md b/doc/devel/Benchmarking.md
new file mode 100644
index 0000000000000000000000000000000000000000..a244a3a64b771060fbf025fc0ee47054b9b95b48
--- /dev/null
+++ b/doc/devel/Benchmarking.md
@@ -0,0 +1,13 @@
+# Manual Java-Side Benchmarking #
+
+Benchmarking can be done using the `TransactionBenchmark` class (in package
+`caosdb.server.database.misc`).
+
+- Single timings can be added to instances of that class via the
+  `addBenchmark(object, time)` method.  Multiple benchmarks for the same object
+  (typically just strings) can be averaged.
+- Benchmarks can be serialized into XML, `Container` and `Query` objects already
+  use this with their included benchmarks to output benchmarking results.
+- To work with the benchmarks of often used objects, use these methods:
+  - `Container.getTransactionBenchmark().addBenchmark()`
+  - `Query.addBenchmark()`
diff --git a/makefile b/makefile
index 642e31c538a98b39fdb07aeaed5b8a2515adf947..53c822deac4faaa058d7b1a04329abf2eb03d0bb 100644
--- a/makefile
+++ b/makefile
@@ -4,6 +4,8 @@
 #
 # Copyright (C) 2018 Research Group Biomedical Physics,
 # Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+# Copyright (C) 2019 IndiScale GmbH
+# Copyright (C) 2019 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,6 +24,7 @@
 #
 
 SHELL:=/bin/bash
+JPDA_PORT ?= 9000
 
 compile: easy-units
 	mvn compile
@@ -32,8 +35,8 @@ runserver:
 run: compile
 	mvn exec:java@run
 
-run-debug:
-	java -Dcaosdb.debug=true -jar target/caosdb-server-0.1-SNAPSHOT-jar-with-dependencies.jar
+run-debug: jar
+	java -Xrunjdwp:transport=dt_socket,address=0.0.0.0:$(JPDA_PORT),server=y,suspend=n -Dcaosdb.debug=true -jar target/caosdb-server-0.1-SNAPSHOT-jar-with-dependencies.jar
 
 run-single:
 	java -jar target/caosdb-server-0.1-SNAPSHOT-jar-with-dependencies.jar
@@ -42,15 +45,15 @@ formatting:
 	mvn fmt:format
 
 # Compile into a standalone jar file
-jar: compile
-	mvn clean compile assembly:single
-#	mvn assembly:single
+jar: easy-units
+	mvn package -DskipTests
 
 antlr:
 	mvn antlr4:antlr4
 
 test: easy-units
-	mvn test
+	MAVEN_DEBUG_OPTS="-Xdebug -Xnoagent -Djava.compiler=NONE -Dcaosdb.debug=true -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=0.0.0.0:9000"
+	mvn test -X
 
 clean: clean-antlr
 	mvn clean
@@ -105,4 +108,5 @@ stop-debug-screen:
 	mkdir .m2-local
 
 easy-units: .m2-local
+	mvn clean
 	mvn deploy:deploy-file -DgroupId=de.timmfitschen -DartifactId=easy-units -Dversion=0.0.1-SNAPSHOT -Durl=file:./.m2-local/ -DrepositoryId=local-maven-repo -DupdateReleaseInfo=true -Dfile=./lib/easy-units-0.0.1-SNAPSHOT-jar-with-dependencies.jar
diff --git a/pom.xml b/pom.xml
index 42e44ca49b82963a16ac655755ccf29b8fcde6e0..ada3403287a657f7684ba8984c57cc80a51820a9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -49,16 +49,6 @@
     </repository>
   </repositories>
   <dependencies>
-    <dependency>
-      <groupId>org.apache.logging.log4j</groupId>
-      <artifactId>log4j-slf4j18-impl</artifactId>
-      <version>2.11.2</version>
-    </dependency>
-    <dependency>
-      <groupId>org.slf4j</groupId>
-      <artifactId>slf4j-api</artifactId>
-      <version>1.8.0-beta4</version>
-    </dependency>
     <dependency>
       <groupId>de.timmfitschen</groupId>
       <artifactId>easy-units</artifactId>
@@ -67,7 +57,7 @@
     <dependency>
       <groupId>org.apache.shiro</groupId>
       <artifactId>shiro-core</artifactId>
-      <version>1.3.2</version>
+      <version>1.4.1</version>
     </dependency>
     <dependency>
       <groupId>junit</groupId>
@@ -162,15 +152,25 @@
       <artifactId>jetty-util-ajax</artifactId>
       <version>9.2.14.v20151106</version>
     </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-slf4j-impl</artifactId>
+      <version>2.11.1</version>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>1.7.21</version>
+    </dependency>
     <dependency>
       <groupId>org.apache.logging.log4j</groupId>
       <artifactId>log4j-api</artifactId>
-      <version>2.11.2</version>
+      <version>2.11.1</version>
     </dependency>
     <dependency>
       <groupId>org.apache.logging.log4j</groupId>
       <artifactId>log4j-core</artifactId>
-      <version>2.11.2</version>
+      <version>2.11.1</version>
     </dependency>
   </dependencies>
   <build>
@@ -180,6 +180,35 @@
     <outputDirectory>${basedir}/target/classes</outputDirectory>
     <testOutputDirectory>${basedir}/target/test-classes</testOutputDirectory>
     <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-shade-plugin</artifactId>
+        <version>2.4.3</version>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>shade</goal>
+            </goals>
+            <configuration>
+              <finalName>${project.artifactId}-${project.version}-jar-with-dependencies</finalName>
+              <transformers>
+                <transformer implementation="com.github.edwgiz.mavenShadePlugin.log4j2CacheTransformer.PluginsCacheFileTransformer"></transformer>
+                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                  <mainClass>caosdb.server.CaosDBServer</mainClass>
+                </transformer>
+              </transformers>
+            </configuration>
+          </execution>
+        </executions>
+        <dependencies>
+          <dependency>
+            <groupId>com.github.edwgiz</groupId>
+            <artifactId>maven-shade-plugin.log4j2-cachefile-transformer</artifactId>
+            <version>2.8.1</version>
+          </dependency>
+        </dependencies>
+      </plugin>
       <plugin>
         <groupId>org.antlr</groupId>
         <artifactId>antlr4-maven-plugin</artifactId>
@@ -204,27 +233,6 @@
           <target>1.8</target>
         </configuration>
       </plugin>
-      <plugin>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <configuration>
-          <descriptorRefs>
-            <descriptorRef>jar-with-dependencies</descriptorRef>
-          </descriptorRefs>
-          <archive>
-            <manifest>
-              <mainClass>caosdb.server.CaosDBServer</mainClass>
-            </manifest>
-          </archive>
-        </configuration>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal> <!-- Run with: mvn assembly:single -->
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-surefire-plugin</artifactId>
@@ -238,8 +246,8 @@
             <log4j2.debug>true</log4j2.debug>
           </systemPropertyVariables>
           <reuseForks>false</reuseForks>
-          <!-- Start 0.5 JVMs per CPU core -->
-          <!-- Higher numbers *seem* to lead to higher failure rates... :-/ -->
+          <!-- Start 0.5 JVMs per CPU core
+                    Higher numbers *seem* to lead to higher failure rates... :-/ -->
           <forkCount>0.5C</forkCount>
         </configuration>
       </plugin>
@@ -298,13 +306,11 @@
           </execution>
         </executions>
       </plugin>
-      <!-- Remove easy-units from classpath generation because of
-           https://github.com/jdee-emacs/jdee/issues/125 (no sources inside
-           easy-units) -->
+      <!-- Remove easy-units from classpath generation because of https://github.com/jdee-emacs/jdee/issues/125 
+                (no sources inside easy-units) -->
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-dependency-plugin</artifactId>
-        <version>2.8</version>
         <configuration>
           <excludeArtifactIds>easy-units</excludeArtifactIds>
         </configuration>
diff --git a/scripting/home/readme.md b/scripting/home/readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..9c9744a55cec41f33929a3953d9c582fa97f46e0
--- /dev/null
+++ b/scripting/home/readme.md
@@ -0,0 +1,3 @@
+# The `home` directory #
+This directory will be copied to a temporary directy for each server-side
+scripting invocation and set as the `HOME` environment variable.
diff --git a/src/main/java/caosdb/server/CaosDBServer.java b/src/main/java/caosdb/server/CaosDBServer.java
index 3245902d5721e00d1ae0418a33e7a5f468794ea6..2586c2df7a388061a0986a4ff3a40f94b8657f1b 100644
--- a/src/main/java/caosdb/server/CaosDBServer.java
+++ b/src/main/java/caosdb/server/CaosDBServer.java
@@ -111,7 +111,6 @@ import org.slf4j.LoggerFactory;
 public class CaosDBServer extends Application {
 
   private static Logger logger = LoggerFactory.getLogger(CaosDBServer.class);
-  private static boolean DEBUG_MODE = false;
   private static boolean START_GUI = true;
   private static Properties SERVER_PROPERTIES = null;
   private static Component component = null;
@@ -222,7 +221,26 @@ public class CaosDBServer extends Application {
         INSECURE = true;
       }
     }
-    DEBUG_MODE = Boolean.getBoolean("caosdb.debug");
+  }
+
+  public static Ini getShiroConfig() {
+    final Ini config = new Ini();
+    final Section mainSec = config.addSection("main");
+    mainSec.put("CaosDB", CaosDBDefaultRealm.class.getCanonicalName());
+    mainSec.put("SessionTokenValidator", SessionTokenRealm.class.getCanonicalName());
+    mainSec.put("OneTimeTokenValidator", OneTimeTokenRealm.class.getCanonicalName());
+    mainSec.put("CaosDBAuthorizingRealm", CaosDBAuthorizingRealm.class.getCanonicalName());
+    mainSec.put("AnonymousRealm", AnonymousRealm.class.getCanonicalName());
+    mainSec.put(
+        "securityManager.realms",
+        "$CaosDB, $SessionTokenValidator, $OneTimeTokenValidator, $CaosDBAuthorizingRealm, $AnonymousRealm");
+
+    // disable shiro's default session management. We have quasi-stateless
+    // sessions
+    // using our SessionToken class.
+    mainSec.put(
+        "securityManager.subjectDAO.sessionStorageEvaluator.sessionStorageEnabled", "false");
+    return config;
   }
 
   /**
@@ -248,28 +266,12 @@ public class CaosDBServer extends Application {
       System.exit(1);
     }
 
-    INSECURE = INSECURE && DEBUG_MODE; // only allow insecure in debug mode
-    START_BACKEND = START_BACKEND || !DEBUG_MODE; // always start backend if
+    INSECURE = INSECURE && isDebugMode(); // only allow insecure in debug mode
+    START_BACKEND = START_BACKEND || !isDebugMode(); // always start backend if
     // not in debug mode
 
     // init Shiro (user authentication/authorization and session management)
-    final Ini config = new Ini();
-    final Section mainSec = config.addSection("main");
-    mainSec.put("CaosDB", CaosDBDefaultRealm.class.getCanonicalName());
-    mainSec.put("SessionTokenValidator", SessionTokenRealm.class.getCanonicalName());
-    mainSec.put("OneTimeTokenValidator", OneTimeTokenRealm.class.getCanonicalName());
-    mainSec.put("CaosDBAuthorizingRealm", CaosDBAuthorizingRealm.class.getCanonicalName());
-    mainSec.put("AnonymousRealm", AnonymousRealm.class.getCanonicalName());
-    mainSec.put(
-        "securityManager.realms",
-        "$CaosDB, $SessionTokenValidator, $OneTimeTokenValidator, $CaosDBAuthorizingRealm, $AnonymousRealm");
-
-    // disable shiro's default session management. We have quasi-stateless
-    // sessions
-    // using our SessionToken class.
-    mainSec.put(
-        "securityManager.subjectDAO.sessionStorageEvaluator.sessionStorageEnabled", "false");
-
+    final Ini config = getShiroConfig();
     final Factory<SecurityManager> factory = new IniSecurityManagerFactory(config);
     final SecurityManager securityManager = factory.getInstance();
     SecurityUtils.setSecurityManager(securityManager);
@@ -829,7 +831,7 @@ public class CaosDBServer extends Application {
   }
 
   public static boolean isDebugMode() {
-    return DEBUG_MODE;
+    return Boolean.getBoolean("caosdb.debug");
   }
 
   /**
diff --git a/src/main/java/caosdb/server/ServerProperties.java b/src/main/java/caosdb/server/ServerProperties.java
index 1f90b069df76760864385f6f090e123387bcbd0a..e68016894d65a3b77e38aa2f84f48ece7db05683 100644
--- a/src/main/java/caosdb/server/ServerProperties.java
+++ b/src/main/java/caosdb/server/ServerProperties.java
@@ -4,6 +4,8 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019 IndiScale GmbH
+ * Copyright (C) 2019 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
@@ -90,13 +92,6 @@ public class ServerProperties extends Properties {
 
   public static final String KEY_CACHE_CONF_LOC = "CACHE_CONF_LOC";
 
-  public static final String KEY_RULES_CACHE_CAPACITY = "RULES_CACHE_CAPACITY";
-  public static final String KEY_SPARSE_ENTITY_CACHE_CAPACITY = "SPARSE_ENTITY_CACHE_CAPACITY";
-  public static final String KEY_PROPERTIES_CACHE_CAPACITY = "PROPERTIES_CACHE_CAPACITY";
-  public static final String KEY_PARENTS_CACHE_CAPACITY = "PARENTS_CACHE_CAPACITY";
-  public static final String KEY_USER_ACCOUNT_CACHE_CAPACITY = "USER_ACCOUNT_CACHE_CAPACITY";
-  public static final String KEY_GROUP_CACHE_CAPACITY = "GROUP_CACHE_CAPACITY";
-
   public static final String KEY_TRANSACTION_BENCHMARK_ENABLED = "TRANSACTION_BENCHMARK_ENABLED";
 
   public static final String KEY_INSERT_FILES_IN_DIR_ALLOWED_DIRS =
@@ -119,6 +114,7 @@ public class ServerProperties extends Properties {
   public static final String KEY_SERVER_OWNER = "SERVER_OWNER";
 
   public static final String KEY_SERVER_SIDE_SCRIPTING_BIN_DIR = "SERVER_SIDE_SCRIPTING_BIN_DIR";
+  public static final String KEY_SERVER_SIDE_SCRIPTING_HOME_DIR = "SERVER_SIDE_SCRIPTING_HOME_DIR";
   public static final String KEY_SERVER_SIDE_SCRIPTING_WORKING_DIR =
       "SERVER_SIDE_SCRIPTING_WORKING_DIR";
 
diff --git a/src/main/java/caosdb/server/accessControl/Pam.java b/src/main/java/caosdb/server/accessControl/Pam.java
index c222fb83dabe0e4ad4e99d096d33192bf65086a9..99f68faadc15c15d9404a504d15669f0c2604ea3 100644
--- a/src/main/java/caosdb/server/accessControl/Pam.java
+++ b/src/main/java/caosdb/server/accessControl/Pam.java
@@ -4,6 +4,8 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019 IndiScale GmbH
+ * Copyright (C) 2019 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,23 +24,38 @@
  */
 package caosdb.server.accessControl;
 
+import caosdb.server.caching.Cache;
 import java.io.File;
 import java.io.IOException;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
+import org.apache.commons.jcs.access.behavior.ICacheAccess;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
 import org.apache.shiro.authz.AuthorizationException;
 import org.jvnet.libpam.PAMException;
 import org.jvnet.libpam.UnixUser;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
+/**
+ * PAM UserSource for authenticating users via the Host's pam module.
+ *
+ * <p>A User's existence check and the retrieval of a user's groups is done by the org.jvnet.libpam
+ * library.
+ *
+ * <p>The authentication of a user via the password need root-access and is therefore done by a
+ * special shell script running with root privileges on the host.
+ *
+ * @author Timm Fitschen (t.fitschen@indiscale.com)
+ */
 public class Pam implements UserSource {
 
+  private Logger logger = LogManager.getLogger(Pam.class);
+
   public static class DefaultPamScriptCaller implements PamScriptCaller {
 
-    private Logger logger = LoggerFactory.getLogger(getClass());
+    private Logger logger = LogManager.getLogger(Pam.class);
 
     private final String pam_script;
 
@@ -64,7 +81,7 @@ public class Pam implements UserSource {
 
       try {
         pam_authentication = getProcess(username, password);
-        logger.info("call pam script");
+        logger.trace("call pam script");
         return pam_authentication.waitFor() == 0;
       } catch (final IOException e) {
         throw new RuntimeException(e);
@@ -75,6 +92,11 @@ public class Pam implements UserSource {
   }
 
   public static final String DEFAULT_PAM_SCRIPT = "./misc/pam_authentication/pam_authentication.sh";
+
+  /*
+   * following constants are the names of the configuration parameters and parts of parameters in
+   * the user_sources.ini for the pam user source.
+   */
   public static final String KEY_PAM_SCRIPT = "pam_script";
   public static final String KEY_DEFAULT_USER_STATUS = "default_status";
   public static final String KEY_GROUP = "group";
@@ -83,8 +105,9 @@ public class Pam implements UserSource {
   public static final String KEY_INCLUDE = "include";
   public static final String KEY_ROLES = "roles";
   public static final String SEPARATOR = ".";
+  public static final String KEY_EMAIL = "email";
   public static final String REGEX_SPLIT_CSV = "\\s*,\\s*";
-  private static final String KEY_EMAIL = "email";
+
   private String[] EXCLUDE_USERS = null;
   private String[] INCLUDE_USERS = null;
   private String[] EXCLUDE_GROUPS = null;
@@ -92,16 +115,28 @@ public class Pam implements UserSource {
   private Map<String, String> map = null;
   private PamScriptCaller pamScriptCaller = null;
 
+  /* Caches for groups, user-existence and password validation */
+
+  private ICacheAccess<String, HashSet<String>> groupsCache = Cache.getCache("PAM_UnixUserGroups");
+  private ICacheAccess<String, Boolean> userExistsCache = Cache.getCache("PAM_UnixUserExists");
+  private ICacheAccess<String, Boolean> passwordValidCache = Cache.getCache("PAM_Authentication");
+
   @Override
   public void setMap(final Map<String, String> map) {
     this.map = map;
     this.pamScriptCaller = null;
   }
 
-  public Map<String, String> getMap() {
+  /**
+   * Return the current configuration of this user source.
+   *
+   * @return configuration.
+   */
+  private Map<String, String> getMap() {
     return this.map;
   }
 
+  /** @see {@link UserSource#resolveRolesForUsername(String)} */
   @Override
   public Set<String> resolveRolesForUsername(final String username) {
     final Set<String> resulting_roles = new HashSet<String>();
@@ -138,7 +173,40 @@ public class Pam implements UserSource {
     return resulting_roles;
   }
 
-  private static Set<String> getGroups(final String username) {
+  /**
+   * Get the UNIX groups of the user.
+   *
+   * <p>First, try to get them from the cache. Only ask the back-end if necessary and cache the
+   * results.
+   *
+   * @param username
+   * @return A user's UNIX groups.
+   */
+  private Set<String> getGroups(final String username) {
+    Set<String> cached = groupsCache.get(username);
+    if (cached != null) {
+      return cached;
+    }
+
+    Set<String> uncached = getGroupsNoCache(username);
+    if (uncached instanceof HashSet) {
+      groupsCache.put(username, (HashSet<String>) uncached);
+    } else {
+      groupsCache.put(username, new HashSet<String>(uncached));
+    }
+    return uncached;
+  }
+
+  /**
+   * Get the UNIX groups of the user directly from the back-end.
+   *
+   * <p>If the user does not exist, an empty set is returned.
+   *
+   * @param username
+   * @return A user's UNIX groups or an empty set.
+   */
+  private Set<String> getGroupsNoCache(final String username) {
+    logger.trace("Retrieving UnixGroups", username);
     if (UnixUser.exists(username)) {
       try {
         return new UnixUser(username).getGroups();
@@ -146,7 +214,7 @@ public class Pam implements UserSource {
         throw new AuthorizationException(e);
       }
     }
-    return null;
+    return new HashSet<String>();
   }
 
   @Override
@@ -154,11 +222,38 @@ public class Pam implements UserSource {
     return "PAM";
   }
 
+  /**
+   * Check if that user is known by the host's PAM.
+   *
+   * <p>Try the cache first. Only ask PAM directly if necessary and cache the results.
+   *
+   * @see {@link UserSource#isUserExisting(String)}.
+   * @return true iff the user is known.
+   */
   @Override
   public boolean isUserExisting(final String username) {
+    Boolean cached = userExistsCache.get(username);
+    if (cached != null) {
+      return cached;
+    }
+
+    boolean uncached = isUserExistingNoCache(username);
+    userExistsCache.put(username, uncached);
+    return uncached;
+  }
+
+  /**
+   * Check if that user is known by the host's PAM (without caching).
+   *
+   * @param username
+   * @return true iff the user is known.
+   */
+  private boolean isUserExistingNoCache(final String username) {
+    logger.trace("Check UnixUser.exists", username);
     return username != null && UnixUser.exists(username) && isIncorporated(username);
   }
 
+  /** @see {@link UserSource#isValid(String, String)}. */
   @Override
   public boolean isValid(final String username, final String password) {
     if (isUserExisting(username)) {
@@ -174,8 +269,40 @@ public class Pam implements UserSource {
     return this.pamScriptCaller;
   }
 
+  /**
+   * Check the validity of the password for that user by asking the pam script caller.
+   *
+   * <p>Try the cache first, only call the pam script if necessary and cache the results.
+   *
+   * @param caller
+   * @param username
+   * @param password
+   * @return true iff the password is correct.
+   */
   private boolean isValid(
       final PamScriptCaller caller, final String username, final String password) {
+    String key = "<" + password + "::" + username + ">";
+    Boolean cached = passwordValidCache.get(key);
+    if (cached != null) {
+      return cached;
+    }
+
+    boolean uncached = isValidNoCache(caller, username, password);
+    passwordValidCache.put(key, uncached);
+    return uncached;
+  }
+
+  /**
+   * Check the validity of the password for that user by asking the pam script caller.
+   *
+   * @param caller
+   * @param username
+   * @param password
+   * @return true iff the password is correct.
+   */
+  private boolean isValidNoCache(
+      final PamScriptCaller caller, final String username, final String password) {
+    logger.trace("Check Password", username);
     return caller.isValid(username, password);
   }
 
@@ -278,10 +405,15 @@ public class Pam implements UserSource {
           ret = n;
         } else if (ret != n) {
           // conflict -> ignore, go for pam-wide setting
+          ret = null;
           break;
         }
       }
     }
+
+    if (ret != null) {
+      return ret;
+    }
     // by pam-wide setting
     if (this.map.containsKey(KEY_DEFAULT_USER_STATUS)) {
       return UserStatus.valueOf(this.map.get(KEY_DEFAULT_USER_STATUS).toUpperCase());
diff --git a/src/main/java/caosdb/server/accessControl/UserSource.java b/src/main/java/caosdb/server/accessControl/UserSource.java
index 23ce7c45ce6cb9982ae5c7fac05a3a41037a5afe..0ece8d5014227916feb1c50b986aaf4d9a9bf91b 100644
--- a/src/main/java/caosdb/server/accessControl/UserSource.java
+++ b/src/main/java/caosdb/server/accessControl/UserSource.java
@@ -4,6 +4,8 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019 IndiScale GmbH
+ * Copyright (C) 2019 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
@@ -25,19 +27,84 @@ package caosdb.server.accessControl;
 import java.util.Map;
 import java.util.Set;
 
+/**
+ * UserSources are sources for users - i.e. they authenticate users and contain basic information
+ * about users.
+ *
+ * <p>UserSources let you
+ * <li>check if a user exists - {@link #isUserExisting(String)}
+ * <li>authenticate a user via a password - {@link #isValid(String, String)}
+ * <li>get the default {@link UserStatus} - {@link #getDefaultUserStatus(String)}
+ * <li>get the default email address - {@link #getDefaultUserEmail(String)}
+ * <li>retrieve a users roles - {@link #resolveRolesForUsername(String)} The default email and
+ *     default {@link UserStatus} might be overridden by other settings in CaosDB - that's why they
+ *     are called "default".
+ *
+ *     <p>Also, the user's roles might be overridden by the internal user source {@link
+ *     InternalUserSource}.
+ *
+ *     <p>A UserSource is configured via {@link #setMap(Map)}.
+ *
+ * @author Timm Fitschen (t.fitschen@indiscale.com)
+ */
 public interface UserSource {
 
+  /**
+   * Every UserSource has a unique name, e.g. PAM, CaosDB (which is default name of the internal
+   * user source {@link InternalUserSource}).
+   *
+   * @return name
+   */
   public String getName();
 
+  /**
+   * Check if a user exists.
+   *
+   * @param username
+   * @return true iff this user source knows a user with that name
+   */
   public boolean isUserExisting(String username);
 
+  /**
+   * Return all roles that a user is associated with.
+   *
+   * @param username
+   * @return a user's roles
+   */
   public Set<String> resolveRolesForUsername(final String username);
 
+  /**
+   * Configure this user source. The needed parameters are to be defined and documented by the
+   * implementations.
+   *
+   * @param map the configuration
+   */
   public void setMap(Map<String, String> map);
 
+  /**
+   * Return the {@link UserStatus} of that user.
+   *
+   * @param username
+   * @return The user status of that user
+   */
   public UserStatus getDefaultUserStatus(String username);
 
+  /**
+   * Return the email address of that user, or null if none is available as per this user source.
+   *
+   * <p>This method does not check if a user exists. So it will return null for unknown users.
+   *
+   * @param username
+   * @return The email address or null
+   */
   public String getDefaultUserEmail(String username);
 
+  /**
+   * Check if the user can be authenticated by the given password.
+   *
+   * @param username
+   * @param password
+   * @return true iff the password was correct.
+   */
   public boolean isValid(String username, String password);
 }
diff --git a/src/main/java/caosdb/server/caching/Cache.java b/src/main/java/caosdb/server/caching/Cache.java
new file mode 100644
index 0000000000000000000000000000000000000000..338b1d853fd0610be780362a0242193fa46eb320
--- /dev/null
+++ b/src/main/java/caosdb/server/caching/Cache.java
@@ -0,0 +1,50 @@
+/*
+ * ** header v3.0
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2018 Research Group Biomedical Physics,
+ * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019 IndiScale GmbH
+ * Copyright (C) 2019 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 caosdb.server.caching;
+
+import java.io.Serializable;
+import org.apache.commons.jcs.access.behavior.ICacheAccess;
+
+/**
+ * Caching Helper Class used for all caches in the CaosDB Server.
+ *
+ * <p>The actual work is delegated to an instance of {@link JCSCacheHelper}. However, the delegate
+ * {@link #DELEGATE} can be be overridden for testing purposes with {@link #setDelegate(Cache)}.
+ *
+ * @author Timm Fitschen (t.fitschen@indiscale.com)
+ */
+public class Cache {
+
+  /** The default cache helper delegate. */
+  private static CacheHelper DELEGATE = new JCSCacheHelper();
+
+  public static void setDelegate(CacheHelper delegate) {
+    DELEGATE = delegate;
+  }
+
+  public static final <K, V extends Serializable> ICacheAccess<K, V> getCache(String region) {
+    return DELEGATE.getCache(region);
+  }
+}
diff --git a/src/main/java/caosdb/server/caching/CacheHelper.java b/src/main/java/caosdb/server/caching/CacheHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..32c854400310959a7834fe32a6f90de9e1b584d1
--- /dev/null
+++ b/src/main/java/caosdb/server/caching/CacheHelper.java
@@ -0,0 +1,31 @@
+/*
+ * ** header v3.0
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2019 IndiScale GmbH
+ * Copyright (C) 2019 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 caosdb.server.caching;
+
+import java.io.Serializable;
+import org.apache.commons.jcs.access.behavior.ICacheAccess;
+
+public interface CacheHelper {
+
+  public <K, V extends Serializable> ICacheAccess<K, V> getCache(String region);
+}
diff --git a/src/main/java/caosdb/server/caching/JCSCacheHelper.java b/src/main/java/caosdb/server/caching/JCSCacheHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..dab2b77ceaab3d0caaf49d798a4819ca890b5e82
--- /dev/null
+++ b/src/main/java/caosdb/server/caching/JCSCacheHelper.java
@@ -0,0 +1,91 @@
+/*
+ * ** header v3.0
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2018 Research Group Biomedical Physics,
+ * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019 IndiScale GmbH
+ * Copyright (C) 2019 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 caosdb.server.caching;
+
+import caosdb.server.CaosDBServer;
+import caosdb.server.ServerProperties;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.util.Properties;
+import org.apache.commons.jcs.JCS;
+import org.apache.commons.jcs.access.behavior.ICacheAccess;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+/**
+ * A CacheHelper implementation which is configured statically via the {@link
+ * ServerProperties#KEY_CACHE_CONF_LOC} properties file.
+ *
+ * @author Timm Fitschen (t.fitschen@indiscale.com)
+ */
+public class JCSCacheHelper implements CacheHelper {
+
+  protected static Logger logger = LogManager.getLogger(JCSCacheHelper.class);
+
+  // A No-Operation configuration for JCS
+  private static Properties getNOPCachingProperties() {
+    Properties ret = new Properties();
+    ret.setProperty(
+        "jcs.default.cacheattributes", "org.apache.commons.jcs.engine.CompositeCacheAttributes");
+    ret.setProperty("jcs.default.cacheattributes.MaxObjects", "0");
+    return ret;
+  }
+
+  // configure JCS
+  static {
+    Properties config = null;
+    try {
+      Properties p = new Properties();
+      final InputStream is =
+          new FileInputStream(CaosDBServer.getServerProperty(ServerProperties.KEY_CACHE_CONF_LOC));
+      p.load(is);
+      is.close();
+      config = p;
+    } catch (final FileNotFoundException e) {
+      logger.error(e);
+      config = getNOPCachingProperties();
+    } catch (final IOException e) {
+      logger.error(e);
+      config = getNOPCachingProperties();
+    }
+    logger.info("Configuring JCS Caching with {}", config);
+    JCS.setConfigProperties(config);
+  }
+
+  @Override
+  public <K, V extends Serializable> ICacheAccess<K, V> getCache(final String name) {
+    final ICacheAccess<K, V> cache = JCS.getInstance(name);
+    logger.info(
+        "Caching configuration for {}:\n+----{}\n+----{}",
+        name,
+        cache.getCacheAttributes(),
+        cache.getDefaultElementAttributes());
+
+    return cache;
+  }
+}
diff --git a/src/main/java/caosdb/server/database/BackendTransaction.java b/src/main/java/caosdb/server/database/BackendTransaction.java
index 1d5d5c2e68274e664c392a24a901aa4bcd65623b..4899996e6c4df2b18837259112ac7e90c534b793 100644
--- a/src/main/java/caosdb/server/database/BackendTransaction.java
+++ b/src/main/java/caosdb/server/database/BackendTransaction.java
@@ -210,7 +210,7 @@ public abstract class BackendTransaction implements Undoable {
     return t;
   }
 
-  private static <K extends BackendTransactionImpl, L extends K> void setImpl(
+  public static <K extends BackendTransactionImpl, L extends K> void setImpl(
       final Class<K> k, final Class<L> l) {
     impl.put(k, l);
   }
diff --git a/src/main/java/caosdb/server/database/CacheableBackendTransaction.java b/src/main/java/caosdb/server/database/CacheableBackendTransaction.java
index 390a177fe899f89330aeb3d0959aab5bf1d52cbc..fc4c9659d9a8ea80393bebad945bd6badf9580c4 100644
--- a/src/main/java/caosdb/server/database/CacheableBackendTransaction.java
+++ b/src/main/java/caosdb/server/database/CacheableBackendTransaction.java
@@ -4,6 +4,8 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019 IndiScale GmbH
+ * Copyright (C) 2019 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
@@ -24,14 +26,19 @@ package caosdb.server.database;
 
 import caosdb.server.database.exceptions.TransactionException;
 import java.io.Serializable;
-import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.access.behavior.ICacheAccess;
 
 public abstract class CacheableBackendTransaction<K, V extends Serializable>
     extends BackendTransaction {
 
-  public abstract V executeNoCache() throws TransactionException;
-
   private Boolean cached = null;
+  private ICacheAccess<K, V> cache;
+
+  public CacheableBackendTransaction(ICacheAccess<K, V> cache) {
+    this.cache = cache;
+  }
+
+  public abstract V executeNoCache() throws TransactionException;
 
   @Override
   public final void execute() throws TransactionException {
@@ -67,8 +74,8 @@ public abstract class CacheableBackendTransaction<K, V extends Serializable>
 
   protected abstract K getKey();
 
-  protected CacheAccess<K, V> getCache() {
-    return null;
+  protected final ICacheAccess<K, V> getCache() {
+    return cache;
   }
 
   private final boolean cacheIsEnabled() {
diff --git a/src/main/java/caosdb/server/database/backend/transaction/DeleteSparseEntity.java b/src/main/java/caosdb/server/database/backend/transaction/DeleteSparseEntity.java
index 4bd6e5b9706fe743be6c924c3e1f86cb6ff83371..39deb1416c743f838bfc1b96c29fd845dbcef742 100644
--- a/src/main/java/caosdb/server/database/backend/transaction/DeleteSparseEntity.java
+++ b/src/main/java/caosdb/server/database/backend/transaction/DeleteSparseEntity.java
@@ -4,6 +4,8 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019 IndiScale GmbH
+ * Copyright (C) 2019 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
@@ -41,6 +43,9 @@ public class DeleteSparseEntity extends BackendTransaction {
   @Override
   protected void execute() {
     RetrieveSparseEntity.removeCached(this.entity.getId());
+    if (entity.hasFileProperties()) {
+      GetFileRecordByPath.removeCached(this.entity.getFileProperties().getPath());
+    }
 
     final DeleteSparseEntityImpl ret = getImplementation(DeleteSparseEntityImpl.class);
 
diff --git a/src/main/java/caosdb/server/database/backend/transaction/GetFileRecordByPath.java b/src/main/java/caosdb/server/database/backend/transaction/GetFileRecordByPath.java
index 195f16db5341fdcd204debbaad0217635d1fd222..8e0afcbdc50985f1fa1a939940c05f8429562c90 100644
--- a/src/main/java/caosdb/server/database/backend/transaction/GetFileRecordByPath.java
+++ b/src/main/java/caosdb/server/database/backend/transaction/GetFileRecordByPath.java
@@ -4,6 +4,8 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019 IndiScale GmbH
+ * Copyright (C) 2019 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,20 +24,31 @@
  */
 package caosdb.server.database.backend.transaction;
 
+import caosdb.server.caching.Cache;
 import caosdb.server.database.CacheableBackendTransaction;
 import caosdb.server.database.backend.interfaces.GetFileRecordByPathImpl;
 import caosdb.server.database.exceptions.TransactionException;
 import caosdb.server.database.proto.SparseEntity;
+import org.apache.commons.jcs.access.behavior.ICacheAccess;
 
 public class GetFileRecordByPath extends CacheableBackendTransaction<String, SparseEntity> {
 
+  private static final ICacheAccess<String, SparseEntity> cache =
+      Cache.getCache("BACKEND_SparseFileRecordsByPath");
   private final String path;
   private SparseEntity entity;
 
   public GetFileRecordByPath(final String path) {
+    super(cache);
     this.path = path;
   }
 
+  public static void removeCached(final String path) {
+    if (path != null && cache != null) {
+      cache.remove(path);
+    }
+  }
+
   @Override
   protected String getKey() {
     return this.path;
diff --git a/src/main/java/caosdb/server/database/backend/transaction/RetrieveParents.java b/src/main/java/caosdb/server/database/backend/transaction/RetrieveParents.java
index 5d1545d29b9bab295c9f335fe4eb083f12f748b2..5c1b0178f51496f83a4fac6a4ce3b2447abdccd5 100644
--- a/src/main/java/caosdb/server/database/backend/transaction/RetrieveParents.java
+++ b/src/main/java/caosdb/server/database/backend/transaction/RetrieveParents.java
@@ -4,6 +4,8 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019 IndiScale GmbH
+ * Copyright (C) 2019 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,26 +24,21 @@
  */
 package caosdb.server.database.backend.transaction;
 
-import caosdb.server.CaosDBServer;
-import caosdb.server.ServerProperties;
+import caosdb.server.caching.Cache;
 import caosdb.server.database.CacheableBackendTransaction;
 import caosdb.server.database.DatabaseUtils;
 import caosdb.server.database.backend.interfaces.RetrieveParentsImpl;
 import caosdb.server.database.exceptions.TransactionException;
-import caosdb.server.database.misc.Cache;
 import caosdb.server.database.proto.VerySparseEntity;
 import caosdb.server.entity.EntityInterface;
 import java.util.ArrayList;
-import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.access.behavior.ICacheAccess;
 
 public class RetrieveParents
     extends CacheableBackendTransaction<Integer, ArrayList<VerySparseEntity>> {
 
-  private static final CacheAccess<Integer, ArrayList<VerySparseEntity>> cache =
-      Cache.getCache(
-          "ParentsCache",
-          Integer.valueOf(
-              CaosDBServer.getServerProperty(ServerProperties.KEY_SPARSE_ENTITY_CACHE_CAPACITY)));
+  private static final ICacheAccess<Integer, ArrayList<VerySparseEntity>> cache =
+      Cache.getCache("BACKEND_EntityParents");
 
   /**
    * To be called by DeleteEntityProperties on execution.
@@ -54,14 +51,10 @@ public class RetrieveParents
     }
   }
 
-  @Override
-  protected CacheAccess<Integer, ArrayList<VerySparseEntity>> getCache() {
-    return cache;
-  }
-
   private final EntityInterface entity;
 
   public RetrieveParents(final EntityInterface entity) {
+    super(cache);
     this.entity = entity;
   }
 
diff --git a/src/main/java/caosdb/server/database/backend/transaction/RetrievePermissionRules.java b/src/main/java/caosdb/server/database/backend/transaction/RetrievePermissionRules.java
index c0daecbc46ac90630eac83d0c7334d7da148d429..7e9925777c21a6d041759cfdade7682b85458566 100644
--- a/src/main/java/caosdb/server/database/backend/transaction/RetrievePermissionRules.java
+++ b/src/main/java/caosdb/server/database/backend/transaction/RetrievePermissionRules.java
@@ -4,6 +4,8 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019 IndiScale GmbH
+ * Copyright (C) 2019 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,28 +24,24 @@
  */
 package caosdb.server.database.backend.transaction;
 
-import caosdb.server.CaosDBServer;
-import caosdb.server.ServerProperties;
+import caosdb.server.caching.Cache;
 import caosdb.server.database.CacheableBackendTransaction;
 import caosdb.server.database.backend.interfaces.RetrievePermissionRulesImpl;
 import caosdb.server.database.exceptions.TransactionException;
-import caosdb.server.database.misc.Cache;
 import caosdb.server.permissions.PermissionRule;
 import java.util.HashSet;
-import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.access.behavior.ICacheAccess;
 
 public class RetrievePermissionRules
     extends CacheableBackendTransaction<String, HashSet<PermissionRule>> {
 
-  private static final CacheAccess<String, HashSet<PermissionRule>> cache =
-      Cache.getCache(
-          "PermissionRulesCache",
-          Integer.valueOf(
-              CaosDBServer.getServerProperty(ServerProperties.KEY_GROUP_CACHE_CAPACITY)));
+  private static final ICacheAccess<String, HashSet<PermissionRule>> cache =
+      Cache.getCache("BACKEND_PermissionRules");
   private HashSet<PermissionRule> rules;
   private final String role;
 
   public RetrievePermissionRules(final String role) {
+    super(cache);
     this.role = role;
   }
 
@@ -51,11 +49,6 @@ public class RetrievePermissionRules
     cache.remove(role);
   }
 
-  @Override
-  protected CacheAccess<String, HashSet<PermissionRule>> getCache() {
-    return cache;
-  }
-
   @Override
   public HashSet<PermissionRule> executeNoCache() throws TransactionException {
     final RetrievePermissionRulesImpl t = getImplementation(RetrievePermissionRulesImpl.class);
diff --git a/src/main/java/caosdb/server/database/backend/transaction/RetrieveProperties.java b/src/main/java/caosdb/server/database/backend/transaction/RetrieveProperties.java
index b75d91acba76a4d97b16395701afe42ff05c5fac..650cae3676ed2b6b50f86c76a5ce92b195290fc3 100644
--- a/src/main/java/caosdb/server/database/backend/transaction/RetrieveProperties.java
+++ b/src/main/java/caosdb/server/database/backend/transaction/RetrieveProperties.java
@@ -4,6 +4,8 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019 IndiScale GmbH
+ * Copyright (C) 2019 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,29 +24,24 @@
  */
 package caosdb.server.database.backend.transaction;
 
-import caosdb.server.CaosDBServer;
-import caosdb.server.ServerProperties;
+import caosdb.server.caching.Cache;
 import caosdb.server.database.CacheableBackendTransaction;
 import caosdb.server.database.DatabaseUtils;
 import caosdb.server.database.backend.interfaces.RetrievePropertiesImpl;
 import caosdb.server.database.exceptions.TransactionException;
-import caosdb.server.database.misc.Cache;
 import caosdb.server.database.proto.ProtoProperty;
 import caosdb.server.entity.EntityInterface;
 import caosdb.server.entity.Role;
 import caosdb.server.entity.wrapper.Property;
 import java.util.ArrayList;
-import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.access.behavior.ICacheAccess;
 
 public class RetrieveProperties
     extends CacheableBackendTransaction<Integer, ArrayList<ProtoProperty>> {
 
   private final EntityInterface entity;
-  private static final CacheAccess<Integer, ArrayList<ProtoProperty>> cache =
-      Cache.getCache(
-          "PropertiesCache",
-          Integer.valueOf(
-              CaosDBServer.getServerProperty(ServerProperties.KEY_PROPERTIES_CACHE_CAPACITY)));
+  private static final ICacheAccess<Integer, ArrayList<ProtoProperty>> cache =
+      Cache.getCache("BACKEND_EntityProperties");
 
   /**
    * To be called by DeleteEntityProperties on execution.
@@ -57,12 +54,8 @@ public class RetrieveProperties
     }
   }
 
-  @Override
-  protected CacheAccess<Integer, ArrayList<ProtoProperty>> getCache() {
-    return cache;
-  }
-
   public RetrieveProperties(final EntityInterface entity) {
+    super(cache);
     this.entity = entity;
   }
 
diff --git a/src/main/java/caosdb/server/database/backend/transaction/RetrieveRole.java b/src/main/java/caosdb/server/database/backend/transaction/RetrieveRole.java
index e45879964ef8bafad0106238073ff8c9d87e2287..5bb6150a8640f45997a4a3432ad4ac27196cd15c 100644
--- a/src/main/java/caosdb/server/database/backend/transaction/RetrieveRole.java
+++ b/src/main/java/caosdb/server/database/backend/transaction/RetrieveRole.java
@@ -4,6 +4,8 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019 IndiScale GmbH
+ * Copyright (C) 2019 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,36 +24,26 @@
  */
 package caosdb.server.database.backend.transaction;
 
-import caosdb.server.CaosDBServer;
-import caosdb.server.ServerProperties;
 import caosdb.server.accessControl.Role;
+import caosdb.server.caching.Cache;
 import caosdb.server.database.CacheableBackendTransaction;
 import caosdb.server.database.backend.interfaces.RetrieveRoleImpl;
 import caosdb.server.database.exceptions.TransactionException;
-import caosdb.server.database.misc.Cache;
-import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.access.behavior.ICacheAccess;
 
 public class RetrieveRole extends CacheableBackendTransaction<String, Role> {
 
-  private static final CacheAccess<String, Role> cache =
-      Cache.getCache(
-          "RolesCache",
-          Integer.valueOf(
-              CaosDBServer.getServerProperty(ServerProperties.KEY_GROUP_CACHE_CAPACITY)));
+  private static final ICacheAccess<String, Role> cache = Cache.getCache("BACKEND_UserRoles");
 
   private final String role_name;
   private Role role;
 
-  @Override
-  protected CacheAccess<String, Role> getCache() {
-    return cache;
-  }
-
   public Role getRole() {
     return this.role;
   }
 
   public RetrieveRole(final String role) {
+    super(cache);
     this.role_name = role;
   }
 
diff --git a/src/main/java/caosdb/server/database/backend/transaction/RetrieveSparseEntity.java b/src/main/java/caosdb/server/database/backend/transaction/RetrieveSparseEntity.java
index df9f58671184fb70fca99ca7707f9270f75c87a1..a91b5021f1fa722a8e8831234fc8ebc2032ba115 100644
--- a/src/main/java/caosdb/server/database/backend/transaction/RetrieveSparseEntity.java
+++ b/src/main/java/caosdb/server/database/backend/transaction/RetrieveSparseEntity.java
@@ -4,6 +4,8 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019 IndiScale GmbH
+ * Copyright (C) 2019 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,30 +24,25 @@
  */
 package caosdb.server.database.backend.transaction;
 
-import caosdb.server.CaosDBServer;
-import caosdb.server.ServerProperties;
+import caosdb.server.caching.Cache;
 import caosdb.server.database.CacheableBackendTransaction;
 import caosdb.server.database.DatabaseUtils;
 import caosdb.server.database.backend.interfaces.RetrieveSparseEntityImpl;
 import caosdb.server.database.exceptions.TransactionException;
-import caosdb.server.database.misc.Cache;
 import caosdb.server.database.proto.SparseEntity;
 import caosdb.server.entity.Entity;
 import caosdb.server.entity.EntityInterface;
 import caosdb.server.utils.EntityStatus;
-import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.access.behavior.ICacheAccess;
 
 public class RetrieveSparseEntity extends CacheableBackendTransaction<Integer, SparseEntity> {
 
   private final EntityInterface entity;
-  private static final CacheAccess<Integer, SparseEntity> cache =
-      Cache.getCache(
-          "SparseEntityCache",
-          Integer.valueOf(
-              CaosDBServer.getServerProperty(ServerProperties.KEY_SPARSE_ENTITY_CACHE_CAPACITY)));
+  private static final ICacheAccess<Integer, SparseEntity> cache =
+      Cache.getCache("BACKEND_SparseEntities");
 
   /**
-   * To be called by UpdateSparseEntity and DeleteEntity on execution.
+   * To be called by {@link UpdateSparseEntity} and {@link DeleteEntity} on execution.
    *
    * @param id
    */
@@ -55,17 +52,13 @@ public class RetrieveSparseEntity extends CacheableBackendTransaction<Integer, S
     }
   }
 
-  @Override
-  protected CacheAccess<Integer, SparseEntity> getCache() {
-    return cache;
-  }
-
   public RetrieveSparseEntity(final EntityInterface entity) {
+    super(cache);
     this.entity = entity;
   }
 
   public RetrieveSparseEntity(final int id) {
-    this.entity = new Entity(id);
+    this(new Entity(id));
   }
 
   @Override
diff --git a/src/main/java/caosdb/server/database/backend/transaction/RetrieveTransactionHistory.java b/src/main/java/caosdb/server/database/backend/transaction/RetrieveTransactionHistory.java
index cb9cdd9e993b9399f2bec5c9bff7316e0754b9ed..72bc1e5b1ec3a3509cc5cbba23e38b908d4e969f 100644
--- a/src/main/java/caosdb/server/database/backend/transaction/RetrieveTransactionHistory.java
+++ b/src/main/java/caosdb/server/database/backend/transaction/RetrieveTransactionHistory.java
@@ -4,6 +4,8 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019 IndiScale GmbH
+ * Copyright (C) 2019 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,17 +25,15 @@
 package caosdb.server.database.backend.transaction;
 
 import caosdb.datetime.UTCDateTime;
-import caosdb.server.database.CacheableBackendTransaction;
+import caosdb.server.database.BackendTransaction;
 import caosdb.server.database.backend.interfaces.RetrieveTransactionHistoryImpl;
 import caosdb.server.database.exceptions.TransactionException;
 import caosdb.server.database.proto.ProtoTransactionLogMessage;
 import caosdb.server.entity.EntityInterface;
 import caosdb.server.utils.TransactionLogMessage;
 import java.util.ArrayList;
-import org.apache.commons.jcs.access.CacheAccess;
 
-public class RetrieveTransactionHistory
-    extends CacheableBackendTransaction<Integer, ArrayList<ProtoTransactionLogMessage>> {
+public class RetrieveTransactionHistory extends BackendTransaction {
 
   private final EntityInterface entity;
 
@@ -42,25 +42,13 @@ public class RetrieveTransactionHistory
   }
 
   @Override
-  protected Integer getKey() {
-    return this.entity.getId();
-  }
-
-  @Override
-  protected CacheAccess<Integer, ArrayList<ProtoTransactionLogMessage>> getCache() {
-    return null;
-  }
-
-  @Override
-  public ArrayList<ProtoTransactionLogMessage> executeNoCache() throws TransactionException {
+  protected void execute() {
     final RetrieveTransactionHistoryImpl t =
         getImplementation(RetrieveTransactionHistoryImpl.class);
-    return t.execute(getKey());
+    process(t.execute(entity.getId()));
   }
 
-  @Override
-  protected void process(final ArrayList<ProtoTransactionLogMessage> l)
-      throws TransactionException {
+  private void process(final ArrayList<ProtoTransactionLogMessage> l) throws TransactionException {
     for (final ProtoTransactionLogMessage t : l) {
       final UTCDateTime dateTime = UTCDateTime.UTCSeconds(t.seconds, t.nanos);
 
diff --git a/src/main/java/caosdb/server/database/backend/transaction/RetrieveUser.java b/src/main/java/caosdb/server/database/backend/transaction/RetrieveUser.java
index 7492df38adfcfd24d18f1a263be5070a8916c9c8..9f27152030f5c953ee7bd4ecfff8c83d3cbc8a73 100644
--- a/src/main/java/caosdb/server/database/backend/transaction/RetrieveUser.java
+++ b/src/main/java/caosdb/server/database/backend/transaction/RetrieveUser.java
@@ -4,6 +4,8 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019 IndiScale GmbH
+ * Copyright (C) 2019 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,43 +24,29 @@
  */
 package caosdb.server.database.backend.transaction;
 
-import caosdb.server.CaosDBServer;
-import caosdb.server.ServerProperties;
 import caosdb.server.accessControl.Principal;
+import caosdb.server.caching.Cache;
 import caosdb.server.database.CacheableBackendTransaction;
 import caosdb.server.database.backend.interfaces.RetrieveUserImpl;
 import caosdb.server.database.exceptions.TransactionException;
-import caosdb.server.database.misc.Cache;
 import caosdb.server.database.proto.ProtoUser;
-import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.access.behavior.ICacheAccess;
 
 public class RetrieveUser extends CacheableBackendTransaction<Principal, ProtoUser> {
 
   private ProtoUser user;
-  private static final CacheAccess<Principal, ProtoUser> cache =
-      Cache.getCache(
-          "UserAccountCache",
-          Integer.valueOf(
-              CaosDBServer.getServerProperty(ServerProperties.KEY_USER_ACCOUNT_CACHE_CAPACITY)));
+  private static final ICacheAccess<Principal, ProtoUser> cache = Cache.getCache("BACKEND_Users");
   private final Principal principal;
 
-  /**
-   * To be called by DeleteSparseEntity, SetPassword, and UpdateSparseEntity on execution.
-   *
-   * @param u
-   */
+  /** To be called by DeleteSparseEntity, SetPassword, and UpdateSparseEntity on execution. */
   public static void removeCached(final Principal principal) {
     if (principal != null && cache != null) {
       cache.remove(principal);
     }
   }
 
-  @Override
-  protected CacheAccess<Principal, ProtoUser> getCache() {
-    return cache;
-  }
-
   public RetrieveUser(final Principal principal) {
+    super(cache);
     this.principal = principal;
   }
 
diff --git a/src/main/java/caosdb/server/database/backend/transaction/RuleLoader.java b/src/main/java/caosdb/server/database/backend/transaction/RuleLoader.java
index 1a8698ac1d698946e7a3e239c03e386bc200e2b6..303029b42179a697c939767e2caf4d490a803a69 100644
--- a/src/main/java/caosdb/server/database/backend/transaction/RuleLoader.java
+++ b/src/main/java/caosdb/server/database/backend/transaction/RuleLoader.java
@@ -4,6 +4,8 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019 IndiScale GmbH
+ * Copyright (C) 2019 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,22 +24,22 @@
  */
 package caosdb.server.database.backend.transaction;
 
-import caosdb.server.CaosDBServer;
-import caosdb.server.ServerProperties;
+import caosdb.server.caching.Cache;
 import caosdb.server.database.CacheableBackendTransaction;
 import caosdb.server.database.backend.interfaces.RuleLoaderImpl;
 import caosdb.server.database.exceptions.TransactionException;
-import caosdb.server.database.misc.Cache;
 import caosdb.server.database.proto.Rule;
 import caosdb.server.entity.EntityInterface;
 import caosdb.server.entity.container.TransactionContainer;
 import caosdb.server.jobs.Job;
 import caosdb.server.transaction.Transaction;
 import java.util.ArrayList;
-import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.access.behavior.ICacheAccess;
 
 public class RuleLoader extends CacheableBackendTransaction<String, ArrayList<Rule>> {
 
+  private static final ICacheAccess<String, ArrayList<Rule>> cache =
+      Cache.getCache("BACKEND_JobRules");
   private final Transaction<? extends TransactionContainer> transaction;
   private final EntityInterface e;
   private final Integer entity;
@@ -49,6 +51,7 @@ public class RuleLoader extends CacheableBackendTransaction<String, ArrayList<Ru
       final Integer entity,
       final EntityInterface e,
       final Transaction<? extends TransactionContainer> transaction) {
+    super(cache);
     this.domain = domain;
     this.entity = entity;
     this.e = e;
@@ -66,17 +69,6 @@ public class RuleLoader extends CacheableBackendTransaction<String, ArrayList<Ru
         + ">";
   }
 
-  @Override
-  protected CacheAccess<String, ArrayList<Rule>> getCache() {
-    return rulesCache;
-  }
-
-  private static CacheAccess<String, ArrayList<Rule>> rulesCache =
-      Cache.getCache(
-          "RulesCache",
-          Integer.valueOf(
-              CaosDBServer.getServerProperty(ServerProperties.KEY_RULES_CACHE_CAPACITY)));
-
   public ArrayList<Job> getJobs() {
     return this.jobs;
   }
diff --git a/src/main/java/caosdb/server/database/backend/transaction/UpdateSparseEntity.java b/src/main/java/caosdb/server/database/backend/transaction/UpdateSparseEntity.java
index 57d22ef226e83caf7e3b37b48b53c1025afaa93f..c6a1c7683409e0c875484e14553e4aeac2757a71 100644
--- a/src/main/java/caosdb/server/database/backend/transaction/UpdateSparseEntity.java
+++ b/src/main/java/caosdb/server/database/backend/transaction/UpdateSparseEntity.java
@@ -4,6 +4,8 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019 IndiScale GmbH
+ * Copyright (C) 2019 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
@@ -39,6 +41,9 @@ public class UpdateSparseEntity extends BackendTransaction {
   @Override
   public void execute() throws TransactionException {
     RetrieveSparseEntity.removeCached(this.entity.getId());
+    if (entity.hasFileProperties()) {
+      GetFileRecordByPath.removeCached(this.entity.getFileProperties().getPath());
+    }
 
     final UpdateSparseEntityImpl t = getImplementation(UpdateSparseEntityImpl.class);
 
diff --git a/src/main/java/caosdb/server/database/misc/Cache.java b/src/main/java/caosdb/server/database/misc/Cache.java
deleted file mode 100644
index 7f364d4ab3e2ddc2d3e5e0638be4fe5dfa252ccd..0000000000000000000000000000000000000000
--- a/src/main/java/caosdb/server/database/misc/Cache.java
+++ /dev/null
@@ -1,78 +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 caosdb.server.database.misc;
-
-import caosdb.server.CaosDBServer;
-import caosdb.server.ServerProperties;
-import caosdb.server.terminal.StatLabel;
-import caosdb.server.terminal.StatsPanel;
-import caosdb.server.utils.AbstractObservable;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Serializable;
-import java.util.Properties;
-import org.apache.commons.jcs.JCS;
-import org.apache.commons.jcs.access.CacheAccess;
-
-public class Cache {
-
-  static {
-    final Properties p = new Properties();
-    try {
-      final InputStream is =
-          new FileInputStream(CaosDBServer.getServerProperty(ServerProperties.KEY_CACHE_CONF_LOC));
-      p.load(is);
-      is.close();
-    } catch (final FileNotFoundException e) {
-      e.printStackTrace();
-    } catch (final IOException e) {
-      e.printStackTrace();
-    }
-    JCS.setConfigProperties(p);
-  }
-
-  public static <K, V extends Serializable> CacheAccess<K, V> getCache(
-      final String name, final int capacity) {
-    if (capacity > 0) {
-      final CacheAccess<K, V> cache = JCS.getInstance(name);
-      cache.getCacheAttributes().setMaxObjects(capacity);
-      StatsPanel.addStat(
-          name,
-          new AbstractObservable() {
-            @Override
-            public String toString() {
-              return cache.getStats();
-            };
-          });
-      StatsPanel.addStat(
-          name,
-          new StatLabel(
-              "Configuration", cache.getCacheAttributes().toString().replaceAll(",", "\n")));
-
-      return cache;
-    }
-    return null;
-  }
-}
diff --git a/src/main/java/caosdb/server/datatype/GenericValue.java b/src/main/java/caosdb/server/datatype/GenericValue.java
index d6140ff75a80908e0cf82880aeee336a6f21cb87..8e8f1dd41e49305ddba2090b1f897a35a3765f93 100644
--- a/src/main/java/caosdb/server/datatype/GenericValue.java
+++ b/src/main/java/caosdb/server/datatype/GenericValue.java
@@ -44,7 +44,11 @@ public class GenericValue implements SingleValue {
       throw new NullPointerException();
     }
     this.value = d;
-    this.table = Table.double_data;
+    if (d.isNaN()) {
+      this.table = Table.text_data;
+    } else {
+      this.table = Table.double_data;
+    }
   }
 
   public GenericValue(final String s) {
diff --git a/src/main/java/caosdb/server/jobs/core/InsertFilesInDir.java b/src/main/java/caosdb/server/jobs/core/InsertFilesInDir.java
index 773ac27525e0b28a1a10f23f1a2ee9cfd9fbf799..9dbc2a68b684b7a5757a00c816f81f3716e55dc9 100644
--- a/src/main/java/caosdb/server/jobs/core/InsertFilesInDir.java
+++ b/src/main/java/caosdb/server/jobs/core/InsertFilesInDir.java
@@ -22,6 +22,7 @@
  */
 package caosdb.server.jobs.core;
 
+import caosdb.server.CaosDBException;
 import caosdb.server.CaosDBServer;
 import caosdb.server.FileSystem;
 import caosdb.server.ServerProperties;
@@ -153,7 +154,7 @@ public class InsertFilesInDir extends FlagJob {
                   try {
                     final Undoable delete = FileUtils.delete(InsertFilesInDir.this.tmp, true);
                     delete.cleanUp();
-                  } catch (final IOException e) {
+                  } catch (final IOException | CaosDBException | InterruptedException e) {
                     e.printStackTrace();
                   }
                 }
@@ -163,7 +164,7 @@ public class InsertFilesInDir extends FlagJob {
                   try {
                     final Undoable delete = FileUtils.delete(InsertFilesInDir.this.tmp, true);
                     delete.cleanUp();
-                  } catch (final IOException e) {
+                  } catch (final IOException | CaosDBException | InterruptedException e) {
                     e.printStackTrace();
                   }
                 }
diff --git a/src/main/java/caosdb/server/query/Query.java b/src/main/java/caosdb/server/query/Query.java
index c9bff5a5f11aa5775582c613d80062419f9adb4c..d3e0e927d14d0b2021f056ad52f10df548c5734c 100644
--- a/src/main/java/caosdb/server/query/Query.java
+++ b/src/main/java/caosdb/server/query/Query.java
@@ -415,6 +415,7 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
   }
 
   public void parse() throws ParsingException {
+    final long t1 = System.currentTimeMillis();
     CQLLexer lexer;
     lexer = new CQLLexer(CharStreams.fromString(this.query));
     final CommonTokenStream tokens = new CommonTokenStream(lexer);
@@ -434,6 +435,8 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
     this.type = cq.t;
     this.filter = cq.filter;
     this.selections = cq.s;
+    final long t2 = System.currentTimeMillis();
+    addBenchmark("parse (" + this.query + ")", t2 - t1);
   }
 
   private String executeStrategy() throws QueryException {
diff --git a/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java b/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java
index c88bbd75a34aea81198b2c9d48702ca6f66a41e0..948b4eab2fda1c906f4acf2802c0d83184b875bc 100644
--- a/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java
+++ b/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java
@@ -101,7 +101,8 @@ public abstract class AbstractCaosDBServerResource extends ServerResource {
   }
 
   public Subject getUser() {
-    return SecurityUtils.getSubject();
+    Subject ret = SecurityUtils.getSubject();
+    return ret;
   }
 
   /**
@@ -331,6 +332,7 @@ public abstract class AbstractCaosDBServerResource extends ServerResource {
     this.xslScript = s;
   }
 
+  /** Wrap an element for an "OK" response. */
   protected JdomRepresentation ok(Element root) {
     return ok(new Document(root));
   }
diff --git a/src/main/java/caosdb/server/resource/InfoResource.java b/src/main/java/caosdb/server/resource/InfoResource.java
index 96459212a804d2451c2b978bd858bba954640bca..c2342cc46f7e62fd6a398218b39e804ab9f1767d 100644
--- a/src/main/java/caosdb/server/resource/InfoResource.java
+++ b/src/main/java/caosdb/server/resource/InfoResource.java
@@ -30,10 +30,15 @@ import org.jdom2.Document;
 import org.jdom2.Element;
 import org.jdom2.JDOMException;
 import org.restlet.representation.Representation;
-import org.restlet.resource.Options;
 
+/** This class represents the information retrieved by /Info requests (only GET) to CaosDB. */
 public class InfoResource extends AbstractCaosDBServerResource {
 
+  /**
+   * The response to the HTTP GET request is generated here.
+   *
+   * @return The response code for OK and the resulting info XML document.
+   */
   @Override
   protected Representation httpGetInChildClass() throws Exception {
     final Document doc = new Document();
@@ -45,14 +50,10 @@ public class InfoResource extends AbstractCaosDBServerResource {
     return ok(doc);
   }
 
+  /** There is no POST request specified for the /Info resource. */
   @Override
   protected Representation httpPostInChildClass(final Representation entity)
       throws ConnectionException, JDOMException {
     return null;
   }
-
-  @Options
-  public static Representation describeResource() {
-    return null;
-  }
 }
diff --git a/src/main/java/caosdb/server/resource/ScriptingResource.java b/src/main/java/caosdb/server/resource/ScriptingResource.java
index 81a84ab631d9cd2206fd163b5c64eaac4c967286..57ada4aa7b4ed2a588e7de6e50c85de1b7fb6670 100644
--- a/src/main/java/caosdb/server/resource/ScriptingResource.java
+++ b/src/main/java/caosdb/server/resource/ScriptingResource.java
@@ -27,6 +27,7 @@ package caosdb.server.resource;
 import caosdb.server.FileSystem;
 import caosdb.server.accessControl.Principal;
 import caosdb.server.accessControl.SessionToken;
+import caosdb.server.accessControl.UserSources;
 import caosdb.server.entity.FileProperties;
 import caosdb.server.entity.Message;
 import caosdb.server.scripting.CallerSerializer;
@@ -82,6 +83,9 @@ public class ScriptingResource extends AbstractCaosDBServerResource {
   @Override
   protected Representation httpPostInChildClass(Representation entity) throws Exception {
 
+    if (isAnonymous()) {
+      return error(ServerMessages.AUTHORIZATION_ERROR, Status.CLIENT_ERROR_FORBIDDEN);
+    }
     MediaType mediaType = entity.getMediaType();
     try {
       if (mediaType.equals(MediaType.MULTIPART_FORM_DATA, true)) {
@@ -202,6 +206,11 @@ public class ScriptingResource extends AbstractCaosDBServerResource {
     return SessionToken.generate((Principal) getUser().getPrincipal(), null);
   }
 
+  boolean isAnonymous() {
+    boolean ret = getUser().hasRole(UserSources.ANONYMOUS_ROLE);
+    return ret;
+  }
+
   public int callScript(
       List<String> commandLine, Integer timeoutMs, List<FileProperties> files, Object authToken)
       throws Message {
diff --git a/src/main/java/caosdb/server/resource/transaction/EntityResource.java b/src/main/java/caosdb/server/resource/transaction/EntityResource.java
index 0e78c46a1f20fd870aca972f5c272c38c78438d0..f55c2b7ae0cb736305086ae131c3a3eeac1ba0ab 100644
--- a/src/main/java/caosdb/server/resource/transaction/EntityResource.java
+++ b/src/main/java/caosdb/server/resource/transaction/EntityResource.java
@@ -99,6 +99,7 @@ public class EntityResource extends AbstractCaosDBServerResource {
       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;
@@ -112,6 +113,10 @@ public class EntityResource extends AbstractCaosDBServerResource {
     final Retrieve retrieve = new Retrieve(entityContainer);
     retrieve.execute();
 
+    final long t2 = System.currentTimeMillis();
+    entityContainer
+        .getTransactionBenchmark()
+        .addBenchmark(getClass().getSimpleName() + ".httpGetInChildClass", t2 - t1);
     final Element rootElem = generateRootElement();
     entityContainer.addToElement(rootElem);
     doc.setRootElement(rootElem);
diff --git a/src/main/java/caosdb/server/scripting/ServerSideScriptingCaller.java b/src/main/java/caosdb/server/scripting/ServerSideScriptingCaller.java
index 7af6ba1ca5ac15868f991e6a2a8c7e85b2847bd7..6fa5d1a479df47c20d9d3a5d3a7134e1c0898ad1 100644
--- a/src/main/java/caosdb/server/scripting/ServerSideScriptingCaller.java
+++ b/src/main/java/caosdb/server/scripting/ServerSideScriptingCaller.java
@@ -25,7 +25,9 @@
 package caosdb.server.scripting;
 
 import caosdb.server.CaosDBException;
+import caosdb.server.CaosDBServer;
 import caosdb.server.FileSystem;
+import caosdb.server.ServerProperties;
 import caosdb.server.entity.FileProperties;
 import caosdb.server.entity.Message;
 import caosdb.server.utils.ServerMessages;
@@ -54,6 +56,7 @@ public class ServerSideScriptingCaller {
   private ScriptingUtils utils;
   private List<FileProperties> files;
   private File workingDir;
+  private File tmpHome;
   private File sharedDir;
   private Object authToken;
   private Map<String, String> env;
@@ -110,6 +113,7 @@ public class ServerSideScriptingCaller {
     this.authToken = authToken;
     this.env = env;
     this.workingDir = workingDir;
+    this.tmpHome = (new File(getTmpWorkingDir(), "temphome")).getAbsoluteFile();
     this.stdOutFile = utils.getStdOutFile(workingDir);
     this.stdErrFile = utils.getStdErrFile(workingDir);
   }
@@ -122,6 +126,7 @@ public class ServerSideScriptingCaller {
         createWorkingDir();
         putFilesInWorkingDir(files);
         createSharedDir();
+        createTmpHomeDir();
         updateEnvironment();
       } catch (final Exception e) {
         e.printStackTrace();
@@ -171,17 +176,41 @@ public class ServerSideScriptingCaller {
     }
 
     // create working dir
-    getTmpWorkingDir().mkdirs();
+    if (!getTmpWorkingDir().mkdirs()) {
+      throw new Exception("Creating the temporary working dir failed.");
+    }
   }
 
-  /** Creates a temporary directory for shareing by the script and sets `sharedDir` accordingly. */
+  /**
+   * Create a writable "home like" directory, needed for some Python libs, e.g. caosdb-pylib.
+   *
+   * <p>The temporary working directory must exist when this method is called.
+   */
+  void createTmpHomeDir() throws Exception, IOException {
+    if (!getTmpWorkingDir().exists()) {
+      throw new Exception(
+          "The temp working dir ("
+              + getTmpWorkingDir().toString()
+              + ") must exist before the home dir can be created.");
+    }
+    if (getTmpHomeDir().exists()) {
+      throw new Exception("The home directory must be non-existing when the caller is invoked.");
+    }
+    File srcDir =
+        new File(
+            CaosDBServer.getServerProperty(ServerProperties.KEY_SERVER_SIDE_SCRIPTING_HOME_DIR));
+    FileUtils.copyDirectory(srcDir, this.tmpHome);
+  }
+
+  /** Creates a temporary directory for sharing by the script and sets `sharedDir` accordingly. */
   void createSharedDir() throws Exception {
     sharedDir = new File(FileSystem.assertDir(null));
   }
 
   /** Sets environment variables for the script, according to the current state of the caller. */
-  void updateEnvironment() {
+  void updateEnvironment() throws Exception {
     env.put("SHARED_DIR", sharedDir.toString());
+    env.put("HOME", tmpHome.toString());
   }
 
   void cleanup() {
@@ -263,6 +292,10 @@ public class ServerSideScriptingCaller {
     return this.workingDir;
   }
 
+  File getTmpHomeDir() {
+    return this.tmpHome;
+  }
+
   File getSharedDir() {
     return this.sharedDir;
   }
diff --git a/src/main/java/caosdb/server/utils/FileUtils.java b/src/main/java/caosdb/server/utils/FileUtils.java
index 7441e73c1223f39d31ce4017618572486dfd8a64..824646caab6983863acd7d35a276a1213b393235 100644
--- a/src/main/java/caosdb/server/utils/FileUtils.java
+++ b/src/main/java/caosdb/server/utils/FileUtils.java
@@ -303,17 +303,34 @@ public class FileUtils {
     return java.nio.file.Files.isDirectory(file.toPath(), LinkOption.NOFOLLOW_LINKS);
   }
 
-  private static void moveReplace(File file, File target) throws IOException {
+  private static void moveReplace(File file, File target)
+      throws IOException, CaosDBException, InterruptedException {
     if (exists(target)) {
       org.apache.commons.io.FileUtils.forceDelete(target);
     }
     if (isDir(file)) {
       org.apache.commons.io.FileUtils.moveDirectory(file, target);
+    } else if (isSymlink(file)) {
+      moveSymlink(file, target);
     } else {
       org.apache.commons.io.FileUtils.moveFile(file, target);
     }
   }
 
+  /**
+   * @param oldlink
+   * @param newlink
+   * @throws IOException
+   * @throws InterruptedException
+   * @throws CaosDBException
+   */
+  private static void moveSymlink(File oldlink, File newlink)
+      throws IOException, CaosDBException, InterruptedException {
+    File target = oldlink.toPath().toRealPath().toFile();
+    createSymlink(newlink, target);
+    callPosixUnlink(oldlink);
+  }
+
   public static Undoable rename(final File file, final File target) throws Message, IOException {
     final File backup;
     if (target.getAbsoluteFile().equals(file.getAbsoluteFile())) {
@@ -325,16 +342,16 @@ public class FileUtils {
         public void cleanUp() {}
       };
     }
-    if (exists(target)) {
-      // in case this is a update transaction, the old version of the file
-      // must be stored somewhere until the transaction is done.
-      final File tmp = getTmpFile(target.getName());
-      moveReplace(target, tmp);
-      backup = tmp;
-    } else {
-      backup = null;
-    }
     try {
+      if (exists(target)) {
+        // in case this is a update transaction, the old version of the file
+        // must be stored somewhere until the transaction is done.
+        final File tmp = getTmpFile(target.getName());
+        moveReplace(target, tmp);
+        backup = tmp;
+      } else {
+        backup = null;
+      }
       moveReplace(file, target);
       return new UndoHandler() {
 
@@ -383,7 +400,8 @@ public class FileUtils {
     org.apache.commons.io.FileUtils.forceDelete(file);
   }
 
-  public static Undoable delete(final File file) throws IOException {
+  public static Undoable delete(final File file)
+      throws IOException, CaosDBException, InterruptedException {
     return delete(file, false);
   }
 
@@ -397,8 +415,11 @@ public class FileUtils {
    * @param recursive
    * @return
    * @throws IOException
+   * @throws InterruptedException
+   * @throws CaosDBException
    */
-  public static Undoable delete(final File file, final boolean recursive) throws IOException {
+  public static Undoable delete(final File file, final boolean recursive)
+      throws IOException, CaosDBException, InterruptedException {
     if (file.exists() && file.isFile()) {
       // delete file
       final File backup = getTmpFile(file.getName());
@@ -496,18 +517,22 @@ public class FileUtils {
     return isSymbolicLink(file.toPath());
   }
 
-  public static Undoable unlink(final File file)
-      throws IOException, InterruptedException, CaosDBException {
-    final String target = file.getCanonicalPath();
+  private static void callPosixUnlink(File file) throws IOException, InterruptedException {
     final Process cmd = Runtime.getRuntime().exec("unlink " + file.getAbsolutePath());
     if (cmd.waitFor() != 0) {
       throw new CaosDBException("could not unlink " + file.getAbsolutePath());
     }
+  }
+
+  public static Undoable unlink(final File file) throws IOException, InterruptedException {
+    callPosixUnlink(file);
+
+    final String path = file.getCanonicalPath();
     return new Undoable() {
       @Override
       public void undo() {
         try {
-          Runtime.getRuntime().exec("ln -s " + target + " " + file.getAbsolutePath());
+          Runtime.getRuntime().exec("ln -s " + path + " " + file.getAbsolutePath());
         } catch (final IOException e) {
           e.printStackTrace();
         }
diff --git a/src/main/java/caosdb/server/utils/Info.java b/src/main/java/caosdb/server/utils/Info.java
index bba346716624f3802aac449b86b2191c7cbcee10..445374a105fbe8fbff1bc95a90fcceba197b09f4 100644
--- a/src/main/java/caosdb/server/utils/Info.java
+++ b/src/main/java/caosdb/server/utils/Info.java
@@ -95,6 +95,18 @@ public class Info extends AbstractObservable implements Observer, TransactionInt
     return recordTypesCount;
   }
 
+  /**
+   * Utility function that takes an array of file objects and returns a linked list containing XML
+   * representations of all files within each contained directory. All files in the array that are
+   * directories are recursively traversed. This function is used for files within the drop off box
+   * only.
+   *
+   * @param files An array of file objects.
+   * @return A linked list containing XML elements. The path attribute of these elements is set to
+   *     the absolute path of the filenames where the path to the drop off box is removed at the
+   *     beginning.
+   * @fixme Should check if the files are inside the DropOffBox path.
+   */
   private static LinkedList<Element> getFlatList(final File[] files) {
     try {
       final LinkedList<Element> ret = new LinkedList<Element>();
@@ -104,7 +116,7 @@ public class Info extends AbstractObservable implements Observer, TransactionInt
         } else {
           final Element element = new Element("file");
           final String tempPath =
-              file.getAbsolutePath().substring(dropOffBox.getCanonicalPath().length() + 1);
+              file.getCanonicalPath().substring(dropOffBox.getCanonicalPath().length() + 1);
           element.setAttribute("path", tempPath);
           ret.add(element);
         }
@@ -158,6 +170,22 @@ public class Info extends AbstractObservable implements Observer, TransactionInt
     return toElement(false);
   }
 
+  /**
+   * Generates an XML element showing the information for the /Info resource in CaosDB.
+   *
+   * @return An XML element containing:
+   *     <ul>
+   *       <li>The number of records
+   *       <li>The number of properties
+   *       <li>The number of record types
+   *       <li>The number of files
+   *       <li>The total file size
+   *       <li>The number of temp files
+   *       <li>The path of the DropOffBox
+   *       <li>A tree of files in the DropOffBox
+   *     </ul>
+   *     TODO: The error format for missing or not readable drop off box has to be specified.
+   */
   public static Element toElement(final boolean tree) throws Exception, SQLException {
     dropOffBox = new File(FileSystem.getDropOffBox());
     final Element info = new Element("Stats");
@@ -173,14 +201,20 @@ public class Info extends AbstractObservable implements Observer, TransactionInt
     }
     final Element e = new Element("dropOffBox");
     if (dropOffBox.isDirectory()) {
-      if (tree) {
-        e.setAttribute("path", dropOffBox.getAbsolutePath());
-        e.addContent(getTree(dropOffBox.listFiles()));
+      if (dropOffBox.canRead()) {
+        if (tree) {
+          e.setAttribute("path", dropOffBox.getAbsolutePath());
+          e.addContent(getTree(dropOffBox.listFiles()));
+        } else {
+          e.setAttribute("path", dropOffBox.getAbsolutePath());
+          e.addContent(getFlatList(dropOffBox.listFiles()));
+        }
       } else {
-        e.setAttribute("path", dropOffBox.getAbsolutePath());
-        e.addContent(getFlatList(dropOffBox.listFiles()));
+        // TODO: return a message that the DropOffBox is not readable.
       }
     } else {
+      // TODO: This function should at least return a message that the DropOffBox is disabled or not
+      // present.
     }
     info.addContent(counts);
     info.addContent(e);
diff --git a/src/test/java/caosdb/server/Misc.java b/src/test/java/caosdb/server/Misc.java
index e94d91513be40459910a05559a6f526bb94e9872..92e8b198bf8cd60b8407dac4e7fcd4e9ed671a25 100644
--- a/src/test/java/caosdb/server/Misc.java
+++ b/src/test/java/caosdb/server/Misc.java
@@ -43,6 +43,7 @@ import java.util.regex.Pattern;
 import org.apache.commons.jcs.JCS;
 import org.apache.commons.jcs.access.CacheAccess;
 import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.config.Ini;
 import org.apache.shiro.config.IniSecurityManagerFactory;
 import org.apache.shiro.mgt.SecurityManager;
 import org.apache.shiro.subject.Subject;
@@ -316,7 +317,8 @@ public class Misc {
 
   @Test
   public void testShiro() {
-    final Factory<SecurityManager> factory = new IniSecurityManagerFactory();
+    Ini config = CaosDBServer.getShiroConfig();
+    final Factory<SecurityManager> factory = new IniSecurityManagerFactory(config);
 
     final SecurityManager securityManager = factory.getInstance();
 
diff --git a/src/test/java/caosdb/server/logging/TestLogging.java b/src/test/java/caosdb/server/logging/TestLogging.java
index 35667f35363af5165596ea1f32fa2f96963595aa..6e35d96585683d99c81a0fc2c669d4dc977bc09a 100644
--- a/src/test/java/caosdb/server/logging/TestLogging.java
+++ b/src/test/java/caosdb/server/logging/TestLogging.java
@@ -51,6 +51,8 @@ public class TestLogging {
 
   @Test
   public void testRequestTimeLogger() {
+    Assert.assertTrue(CaosDBServer.isDebugMode());
     Assert.assertFalse(request_time_logger.isErrorEnabled());
+    Assert.assertFalse(request_time_logger.isTraceEnabled());
   }
 }
diff --git a/src/test/java/caosdb/server/resource/TestScriptingResource.java b/src/test/java/caosdb/server/resource/TestScriptingResource.java
index 3b035fe703409605c59a77df70421e7516230376..8ea1cc2e888ceacd51213ef1d1516ede498a8267 100644
--- a/src/test/java/caosdb/server/resource/TestScriptingResource.java
+++ b/src/test/java/caosdb/server/resource/TestScriptingResource.java
@@ -23,23 +23,114 @@
 package caosdb.server.resource;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
+import caosdb.server.CaosDBServer;
+import caosdb.server.accessControl.AuthenticationUtils;
+import caosdb.server.accessControl.CredentialsValidator;
+import caosdb.server.accessControl.Principal;
+import caosdb.server.accessControl.Role;
+import caosdb.server.accessControl.SessionToken;
+import caosdb.server.database.BackendTransaction;
+import caosdb.server.database.access.Access;
+import caosdb.server.database.backend.interfaces.RetrievePasswordValidatorImpl;
+import caosdb.server.database.backend.interfaces.RetrievePermissionRulesImpl;
+import caosdb.server.database.backend.interfaces.RetrieveRoleImpl;
+import caosdb.server.database.backend.interfaces.RetrieveUserImpl;
+import caosdb.server.database.exceptions.TransactionException;
+import caosdb.server.database.proto.ProtoUser;
 import caosdb.server.entity.Message;
+import caosdb.server.permissions.PermissionRule;
 import java.io.IOException;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.List;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.config.Ini;
+import org.apache.shiro.config.IniSecurityManagerFactory;
+import org.apache.shiro.mgt.SecurityManager;
+import org.apache.shiro.subject.Subject;
+import org.apache.shiro.util.Factory;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.restlet.Request;
 import org.restlet.Response;
 import org.restlet.data.Form;
 import org.restlet.data.MediaType;
 import org.restlet.data.Method;
+import org.restlet.data.Reference;
 import org.restlet.data.Status;
 import org.restlet.representation.Representation;
 import org.restlet.representation.StringRepresentation;
 
 public class TestScriptingResource {
 
+  public static class RetrieveRole implements RetrieveRoleImpl {
+
+    public RetrieveRole(Access a) {}
+
+    @Override
+    public Role retrieve(String role) throws TransactionException {
+      Role ret = new Role();
+      ret.name = "anonymous";
+      ret.description = "bla";
+      return ret;
+    }
+  }
+
+  public static class RetrievePermissionRules implements RetrievePermissionRulesImpl {
+
+    public RetrievePermissionRules(Access a) {}
+
+    @Override
+    public HashSet<PermissionRule> retrievePermissionRule(String role) throws TransactionException {
+      return new HashSet<>();
+    }
+  }
+
+  public static class RetrieveUser implements RetrieveUserImpl {
+
+    public RetrieveUser(Access a) {}
+
+    @Override
+    public ProtoUser execute(Principal principal) throws TransactionException {
+      return new ProtoUser();
+    }
+  }
+
+  public static class RetrievePasswordValidator implements RetrievePasswordValidatorImpl {
+
+    public RetrievePasswordValidator(Access a) {}
+
+    @Override
+    public CredentialsValidator<String> execute(String name) throws TransactionException {
+      return new CredentialsValidator<String>() {
+
+        @Override
+        public boolean isValid(String credential) {
+          return true;
+        }
+      };
+    }
+  }
+
+  @BeforeClass
+  public static void setupShiro() throws IOException {
+    BackendTransaction.setImpl(RetrieveRoleImpl.class, RetrieveRole.class);
+    BackendTransaction.setImpl(RetrievePermissionRulesImpl.class, RetrievePermissionRules.class);
+    BackendTransaction.setImpl(RetrieveUserImpl.class, RetrieveUser.class);
+    BackendTransaction.setImpl(
+        RetrievePasswordValidatorImpl.class, RetrievePasswordValidator.class);
+
+    CaosDBServer.initServerProperties();
+    Ini config = CaosDBServer.getShiroConfig();
+    final Factory<SecurityManager> factory = new IniSecurityManagerFactory(config);
+
+    final SecurityManager securityManager = factory.getInstance();
+
+    SecurityUtils.setSecurityManager(securityManager);
+  }
+
   ScriptingResource resource =
       new ScriptingResource() {
         @Override
@@ -60,6 +151,12 @@ public class TestScriptingResource {
 
   @Test
   public void testUnsupportedMediaType() {
+    Subject user = SecurityUtils.getSubject();
+    if (user.isAuthenticated()) {
+      user.logout();
+    }
+    SessionToken t = SessionToken.generate(new Principal("CaosDB", "user"), null);
+    user.login(t);
     Representation entity = new StringRepresentation("asdf");
     entity.setMediaType(MediaType.TEXT_ALL);
     Request request = new Request(Method.POST, "../test", entity);
@@ -70,6 +167,23 @@ public class TestScriptingResource {
     assertEquals(Status.CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE, resource.getResponse().getStatus());
   }
 
+  @Test
+  public void testAnonymous() {
+    Subject user = SecurityUtils.getSubject();
+    user.login(AuthenticationUtils.ANONYMOUS_USER);
+    assertTrue(resource.isAnonymous());
+    Representation entity = new StringRepresentation("asdf");
+    entity.setMediaType(MediaType.TEXT_ALL);
+    Request request = new Request(Method.POST, "../test", entity);
+    request.setRootRef(new Reference("bla"));
+    request.getAttributes().put("SRID", "asdf1234");
+    request.setDate(new Date());
+    resource.init(null, request, new Response(null));
+
+    resource.handle();
+    assertEquals(Status.CLIENT_ERROR_FORBIDDEN, resource.getResponse().getStatus());
+  }
+
   @Test
   public void testForm2invocation() throws Message {
     Form form =
diff --git a/src/test/java/caosdb/server/scripting/TestServerSideScriptingCaller.java b/src/test/java/caosdb/server/scripting/TestServerSideScriptingCaller.java
index 668d75fbc9271babc8df20a872f661d3bd0a9646..f752c29c2f99b33d30a75ff3e09c7043c92fc464 100644
--- a/src/test/java/caosdb/server/scripting/TestServerSideScriptingCaller.java
+++ b/src/test/java/caosdb/server/scripting/TestServerSideScriptingCaller.java
@@ -492,6 +492,9 @@ public class TestServerSideScriptingCaller extends CaosDBTestClass {
           public void putFilesInWorkingDir(final Collection<FileProperties> files)
               throws FileNotFoundException, CaosDBException {}
 
+          @Override
+          public void createTmpHomeDir() throws Exception {}
+
           @Override
           public int callScript() throws IOException {
             throw new IOException();
@@ -609,4 +612,76 @@ public class TestServerSideScriptingCaller extends CaosDBTestClass {
             null, -1, null, "authToken", emptyEnv, new ScriptingUtils(), this.pwd);
     caller.cleanup();
   }
+
+  /** Does the order of directory creation matter? */
+  @Test
+  public void testDirectoriesInWrongOrder() throws Message {
+    final String[] cmd = {testExecutable.getAbsolutePath()};
+    final ServerSideScriptingCaller caller =
+        new ServerSideScriptingCaller(
+            cmd, -1, null, null, emptyEnv, new ScriptingUtils(), this.pwd);
+    this.exception.expect(
+        new BaseMatcher<Exception>() {
+
+          @Override
+          public boolean matches(final Object item) {
+            return item == ServerMessages.SERVER_SIDE_SCRIPT_ERROR;
+          }
+
+          @Override
+          public void describeTo(final Description description) {
+            description.appendValue(ServerMessages.SERVER_SIDE_SCRIPT_ERROR);
+          }
+        });
+    try {
+      caller.createTmpHomeDir();
+      caller.createWorkingDir();
+    } catch (final Exception e) {
+      e.printStackTrace();
+      throw ServerMessages.SERVER_SIDE_SCRIPT_ERROR;
+    }
+    caller.cleanup();
+  }
+
+  /** Is the new home directory created correctly? */
+  @Test
+  public void testWorkingDirCreation() throws Exception {
+    final String[] cmd = {testExecutable.getAbsolutePath()};
+    final ServerSideScriptingCaller caller =
+        new ServerSideScriptingCaller(
+            cmd, -1, null, null, emptyEnv, new ScriptingUtils(), this.pwd);
+    caller.createWorkingDir();
+    caller.createTmpHomeDir();
+    assertTrue(caller.getTmpHomeDir().exists());
+    caller.cleanup();
+  }
+
+  /** Does copying files over to the new home directory work? */
+  @Test
+  public void testWorkingDirCopying() throws Exception {
+    final String[] cmd = {testExecutable.getAbsolutePath()};
+    final ServerSideScriptingCaller caller =
+        new ServerSideScriptingCaller(
+            cmd, -1, null, null, emptyEnv, new ScriptingUtils(), this.pwd);
+    caller.createWorkingDir();
+    // Create source folder in working directory
+    String scriptingHomeDir = CaosDBServer.getServerProperty("SERVER_SIDE_SCRIPTING_HOME_DIR");
+    File tempHomeDirSrc = new File(caller.getTmpWorkingDir(), "home_source");
+    CaosDBServer.setProperty("SERVER_SIDE_SCRIPTING_HOME_DIR", tempHomeDirSrc.toString());
+    tempHomeDirSrc.mkdir();
+    String testFileName = "testfile.txt";
+    String testContent = "sloikdfgu89w4t78930tvgyum9q238456t27ht2";
+    File testFile = new File(tempHomeDirSrc, testFileName);
+    // testFile.createNewFile();
+    FileUtils.write(testFile, testContent, "UTF-8");
+    // Attempt to copy the source to the new home
+    caller.createTmpHomeDir();
+    // Compare source and new home
+    File expectedFile = new File(caller.getTmpHomeDir(), testFileName);
+    assertTrue(expectedFile.exists());
+    String expectedFileContent = FileUtils.readFileToString(expectedFile, "UTF-8");
+    assertEquals(expectedFileContent, testContent);
+    caller.cleanup();
+    CaosDBServer.setProperty("SERVER_SIDE_SCRIPTING_HOME_DIR", scriptingHomeDir);
+  }
 }
diff --git a/src/test/java/caosdb/server/utils/FileUtilsTest.java b/src/test/java/caosdb/server/utils/FileUtilsTest.java
index 183f5cf834783f7ad9c6e63bcdb25df1c7870822..c80f8ebb8b71785dcf8b6f5c8adaaec5e61b0332 100644
--- a/src/test/java/caosdb/server/utils/FileUtilsTest.java
+++ b/src/test/java/caosdb/server/utils/FileUtilsTest.java
@@ -69,9 +69,11 @@ public class FileUtilsTest {
   public static File tmpFolderCaosDB;
 
   @BeforeClass
-  public static void setup() throws Message, IOException {
+  public static void setup() throws Message, IOException, CaosDBException, InterruptedException {
     CaosDBServer.initServerProperties();
     testRoot = tempFolder.newFolder("fileutils_testfolder");
+    File basePath = tempFolder.newFolder("caosdbRoot");
+    File dropOffBox = tempFolder.newFolder("dropOffBox");
     someDir = testRoot.toPath().resolve("some_dir").toFile();
     linkToSomeDir = testRoot.toPath().resolve("link_to_some_dir").toFile();
     someFile = testRoot.toPath().resolve("some_file").toFile();
@@ -79,12 +81,15 @@ public class FileUtilsTest {
     linkToSomeFile = testRoot.toPath().resolve("link_to_some_file").toFile();
 
     tmpFolderCaosDB = tempFolder.newFolder();
+    CaosDBServer.setProperty(ServerProperties.KEY_FILE_SYSTEM_ROOT, basePath.toString());
+    CaosDBServer.setProperty(ServerProperties.KEY_DROP_OFF_BOX, dropOffBox.toString());
     CaosDBServer.setProperty(ServerProperties.KEY_TMP_FILES, tmpFolderCaosDB.toString());
     FileSystem.init();
 
-    Assert.assertTrue(new File(FileSystem.getBasepath()).canWrite());
-    Assert.assertTrue(new File(FileSystem.getBasepath()).canRead());
-    Assert.assertTrue(new File(FileSystem.getBasepath()).canExecute());
+    basePath = new File(FileSystem.getBasepath());
+    Assert.assertTrue(basePath.canWrite());
+    Assert.assertTrue(basePath.canRead());
+    Assert.assertTrue(basePath.canExecute());
     Assert.assertTrue(new File(FileSystem.getTmp()).canWrite());
     Assert.assertTrue(new File(FileSystem.getTmp()).canRead());
     Assert.assertTrue(new File(FileSystem.getTmp()).canExecute());
@@ -102,7 +107,7 @@ public class FileUtilsTest {
   }
 
   @AfterClass
-  public static void teardown() throws IOException {
+  public static void teardown() throws IOException, CaosDBException, InterruptedException {
     System.err.println("teardown");
     FileUtils.delete(testRoot, true);
     deleteTmp();
@@ -110,9 +115,13 @@ public class FileUtilsTest {
     // assertEquals(0, new File(FileSystem.getTmp()).listFiles().length);
   }
 
-  /** @fixme Currently still fails due to https://github.com/junit-team/junit4/issues/1223 */
+  /**
+   * @throws InterruptedException
+   * @throws CaosDBException
+   * @fixme Currently still fails due to https://github.com/junit-team/junit4/issues/1223
+   */
   @Before
-  public void testTmpEmpty() throws IOException {
+  public void testTmpEmpty() throws IOException, CaosDBException, InterruptedException {
     if (new File(FileSystem.getTmp()).exists()) {
       if (0 != new File(FileSystem.getTmp()).list().length) {
         System.err.println("TMP not empty, aborting test!");
@@ -128,7 +137,7 @@ public class FileUtilsTest {
     }
   }
 
-  public static void deleteTmp() throws IOException {
+  public static void deleteTmp() throws IOException, CaosDBException, InterruptedException {
     File tmpDir = new File(FileSystem.getTmp());
     if (tmpDir.exists())
       for (File f : tmpDir.listFiles()) {
@@ -138,7 +147,7 @@ public class FileUtilsTest {
   }
 
   @After
-  public void callDeleteTmp() throws IOException {
+  public void callDeleteTmp() throws IOException, CaosDBException, InterruptedException {
     deleteTmp();
   }