diff --git a/.gitignore b/.gitignore
index c30895c42d4a4bae9f1bea8750f63899102b2fa9..6ebd032b84c79dfa2a4bbf4df81cadce8a2a1555 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,3 +38,4 @@ authtoken/
 
 # python
 __pycache__
+*~
diff --git a/CHANGELOG.md b/CHANGELOG.md
index df5b85f849385868b6eb8e1d7daa605bc1b4e8d8..698408a6f92ede94a957a5e302eeb5b6b8a36978 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Added
 
+### Changed
+
+### Deprecated
+
+### Removed
+
+### Fixed
+
+- #145 Documentation of importances and inheritance
+
+### Security
+
+## [v0.4.0] - 2021-06-21
+
+### Added
+
+* Related to #146, a new flag for entities and complete transactions:
+  `force-missing-obligatory=[ignore|warn|error]`. The flag overrides the
+  default behavior of the server (throwing an error when an obligatory property
+  is missing). `ignore` just discards the consistency check, `warn` only issues
+  a warning when obligatory properties are missing and `error` throws an error
+  in that case. The flag can be set for the complete transaction and each
+  single entity, while the entity flag takes precedence.
 * New EntityState plug-in. The plug-in disabled by default and can be enabled
   by setting the server property `EXT_ENTITY_STATE=ENABLED`. See
   [!62](https://gitlab.com/caosdb/caosdb-server/-/merge_requests/62) for more
@@ -25,12 +48,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Changed
 
+* The default session timeout changed from 10 min to 60 min. Please set it to
+  your needs via the server config option `SESSION_TIMEOUT_MS`.
+
 ### Deprecated
 
 ### Removed
 
 ### Fixed
 
+* #146 - Default behavior for missing obligatory properties
 * #131 - CQL Parsing error when white space characters before some units.
 * #134 - CQL Parsing error when multiple white space characters after `FROM`.
 * #130 - Error during `FIND ENTITY` when
diff --git a/DEPENDENCIES.md b/DEPENDENCIES.md
index 1b0ba73efbf05f7c884d4067f5f0cf49694c4e09..3af219c2748c568d29de9044a1e3bdb077335f6e 100644
--- a/DEPENDENCIES.md
+++ b/DEPENDENCIES.md
@@ -1,4 +1,4 @@
-* caosdb-mysqlbackend == 4.0.0
+* caosdb-mysqlbackend == 4.1.0
 * Java 11
 * Apache Maven >= 3.6.0
 * make >= 4.2.0
diff --git a/README_SETUP.md b/README_SETUP.md
index d83ab788738d10924009bccb19707fa246686064..81816e3c9cc43b9ed8de47bba5a3e8202a8cfa35 100644
--- a/README_SETUP.md
+++ b/README_SETUP.md
@@ -80,7 +80,7 @@ server:
      Replace `localhost` by your host name, if you want.
    - `keytool -importkeystore -srckeystore caosdb.jks -destkeystore caosdb.p12 -deststoretype PKCS12 -srcalias selfsigned`
    - Export the public part only: `openssl pkcs12 -in caosdb.p12 -nokeys -out cert.pem`.
-	 The resulting `cert.pem` can safely be given to users to allow ssl verification.
+     The resulting `cert.pem` can safely be given to users to allow ssl verification.
    - You can check the content of the certificate with `openssl x509 -in cert.pem -text`
 
    Alternatively, you can create a keystore from certificate files that you already have:
@@ -93,14 +93,14 @@ server:
    appropriately:
     * Setup for MySQL back-end:
       specify the fields `MYSQL_USER_NAME`, `MYSQL_USER_PASSWORD`,
-	  `MYSQL_DATABASE_NAME`, and `MYSQL_HOST`.
+      `MYSQL_DATABASE_NAME`, and `MYSQL_HOST`.
     * Choose the ports under which CaosDB will be accessible.
     * Setup the SSL certificate: Assuming that there is an appropriate `Java Key
       Store` file (see above), change the fields `CERTIFICATES_KEY_PASSWORD`,
       `CERTIFICATES_KEY_STORE_PATH`, and `CERTIFICATES_KEY_STORE_PASSWORD`.
       Make sure that the conf file is not readable by other users because the
       certificate passwords are stored in plaintext.
-	- Set the path to the authtoken config (see step 4)
+    - Set the path to the authtoken config (see step 4)
     * Set the file system paths:
       - `FILE_SYSTEM_ROOT`: The root for all the files managed by CaosDB.
       - `DROP_OFF_BOX`: Files can be put here for insertion into CaosDB.
@@ -121,7 +121,7 @@ server:
     * See also [README_CONFIGURATION.md](README_CONFIGURATION.md)
 6. Copy `conf/core/usersources.ini.template` to `conf/ext/usersources.ini`.
     * You can skip this if you do not want to use an external authentication. 
-	  Local users (CaosDB realm) are always available.
+      Local users (CaosDB realm) are always available.
     * Define the users/groups who you want to include/exclude.
     * Assign at least one user the `administration` role.
       * For example, if the admin user is called `caosdb`, there should be the
diff --git a/caosdb-webui b/caosdb-webui
index 5dfe879722bd01acc5209c581b60bf0ac49635b6..c2dc8e9e9e0517ee7fb0d280717211a015906f64 160000
--- a/caosdb-webui
+++ b/caosdb-webui
@@ -1 +1 @@
-Subproject commit 5dfe879722bd01acc5209c581b60bf0ac49635b6
+Subproject commit c2dc8e9e9e0517ee7fb0d280717211a015906f64
diff --git a/conf/core/jobs.csv b/conf/core/jobs.csv
new file mode 100644
index 0000000000000000000000000000000000000000..84795bee8657288473188cd9e7a6af81f6295221
--- /dev/null
+++ b/conf/core/jobs.csv
@@ -0,0 +1,110 @@
+#
+# This file is a part of the CaosDB Project.
+#
+# Copyright (C) 2021 Timm Fitsche <t.fitschen@indiscale.com>
+# Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+#
+# 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/>.
+#
+
+# Five columns:
+# DOMAIN_ID, ENTITY_ID, TRANSACTION, JOB, JOB_FAILURE_SEVERITY
+
+# general rules
+0,0,INSERT,CheckPropValid,ERROR
+0,0,INSERT,CheckParValid,ERROR
+0,0,INSERT,CheckParOblPropPresent,ERROR
+0,0,INSERT,CheckValueParsable,ERROR
+0,0,UPDATE,CheckPropValid,ERROR
+0,0,UPDATE,CheckParValid,ERROR
+0,0,UPDATE,CheckParOblPropPresent,ERROR
+0,0,UPDATE,CheckValueParsable,ERROR
+0,0,DELETE,CheckReferenceDependencyExistent,ERROR
+0,0,DELETE,CheckChildDependencyExistent,ERROR
+
+
+# role specific rules
+## recordtype rules
+0,1,INSERT,CheckNamePresent,ERROR
+0,1,INSERT,CheckPropPresent,WARN
+0,1,INSERT,SetImpToRecByDefault,ERROR
+
+0,1,UPDATE,CheckNamePresent,ERROR
+0,1,UPDATE,CheckPropPresent,WARN
+0,1,UPDATE,SetImpToRecByDefault,ERROR
+
+## record rules
+0,2,INSERT,CheckNamePresent,WARN
+0,2,INSERT,CheckPropPresent,WARN
+0,2,INSERT,CheckParPresent,ERROR
+0,2,INSERT,SetImpToFix,ERROR
+
+0,2,UPDATE,CheckNamePresent,WARN
+0,2,UPDATE,CheckPropPresent,WARN
+0,2,UPDATE,CheckParPresent,ERROR
+0,2,UPDATE,SetImpToFix,ERROR
+
+## file rules
+0,3,INSERT,CheckNamePresent,WARN
+0,3,INSERT,MatchFileProp,ERROR
+0,3,INSERT,CheckTargetPathValid,ERROR
+0,3,INSERT,SetImpToFix,ERROR
+
+0,3,UPDATE,CheckNamePresent,WARN
+0,3,UPDATE,MatchFileProp,ERROR
+0,3,UPDATE,CheckTargetPathValid,ERROR
+0,3,UPDATE,SetImpToFix,ERROR
+
+## property rules
+0,4,INSERT,CheckDatatypePresent,ERROR
+0,4,UPDATE,CheckDatatypePresent,ERROR
+0,4,INSERT,CheckNamePresent,ERROR
+0,4,UPDATE,CheckNamePresent,ERROR
+0,4,INSERT,SetImpToFix,ERROR
+0,4,UPDATE,SetImpToFix,ERROR
+
+## query template rules
+0,8,UPDATE,CheckQueryTemplate,ERROR
+0,8,INSERT,CheckQueryTemplate,ERROR
+
+# data type specific rules
+## reference rules
+0,11,INSERT,CheckRefidPresent,WARN
+0,11,INSERT,CheckRefidValid,ERROR
+0,11,INSERT,CheckRefidIsaParRefid,WARN
+
+0,11,UPDATE,CheckRefidPresent,WARN
+0,11,UPDATE,CheckRefidValid,ERROR
+0,11,UPDATE,CheckRefidIsaParRefid,WARN
+
+## integer rules
+0,12,INSERT,CheckUnitPresent,WARN
+0,12,INSERT,ParseUnit,WARN
+
+0,12,UPDATE,CheckUnitPresent,WARN
+0,12,UPDATE,ParseUnit,WARN
+
+## double rules
+0,13,INSERT,CheckUnitPresent,WARN
+0,13,INSERT,ParseUnit,WARN
+
+0,13,UPDATE,CheckUnitPresent,WARN
+0,13,UPDATE,ParseUnit,WARN
+
+## filereference rules
+0,17,INSERT,CheckRefidValid,ERROR
+0,17,INSERT,CheckRefidIsaParRefid,ERROR
+
+0,17,UPDATE,CheckRefidValid,ERROR
+0,17,UPDATE,CheckRefidIsaParRefid,ERROR
diff --git a/conf/core/server.conf b/conf/core/server.conf
index 76ed6030523f24f977f41a510c703fe075f30042..153f76914618d8c524ea417586cb48481e16bf88 100644
--- a/conf/core/server.conf
+++ b/conf/core/server.conf
@@ -68,7 +68,7 @@ MYSQL_USER_NAME=caosdb
 # Password for the user
 MYSQL_USER_PASSWORD=caosdb
 # Schema of mysql procedures and tables which is required by this CaosDB instance
-MYSQL_SCHEMA_VERSION=v4.0.0
+MYSQL_SCHEMA_VERSION=v5.0
 
 
 # --------------------------------------------------
@@ -124,6 +124,9 @@ ONE_TIME_TOKEN_EXPIRES_MS=604800000
 # Path to config file for one time tokens, see authtoken.example.yml.
 AUTHTOKEN_CONFIG=
 
+# Path to config file for the job rules (e.g. which job is loaded for which kind of entity)
+JOB_RULES_CONFIG=conf/core/jobs.csv
+
 # Timeout after which a one-time token expires once it has been first consumed,
 # regardless of the maximum of replays that are allowed for that token. This is
 # only a default value. The actual timeout of tokens can be configured
@@ -190,9 +193,5 @@ GLOBAL_ENTITY_PERMISSIONS_FILE=./conf/core/global_entity_permissions.xml
 ENTITY_VERSIONING_ENABLED=true
 
 
-# --------------------------------------------------
-# Extension settings
-# --------------------------------------------------
-
 # Enabling the state machine extension
 # EXT_STATE_ENTITY=ENABLE
diff --git a/pom.xml b/pom.xml
index 4c4435934c97633ecadf7bc1ca2d2b6917198bd6..7fece112f6b7bff37e0f8b8afdab0579e073a842 100644
--- a/pom.xml
+++ b/pom.xml
@@ -25,7 +25,7 @@
   <modelVersion>4.0.0</modelVersion>
   <groupId>org.caosdb</groupId>
   <artifactId>caosdb-server</artifactId>
-  <version>0.4.0-SNAPSHOT</version>
+  <version>0.5.0-SNAPSHOT</version>
   <packaging>jar</packaging>
   <name>CaosDB Server</name>
   <properties>
diff --git a/src/doc/Data-Model.md b/src/doc/Data-Model.md
index 3fa64ec2fdab693552b742572d05b1e3be88d11b..d11a88f67141e366a28ed84895903df7ff062822 100644
--- a/src/doc/Data-Model.md
+++ b/src/doc/Data-Model.md
@@ -1,40 +1,67 @@
 
 # CaosDB Data Model
 
-The data structure is build from some basic building blocks which are shown in the following picture:
+The data structure is built from some basic building blocks which are
+shown in the following picture:
 
 ![entities](entities.png)
 
-It has a base object called **Entity**. Entities are either **Record Types**, **Records**, or **Abstract
-Properties** (and Record like objects for files).
-What *kind of data* is stored is defined by the Record Types. Actual data is then stored in CaosDB as Records which are of some Record Type. Those Records can have Properties that contain information about the Record. The following is a more detailed explanation (also see this [paper](https://www.mdpi.com/2306-5729/4/2/83)).
-
-> Record Types and Abstract Properties are used to define the ontology for a particular
-> domain in which the RDMS is used. Records are used to store the actual data and therefore
-> represent individuals or particular things, e.g. a particular experiment, a particular time series, etc.
-
-**Record Types** define classes or types of things, e.g. persons, experiments, timeseries, etc. Records
-can be viewed as members of the class defined by its Record Type. These classes can contain
-Abstract Properties which define key-value relationships for properties of the things along
-with the expected data type and possibly the default unit, a default value, or a range of permitted
-values. As files on the back-end file system are a major focus of this database management system,
-there is a special entity File that encapsulates typical file properties like path, size and checksum.
-
-**Entities** can be related via binary, directed, transitive is-a relations which model both subtyping
-and instantiation, depending on the relata. These relations construct a directed graph of the
-Entities. If A is-a B we call A the child of B and B the parent of A. No adamant restrictions are
-imposed on the relate of the is-a relation and thus, Entities can be children of multiple Entities.
-
-Each Entity has a list of Entity Properties, or in short just **Properties**. An Entity Property is not an Entity of its own, but a triple of an Abstract Property, a value or Null, and
-an Importance. The values can be numericals, strings, dates, any other valid value that fits into
-one of several builtin data types, or, most notably, references to other Entities. The importance
-is either obligatory, recommended, suggested, or fix. A valid child of an Entity implicitly
-inherits its parent’s Properties according to their Importance, which means that it is obliged,
-recommended or only suggested to have a Property with the same Abstract Property (or
-any subtype thereof).
-
-As opposed to Properties with other priorities, **Fixed Properties** have no effect on the Entity’s children. During the creation or update of Entities, the importances of the parents are
-being checked by the Server. Missing obligatory Properties invalidate the transaction and
-result in an error, by default. Missing Properties, when they are recommended, result in a
-warning, but the transaction is considered valid. Entities with missing suggested Properties
-are silently accepted as valid.
+It has a base object called **Entity**. Entities are either **Record
+Types**, **Records**, or **Abstract Properties** (and Record like
+objects for files). What *kind of data* is stored is defined by the
+Record Types. Actual data is then stored in CaosDB as Records which
+are of some Record Type. Those Records can have Properties that
+contain information about the Record. The following is a more detailed
+explanation (also see this
+[paper](https://www.mdpi.com/2306-5729/4/2/83)).
+
+> Record Types and Abstract Properties are used to define the ontology
+> for a particular domain in which the RDMS is used. Records are used
+> to store the actual data and therefore represent individuals or
+> particular things, e.g. a particular experiment, a particular time
+> series, etc.
+
+## Record Types
+
+**Record Types** define classes or types of things, e.g. persons,
+experiments, timeseries, etc. Records can be viewed as members of the
+class defined by its Record Type. These classes can contain Abstract
+Properties which define key-value relationships for properties of the
+things along with the expected data type and possibly the default
+unit, a default value, or a range of permitted values. As files on the
+backend file system are a major focus of this database management
+system, there is a special File entity that encapsulates typical file
+properties like path, size and checksum.
+
+## Entities
+
+**Entities** can be related via binary, directed, transitive is-a
+relations which model both subtyping and instantiation, depending on
+the relata. These relations construct a directed graph of the
+Entities. If A is-a B we call A the child of B and B the parent of
+A. No adamant restrictions are imposed on the relate of the is-a
+relation and thus, Entities can be children of multiple Entities.
+
+## Properties
+
+Each Entity has a list of Entity Properties, or in short just
+**Properties**. An Entity Property is not an Entity of its own, but a
+triple of an Abstract Property, a value or Null, and an
+Importance. The values can be numerals, strings, dates, any other
+valid value that fits into one of several builtin data types, or, most
+notably, references to other Entities. The Importance is either
+`obligatory`, `recommended`, `suggested`, or `fix`. A valid child of
+an Entity implicitly inherits its parent’s Properties according to
+their Importance, which means that it is obliged, recommended or only
+suggested to have a Property with the same Abstract Property (or any
+subtype thereof). As opposed to Properties with other priorities,
+**Fixed Properties** have no effect on the Entity’s children.
+
+During the creation or update of Entities, the importances of the
+parents are checked by the server. Missing obligatory Properties
+invalidate the transaction and result in an error, by default. Missing
+Properties, when they are recommended, result in a warning, but the
+transaction is considered valid. Entities with missing suggested
+Properties are silently accepted as valid. See [the detailed
+specification of RecordTypes](specification/RecordType) for more
+information on importances and inheritance.
diff --git a/src/doc/Glossary.md b/src/doc/Glossary.md
index 080c2aea4ff393d349553f7d7211d35518ff7c11..39baab24d17fae531dd18d0f4a1fcc2329ec1035 100644
--- a/src/doc/Glossary.md
+++ b/src/doc/Glossary.md
@@ -1,5 +1,9 @@
 # Glossary
 
+## Valid ID
+
+The ID of an existing entity. It is by definition unique among the IDs of all existing entities and is a positive integer."
+
 ## Valid Unique Existing Name
 
 A name of an exiting entity which is unique among the names of all existing entities.
@@ -8,10 +12,6 @@ A name of an exiting entity which is unique among the names of all existing enti
 
 A name of a to-be-inserted/updated entity _e_ which is unique among the names of all other entities that are to be inserted or updated along with the entity _e_.
 
-## Valid ID
-
-The ID of an existing entity. It is by definition unique among the IDs of all existing entities and is a positive integer."
-
 ## Valid Unique Temporary ID
 
 The negative integer ID of a to-be-inserted entity _e_ which is unique among the ids of all other entities that are to be inserted along with the entity _e_. 
diff --git a/src/doc/_static/css/server_docu.css b/src/doc/_static/css/server_docu.css
new file mode 100644
index 0000000000000000000000000000000000000000..b3eeaacec8769adffcd8ee6f2b25eb0f350ef4cf
--- /dev/null
+++ b/src/doc/_static/css/server_docu.css
@@ -0,0 +1,9 @@
+table, .wy-table-responsive table {
+    border-collapse: collapse;
+    table-layout: fixed;
+}
+
+table td, .wy-table-responsive table td {
+    word-wrap: break-word;
+    white-space: unset;
+}
diff --git a/src/doc/concepts.rst b/src/doc/concepts.rst
index 0db6186302302767c49735bcdfa81364685573f9..4403c95810f9b2edaa67e8561306b80031ac803e 100644
--- a/src/doc/concepts.rst
+++ b/src/doc/concepts.rst
@@ -7,7 +7,7 @@ Basic concepts of the CaosDB server
    :glob:
 
    Data Model <Data-Model>
-   Permissions
+   permissions
    roles
 
 The CaosDB server provides the HTTP API resources to users and client libraries.  It uses a plain
diff --git a/src/doc/conf.py b/src/doc/conf.py
index 46580733a72952fff673567ba1564903b60d584c..16e049be56d85a6246ebd58b921dd6a5adcf1938 100644
--- a/src/doc/conf.py
+++ b/src/doc/conf.py
@@ -25,9 +25,9 @@ copyright = '2020, IndiScale GmbH'
 author = 'Daniel Hornung'
 
 # The short X.Y version
-version = '0.3'
+version = '0.5'
 # The full version, including alpha/beta/rc tags
-release = '0.3'
+release = '0.5.0-SNAPSHOT'
 
 
 # -- General configuration ---------------------------------------------------
@@ -94,7 +94,13 @@ html_theme = 'sphinx_rtd_theme'
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-# html_static_path = ['_static']
+html_static_path = ['_static']
+
+# These paths are either relative to html_static_path
+# or fully qualified paths (eg. https://...)
+html_css_files = [
+    'css/server_docu.css',
+]
 
 # Custom sidebar templates, must be a dictionary that maps document names
 # to template names.
diff --git a/src/doc/Permissions.rst b/src/doc/permissions.rst
similarity index 96%
rename from src/doc/Permissions.rst
rename to src/doc/permissions.rst
index 2a1278ec450a9bf38ee03546b7301f2aabd76151..e61ee49a948e905f1800ee5fdb2f0efb6a4ab207 100644
--- a/src/doc/Permissions.rst
+++ b/src/doc/permissions.rst
@@ -60,7 +60,7 @@ Possible actions
 Until it is completely added to this documentation, a detailed
 description of the actions governed by these permissions can be found
 `in the
-sources <https://gitlab.com/caosdb/caosdb-server/blob/dev/src/main/java/caosdb/server/permissions/EntityPermission.java#L119>`__.
+sources <https://gitlab.com/caosdb/caosdb-server/-/blob/dev/src/main/java/org/caosdb/server/permissions/EntityPermission.java#L119>`__.
 
 Typical permissions are:
 
diff --git a/src/doc/specification/AbstractProperty.md b/src/doc/specification/AbstractProperty.md
index a337fa5ed053742a0e0cee4e9771cee862c19d7c..dee88e665c1609224bdab185ea147b91c49dc8f3 100644
--- a/src/doc/specification/AbstractProperty.md
+++ b/src/doc/specification/AbstractProperty.md
@@ -1,12 +1,14 @@
-.. note ::
+# Note #
 
-  This document has not been updated for a long time. Although it is concerned with the mostly
-  stable API, its content may no longer reflect the actual CaosDB behavior.
+>   This document has not been updated for a long time. Although it is concerned with the mostly
+>   stable API, its content may no longer reflect the actual CaosDB behavior.
 
 # AbstractProperty Specification
 
+**Warning:** This specification is outdated. It is included to serve as a starting point for a more up-to-date description of the `Property` entity.
+
 ## Introduction
-An `AbstractProperty` is one of the basal objects of HeartDB.
+An `AbstractProperty` is one of the basal objects of CaosDB.
 An `AbstractProperty` MUST have the following _qualities_ (shortcut in brackets):
 * a persistent id (`id`)
 * an unique name (`name`)
@@ -39,7 +41,7 @@ An `AbstractProperty` is represented in xml by a `<Property/>` tag. It's _qualit
 Depending on the purpose of the xml document (shall it represent an object in the database or an object _to be posted _to the database?) the `<Property/>` tag may actually have just a few of the mentioned "quality-attributes".
 
 ### GET AbstractProperty
-Any xml representation of an `AbstractProperty` that is retrieved from the HeartDB Server MUST have exactly ONE of the following forms, depending on the `AbstractProperty's` type:
+Any xml representation of an `AbstractProperty` that is retrieved from the CaosDB Server MUST have exactly ONE of the following forms, depending on the `AbstractProperty's` type:
 #### text
 
         <Property id="$id" name="$name" description="$description" generator="$generator" creator="$creator" created="$created" type="text" />
@@ -59,10 +61,10 @@ Any xml representation of an `AbstractProperty` that is retrieved from the Heart
 
         <Property id="$id" name="$name" description="$description" generator="$generator" creator="$creator" created="$created" type="file" />
 '''General Notes:
-* If the called Property does not exist or if the Property called without permission, the HeartDB Server will return an Error.
+* If the called Property does not exist or if the Property called without permission, the CaosDB Server will return an Error.
 
 ### POST AbstractProperty
-Any xml representation of an `AbstractProperty` that is to be posted to the HeartDB server MUST have exactly ONE of the following forms, depending on the `AbstractProperty's` type:
+Any xml representation of an `AbstractProperty` that is to be posted to the CaosDB server MUST have exactly ONE of the following forms, depending on the `AbstractProperty's` type:
 #### text
 
         <Property name="$name" description="$description" generator="$generator" type="text" />
@@ -82,10 +84,10 @@ Any xml representation of an `AbstractProperty` that is to be posted to the Hear
 
         <Property name="$name" description="$description" generator="$generator" type="file" />
 *General Notes:*
-* The `AbstractProperty's` `id` and timestamp (`created`) will be generated by the HeartDB Server.
-* The `AbstractProperty's` creator will be determined by the HeartDB Server depending on it's policy configuration.
+* The `AbstractProperty's` `id` and timestamp (`created`) will be generated by the CaosDB Server.
+* The `AbstractProperty's` creator will be determined by the CaosDB Server depending on it's policy configuration.
 * Any given attribute beyond these will be *ignored*.
-* If the `<Property/>` tag isn't compliant with these the HeartDB Server will return an Error.
+* If the `<Property/>` tag isn't compliant with these the CaosDB Server will return an Error.
 
 ----
 ## Examples
diff --git a/src/doc/specification/Authentication.md b/src/doc/specification/Authentication.md
index 9ec5bcd74ffb68ee33084f739d6d547a3e7a9bb8..6040d3792db553aea8cdd7206367be2bfec9257b 100644
--- a/src/doc/specification/Authentication.md
+++ b/src/doc/specification/Authentication.md
@@ -1,5 +1,5 @@
 # Authentication
- Some features of HeartDB are available to registered users only. Making any changes to the data stock via HTTP requires authentication by `username` _plus_ `password`. They are to be send as a HTTP header, while the password is to be hashed by the sha512 algorithm:
+ Some features of CaosDB are available to registered users only. Making any changes to the data stock via HTTP requires authentication by `username` _plus_ `password`. They are to be send as a HTTP header, while the password is to be hashed by the sha512 algorithm:
 
 | `username:` | `$username` | 
 |-------------|-------------|-
diff --git a/src/doc/specification/Fileserver.md b/src/doc/specification/Fileserver.md
index badb4b413f4ff898ee9455fce303f7bcaad90b9d..dd3e0f37f4da98aef413c3a4aed15da2e8230b66 100644
--- a/src/doc/specification/Fileserver.md
+++ b/src/doc/specification/Fileserver.md
@@ -1,12 +1,12 @@
 # Fileserver
 
 ## Info
-There are several ways to utilize the file server component of HeartDB. It is possible to upload a file or a whole folder including subfolders via HTTP and the _drop off box_. It is possible to download a file via HTTP identified by its ID or by its path in the internal file system. Furthermore, it is possible to get the files metadata via HTTP as an xml. 
+There are several ways to utilize the file server component of CaosDB. It is possible to upload a file or a whole folder including subfolders via HTTP and the _drop off box_. It is possible to download a file via HTTP identified by its ID or by its path in the internal file system. Furthermore, it is possible to get the files metadata via HTTP as an xml. 
 
 ## File upload
 ### Drop off box
 
-The drop off box is a directory on the HeartDB server's local file system, specified in the `server.conf` file in the server's basepath (something like `~/HeartDB/server/server.conf`). The key in the `server.conf` is called `dropoffbox`. Since the drop off box directory is writable for all, users can push their files or complete folders via a `mv` or a `cp` (recommended!) in that folder. The server deletes files older than their maximum lifetime (24 hours by default, specified `in server.conf`). But within their lifetime a user can prompt the server to pick up the file (or folder) from the drop off box in order to transfer it to the internal file system. 
+The drop off box is a directory on the CaosDB server's local file system, specified in the `server.conf` file in the server's basepath (something like `~/CaosDB/server/server.conf`). The key in the `server.conf` is called `dropoffbox`. Since the drop off box directory is writable for all, users can push their files or complete folders via a `mv` or a `cp` (recommended!) in that folder. The server deletes files older than their maximum lifetime (24 hours by default, specified `in server.conf`). But within their lifetime a user can prompt the server to pick up the file (or folder) from the drop off box in order to transfer it to the internal file system. 
 
 Now, the user may send a pick up request to `POST http://host:port/mpidsserver/FilesDropOff` with a similar body:
 
@@ -38,9 +38,9 @@ where
 There is an example on file upload using cURL described in detail in [the curl section of this
 documentation](../administration/curl-access.md).
 
-File upload via HTTP is implemented in a [rfc1867](http://www.ietf.org/rfc/rfc1867.txt) consistent way. This is a de-facto standard that defines a file upload as a part of an HTML form submission. This concept shall not be amplified here. But it has to be noticed that this protocol is not designed for uploads of complete structured folders. Therefore the HeartDB file components have to impose that structure on the upload protocol. 
+File upload via HTTP is implemented in a [rfc1867](http://www.ietf.org/rfc/rfc1867.txt) consistent way. This is a de-facto standard that defines a file upload as a part of an HTML form submission. This concept shall not be amplified here. But it has to be noticed that this protocol is not designed for uploads of complete structured folders. Therefore the CaosDB file components have to impose that structure on the upload protocol. 
 
-HeartDB's file upload resource does exclusively accept POST requests of MIME media type `multipart/form-data`. The first part of each POST body is expected to be a form-data text field, containing information about the files to be uploaded. It has to meet the following requirements:
+CaosDB's file upload resource does exclusively accept POST requests of MIME media type `multipart/form-data`. The first part of each POST body is expected to be a form-data text field, containing information about the files to be uploaded. It has to meet the following requirements:
 * `Content-type: text/plain; charset=UTF-8`
 * `Content-disposition: form-data; name="FileRepresentation"`
 
diff --git a/src/doc/specification/Record.md b/src/doc/specification/Record.md
index 1e43e372f754d884da2ff15cfb7b71f6813f1785..d41896962540486282e7b98be1a1f982ee9e1c74 100644
--- a/src/doc/specification/Record.md
+++ b/src/doc/specification/Record.md
@@ -1,5 +1,6 @@
 # Record
 
+**Warning:** This specification is outdated. It is included to serve as a starting point for a more up-to-date description of the `Record` entity.
 
 = GET records = 
 
diff --git a/src/doc/specification/RecordType.md b/src/doc/specification/RecordType.md
deleted file mode 100644
index a7830b863712d8b1db3e54c617572ee16bf4e166..0000000000000000000000000000000000000000
--- a/src/doc/specification/RecordType.md
+++ /dev/null
@@ -1,164 +0,0 @@
-# RecordType
-
-## Overview
-RecordTypes function as templates for [[Record|Records]], they provide a description for a type of Record and define which [[Property|Properties]] should be present.  Properties come with an _importance_ attribute which tells the user or client program how strongly necessary the Property is.  (As all other entities,) RecordTypes can be inherited from other RecordTypes (or any Entities).  When RecordTypes inherit from other RecordTypes, the _inheritance_ flag tells which properties shall be inherited.
-
-### Importance
-
-Importances are more of a recommendation, Records with omitted Properties can technically still be committed.
-
-| *Importance* | *Meaning*            | *Consequence when omitted* |
-|--------------|----------------------|----------------------------|
-| `OBLIGATORY`     | Property must be present | *FIXME* Can be forced to be committed the server, but usually an error will be returned. |
-| `RECOMMENDED`    | Property should be present (makes sense for most users) | *FIXME* Usually an error will be returned? |
-| `SUGGESTED` (*FIXME* the default?)      | Property may be present (may make sense for some users) | *FIXME* No negative consequence? |
-
-*FIXME* Did I get recommended and suggested right?
-
-### Inheritance
-
-The _inheritance_ flag decides which [[Property|Properties]] are inherited from parents.
-
-| *Inheritance*  | *Meaning* |
-|----------------|-----------|
-| _None_ (default) | Nothing is being inherited |
-| `OBLIGATORY`     | *FIXME* Properteis of importance `OBLIGATORY` and above? |
-| `SUGGESTED`      | *FIXME* Properteis of importance `SIGGESTED` and above, so the same effect as `ALL`? |
-| `ALL`            | Copy everything from the parent. |
-| `FIX`            | *FIXME* in `tests/test_inheritance.py` of pyint_test this is used, what does it mean???|
-
-
-## Examples
-### GET Requests
-#### Single-Get
-*Request:*
-
-        GET http://localhost:8122/mpidsserver/RecordType/1
-
-*Response:*
-
-        <?xml version="1.0" encoding="UTF-8"?>
-        <Response>
-          <RecordType id="1" name="Experiment" description="Description Experiment">
-            <Property id="1" name="explanation" type="text" description="explains the thing" importance="obligatory" />
-            <Property id="4" name="id" type="integer" description="identifier" exponent="0" importance="recommended" />
-            <Property id="6" name="startDate" type="datetime" description="start" importance="obligatory" />
-            <Property id="7" name="stopDate" type="datetime" description="stop" importance="recommended" />
-            <Property id="12" name="file" type="file" description="file" importance="suggested" />
-          </RecordType>
-        </Response>
-----
-#### Multi-Get
-*Request:*
-
-        GET http://localhost:8122/mpidsserver/RecordType/1&2&3
-
-*Response:*
-
-        <?xml version="1.0" encoding="UTF-8"?>
-        <?xml version="1.0" encoding="UTF-8"?>
-        <Response>
-          <RecordType id="1" name="Experiment" description="Description Experiment">
-            <Property id="1" name="explanation" type="text" description="explains the thing" importance="obligatory" />
-            <Property id="4" name="id" type="integer" description="identifier" exponent="0" importance="recommended" />
-            <Property id="6" name="startDate" type="datetime" description="start" importance="obligatory" />
-            <Property id="7" name="stopDate" type="datetime" description="stop" importance="recommended" />
-            <Property id="12" name="file" type="file" description="file" importance="suggested" />
-          </RecordType>
-          <RecordType id="2" name="Measurement" description="Description Measurement">
-            <Property id="1" name="explanation" type="text" description="explains the thing" importance="obligatory" />
-            <Property id="4" name="id" type="integer" description="identifier" exponent="0" importance="recommended" />
-            <Property id="6" name="startDate" type="datetime" description="start" importance="obligatory" />
-            <Property id="7" name="stopDate" type="datetime" description="stop" importance="recommended" />
-            <Property id="12" name="file" type="file" description="file" importance="suggested" />
-          </RecordType>
-          <RecordType id="3" name="Video" description="Description Video">
-            <Property id="1" name="explanation" type="text" description="explains the thing" importance="recommended" />
-            <Property id="6" name="startDate" type="datetime" description="start" importance="recommended" />
-            <Property id="12" name="file" type="file" description="file" importance="obligatory" />
-          </RecordType>
-        </Response>
-----
-#### Get all
-*Request:*
-
-        GET http://localhost:8122/mpidsserver/RecordType/
-        GET http://localhost:8122/mpidsserver/RecordType
-
-*Response:*
-
-        <?xml version="1.0" encoding="UTF-8"?>
-        <Response />
-[View Ticket #2](http://dev.bmp.ds.mpg.de/heartdb/ticket/2)
-
-----
-### POST Requests
-
-*Request is to be send to:*
-
-        POST http://localhost:8122/mpidsserver/RecordType/
-        POST http://localhost:8122/mpidsserver/RecordType/
-----
-#### Single-Post
-*HTTP Body:*
-
-        <Post>  
-          <RecordType name="TimmsRecordType11" >
-            <Property id="3" importance="recommended"/>
-            <Property id="4" importance="obligatory"/>
-            <Property name="age" />
-          </RecordType>
-        </Post>
-
-*Response:*
-
-        <?xml version="1.0" encoding="UTF-8"?>
-        <Response>
-          <RecordType id="40" name="TimmsRecordType11">
-            <Property id="3" name="name" type="text" description="Name" importance="recommended" />
-            <Property id="4" name="id" type="integer" description="identifier" importance="suggested" />
-          </RecordType>
-        </Response>
-----
-#### Multi-Post
-*HTTP Body:*
-
-        <Post>  
-          <RecordType name="TimmsRecordType11" >
-            <Property id="3" importance="recommended"/>
-            <Property id="4" importance="obligatory"/>
-            <Property name="age" />
-          </RecordType>
-          <RecordType name="TimmsRecordType12" >
-            <Property id="6" importance="recommended"/>
-            <Property id="7" importance="obligatory"/>
-            <Property id="15" />
-          </RecordType>
-        </Post>
-
-*Response:*
-
-        <?xml version="1.0" encoding="UTF-8"?>
-        <Response>
-          <RecordType id="40" name="TimmsRecordType11" description="null">
-            <Property id="3" name="name" type="text" description="Name" importance="recommended" />
-            <Property id="4" name="id" type="integer" description="identifier" importance="suggested" />
-            <Property id="3" name="name" type="text" description="Name" importance="recommended" />
-            <Property id="4" name="id" type="integer" description="identifier" exponent="0" importance="suggested" />
-          </RecordType>
-          <RecordType id="41" name="TimmsRecordType12">
-            <Property id="6" name="startDate" type="datetime" description="start" importance="recommended" />
-            <Property id="7" name="stopDate" type="datetime" description="stop" importance="suggested" />
-            <Property id="15" name="TimmsIntProperty" type="integer" unit="kg" description="This is TimmsIntProperty" importance="suggested" />
-          </RecordType>
-        </Response>
-
-= DELETE RecordTypes = 
-
-HTTP-DELETE-requests are to be send to `http://${host}:${port}/mpidsserver/RecordType/...`. Default port is 8123.
-
-*Example*
-{{{ 
-DELETE http://${host}:${port}/mpidsserver/RecordType/1&2&3&4
-}}}
-
diff --git a/src/doc/specification/RecordType.rst b/src/doc/specification/RecordType.rst
new file mode 100644
index 0000000000000000000000000000000000000000..0c196fea86e74f1014e34c0bd1e5b096acd1a5d9
--- /dev/null
+++ b/src/doc/specification/RecordType.rst
@@ -0,0 +1,283 @@
+RecordType
+==========
+
+Overview
+--------
+
+RecordTypes act as templates for :doc:`Records <Record>`. They
+provide a description for a type of Record and define which
+:doc:`Properties <AbstractProperty>` could, should, or must be
+present. Properties come with an `importance` attribute which tells
+the user or client program how strongly necessary the Property is. (As
+all other entities,) RecordTypes can inherit from other
+RecordTypes (or any Entities). When RecordTypes inherit from other
+RecordTypes, the `inheritance` flag tells which properties shall be
+inherited from the parents to the children.
+
+Importance
+~~~~~~~~~~
+
+The `importance` value of a Property states whether it may, should, or
+must be present in all children of this Entity. The table below
+lists the possible importances, together with the default behavior of
+the server when the property is not present in a child.
+
+.. list-table::
+   :widths: 25 25 50
+   :width: 100%
+   :header-rows: 1
+
+   * - Importance
+     - Meaning
+     - Consequence when omitted
+   * - ``OBLIGATORY``
+     - Property must be present
+     - Can be forced to be committed the server, but usually
+       an error will be returned.
+   * - ``RECOMMENDED``
+     - Property should be present (makes sense for most users)
+     - No consequence right now, but in future versions a warning will
+       be returned.
+   * - ``SUGGESTED`` (default)
+     - Property may be present (may make sense for some users)
+     - No negative consequence
+   * - ``FIX``
+     - Property is not present in any child
+     - ---
+
+The default behavior can be overridden for individual transaction by setting the
+`force-missing-obligatory` flag to ``ERROR`` (the default), ``WARN``, or ``IGNORE``.  These flags
+cause a transaction that would result in an entity with a missing `obligatory` property to return an
+error, a warning, or to execute without a warning, respectively. The importance value ``FIX`` is
+used for properties that are specific to the Entity itself and is explained below.
+       
+.. note::
+
+   Note that in contrast to RecordTypes, all properties of a
+   :doc:`Record` have the importance ``FIX`` since in general, even in the
+   case of a Record having children, its properties are not
+   inherited.
+
+Inheritance
+~~~~~~~~~~~
+
+The `inheritance` flag specifies which :doc:`Properties
+<AbstractProperty>` are inherited from the parent RecordType(s) to the
+child.
+
+.. list-table::
+   :widths: 40 60
+   :header-rows: 1
+
+   * - Inheritance
+     - Meaning
+   * - `None` (default)
+     - Nothing is inherited
+   * - ``OBLIGATORY``
+     - Properties of importance ``OBLIGATORY`` and above
+   * - ``RECOMMENDED``
+     - Properties of importance ``RECOMMENDED`` and above
+   * - ``SUGGESTED``
+     - Properties of importance ``SUGGESTED`` and above
+   * - ``ALL``
+     - Copy everything from the parent.
+
+Note that inheritance ``ALL`` is essentially the same as ``SUGGESTED``
+right now, but this behavior might change in the future. Also note
+that properties of importance ``FIX`` are never inherited this
+way. This is precisely what this importance level is meant for:
+properties that are specific to the parent entity and are not
+inherited by default. You can still add this property to the child
+manually if you wish, though, but this is not covered by the
+automatic inheritance.
+
+.. note::
+
+   The behavior of `inheritance` is currently not well-defined if the
+   child is a :doc:`Record`, not a RecordType.
+
+Examples
+--------
+
+.. warning::
+
+   The following examples have not been updated in a while and may
+   serve as a starting point, but are not guaranteed to be fully
+   applicable to the most recent versions of CaosDB.
+
+GET Requests
+~~~~~~~~~~~~
+
+Single-Get
+^^^^^^^^^^
+
+*Request:*
+
+.. code-block::
+
+   GET http://localhost:8122/RecordType/1
+
+*Response:*
+
+.. code-block:: XML
+		
+   <?xml version="1.0" encoding="UTF-8"?>
+     <Response>
+       <RecordType id="1" name="Experiment" description="Description Experiment">
+         <Property id="1" name="explanation" type="text" description="explains the thing" importance="obligatory" />
+	 <Property id="4" name="id" type="integer" description="identifier" exponent="0" importance="recommended" />
+	 <Property id="6" name="startDate" type="datetime" description="start" importance="obligatory" />
+	 <Property id="7" name="stopDate" type="datetime" description="stop" importance="recommended" />
+	 <Property id="12" name="file" type="file" description="file" importance="suggested" />
+       </RecordType>
+     </Response>
+
+Multi-Get
+^^^^^^^^^
+
+*Request:*
+
+.. code-block::
+
+   GET http://localhost:8122/RecordType/1&2&3
+
+*Response:*
+
+.. code-block:: XML
+
+   <?xml version="1.0" encoding="UTF-8"?>
+     <Response>
+       <RecordType id="1" name="Experiment" description="Description Experiment">
+         <Property id="1" name="explanation" type="text" description="explains the thing" importance="obligatory" />
+	 <Property id="4" name="id" type="integer" description="identifier" exponent="0" importance="recommended" />
+         <Property id="6" name="startDate" type="datetime" description="start" importance="obligatory" />
+         <Property id="7" name="stopDate" type="datetime" description="stop" importance="recommended" />
+         <Property id="12" name="file" type="file" description="file" importance="suggested" />
+       </RecordType>
+       <RecordType id="2" name="Measurement" description="Description Measurement">
+         <Property id="1" name="explanation" type="text" description="explains the thing" importance="obligatory" />
+         <Property id="4" name="id" type="integer" description="identifier" exponent="0" importance="recommended" />
+         <Property id="6" name="startDate" type="datetime" description="start" importance="obligatory" />
+         <Property id="7" name="stopDate" type="datetime" description="stop" importance="recommended" />
+         <Property id="12" name="file" type="file" description="file" importance="suggested" />
+       </RecordType>
+       <RecordType id="3" name="Video" description="Description Video">
+         <Property id="1" name="explanation" type="text" description="explains the thing" importance="recommended" />
+         <Property id="6" name="startDate" type="datetime" description="start" importance="recommended" />
+         <Property id="12" name="file" type="file" description="file" importance="obligatory" />
+       </RecordType>
+     </Response>
+
+	
+Get all
+^^^^^^^
+
+*Request:*
+
+.. code-block::
+
+   GET http://localhost:8122/RecordType/
+   GET http://localhost:8122/RecordType
+
+*Response:*
+
+.. note::
+
+   Outdated? At least the bug tracker doesn't exist anymore.
+
+.. code-block:: XML
+
+   <?xml version="1.0" encoding="UTF-8"?>
+     <Response />
+
+[View Ticket #2](http://caosdb.example.com/caosdb/ticket/2)
+
+
+POST Requests
+~~~~~~~~~~~~~
+
+*Request is to be send to:*
+
+.. code-block::
+
+   POST http://localhost:8122/RecordType/
+   POST http://localhost:8122/RecordType/
+
+Single-Post
+^^^^^^^^^^^
+
+*HTTP Body:*
+
+.. code-block:: XML
+
+   <Post>  
+     <RecordType name="TimmsRecordType11" >
+       <Property id="3" importance="recommended"/>
+       <Property id="4" importance="obligatory"/>
+       <Property name="age" />
+     </RecordType>
+   </Post>
+
+*Response:*
+
+.. code-block:: XML
+
+   <?xml version="1.0" encoding="UTF-8"?>
+     <Response>
+       <RecordType id="40" name="TimmsRecordType11">
+         <Property id="3" name="name" type="text" description="Name" importance="recommended" />
+         <Property id="4" name="id" type="integer" description="identifier" importance="suggested" />
+       </RecordType>
+     </Response>
+
+Multi-Post
+^^^^^^^^^^
+
+*HTTP Body:*
+
+.. code-block:: XML
+
+   <Post>  
+     <RecordType name="TimmsRecordType11" >
+       <Property id="3" importance="recommended"/>
+       <Property id="4" importance="obligatory"/>
+       <Property name="age" />
+     </RecordType>
+     <RecordType name="TimmsRecordType12" >
+       <Property id="6" importance="recommended"/>
+       <Property id="7" importance="obligatory"/>
+       <Property id="15" />
+     </RecordType>
+   </Post>
+
+*Response:*
+
+.. code-block:: XML
+
+   <?xml version="1.0" encoding="UTF-8"?>
+     <Response>
+       <RecordType id="40" name="TimmsRecordType11" description="null">
+         <Property id="3" name="name" type="text" description="Name" importance="recommended" />
+         <Property id="4" name="id" type="integer" description="identifier" importance="suggested" />
+         <Property id="3" name="name" type="text" description="Name" importance="recommended" />
+         <Property id="4" name="id" type="integer" description="identifier" exponent="0" importance="suggested" />
+       </RecordType>
+       <RecordType id="41" name="TimmsRecordType12">
+         <Property id="6" name="startDate" type="datetime" description="start" importance="recommended" />
+         <Property id="7" name="stopDate" type="datetime" description="stop" importance="suggested" />
+         <Property id="15" name="TimmsIntProperty" type="integer" unit="kg" description="This is TimmsIntProperty" importance="suggested" />
+       </RecordType>
+     </Response>
+
+DELETE RecordTypes
+~~~~~~~~~~~~~~~~~~
+
+HTTP-DELETE-requests are to be send to ``http://${host}:${port}/RecordType/...``. Default port is 8123.
+
+*Example*
+
+.. code-block:: 
+
+   DELETE http://${host}:${port}/RecordType/1&2&3&4
+
+
diff --git a/src/doc/specification/Specification-of-the-Entity-API.md b/src/doc/specification/Specification-of-the-Entity-API.md
index 93b39f5333b9f80c9f4485fc00c2f827ad60792a..5ef05b7898d40e43f320c9e3519a6d9520efffcd 100644
--- a/src/doc/specification/Specification-of-the-Entity-API.md
+++ b/src/doc/specification/Specification-of-the-Entity-API.md
@@ -1,4 +1,7 @@
 # Specification of the Entity API
+
+**Warning:** This specification is outdated. It is included to serve as a starting point for a more up-to-date description of the entity API.
+
 Version: 0.1.0r1
 
 Author: Timm Fitschen
diff --git a/src/main/java/org/caosdb/server/ServerProperties.java b/src/main/java/org/caosdb/server/ServerProperties.java
index 3172a3e88e7790580af1b7865c024fa2b40c017b..176492d691cc63c422e5996bb055763f8d05a751 100644
--- a/src/main/java/org/caosdb/server/ServerProperties.java
+++ b/src/main/java/org/caosdb/server/ServerProperties.java
@@ -133,6 +133,7 @@ public class ServerProperties extends Properties {
   public static final String KEY_WEBUI_HTTP_HEADER_CACHE_MAX_AGE =
       "WEBUI_HTTP_HEADER_CACHE_MAX_AGE";
   public static final String KEY_AUTHTOKEN_CONFIG = "AUTHTOKEN_CONFIG";
+  public static final String KEY_JOB_RULES_CONFIG = "JOB_RULES_CONFIG";
 
   /**
    * Read the config files and initialize the server properties.
@@ -150,11 +151,11 @@ public class ServerProperties extends Properties {
     loadConfigFile(serverProperties, new File(basepath + "/conf/ext/server.conf"));
 
     // load ext/server.conf.d/ (ordering is determined by system collation)
-    File confDir = new File(basepath + "/conf/ext/server.conf.d");
+    final File confDir = new File(basepath + "/conf/ext/server.conf.d");
     if (confDir.exists() && confDir.isDirectory()) {
-      String[] confFiles = confDir.list();
+      final String[] confFiles = confDir.list();
       Arrays.sort(confFiles, Comparator.naturalOrder());
-      for (String confFile : confFiles) {
+      for (final String confFile : confFiles) {
         // prevent backup files from being read
         if (confFile.endsWith(".conf")) {
           loadConfigFile(serverProperties, new File(confDir, confFile));
@@ -163,7 +164,7 @@ public class ServerProperties extends Properties {
     }
 
     // load env vars
-    for (java.util.Map.Entry<String, String> envvar : System.getenv().entrySet()) {
+    for (final java.util.Map.Entry<String, String> envvar : System.getenv().entrySet()) {
       if (envvar.getKey().startsWith("CAOSDB_CONFIG_") && envvar.getKey().length() > 14) {
         serverProperties.setProperty(envvar.getKey().substring(14), envvar.getValue());
       }
@@ -171,11 +172,11 @@ public class ServerProperties extends Properties {
 
     // log configuration alphabetically
     if (logger.isInfoEnabled()) {
-      ArrayList<String> names = new ArrayList<>(serverProperties.stringPropertyNames());
+      final ArrayList<String> names = new ArrayList<>(serverProperties.stringPropertyNames());
       Collections.sort(names);
-      for (String name : names) {
-        String val =
-            (name.contains("PASSW") || name.contains("SECRET"))
+      for (final String name : names) {
+        final String val =
+            name.contains("PASSW") || name.contains("SECRET")
                 ? "****"
                 : serverProperties.getProperty(name);
         logger.info(name + "=" + val);
@@ -184,7 +185,7 @@ public class ServerProperties extends Properties {
     return serverProperties;
   }
 
-  private static void loadConfigFile(Properties serverProperties, File confFile)
+  private static void loadConfigFile(final Properties serverProperties, final File confFile)
       throws IOException {
     if (confFile.exists() && confFile.isFile()) {
       logger.info("Reading configuration from " + confFile.getAbsolutePath());
diff --git a/src/main/java/org/caosdb/server/database/BackendTransaction.java b/src/main/java/org/caosdb/server/database/BackendTransaction.java
index 965e7181d1d010528b341f6cbf34d4c0c67a9e5c..7c26db1ed05e76c30f9d30f0c3d9965ed0c59b6b 100644
--- a/src/main/java/org/caosdb/server/database/BackendTransaction.java
+++ b/src/main/java/org/caosdb/server/database/BackendTransaction.java
@@ -1,11 +1,10 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
- *   Max-Planck-Institute for Dynamics and Self-Organization Göttingen
- * Copyright (C) 2020 IndiScale GmbH
- * Copyright (C) 2020 Timm Fitschen (t.fitschen@indiscale.com)
+ * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019-2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2019-2021 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -19,8 +18,6 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
 package org.caosdb.server.database;
 
@@ -61,7 +58,6 @@ import org.caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveRole
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveSparseEntity;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveUser;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveVersionHistory;
-import org.caosdb.server.database.backend.implementation.MySQL.MySQLRuleLoader;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLSetFileCheckedTimestampImpl;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLSetFileChecksum;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLSetPassword;
@@ -117,7 +113,6 @@ import org.caosdb.server.database.backend.interfaces.RetrieveRoleImpl;
 import org.caosdb.server.database.backend.interfaces.RetrieveSparseEntityImpl;
 import org.caosdb.server.database.backend.interfaces.RetrieveUserImpl;
 import org.caosdb.server.database.backend.interfaces.RetrieveVersionHistoryImpl;
-import org.caosdb.server.database.backend.interfaces.RuleLoaderImpl;
 import org.caosdb.server.database.backend.interfaces.SetFileCheckedTimestampImpl;
 import org.caosdb.server.database.backend.interfaces.SetFileChecksumImpl;
 import org.caosdb.server.database.backend.interfaces.SetPasswordImpl;
@@ -129,8 +124,6 @@ import org.caosdb.server.database.backend.interfaces.UpdateUserImpl;
 import org.caosdb.server.database.backend.interfaces.UpdateUserRolesImpl;
 import org.caosdb.server.database.exceptions.TransactionException;
 import org.caosdb.server.database.misc.TransactionBenchmark;
-import org.caosdb.server.entity.EntityInterface;
-import org.caosdb.server.entity.RetrieveEntity;
 import org.caosdb.server.utils.UndoHandler;
 import org.caosdb.server.utils.Undoable;
 
@@ -183,7 +176,6 @@ public abstract class BackendTransaction implements Undoable {
       setImpl(GetFileRecordByPathImpl.class, MySQLGetFileRecordByPath.class);
       setImpl(RetrievePropertiesImpl.class, MySQLRetrieveProperties.class);
       setImpl(RetrieveSparseEntityImpl.class, MySQLRetrieveSparseEntity.class);
-      setImpl(RuleLoaderImpl.class, MySQLRuleLoader.class);
       setImpl(SyncStatsImpl.class, MySQLSyncStats.class);
       setImpl(FileExists.class, UnixFileSystemFileExists.class);
       setImpl(FileWasModifiedAfter.class, UnixFileSystemFileWasModifiedAfter.class);
@@ -251,7 +243,7 @@ public abstract class BackendTransaction implements Undoable {
   protected <T extends BackendTransactionImpl> T getImplementation(final Class<T> clz) {
     init();
     try {
-      Class<?> implclz = impl.get(clz);
+      final Class<?> implclz = impl.get(clz);
       final T ret = (T) implclz.getConstructor(Access.class).newInstance(this.access);
       if (ret instanceof Undoable) {
         this.undoHandler.append((Undoable) ret);
@@ -289,18 +281,13 @@ public abstract class BackendTransaction implements Undoable {
   }
 
   /** Set the benchmark object for this AbstractTransaction. */
-  public void setTransactionBenchmark(TransactionBenchmark b) {
+  public void setTransactionBenchmark(final TransactionBenchmark b) {
     this.benchmark = b;
   }
 
-  public void addMeasurement(Object o, long time) {
+  public void addMeasurement(final Object o, final long time) {
     if (this.benchmark != null) {
       this.benchmark.addMeasurement(o, time);
     }
   }
-
-  /** Return the type of transaction of an entity, e.g. "Retrieve" for a {@link RetrieveEntity}. */
-  public String getTransactionType(EntityInterface e) {
-    return e.getClass().getSimpleName().replace("Entity", "");
-  }
 }
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseConnectionPool.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseConnectionPool.java
index f77f810a93428a66247b6559e2bef2ac344cd5d9..997de3141db6fd49ac510fa61150a2d16e7d5416 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseConnectionPool.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/DatabaseConnectionPool.java
@@ -175,25 +175,69 @@ class DatabaseConnectionPool {
     logger.debug("####################");
   }
 
+  /**
+   * Check the version of the SQL server's database.
+   *
+   * <p>Current behaviour: Major versions must match and the database's minor version must be at
+   * least the expected minor version as defined in {@link
+   * ServerProperties.KEY_MYSQL_SCHEMA_VERSION}.
+   *
+   * <p>@todo Patch versions are not handled yet.
+   */
   private static void checkVersion(final Connection con)
       throws SQLException, ConnectionException, CaosDBException {
     try {
+      final String[] expected_version =
+          CaosDBServer.getServerProperty(ServerProperties.KEY_MYSQL_SCHEMA_VERSION)
+              .toLowerCase()
+              .split("\\.");
+      final String expected_major = expected_version[0];
+      final String expected_minor = expected_version[1];
+
       con.setReadOnly(false);
-      final PreparedStatement prepareStatement = con.prepareStatement("SELECT CaosDBVersion()");
+      final PreparedStatement prepared = con.prepareStatement("SELECT CaosDBVersion()");
       try {
-        final ResultSet executeQuery = prepareStatement.executeQuery();
+        final ResultSet executeQuery = prepared.executeQuery();
         if (executeQuery.next()) {
-          final String v_e =
-              CaosDBServer.getServerProperty(ServerProperties.KEY_MYSQL_SCHEMA_VERSION)
-                  .toLowerCase();
-          final String v_a = executeQuery.getString(1).toLowerCase();
-          if (!Objects.equal(v_a, v_e)) {
-            logger.error(
-                "Version of the MySQL schema is wrong.\n\tExpected: {}\n\tActual: {}n\nPlease upgrade the mysql backend.\n\n",
-                v_e,
-                v_a);
+
+          final String actual_version = executeQuery.getString(1).toLowerCase();
+          final String[] actual_version_split = actual_version.split("\\.");
+          final String actual_major = actual_version_split[0];
+          final String actual_minor = actual_version_split[1];
+
+          if (!Objects.equal(actual_major, expected_major)) {
+            if (Integer.parseInt(actual_major.replace("v", ""))
+                < Integer.parseInt(expected_major.replace("v", ""))) {
+              logger.error(
+                  "Version of the MySQL/MariaDB schema is incompatible.\n\tExpected: {}.{}\n\tActual: {}\nPlease upgrade the MySQL backend.\n\n",
+                  expected_major,
+                  expected_minor,
+                  actual_version);
+            } else {
+              logger.error(
+                  "Version of the MySQL/MariaDB schema is incompatible.\n\tExpected: {}.{}\n\tActual: {}\nPlease upgrade the CaosDB server.\n\n",
+                  expected_major,
+                  expected_minor,
+                  actual_version);
+            }
             System.exit(1);
           }
+          if (!Objects.equal(actual_minor, expected_minor)) {
+            if (Integer.parseInt(actual_minor) < Integer.parseInt(expected_minor)) {
+              logger.error(
+                  "Version of the MySQL/MariaDB schema is incompatible.\n\tExpected: {}.{}\n\tActual: {}\nPlease upgrade the MySQL backend.\n\n",
+                  expected_major,
+                  expected_minor,
+                  actual_version);
+              System.exit(1);
+            }
+          }
+        } else {
+          logger.error(
+              "Version of the MySQL schema could not be determined. This probably means that the version is too old.\n\tExpected: {}.{}\n\tActual: unknown\nPlease upgrade the MySQL backend.\n\n",
+              expected_major,
+              expected_minor);
+          System.exit(1);
         }
       } catch (final SQLException e) {
         logger.error("Could not check the version of the MySQL schema.", e);
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveAll.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveAll.java
index 7af4022595f64b909231c72c83ae1e151a9d06c1..77618e92d56a166eb9d0df96258b943fb9df52d5 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveAll.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveAll.java
@@ -1,9 +1,10 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019-2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2019-2021 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -17,8 +18,6 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
 package org.caosdb.server.database.backend.implementation.MySQL;
 
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRuleLoader.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRuleLoader.java
deleted file mode 100644
index 525c3973e5267d336727e723a383aff575dd8493..0000000000000000000000000000000000000000
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRuleLoader.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * ** header v3.0
- * This file is a part of the CaosDB Project.
- *
- * Copyright (C) 2018 Research Group Biomedical Physics,
- * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
- */
-package org.caosdb.server.database.backend.implementation.MySQL;
-
-import static org.caosdb.server.database.DatabaseUtils.bytes2UTF8;
-
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import org.caosdb.server.database.access.Access;
-import org.caosdb.server.database.backend.interfaces.RuleLoaderImpl;
-import org.caosdb.server.database.exceptions.TransactionException;
-import org.caosdb.server.database.proto.Rule;
-import org.caosdb.server.jobs.core.Mode;
-
-public class MySQLRuleLoader extends MySQLTransaction implements RuleLoaderImpl {
-
-  public MySQLRuleLoader(final Access access) {
-    super(access);
-  }
-
-  public static final String STMT_GET_RULES =
-      "SELECT rules.transaction, rules.criterion, rules.modus from rules where rules.domain_id=? AND rules.entity_id=? AND rules.transaction=?";
-
-  @Override
-  public ArrayList<Rule> executeNoCache(
-      final Integer domain, final Integer entity, final String transaction)
-      throws TransactionException {
-    try {
-      final PreparedStatement stmt = prepareStatement(STMT_GET_RULES);
-
-      stmt.setInt(1, domain);
-      stmt.setInt(2, entity);
-      stmt.setString(3, transaction);
-      stmt.execute();
-
-      final ResultSet rs = stmt.executeQuery();
-      try {
-        final ArrayList<Rule> ret = new ArrayList<Rule>();
-        while (rs.next()) {
-          final Rule r = new Rule();
-          r.mode = Mode.valueOf(bytes2UTF8(rs.getBytes("modus")));
-          r.job = bytes2UTF8(rs.getBytes("criterion"));
-          r.domain = domain;
-          r.entity = entity;
-          r.transaction = transaction;
-          ret.add(r);
-        }
-        return ret;
-      } finally {
-        rs.close();
-      }
-    } catch (final SQLException e) {
-      throw new TransactionException(e);
-    } catch (final ConnectionException e) {
-      throw new TransactionException(e);
-    }
-  }
-}
diff --git a/src/main/java/org/caosdb/server/database/backend/interfaces/RuleLoaderImpl.java b/src/main/java/org/caosdb/server/database/backend/interfaces/RuleLoaderImpl.java
deleted file mode 100644
index 151e3d9d21d02d953cffeec8a55b4e01377fc023..0000000000000000000000000000000000000000
--- a/src/main/java/org/caosdb/server/database/backend/interfaces/RuleLoaderImpl.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * ** header v3.0
- * This file is a part of the CaosDB Project.
- *
- * Copyright (C) 2018 Research Group Biomedical Physics,
- * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
- */
-package org.caosdb.server.database.backend.interfaces;
-
-import java.util.ArrayList;
-import org.caosdb.server.database.exceptions.TransactionException;
-import org.caosdb.server.database.proto.Rule;
-
-public interface RuleLoaderImpl extends BackendTransactionImpl {
-
-  public ArrayList<Rule> executeNoCache(Integer domain, Integer entity, String transaction)
-      throws TransactionException;
-}
diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/InsertEntityTransaction.java b/src/main/java/org/caosdb/server/database/backend/transaction/InsertEntityTransaction.java
index 9c86ba33bf47b8051bc365bb32a6f36d0342ff92..5871c97e8ff172fe58b37627cc93824e96c7b32e 100644
--- a/src/main/java/org/caosdb/server/database/backend/transaction/InsertEntityTransaction.java
+++ b/src/main/java/org/caosdb/server/database/backend/transaction/InsertEntityTransaction.java
@@ -1,9 +1,10 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019-2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2019-2021 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -17,8 +18,6 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
 package org.caosdb.server.database.backend.transaction;
 
diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/InsertTransactionHistory.java b/src/main/java/org/caosdb/server/database/backend/transaction/InsertTransactionHistory.java
index 9b798de4279de8034168c4ecfbc1738c70370a65..6282ede52aa514353c21f0b8dd3345a85305725b 100644
--- a/src/main/java/org/caosdb/server/database/backend/transaction/InsertTransactionHistory.java
+++ b/src/main/java/org/caosdb/server/database/backend/transaction/InsertTransactionHistory.java
@@ -1,9 +1,10 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019-2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2019-2021 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -17,8 +18,6 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
 package org.caosdb.server.database.backend.transaction;
 
@@ -34,7 +33,9 @@ import org.caosdb.server.utils.EntityStatus;
  * Record the current transaction in the entities transaction history.
  *
  * @author Timm Fitschen (t.fitschen@indiscale.com)
+ * @deprecated To be replaced by versioning history.
  */
+@Deprecated
 public class InsertTransactionHistory extends BackendTransaction {
 
   private final TransactionContainer container;
@@ -62,7 +63,7 @@ public class InsertTransactionHistory extends BackendTransaction {
           || e.getEntityStatus() == EntityStatus.VALID) {
 
         t.execute(
-            getTransactionType(e),
+            e.getClass().getSimpleName().replace("Entity", ""),
             this.realm,
             this.user,
             this.datetime.getUTCSeconds(),
diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/RuleLoader.java b/src/main/java/org/caosdb/server/database/backend/transaction/RuleLoader.java
deleted file mode 100644
index c73023bd4429db19ca0a968aa99b657f0d0cbea4..0000000000000000000000000000000000000000
--- a/src/main/java/org/caosdb/server/database/backend/transaction/RuleLoader.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * ** header v3.0
- * This file is a part of the CaosDB Project.
- *
- * Copyright (C) 2018 Research Group Biomedical Physics,
- * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
- * Copyright (C) 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 org.caosdb.server.database.backend.transaction;
-
-import java.util.ArrayList;
-import org.apache.commons.jcs.access.behavior.ICacheAccess;
-import org.caosdb.server.caching.Cache;
-import org.caosdb.server.database.CacheableBackendTransaction;
-import org.caosdb.server.database.backend.interfaces.RuleLoaderImpl;
-import org.caosdb.server.database.exceptions.TransactionException;
-import org.caosdb.server.database.proto.Rule;
-import org.caosdb.server.entity.EntityInterface;
-import org.caosdb.server.entity.container.TransactionContainer;
-import org.caosdb.server.entity.wrapper.Property;
-import org.caosdb.server.jobs.Job;
-import org.caosdb.server.transaction.Transaction;
-
-/**
- * Load transaction rules (e.g. "Referenced entities must always exist") and their configuration
- * from the back-end.
- *
- * @author Timm Fitschen (t.fitschen@indiscale.com)
- */
-public class RuleLoader extends CacheableBackendTransaction<String, ArrayList<Rule>> {
-
-  private static final ICacheAccess<String, ArrayList<Rule>> cache =
-      Cache.getCache("BACKEND_JobRules");
-  private final Transaction<? extends TransactionContainer> transaction;
-  private final EntityInterface e;
-  private final Integer entity;
-  private final Integer domain;
-  private ArrayList<Job> jobs;
-  private String transactionType;
-
-  public RuleLoader(
-      final Integer domain,
-      final Integer entity,
-      final EntityInterface e,
-      final Transaction<? extends TransactionContainer> transaction) {
-    super(cache);
-    this.domain = domain;
-    this.entity = entity;
-    this.e = e;
-    if (e instanceof Property) {
-      this.transactionType = getTransactionType(((Property) e).getDomainEntity());
-    } else {
-      this.transactionType = getTransactionType(e);
-    }
-    this.transaction = transaction;
-  }
-
-  @Override
-  protected String getKey() {
-    return "<" + this.domain + "," + this.entity + "," + this.transactionType + ">";
-  }
-
-  public ArrayList<Job> getJobs() {
-    return this.jobs;
-  }
-
-  @Override
-  public ArrayList<Rule> executeNoCache() throws TransactionException {
-    final RuleLoaderImpl t = getImplementation(RuleLoaderImpl.class);
-    return t.executeNoCache(this.domain, this.entity, this.transactionType);
-  }
-
-  @Override
-  protected void process(final ArrayList<Rule> t) throws TransactionException {
-    this.jobs = new ArrayList<Job>();
-    for (final Rule r : t) {
-      this.jobs.add(Job.getJob(r.job, r.mode, this.e, this.transaction));
-    }
-  }
-}
diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/UpdateEntityTransaction.java b/src/main/java/org/caosdb/server/database/backend/transaction/UpdateEntityTransaction.java
index 275947abd9b42a2424141bc28111889859fb1475..17a9bc7f144474af2ad2a7f1f91ba32f84f3809b 100644
--- a/src/main/java/org/caosdb/server/database/backend/transaction/UpdateEntityTransaction.java
+++ b/src/main/java/org/caosdb/server/database/backend/transaction/UpdateEntityTransaction.java
@@ -1,9 +1,10 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019-2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2019-2021 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -17,8 +18,6 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
 package org.caosdb.server.database.backend.transaction;
 
diff --git a/src/main/java/org/caosdb/server/database/proto/Rule.java b/src/main/java/org/caosdb/server/database/proto/Rule.java
deleted file mode 100644
index 3d25f7662052b5b59f45255218c1191cf011761e..0000000000000000000000000000000000000000
--- a/src/main/java/org/caosdb/server/database/proto/Rule.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * ** header v3.0
- * This file is a part of the CaosDB Project.
- *
- * Copyright (C) 2018 Research Group Biomedical Physics,
- * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
- */
-package org.caosdb.server.database.proto;
-
-import java.io.Serializable;
-import org.caosdb.server.jobs.core.Mode;
-
-public class Rule implements Serializable {
-
-  private static final long serialVersionUID = 1122097540596265945L;
-
-  public int domain = 0;
-  public int entity = 0;
-  public String job = null;
-  public String transaction = null;
-  public Mode mode = null;
-}
diff --git a/src/main/java/org/caosdb/server/entity/Message.java b/src/main/java/org/caosdb/server/entity/Message.java
index 3e469b2dc84b07e3ac3de378f8990cc520989cc0..91720aed492a8b1875bb5613df2aaace7df3bef5 100644
--- a/src/main/java/org/caosdb/server/entity/Message.java
+++ b/src/main/java/org/caosdb/server/entity/Message.java
@@ -1,9 +1,10 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019-2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2019-2021 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -17,8 +18,6 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
 package org.caosdb.server.entity;
 
diff --git a/src/main/java/org/caosdb/server/entity/StatementStatus.java b/src/main/java/org/caosdb/server/entity/StatementStatus.java
index b3d7a613d26eba432308272b3bd932d2fdb54b26..0a7971751bffcdfc0323835433fc006bf5269ec9 100644
--- a/src/main/java/org/caosdb/server/entity/StatementStatus.java
+++ b/src/main/java/org/caosdb/server/entity/StatementStatus.java
@@ -23,13 +23,20 @@
 package org.caosdb.server.entity;
 
 /**
- * The statement status has two purposes.
+ * The statement status has two purposes:
  *
- * <p>1. Storing the importance of an entity (any of OBLIGATORY, RECOMMENDED, SUGGESTED, or FIX). 2.
- * Marking an entity as a REPLACEMENT which is needed for flat representation of deeply nested
- * properties. This constant is only used for internal processes and has no meaning in the API. That
- * is also the reason why this enum is not called "Importance". Apart from that, in most cases its
- * meaning is identical to the importance of an entity.
+ * <p>1. Storing the importance of an entity (any of ``OBLIGATORY``, ``RECOMMENDED``, ``SUGGESTED``,
+ * or ``FIX``). ``OBLIGATORY``, ``RECOMMENDED``, and ``SUGGESTED`` specify whether this Entity must,
+ * should or may be present, and whether the server will throw an error, a warning or nothing,
+ * respectively, if it is missing. The importance is also used to specify the level of inheritance
+ * whereby ``FIX`` means that the entity is never inherited. See <a
+ * href="../../../../../specification/RecordType.html">the documentation of RecordTypes</a> for more
+ * information.
+ *
+ * <p>2. Marking an entity as a ``REPLACEMENT`` which is needed for flat representation of deeply
+ * nested properties. This constant is only used for internal processes and has no meaning in the
+ * API. That is also the reason why this enum is not called "Importance". Apart from that, in most
+ * cases its meaning is identical to the importance of an entity.
  *
  * @author Timm Fitschen (t.fitschen@indiscale.com)
  */
diff --git a/src/main/java/org/caosdb/server/entity/UpdateEntity.java b/src/main/java/org/caosdb/server/entity/UpdateEntity.java
index 884632b5ed3f92d740f2eea69ed49ef77511fd1b..221888b3da57694e5eabb64d0955cd6fca5e0b0e 100644
--- a/src/main/java/org/caosdb/server/entity/UpdateEntity.java
+++ b/src/main/java/org/caosdb/server/entity/UpdateEntity.java
@@ -1,9 +1,10 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019-2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2019-2021 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -17,8 +18,6 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
 package org.caosdb.server.entity;
 
diff --git a/src/main/java/org/caosdb/server/entity/WritableEntity.java b/src/main/java/org/caosdb/server/entity/WritableEntity.java
index ab8ccbb995430e267e84d1f2a235c0d928e6762d..22ffd6e869e871cf568e87363e5ead762e444400 100644
--- a/src/main/java/org/caosdb/server/entity/WritableEntity.java
+++ b/src/main/java/org/caosdb/server/entity/WritableEntity.java
@@ -1,9 +1,10 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019-2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2019-2021 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -17,8 +18,6 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
 package org.caosdb.server.entity;
 
diff --git a/src/main/java/org/caosdb/server/entity/container/WritableContainer.java b/src/main/java/org/caosdb/server/entity/container/WritableContainer.java
index 5fdb8c39ec7f01280278bec0a1f2928bfed7f5c2..fcc8de4e7a44ddcfff1e85b282c4f70311c80f34 100644
--- a/src/main/java/org/caosdb/server/entity/container/WritableContainer.java
+++ b/src/main/java/org/caosdb/server/entity/container/WritableContainer.java
@@ -1,5 +1,4 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
@@ -19,8 +18,6 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
 package org.caosdb.server.entity.container;
 
diff --git a/src/main/java/org/caosdb/server/jobs/Job.java b/src/main/java/org/caosdb/server/jobs/Job.java
index 9432fc3916606cddbf64922850d7f2f9ff5284c2..2de6b08537d82504bb989a4cf661f89ff473e4c2 100644
--- a/src/main/java/org/caosdb/server/jobs/Job.java
+++ b/src/main/java/org/caosdb/server/jobs/Job.java
@@ -1,11 +1,10 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  *   Max-Planck-Institute for Dynamics and Self-Organization Göttingen
- * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
- * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2020-2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2020-2021 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -19,8 +18,6 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
 package org.caosdb.server.jobs;
 
@@ -40,15 +37,17 @@ import org.caosdb.server.database.backend.transaction.IsSubType;
 import org.caosdb.server.database.backend.transaction.RetrieveFullEntityTransaction;
 import org.caosdb.server.database.backend.transaction.RetrieveParents;
 import org.caosdb.server.database.backend.transaction.RetrieveSparseEntity;
-import org.caosdb.server.database.backend.transaction.RuleLoader;
+import org.caosdb.server.database.exceptions.EntityWasNotUniqueException;
 import org.caosdb.server.database.exceptions.TransactionException;
 import org.caosdb.server.datatype.AbstractCollectionDatatype;
 import org.caosdb.server.datatype.AbstractDatatype;
 import org.caosdb.server.datatype.ReferenceDatatype2;
 import org.caosdb.server.entity.EntityInterface;
 import org.caosdb.server.entity.Message;
+import org.caosdb.server.entity.container.ParentContainer;
 import org.caosdb.server.entity.container.TransactionContainer;
-import org.caosdb.server.jobs.core.Mode;
+import org.caosdb.server.entity.wrapper.Parent;
+import org.caosdb.server.jobs.core.JobFailureSeverity;
 import org.caosdb.server.transaction.Transaction;
 import org.caosdb.server.utils.EntityStatus;
 import org.caosdb.server.utils.ServerMessages;
@@ -63,10 +62,12 @@ public abstract class Job {
   /** All known Job classes, by name (actually lowercase getSimpleName()). */
   static HashMap<String, Class<? extends Job>> allClasses = null;
 
+  private final JobConfig jobConfig = JobConfig.getInstance();
+
   private static List<Class<? extends Job>> loadAlways;
 
   private Transaction<? extends TransactionContainer> transaction = null;
-  private Mode mode = null;
+  private JobFailureSeverity failureSeverity = null;
   private final TransactionStage stage;
   private EntityInterface entity = null;
 
@@ -95,7 +96,7 @@ public abstract class Job {
   protected ScheduledJob appendJob(final EntityInterface entity, final Class<? extends Job> clazz) {
     try {
       final Job job = clazz.getDeclaredConstructor().newInstance();
-      job.init(getMode(), entity, getTransaction());
+      job.init(getFailureSeverity(), entity, getTransaction());
       return getTransaction().getSchedule().add(job);
     } catch (InstantiationException
         | IllegalAccessException
@@ -124,10 +125,10 @@ public abstract class Job {
   }
 
   public final Job init(
-      final Mode mode,
+      final JobFailureSeverity severity,
       final EntityInterface entity,
       final Transaction<? extends TransactionContainer> transaction) {
-    this.mode = mode;
+    this.failureSeverity = severity;
     this.entity = entity;
     this.transaction = transaction;
     return this;
@@ -145,14 +146,85 @@ public abstract class Job {
     return this.entity;
   }
 
-  protected final Mode getMode() {
-    return this.mode;
+  protected final JobFailureSeverity getFailureSeverity() {
+    return this.failureSeverity;
+  }
+
+  protected final void setFailureSeverity(final JobFailureSeverity severiy) {
+    this.failureSeverity = severiy;
   }
 
   TransactionContainer getContainer() {
     return getTransaction().getContainer();
   }
 
+  /**
+   * Check if an entity ('child') is a direct or indirect child of another entity ('targetParent').
+   *
+   * <p>This assumes that the parent has either an id (persistent or temporary) or a name.
+   *
+   * <p>If the targetParent is not in the set of parents of the child, this method iterates through
+   * the direct parents of the child.
+   *
+   * <p>If both entities are part of this transaction, they are resolved within this transaction.
+   * Otherwise they are fetched from the database backend or the whole evaluation takes place in the
+   * backend (if both have persistent ids and are not part of this transaction).
+   *
+   * <p>If both entities have the same id or name, the return value is true without any further
+   * checks.
+   *
+   * @param child the child entity
+   * @param targetParent the parent entity
+   * @return true iff targetParent is a direct or indirect parent of the child or when the ids or
+   *     names match.
+   * @throws Message
+   */
+  protected final boolean isSubType(final EntityInterface child, final EntityInterface targetParent)
+      throws EntityWasNotUniqueException {
+    if (targetParent.hasId()) {
+      if (targetParent.getId().equals(child.getId())) {
+        return true;
+      }
+
+    } else if (targetParent.hasName()) {
+      if (targetParent.getName().equals(child.getName())) {
+        return true;
+      }
+    }
+
+    // check direct parents of child
+    ParentContainer directParents;
+    if (child.hasParents()) {
+      directParents = child.getParents();
+    } else {
+      directParents = resolve(child).getParents();
+    }
+    for (final Parent directParent : directParents) {
+      EntityInterface resolvedDirectParent = null;
+      if (directParent.hasId()) {
+        resolvedDirectParent = getEntityById(directParent.getId());
+      } else if (directParent.hasName()) {
+        resolvedDirectParent = getEntityByName(directParent.getName());
+      }
+
+      if (resolvedDirectParent != null) {
+        if (isSubType(resolvedDirectParent, targetParent)) {
+          return true;
+        }
+      } else if (directParent.hasId()) {
+        if (isValidSubType(directParent.getId(), targetParent.getId())) {
+          return true;
+        }
+      } else if (directParent.hasName()) {
+        if (isValidSubType(resolve(directParent).getId(), targetParent.getId())) {
+          return true;
+        }
+      }
+    }
+
+    return false;
+  }
+
   protected final boolean isValidSubType(final int child, final int parent) {
     if (!"false".equals(getContainer().getFlags().get("cache"))) {
       final HashMap<String, Boolean> isSubTypeCache = getCache("isSubType");
@@ -187,17 +259,17 @@ public abstract class Job {
       // the targeted entity version is the entity after the transaction or the
       // entity without a specific version. Thus we have to fetch the entity
       // from the container if possible.
-      EntityInterface ret = getEntityById(id);
+      final EntityInterface ret = getEntityById(id);
       if (ret != null) {
         return ret;
       }
     } else if (version.startsWith("HEAD~")) {
-      EntityInterface entById = getEntityById(id);
+      final EntityInterface entById = getEntityById(id);
       if (entById != null && entById.getEntityStatus() != EntityStatus.VALID) {
         // if version is HEAD~{OFFSET} with {OFFSET} > 0 and the targeted entity is is to be
         // updated, the actual offset has to be reduced by 1. HEAD always denotes the entity@HEAD
         // *after* the successful transaction, so that it is consistent with subsequent retrieves.
-        int offset = Integer.parseInt(version.substring(5)) - 1;
+        final int offset = Integer.parseInt(version.substring(5)) - 1;
         if (offset == 0) {
           // special case HEAD~1
           resulting_version = "HEAD";
@@ -215,7 +287,7 @@ public abstract class Job {
     return ret;
   }
 
-  protected final EntityInterface retrieveValidEntity(Integer id) {
+  protected final EntityInterface retrieveValidEntity(final Integer id) {
     return execute(new RetrieveFullEntityTransaction(id)).getContainer().get(0);
   }
 
@@ -245,7 +317,7 @@ public abstract class Job {
    */
   public static Job getJob(
       final String job,
-      final Mode mode,
+      final JobFailureSeverity severiy,
       final EntityInterface entity,
       final Transaction<? extends TransactionContainer> transaction) {
     // Fill `allClasses` with available subclasses
@@ -253,7 +325,7 @@ public abstract class Job {
 
     // Get matching class for Job and generate it.
     final Class<? extends Job> jobClass = allClasses.get(job.toLowerCase());
-    return getJob(jobClass, mode, entity, transaction);
+    return getJob(jobClass, severiy, entity, transaction);
   }
 
   /**
@@ -316,14 +388,12 @@ public abstract class Job {
       return new ArrayList<Job>();
     }
     if (dt instanceof ReferenceDatatype2) {
-      final RuleLoader t = new RuleLoader(0, 17, entity, transaction);
-      return execute(t).getJobs();
+      return jobConfig.getConfiguredJobs(0, 17, entity, transaction);
     } else if (dt instanceof AbstractCollectionDatatype) {
       final AbstractDatatype datatype = ((AbstractCollectionDatatype) dt).getDatatype();
       return loadDataTypeSpecificJobs(datatype, entity, transaction);
     } else if (dt.getId() != null) {
-      final RuleLoader t = new RuleLoader(0, dt.getId(), entity, transaction);
-      return execute(t).getJobs();
+      return jobConfig.getConfiguredJobs(0, dt.getId(), entity, transaction);
     } else {
       return null;
     }
@@ -334,42 +404,48 @@ public abstract class Job {
 
     final ArrayList<Job> jobs = new ArrayList<>();
     // load permanent jobs
-    for (Class<? extends Job> j : loadAlways) {
+    for (final Class<? extends Job> j : loadAlways) {
       if (EntityJob.class.isAssignableFrom(j)
           && j.getAnnotation(JobAnnotation.class).transaction().isInstance(transaction)) {
-        jobs.add(getJob(j, Mode.MUST, entity, transaction));
+        jobs.add(getJob(j, JobFailureSeverity.ERROR, entity, transaction));
       }
     }
 
     // load general rules
-    {
-      final RuleLoader t = new RuleLoader(0, 0, entity, transaction);
-      jobs.addAll(execute(t).getJobs());
+    final List<Job> generalRules = jobConfig.getConfiguredJobs(0, 0, entity, transaction);
+    if (generalRules != null) {
+      jobs.addAll(generalRules);
     }
 
     // load Role specific rules
     if (entity.hasRole()) {
-      final RuleLoader t = new RuleLoader(0, entity.getRole().getId(), entity, transaction);
-      jobs.addAll(execute(t).getJobs());
+      final List<Job> roleRules =
+          jobConfig.getConfiguredJobs(0, entity.getRole().getId(), entity, transaction);
+      if (roleRules != null) {
+        jobs.addAll(roleRules);
+      }
     }
 
     // load data type specific rules
-    jobs.addAll(loadDataTypeSpecificJobs(entity, transaction));
+    final List<Job> datatypeRules = loadDataTypeSpecificJobs(entity, transaction);
+    if (datatypeRules != null) {
+      jobs.addAll(datatypeRules);
+    }
 
     return jobs;
   }
 
   private static Job getJob(
-      Class<? extends Job> jobClass,
-      Mode mode,
-      EntityInterface entity,
-      Transaction<? extends TransactionContainer> transaction) {
+      final Class<? extends Job> jobClass,
+      final JobFailureSeverity severity,
+      final EntityInterface entity,
+      final Transaction<? extends TransactionContainer> transaction) {
     Job ret;
     try {
 
       if (jobClass != null) {
         ret = jobClass.getDeclaredConstructor().newInstance();
-        ret.init(mode, entity, transaction);
+        ret.init(severity, entity, transaction);
         return ret;
       }
       return null;
@@ -393,7 +469,7 @@ public abstract class Job {
     // load flag jobs
     if (!entity.getFlags().isEmpty()) {
       for (final String key : entity.getFlags().keySet()) {
-        final Job j = getJob(key, Mode.MUST, entity, transaction);
+        final Job j = getJob(key, JobFailureSeverity.ERROR, entity, transaction);
         if (j != null) {
           if (j instanceof FlagJob) {
             ((FlagJob) j).setValue(entity.getFlag(key));
@@ -408,7 +484,7 @@ public abstract class Job {
       for (final EntityInterface p : entity.getParents()) {
         if (!p.getFlags().isEmpty()) {
           for (final String key : p.getFlags().keySet()) {
-            final Job j = getJob(key, Mode.MUST, entity, transaction);
+            final Job j = getJob(key, JobFailureSeverity.ERROR, entity, transaction);
             if (j != null) {
               if (j instanceof FlagJob) {
                 ((FlagJob) j).setValue(p.getFlag(key));
@@ -427,7 +503,7 @@ public abstract class Job {
       for (final EntityInterface p : entity.getProperties()) {
         if (!p.getFlags().isEmpty()) {
           for (final String key : p.getFlags().keySet()) {
-            final Job j = getJob(key, Mode.MUST, entity, transaction);
+            final Job j = getJob(key, JobFailureSeverity.ERROR, entity, transaction);
             if (j != null) {
               if (j instanceof FlagJob) {
                 ((FlagJob) j).setValue(p.getFlag(key));
@@ -460,7 +536,7 @@ public abstract class Job {
         + "-"
         + (getEntity() != null ? getEntity().toString() : "NOENTITY")
         + " #"
-        + getMode().toString()
+        + getFailureSeverity().toString()
         + "]";
   }
 
@@ -472,18 +548,48 @@ public abstract class Job {
     return this.stage;
   }
 
+  /**
+   * Resolve an entity (which might only be specified by it's name or id) to its full representation
+   * (with properties, parents and all).
+   *
+   * @param entity the entity to be resolved.
+   * @return the resolved entity.
+   * @throws EntityWasNotUniqueException if the resolution failed due to ambuiguity of the name.
+   */
+  protected EntityInterface resolve(final EntityInterface entity)
+      throws EntityWasNotUniqueException {
+    EntityInterface resolvedEntity = null;
+    if (!entity.hasId() && entity.hasName()) {
+      resolvedEntity = getEntityByName(entity.getName());
+      if (resolvedEntity == null) {
+        final Integer eid = retrieveValidIDByName(entity.getName());
+        entity.setId(eid);
+      }
+    }
+
+    if (entity.hasId()) {
+      // get entity from container
+      resolvedEntity = getEntityById(entity.getId());
+      if (resolvedEntity == null && entity.getId() > 0) {
+        resolvedEntity = retrieveValidEntity(entity.getId());
+      }
+    }
+
+    return resolvedEntity;
+  }
+
   /**
    * Return those matching jobs which are annotated with the "loadAlways" attribute.
    *
    * @return A list with the jobs.
    */
-  public static List<Job> loadPermanentContainerJobs(Transaction<?> transaction) {
+  public static List<Job> loadPermanentContainerJobs(final Transaction<?> transaction) {
     final ArrayList<Job> jobs = new ArrayList<>();
     // load permanent jobs: ContainerJob classes with the correct transaction
-    for (Class<? extends Job> j : loadAlways) {
+    for (final Class<? extends Job> j : loadAlways) {
       if (ContainerJob.class.isAssignableFrom(j)
           && j.getAnnotation(JobAnnotation.class).transaction().isInstance(transaction)) {
-        jobs.add(getJob(j, Mode.MUST, null, transaction));
+        jobs.add(getJob(j, JobFailureSeverity.ERROR, null, transaction));
       }
     }
     return jobs;
diff --git a/src/main/java/org/caosdb/server/jobs/JobConfig.java b/src/main/java/org/caosdb/server/jobs/JobConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..732a985a78b5f67c88d1c01dff1565e887aab13c
--- /dev/null
+++ b/src/main/java/org/caosdb/server/jobs/JobConfig.java
@@ -0,0 +1,203 @@
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package org.caosdb.server.jobs;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import org.caosdb.server.CaosDBServer;
+import org.caosdb.server.ServerProperties;
+import org.caosdb.server.entity.EntityInterface;
+import org.caosdb.server.entity.RetrieveEntity;
+import org.caosdb.server.entity.container.TransactionContainer;
+import org.caosdb.server.entity.wrapper.Property;
+import org.caosdb.server.jobs.core.JobFailureSeverity;
+import org.caosdb.server.transaction.Transaction;
+import org.caosdb.server.utils.ConfigurationException;
+
+/**
+ * Configuration of the jobs which have to run during all entity transactions with "job rules".
+ *
+ * <p>A job rule contains of a quintuple (domain, entity, transaction type, job name, job failure
+ * severity).
+ *
+ * <p>The domain and entity define for which entities the job (of the given name) should be loaded.
+ * The transaction type (e.g. Insert, Update, Delete) defines the transaction where this rule
+ * applies. The job failure severity sets the related property of a job.
+ *
+ * <p>The configuration is being read by default from a config file {@link
+ * ServerProperties#KEY_JOB_RULES_CONFIG}
+ *
+ * <p>This class works as a singleton, most of the time. It is possible to instanciate it otherwise,
+ * though.
+ *
+ * @author Timm Fitschen <t.fitschen@indiscale.com>
+ */
+public class JobConfig {
+
+  private static JobConfig instance;
+
+  static {
+    try {
+      instance = new JobConfig();
+    } catch (final Exception e) {
+      e.printStackTrace();
+      // CaosDB Server cannot work without a proper job configuration.
+      System.exit(1);
+    }
+  }
+
+  /**
+   * The keys of these rules consist of the domain, entity and transaction. The value is a pair (job
+   * name, job failure severity).
+   */
+  private final Map<String, List<Object[]>> rules;
+
+  public JobConfig() throws FileNotFoundException, IOException {
+    this(new File(CaosDBServer.getServerProperty(ServerProperties.KEY_JOB_RULES_CONFIG)));
+  }
+
+  public JobConfig(final File config) throws FileNotFoundException, IOException {
+    this.rules = getRulesFromCSV(config);
+  }
+
+  /**
+   * Read the rules from the CSV file.
+   *
+   * @param file
+   * @return A map with all the rules.
+   * @throws FileNotFoundException
+   * @throws IOException
+   */
+  private Map<String, List<Object[]>> getRulesFromCSV(final File file)
+      throws FileNotFoundException, IOException {
+    try (final BufferedReader reader = new BufferedReader(new FileReader(file))) {
+      final Map<String, List<Object[]>> result = new HashMap<>();
+      reader
+          .lines()
+          .forEach(
+              line -> {
+                // cut away comments, trim, then split into columns
+                final String[] row = line.replaceFirst("#.*$", "").strip().split("\\s*,\\s*");
+                if (row.length == 1 && row[0].isBlank()) {
+                  // comment or empty line
+                  // ignore
+                  return;
+                }
+                addRule(result, row);
+              });
+      return result;
+    }
+  }
+
+  /** Generate a unique key (for class-internal use only). */
+  private String getKey(final Integer domain, final Integer entity, final String transaction) {
+    final StringBuilder sb = new StringBuilder();
+    sb.append("<");
+    sb.append(domain);
+    sb.append(",");
+    sb.append(transaction.toLowerCase());
+    sb.append(",");
+    sb.append(entity);
+    sb.append(">");
+    return sb.toString();
+  }
+
+  /**
+   * Read a rule from the csv-row and put it into the 'result' map
+   *
+   * @param result map with all the rules.
+   * @param row represents a single rule as a comma-separated quintuple.
+   */
+  private void addRule(final Map<String, List<Object[]>> result, final String[] row) {
+    if (row.length != 5) {
+      throw new ConfigurationException(
+          "Could not parse the job rules. Lines of five comma-separated values expected");
+    }
+    try {
+      final Integer domain = Integer.parseInt(row[0]);
+      final Integer entity = Integer.parseInt(row[1]);
+      final String transaction = row[2];
+      final String job = row[3];
+      final JobFailureSeverity severity = JobFailureSeverity.valueOf(row[4]);
+      final String key = getKey(domain, entity, transaction);
+      if (!result.containsKey(key)) {
+        result.put(key, new ArrayList<>());
+      }
+      result.get(key).add(new Object[] {job, severity});
+
+    } catch (final Exception e) {
+      throw new ConfigurationException("Could not parse the job rules.", e);
+    }
+  }
+
+  /**
+   * Factory method for the instanciation and configuration of jobs for a specific entity.
+   *
+   * @param domain the domain of the rule
+   * @param entity the entity of the rule (this might be a particular datatype or a role, like
+   *     "RecordType")
+   * @param target the entity for which the job will run.
+   * @param transaction the transaction for which the job will run.
+   * @return Fresh job instances, one for each matching job rule.
+   */
+  public List<Job> getConfiguredJobs(
+      final Integer domain,
+      final Integer entity,
+      final EntityInterface target,
+      final Transaction<? extends TransactionContainer> transaction) {
+    String transactionType;
+    if (target instanceof Property) {
+      transactionType = getTransactionType(((Property) target).getDomainEntity());
+    } else {
+      transactionType = getTransactionType(target);
+    }
+    final String key = getKey(domain, entity, transactionType);
+    final List<Object[]> jobRules = rules.get(key);
+    if (jobRules != null) {
+      final List<Job> result = new LinkedList<>();
+      jobRules.forEach(
+          config -> {
+            result.add(
+                Job.getJob(
+                    (String) config[0], (JobFailureSeverity) config[1], target, transaction));
+          });
+      return result;
+    }
+    return null;
+  }
+
+  public static JobConfig getInstance() {
+    return instance;
+  }
+
+  /** Return the type of transaction of an entity, e.g. "Retrieve" for a {@link RetrieveEntity}. */
+  String getTransactionType(final EntityInterface e) {
+    return e.getClass().getSimpleName().replace("Entity", "");
+  }
+}
diff --git a/src/main/java/org/caosdb/server/jobs/Schedule.java b/src/main/java/org/caosdb/server/jobs/Schedule.java
index 0090ec92dffb310052b5b8cc82424e2edd0288f9..f33400099a951aa8fbcf1975208988085813a8b8 100644
--- a/src/main/java/org/caosdb/server/jobs/Schedule.java
+++ b/src/main/java/org/caosdb/server/jobs/Schedule.java
@@ -1,9 +1,10 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2020-2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2020-2021 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -17,8 +18,6 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
 package org.caosdb.server.jobs;
 
diff --git a/src/main/java/org/caosdb/server/jobs/ScheduledJob.java b/src/main/java/org/caosdb/server/jobs/ScheduledJob.java
index 65bc3a1947baf5b76539f281e0d855e878a3bdfd..3affdfd21421961edc4721c25d80f392d914d1bb 100644
--- a/src/main/java/org/caosdb/server/jobs/ScheduledJob.java
+++ b/src/main/java/org/caosdb/server/jobs/ScheduledJob.java
@@ -1,5 +1,4 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
@@ -17,8 +16,6 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
 
 package org.caosdb.server.jobs;
diff --git a/src/main/java/org/caosdb/server/jobs/TransactionStage.java b/src/main/java/org/caosdb/server/jobs/TransactionStage.java
index 8248140d913e08acfb699cbb553a14749c8e2f1f..9e15b4e556269def79d58e532bbe3c9e6cbb9bbd 100644
--- a/src/main/java/org/caosdb/server/jobs/TransactionStage.java
+++ b/src/main/java/org/caosdb/server/jobs/TransactionStage.java
@@ -1,9 +1,10 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -17,8 +18,6 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
 package org.caosdb.server.jobs;
 
diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckDatatypePresent.java b/src/main/java/org/caosdb/server/jobs/core/CheckDatatypePresent.java
index f46013d278f268b95b1b105130e1eabd74f3a9f4..f9c7704c8bd0ff00214b41d5793c8fdb15bcc61a 100644
--- a/src/main/java/org/caosdb/server/jobs/core/CheckDatatypePresent.java
+++ b/src/main/java/org/caosdb/server/jobs/core/CheckDatatypePresent.java
@@ -82,9 +82,12 @@ public final class CheckDatatypePresent extends EntityJob {
 
         // run jobsreturn this.entities;
         final List<Job> datatypeJobs = loadDataTypeSpecificJobs();
-        List<ScheduledJob> scheduledJobs = getTransaction().getSchedule().addAll(datatypeJobs);
-        for (final ScheduledJob job : scheduledJobs) {
-          getTransaction().getSchedule().runJob(job);
+        if (datatypeJobs != null) {
+          final List<ScheduledJob> scheduledJobs =
+              getTransaction().getSchedule().addAll(datatypeJobs);
+          for (final ScheduledJob job : scheduledJobs) {
+            getTransaction().getSchedule().runJob(job);
+          }
         }
       } else {
 
diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckDescPresent.java b/src/main/java/org/caosdb/server/jobs/core/CheckDescPresent.java
index fc3a5114f240c796c114f92f8e6271e4fd3bd9ac..e430a0817e9083013330bc4d59d8f7377d10d62a 100644
--- a/src/main/java/org/caosdb/server/jobs/core/CheckDescPresent.java
+++ b/src/main/java/org/caosdb/server/jobs/core/CheckDescPresent.java
@@ -35,12 +35,12 @@ public class CheckDescPresent extends EntityJob {
   @Override
   public final void run() {
     if (!getEntity().hasDescription()) {
-      switch (getMode()) {
-        case MUST:
+      switch (getFailureSeverity()) {
+        case ERROR:
           getEntity().addError(ServerMessages.ENTITY_HAS_NO_DESCRIPTION);
           getEntity().setEntityStatus(EntityStatus.UNQUALIFIED);
           break;
-        case SHOULD:
+        case WARN:
           getEntity().addWarning(ServerMessages.ENTITY_HAS_NO_DESCRIPTION);
         default:
           break;
diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckNamePresent.java b/src/main/java/org/caosdb/server/jobs/core/CheckNamePresent.java
index f1600ce42117ab5f37fccd33b79d821efeccb774..3166057c95671273efe27357ce11b2ffe10e2c78 100644
--- a/src/main/java/org/caosdb/server/jobs/core/CheckNamePresent.java
+++ b/src/main/java/org/caosdb/server/jobs/core/CheckNamePresent.java
@@ -35,12 +35,12 @@ public class CheckNamePresent extends EntityJob {
   @Override
   public final void run() {
     if (!getEntity().hasName()) {
-      switch (getMode()) {
-        case MUST:
+      switch (getFailureSeverity()) {
+        case ERROR:
           getEntity().addError(ServerMessages.ENTITY_HAS_NO_NAME);
           getEntity().setEntityStatus(EntityStatus.UNQUALIFIED);
           break;
-        case SHOULD:
+        case WARN:
           getEntity().addWarning(ServerMessages.ENTITY_HAS_NO_NAME);
           break;
         default:
diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckNoAdditionalPropertiesPresent.java b/src/main/java/org/caosdb/server/jobs/core/CheckNoAdditionalPropertiesPresent.java
index b04a9151ab5c142e97a6efc9b19607896ab38daf..fb6919649242a906b36526e3725fc25e710c9d60 100644
--- a/src/main/java/org/caosdb/server/jobs/core/CheckNoAdditionalPropertiesPresent.java
+++ b/src/main/java/org/caosdb/server/jobs/core/CheckNoAdditionalPropertiesPresent.java
@@ -45,7 +45,7 @@ public class CheckNoAdditionalPropertiesPresent extends EntityJob {
   }
 
   private void addMessage(EntityInterface property, Message message) {
-    if (getMode() == Mode.MUST) {
+    if (getFailureSeverity() == JobFailureSeverity.ERROR) {
       property.addError(message);
     } else {
       property.addWarning(message);
diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckNoOverridesPresent.java b/src/main/java/org/caosdb/server/jobs/core/CheckNoOverridesPresent.java
index e439354e88f39675491682877dd27aa54fd7a3ef..d6cae3ee40222d3b3d6eb89e1b2d0c8bce1da0e5 100644
--- a/src/main/java/org/caosdb/server/jobs/core/CheckNoOverridesPresent.java
+++ b/src/main/java/org/caosdb/server/jobs/core/CheckNoOverridesPresent.java
@@ -28,7 +28,7 @@ public class CheckNoOverridesPresent extends EntityJob {
   }
 
   private void addMessage(Property p, Message message) {
-    if (getMode() == Mode.MUST) {
+    if (getFailureSeverity() == JobFailureSeverity.ERROR) {
       p.addError(message);
     } else {
       p.addWarning(message);
diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckParOblPropPresent.java b/src/main/java/org/caosdb/server/jobs/core/CheckParOblPropPresent.java
index 7aca2bac3dd3709006497f9ce8f556f267c4ca81..9588a3056e15f7fb3847db37468bb6a628741203 100644
--- a/src/main/java/org/caosdb/server/jobs/core/CheckParOblPropPresent.java
+++ b/src/main/java/org/caosdb/server/jobs/core/CheckParOblPropPresent.java
@@ -1,9 +1,10 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019-2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2019-2021 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -17,14 +18,15 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
 package org.caosdb.server.jobs.core;
 
-import org.caosdb.server.database.backend.transaction.RetrieveFullEntityTransaction;
 import org.caosdb.server.entity.EntityInterface;
+import org.caosdb.server.entity.Message;
+import org.caosdb.server.entity.Message.MessageType;
 import org.caosdb.server.entity.StatementStatus;
+import org.caosdb.server.entity.wrapper.Parent;
+import org.caosdb.server.entity.wrapper.Property;
 import org.caosdb.server.jobs.EntityJob;
 import org.caosdb.server.utils.EntityStatus;
 import org.caosdb.server.utils.ServerMessages;
@@ -32,96 +34,113 @@ import org.caosdb.server.utils.ServerMessages;
 /**
  * Check whether an entity implements all obligatory properties of all parents.
  *
- * @author tf
+ * @author Timm Fitschen <t.fitschen@indiscale.com>
  */
 public class CheckParOblPropPresent extends EntityJob {
-  @Override
-  public final void run() {
 
-    // This test needs the valid/qualified ids of all properties.
-    // Thus, run a CheckPropValid first.
-    runJobFromSchedule(getEntity(), CheckPropValid.class);
+  public static final String OBL_IMPORTANCE_FLAG_KEY = "force-missing-obligatory";
+  public static final Message ENTITY_NOT_UNIQUE =
+      new Message(
+          MessageType.Error, 0, "Could not check importance. Parent was not uniquely resolvable.");
+  public static final Message ILLEGAL_FLAG_VALUE =
+      new Message(MessageType.Warning, "Illegal value for flag 'force-missing-obligatory'.");
 
-    // This test needs the valid/qualified parents. If a parent is only
-    // referenced by name it has to be translated into a (hopefully) unique
-    // id.
-    runJobFromSchedule(getEntity(), CheckParValid.class);
-
-    if (getEntity().hasParents()) {
-      execute(new RetrieveFullEntityTransaction(getEntity().getParents()));
+  @Override
+  public final void run() {
+    if (getEntity().getEntityStatus() != EntityStatus.QUALIFIED
+        || getEntity() instanceof Property
+        || getEntity() instanceof Parent) {
+      return;
     }
-
-    // inherit properties
-    runJobFromSchedule(getEntity(), Inheritance.class);
-
-    // loop over all parents
     if (getEntity().hasParents()) {
-      for (final EntityInterface parent : getEntity().getParents()) {
 
-        // if the parent has an id>0, it is to be a valid entity.
-        if (parent.hasId() && parent.getId() > 0) {
-          if (parent.getEntityStatus() == EntityStatus.NONEXISTENT) {
-            continue;
-          }
+      handleImportanceFlags();
+      if (getFailureSeverity() == JobFailureSeverity.IGNORE) {
+        // importance is to be ignored
+        return;
+      }
 
-          // does this parent have properties?
-          if (parent.hasProperties()) {
+      // inherit properties
+      runJobFromSchedule(getEntity(), Inheritance.class);
 
-            // loop over all properties of this parent
-            outerLoop:
-            for (final EntityInterface parentProperty : parent.getProperties()) {
+      // get importance-flags
 
-              // only obligatory properties are of interest.
-              if (parentProperty.hasStatementStatus()
-                  && parentProperty.getStatementStatus() == StatementStatus.OBLIGATORY) {
+      // loop over all parents
+      for (final EntityInterface parent : getEntity().getParents()) {
+        final EntityInterface resolvedParent = resolve(parent);
+        checkObligatoryPropertiesMissing(resolvedParent);
+      }
+    }
+  }
+
+  /**
+   * Check if all obligatory properties of the given parent are present.
+   *
+   * <p>Add an error or warning if not.
+   *
+   * @param parent
+   * @throws Message
+   */
+  private void checkObligatoryPropertiesMissing(final EntityInterface parent) {
+    // does this parent have properties?
+    if (parent.hasProperties()) {
 
-                // loop over all properties of the entity
-                innerLoop:
-                for (final EntityInterface entityProperty : getEntity().getProperties()) {
+      // loop over all properties of this parent
+      outerLoop:
+      for (final EntityInterface parentProperty : parent.getProperties()) {
 
-                  if (!entityProperty.hasId()) {
-                    // continue inner loop means that this
-                    // is not the entityProperty in
-                    // question, continue search for an
-                    // entityProperty.
-                    continue innerLoop;
-                  }
+        // only obligatory properties are of interest.
+        if (parentProperty.hasStatementStatus()
+            && parentProperty.getStatementStatus() == StatementStatus.OBLIGATORY) {
 
-                  // TODO sub type instead of equals!!!
-                  if (parentProperty.getId().equals(entityProperty.getId())) {
-                    // continue outer loop means that we
-                    // found an entityProperty that
-                    // implements the obligatory
-                    // parentProperty, continue with the
-                    // other parentProperties.
-                    continue outerLoop;
-                  }
-                }
+          // loop over all properties of the entity
+          for (final EntityInterface entityProperty : getEntity().getProperties()) {
 
-                // No entityProperty has been found which
-                // implements this parentProperty. Add the
-                // respective messages.
-                switch (getMode()) {
-                  case MUST:
-                    // TODO add information WHICH property is
-                    // missing.
-                    getEntity().addError(ServerMessages.OBLIGATORY_PROPERTY_MISSING);
-                    break;
-                  case SHOULD:
-                    // TODO add information WHICH property is
-                    // missing.
-                    getEntity().addWarning(ServerMessages.OBLIGATORY_PROPERTY_MISSING);
-                    break;
-                  default:
-                    break;
-                }
-              }
+            if (isSubType(entityProperty, parentProperty)) {
+              // continue outer loop means that we
+              // found an entityProperty that
+              // implements the obligatory
+              // parentProperty, continue with the
+              // other parentProperties.
+              continue outerLoop;
             }
           }
-        } else {
-          // TODO add processing of new parents, new properties
+
+          // No entityProperty has been found which
+          // implements this parentProperty. Add the
+          // respective messages.
+          switch (getFailureSeverity()) {
+            case ERROR:
+              // TODO add information WHICH property is
+              // missing.
+              getEntity().addError(ServerMessages.OBLIGATORY_PROPERTY_MISSING);
+              break;
+            case WARN:
+              // TODO add information WHICH property is
+              // missing.
+              getEntity().addWarning(ServerMessages.OBLIGATORY_PROPERTY_MISSING);
+              break;
+            default:
+              // should never be reached, but ignoring is the way to go anyway
+              break;
+          }
         }
       }
     }
   }
+
+  /** Look at the entity's importance flag first, at the container's flag second. */
+  private void handleImportanceFlags() {
+    final String globalFlag =
+        getTransaction().getContainer().getFlags().get(OBL_IMPORTANCE_FLAG_KEY);
+    final String entityFlag =
+        getEntity().getFlags().getOrDefault(OBL_IMPORTANCE_FLAG_KEY, globalFlag);
+    if (entityFlag != null) {
+      try {
+        setFailureSeverity(JobFailureSeverity.valueOf(entityFlag.toUpperCase()));
+      } catch (final IllegalArgumentException e) {
+        getEntity().addWarning(ILLEGAL_FLAG_VALUE);
+      }
+    }
+  }
 }
diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckParPresent.java b/src/main/java/org/caosdb/server/jobs/core/CheckParPresent.java
index 8851456c236c0afd9f922f187089adc5f4e35ab1..058710c6bbbf5c2bbd3f95f7ae70a243d3bfcd20 100644
--- a/src/main/java/org/caosdb/server/jobs/core/CheckParPresent.java
+++ b/src/main/java/org/caosdb/server/jobs/core/CheckParPresent.java
@@ -35,12 +35,12 @@ public class CheckParPresent extends EntityJob {
   @Override
   public final void run() {
     if (!getEntity().hasParents() || getEntity().getParents().isEmpty()) {
-      switch (getMode()) {
-        case MUST:
+      switch (getFailureSeverity()) {
+        case ERROR:
           getEntity().addError(ServerMessages.ENTITY_HAS_NO_PARENTS);
           getEntity().setEntityStatus(EntityStatus.UNQUALIFIED);
           break;
-        case SHOULD:
+        case WARN:
           getEntity().addWarning(ServerMessages.ENTITY_HAS_NO_PARENTS);
         default:
           break;
diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckPropPresent.java b/src/main/java/org/caosdb/server/jobs/core/CheckPropPresent.java
index 31a460971bc80ccf61d775c96b4632fa38f14f66..cc23d980152c44cfdbf88500e9041044a0418034 100644
--- a/src/main/java/org/caosdb/server/jobs/core/CheckPropPresent.java
+++ b/src/main/java/org/caosdb/server/jobs/core/CheckPropPresent.java
@@ -35,12 +35,12 @@ public class CheckPropPresent extends EntityJob {
   @Override
   public final void run() {
     if (getEntity().getProperties().isEmpty()) {
-      switch (getMode()) {
-        case MUST:
+      switch (getFailureSeverity()) {
+        case ERROR:
           getEntity().addError(ServerMessages.ENTITY_HAS_NO_PROPERTIES);
           getEntity().setEntityStatus(EntityStatus.UNQUALIFIED);
           break;
-        case SHOULD:
+        case WARN:
           getEntity().addWarning(ServerMessages.ENTITY_HAS_NO_PROPERTIES);
         default:
           break;
diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckStateTransition.java b/src/main/java/org/caosdb/server/jobs/core/CheckStateTransition.java
index d82a8a5be4baf265e6af263391751a68458a91f4..1a159910044a079baba3b64c62176883ad31b8ef 100644
--- a/src/main/java/org/caosdb/server/jobs/core/CheckStateTransition.java
+++ b/src/main/java/org/caosdb/server/jobs/core/CheckStateTransition.java
@@ -1,5 +1,4 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
@@ -17,8 +16,6 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
 
 package org.caosdb.server.jobs.core;
@@ -40,7 +37,7 @@ import org.caosdb.server.utils.ServerMessages;
  * <p>This job checks if the attempted state transition is in compliance with the state model. This
  * job runs during the CHECK phase and should do all necessary consistency and permission checks.
  *
- * @author Timm Fitschen (t.fitschen@indiscale.com)
+ * @author Timm Fitschen <t.fitschen@indiscale.com>
  */
 @JobAnnotation(stage = TransactionStage.POST_CHECK, transaction = WriteTransaction.class)
 public class CheckStateTransition extends EntityStateJob {
diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckUnitPresent.java b/src/main/java/org/caosdb/server/jobs/core/CheckUnitPresent.java
index aa87ecfff69635407bc90d372c89f089e4f308dc..5e3e6120fadef8f4b519ec694ebbf7588d7c2488 100644
--- a/src/main/java/org/caosdb/server/jobs/core/CheckUnitPresent.java
+++ b/src/main/java/org/caosdb/server/jobs/core/CheckUnitPresent.java
@@ -40,12 +40,12 @@ public class CheckUnitPresent extends EntityJob {
     runJobFromSchedule(getEntity(), Inheritance.class);
 
     if (!hasUnit(getEntity())) {
-      switch (getMode()) {
-        case MUST:
+      switch (getFailureSeverity()) {
+        case ERROR:
           getEntity().addError(ServerMessages.ENTITY_HAS_NO_UNIT);
           getEntity().setEntityStatus(EntityStatus.UNQUALIFIED);
           break;
-        case SHOULD:
+        case WARN:
           getEntity().addWarning(ServerMessages.ENTITY_HAS_NO_UNIT);
         default:
           break;
diff --git a/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java b/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java
index c20b18b6e4950c07967d2cfcdd776c95c25f4a53..e2f59a8827a682df508a26176a45a9fc1a874b4e 100644
--- a/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java
+++ b/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java
@@ -1,5 +1,4 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
@@ -17,8 +16,6 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
 
 package org.caosdb.server.jobs.core;
@@ -81,7 +78,7 @@ import org.jdom2.Element;
  * <p>Only the 4th job ({@link MakeStateMessage}) runs during Retrieve transitions. During
  * WriteTransactions all four jobs do run.
  *
- * @author Timm Fitschen (t.fitschen@indiscale.com)
+ * @author Timm Fitschen <t.fitschen@indiscale.com>
  */
 public abstract class EntityStateJob extends EntityJob {
 
diff --git a/src/main/java/org/caosdb/server/jobs/core/InheritInitialState.java b/src/main/java/org/caosdb/server/jobs/core/InheritInitialState.java
index 39b66e35330a485bbd8eb4c40724ab3b250ccc45..b905bfa0fcbf98f107506d1fb1e7d2142b3b8657 100644
--- a/src/main/java/org/caosdb/server/jobs/core/InheritInitialState.java
+++ b/src/main/java/org/caosdb/server/jobs/core/InheritInitialState.java
@@ -1,32 +1,56 @@
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
 package org.caosdb.server.jobs.core;
 
 import java.util.List;
+import org.caosdb.server.entity.EntityInterface;
 import org.caosdb.server.entity.Message;
-import org.caosdb.server.entity.wrapper.Parent;
 import org.caosdb.server.entity.wrapper.Property;
 import org.caosdb.server.jobs.JobAnnotation;
 import org.caosdb.server.jobs.TransactionStage;
+import org.caosdb.server.utils.EntityStatus;
 
 @JobAnnotation(stage = TransactionStage.CHECK)
 public class InheritInitialState extends EntityStateJob {
 
   @Override
   protected void run() {
+    if (getEntity().getEntityStatus() != EntityStatus.QUALIFIED) {
+      return;
+    }
     try {
-      State parentState = getFirstParentState();
+      final State parentState = getFirstParentState();
       if (parentState != null) {
         getEntity().addMessage(parentState);
         parentState.getStateEntity();
         getEntity().setEntityACL(parentState.getStateACL());
       }
-    } catch (Message e) {
+    } catch (final Message e) {
       getEntity().addError(e);
     }
   }
 
   private State getFirstParentState() throws Message {
-    for (Parent par : getEntity().getParents()) {
-      List<Property> stateProperties = getStateProperties(par, false);
+    for (EntityInterface par : getEntity().getParents()) {
+      par = resolve(par);
+      final List<Property> stateProperties = getStateProperties(par, false);
       if (stateProperties.size() > 0) {
         return createState(stateProperties.get(0));
       }
diff --git a/src/main/java/org/caosdb/server/jobs/core/Inheritance.java b/src/main/java/org/caosdb/server/jobs/core/Inheritance.java
index 107c5768a5c15fbb05ea6d68b0cb956cbb235687..56ac72fab9f980581cf755c5018dad48ad841f96 100644
--- a/src/main/java/org/caosdb/server/jobs/core/Inheritance.java
+++ b/src/main/java/org/caosdb/server/jobs/core/Inheritance.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-2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2019-2021 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -40,7 +42,7 @@ import org.caosdb.server.utils.EntityStatus;
 /**
  * Add all those properties from the parent to the child which have the same importance (or higher).
  *
- * @author Timm Fitschen (t.fitschen@indiscale.com)
+ * @author Timm Fitschen <t.fitschen@indiscale.com>
  */
 public class Inheritance extends EntityJob {
 
@@ -65,7 +67,7 @@ public class Inheritance extends EntityJob {
   @Override
   protected void run() {
     if (getEntity() instanceof InsertEntity || getEntity() instanceof UpdateEntity) {
-      if (getEntity().hasParents()) {
+      if (getEntity().getEntityStatus() == EntityStatus.QUALIFIED && getEntity().hasParents()) {
         final ArrayList<Property> transfer = new ArrayList<>();
         parentLoop:
         for (final EntityInterface parent : getEntity().getParents()) {
@@ -108,7 +110,7 @@ public class Inheritance extends EntityJob {
             }
           }
           // prop's Datatype might need to be resolved.
-          ScheduledJob job = this.appendJob(prop, CheckDatatypePresent.class);
+          final ScheduledJob job = this.appendJob(prop, CheckDatatypePresent.class);
           getTransaction().getSchedule().runJob(job);
 
           getEntity().addProperty(new Property(prop.getWrapped()));
@@ -133,12 +135,13 @@ public class Inheritance extends EntityJob {
               break propertyLoop;
             }
 
-            runJobFromSchedule(getEntity(), CheckPropValid.class);
-
             EntityInterface validProperty = new Entity(property.getId());
             if (getEntity().hasParents()) {
               outer:
-              for (final EntityInterface par : getEntity().getParents()) {
+              for (EntityInterface par : getEntity().getParents()) {
+                if (!par.hasProperties()) {
+                  par = resolve(par);
+                }
                 for (final EntityInterface prop : par.getProperties()) {
                   if (validProperty.hasId() && validProperty.getId().equals(prop.getId())) {
                     validProperty = prop;
@@ -166,7 +169,7 @@ public class Inheritance extends EntityJob {
               }
             }
             // prop's Datatype might need to be resolved.
-            ScheduledJob job = this.appendJob(prop, CheckDatatypePresent.class);
+            final ScheduledJob job = this.appendJob(prop, CheckDatatypePresent.class);
             getTransaction().getSchedule().runJob(job);
 
             property.addProperty(new Property(prop.getWrapped()));
@@ -187,7 +190,9 @@ public class Inheritance extends EntityJob {
    * @param inheritance
    */
   private void collectInheritedProperties(
-      List<Property> transfer, EntityInterface from, INHERITANCE_MODE inheritance) {
+      final List<Property> transfer,
+      final EntityInterface from,
+      final INHERITANCE_MODE inheritance) {
     if (from.hasProperties()) {
       for (final Property propProperty : from.getProperties()) {
         switch (inheritance) {
diff --git a/src/main/java/org/caosdb/server/jobs/core/InitEntityStateJobs.java b/src/main/java/org/caosdb/server/jobs/core/InitEntityStateJobs.java
index d3a1fc636761298f1b0586a4036be09ced835021..5cd6d67aa7eca6a231eecf627606f272ac0f0775 100644
--- a/src/main/java/org/caosdb/server/jobs/core/InitEntityStateJobs.java
+++ b/src/main/java/org/caosdb/server/jobs/core/InitEntityStateJobs.java
@@ -1,5 +1,4 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
@@ -17,10 +16,7 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
-
 package org.caosdb.server.jobs.core;
 
 import java.util.ArrayList;
@@ -53,7 +49,7 @@ import org.caosdb.server.utils.Observer;
  * have a state anymore but the previous version had, because it has to be checked if the stateModel
  * allows to leave in this state.
  *
- * @author Timm Fitschen (t.fitschen@indiscale.com)
+ * @author Timm Fitschen <t.fitschen@indiscale.com>
  */
 @JobAnnotation(
     loadAlways = true,
diff --git a/src/main/java/org/caosdb/server/jobs/core/Mode.java b/src/main/java/org/caosdb/server/jobs/core/JobFailureSeverity.java
similarity index 51%
rename from src/main/java/org/caosdb/server/jobs/core/Mode.java
rename to src/main/java/org/caosdb/server/jobs/core/JobFailureSeverity.java
index 561a4b0c5fcb4e2c8c66a2b9f9a02ea91c44c119..e95bef34b7b9bea8e1b2b2126da196c1c7c53c12 100644
--- a/src/main/java/org/caosdb/server/jobs/core/Mode.java
+++ b/src/main/java/org/caosdb/server/jobs/core/JobFailureSeverity.java
@@ -4,6 +4,8 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2021 Indiscale GmbH <info@indiscale.com>
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -22,9 +24,22 @@
  */
 package org.caosdb.server.jobs.core;
 
-public enum Mode {
-  MUST,
-  SHOULD,
-  SHOULDNT,
-  MUSTNOT
+/**
+ * Describes the severity of a failure of a job (esp. consitency checks).
+ *
+ * <ul>
+ *   <li>{@link #ERROR} - a failure throws an error and the transaction will not succeed.
+ *   <li>{@link #WARN} - a failure produces a warning but the transaction may succeed anyway.
+ *   <li>{@link #IGNORE} - the job may fail silently. Jobs can also decide to not run at all if that
+ *       is not necessary in this case.
+ *       <p>Jobs where this doesn't make sense at all may also ignore the severity. It is rather a
+ *       convenient way to configure jobs where this makes sense than a definitive normative
+ *       property of a job.
+ *
+ * @author Timm Fitschen <t.fitschen@indiscale.com>
+ */
+public enum JobFailureSeverity {
+  ERROR,
+  WARN,
+  IGNORE,
 }
diff --git a/src/main/java/org/caosdb/server/jobs/core/LoadContainerFlagJobs.java b/src/main/java/org/caosdb/server/jobs/core/LoadContainerFlagJobs.java
index ef8df214c804fb1d55c3ed088c5f7f6e998aca07..c04fd21d588c9a9a2f6e59ce7c1fff445a225c67 100644
--- a/src/main/java/org/caosdb/server/jobs/core/LoadContainerFlagJobs.java
+++ b/src/main/java/org/caosdb/server/jobs/core/LoadContainerFlagJobs.java
@@ -36,7 +36,7 @@ public class LoadContainerFlagJobs extends ContainerJob {
   protected void run() {
     if (getContainer().getFlags() != null) {
       for (final Entry<String, String> flag : getContainer().getFlags().entrySet()) {
-        final Job j = Job.getJob(flag.getKey(), Mode.MUST, null, getTransaction());
+        final Job j = Job.getJob(flag.getKey(), JobFailureSeverity.ERROR, null, getTransaction());
         if (j != null) {
           if (j instanceof FlagJob) {
             ((FlagJob) j).setValue(flag.getValue());
diff --git a/src/main/java/org/caosdb/server/jobs/core/MakeStateMessage.java b/src/main/java/org/caosdb/server/jobs/core/MakeStateMessage.java
index 855d6a139aa80fa0b67cda652708b3e56ef5eb2b..d497a2ab5d955f0d6db0205b360a1a56ef90a4b7 100644
--- a/src/main/java/org/caosdb/server/jobs/core/MakeStateMessage.java
+++ b/src/main/java/org/caosdb/server/jobs/core/MakeStateMessage.java
@@ -1,5 +1,4 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
@@ -17,10 +16,7 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
-
 package org.caosdb.server.jobs.core;
 
 import java.util.List;
diff --git a/src/main/java/org/caosdb/server/jobs/core/UniqueName.java b/src/main/java/org/caosdb/server/jobs/core/UniqueName.java
index 300e32bf1662e4990f52bda0570a65c05e5d60b8..2836ccf742ebbd1bbda5a8ed3ae95ac0d69c2fbb 100644
--- a/src/main/java/org/caosdb/server/jobs/core/UniqueName.java
+++ b/src/main/java/org/caosdb/server/jobs/core/UniqueName.java
@@ -27,10 +27,11 @@ import org.caosdb.server.database.exceptions.EntityWasNotUniqueException;
 import org.caosdb.server.entity.EntityInterface;
 import org.caosdb.server.jobs.FlagJob;
 import org.caosdb.server.jobs.JobAnnotation;
+import org.caosdb.server.jobs.TransactionStage;
 import org.caosdb.server.utils.EntityStatus;
 import org.caosdb.server.utils.ServerMessages;
 
-@JobAnnotation(flag = "uniquename")
+@JobAnnotation(flag = "uniquename", stage = TransactionStage.PRE_CHECK)
 public class UniqueName extends FlagJob {
 
   private void doCheck(final EntityInterface entity) {
diff --git a/src/main/java/org/caosdb/server/permissions/AbstractEntityACLFactory.java b/src/main/java/org/caosdb/server/permissions/AbstractEntityACLFactory.java
index c8cd93e4a1d12125235819b4a2ba69fa12bd0256..d5d818fd42b6a9b65af07df8bab627be2aeb1551 100644
--- a/src/main/java/org/caosdb/server/permissions/AbstractEntityACLFactory.java
+++ b/src/main/java/org/caosdb/server/permissions/AbstractEntityACLFactory.java
@@ -1,9 +1,10 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019-2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2019-2021 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -17,8 +18,6 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
 package org.caosdb.server.permissions;
 
diff --git a/src/main/java/org/caosdb/server/query/CQLLexer.g4 b/src/main/java/org/caosdb/server/query/CQLLexer.g4
index 71b41d480bc171aa02af3b2e61eadd16345b3c6d..a091027c40abafe60b307367929f1736b62ac046 100644
--- a/src/main/java/org/caosdb/server/query/CQLLexer.g4
+++ b/src/main/java/org/caosdb/server/query/CQLLexer.g4
@@ -1,9 +1,10 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019-2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2019-2021 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -17,40 +18,38 @@
  *
  * 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
  */
 lexer grammar CQLLexer;
 
 AS_A:
-	[Aa][Ss] (WHITE_SPACE_f? A)? WHITE_SPACE_f?
+    [Aa][Ss] (WHITE_SPACE_f? A)? WHITE_SPACE_f?
 ;
 
 IS_REFERENCED:
-	(IS_f WHITE_SPACE_f?)? [Rr][Ee][Ff][Ee][Rr][Ee][Nn][Cc][Ee][Dd] WHITE_SPACE_f?
+    (IS_f WHITE_SPACE_f?)? [Rr][Ee][Ff][Ee][Rr][Ee][Nn][Cc][Ee][Dd] WHITE_SPACE_f?
 ;
 
 BY:
-	[Bb][Yy] WHITE_SPACE_f?
+    [Bb][Yy] WHITE_SPACE_f?
 ;
 
 fragment
 OF_f:
-	[Oo][Ff]
+    [Oo][Ff]
 ;
 
 fragment
 ANY_f:
-	[Aa][Nn][Yy]
+    [Aa][Nn][Yy]
 ;
 
 fragment
 VERSION_f:
-	[Vv][Ee][Rr][Ss][Ii][Oo][Nn]
+    [Vv][Ee][Rr][Ss][Ii][Oo][Nn]
 ;
 
 ANY_VERSION_OF:
-	(ANY_f WHITE_SPACE_f VERSION_f WHITE_SPACE_f OF_f) WHITE_SPACE_f?
+    (ANY_f WHITE_SPACE_f VERSION_f WHITE_SPACE_f OF_f) WHITE_SPACE_f?
 ;
 
 SELECT:
@@ -58,27 +57,27 @@ SELECT:
 ;
 
 INSERTED:
-	[Ii][Nn][Ss][Ee][Rr][Tt][Ee][Dd] WHITE_SPACE_f?
+    [Ii][Nn][Ss][Ee][Rr][Tt][Ee][Dd] WHITE_SPACE_f?
 ;
 
 CREATED:
-	[Cc][Rr][Ee][Aa][Tt][Ee][Dd] WHITE_SPACE_f?
+    [Cc][Rr][Ee][Aa][Tt][Ee][Dd] WHITE_SPACE_f?
 ;
 
 UPDATED:
-	[Uu][Pp][Dd][Aa][Tt][Ee][Dd] WHITE_SPACE_f?
+    [Uu][Pp][Dd][Aa][Tt][Ee][Dd] WHITE_SPACE_f?
 ;
 
 ON:
-	[Oo][Nn] WHITE_SPACE_f?
+    [Oo][Nn] WHITE_SPACE_f?
 ;
 
 IN:
-	[Ii][Nn] WHITE_SPACE_f?
+    [Ii][Nn] WHITE_SPACE_f?
 ;
 
 AFTER:
-	[Aa][Ff][Tt][Ee][Rr] WHITE_SPACE_f?
+    [Aa][Ff][Tt][Ee][Rr] WHITE_SPACE_f?
 ;
 
 BEFORE:
@@ -90,258 +89,258 @@ UNTIL:
 ;
 
 SINCE:
-	[Ss][Ii][Nn][Cc][Ee] WHITE_SPACE_f?
+    [Ss][Ii][Nn][Cc][Ee] WHITE_SPACE_f?
 ;
 
 IS_STORED_AT:
-	(IS_f WHITE_SPACE_f?)? [Ss][Tt][Oo][Rr][Ee][Dd] (WHITE_SPACE_f? AT)? WHITE_SPACE_f?
+    (IS_f WHITE_SPACE_f?)? [Ss][Tt][Oo][Rr][Ee][Dd] (WHITE_SPACE_f? AT)? WHITE_SPACE_f?
 ;
 
 AT:
-	[Aa][Tt] WHITE_SPACE_f?
+    [Aa][Tt] WHITE_SPACE_f?
 ;
 
 FIND:
-	[Ff][Ii][Nn][Dd] WHITE_SPACE_f?
+    [Ff][Ii][Nn][Dd] WHITE_SPACE_f?
 ;
 
 COUNT:
-	[Cc][Oo][Uu][Nn][Tt] WHITE_SPACE_f?
+    [Cc][Oo][Uu][Nn][Tt] WHITE_SPACE_f?
 ;
-	
+
 AND:
-	(
-		(
-			[Aa][Nn][Dd]
-		)
-		| '&'
-	) WHITE_SPACE_f?
+    (
+        (
+            [Aa][Nn][Dd]
+        )
+        | '&'
+    ) WHITE_SPACE_f?
 ;
 
 OR:
-	(
-		(
-			[Oo][Rr]
-		)
-		| '|'
-	) WHITE_SPACE_f?
+    (
+        (
+            [Oo][Rr]
+        )
+        | '|'
+    ) WHITE_SPACE_f?
 ;
 
 LPAREN:
-	'(' WHITE_SPACE_f?
+    '(' WHITE_SPACE_f?
 ;
 
 RPAREN:
-	')' WHITE_SPACE_f?
+    ')' WHITE_SPACE_f?
 ;
 
 SINGLE_QUOTE_START:
-	'\'' -> pushMode(SINGLE_QUOTE_MODE)
+    '\'' -> pushMode(SINGLE_QUOTE_MODE)
 ;
 
 DOUBLE_QUOTE_START:
-	'"' -> pushMode(DOUBLE_QUOTE_MODE)
+    '"' -> pushMode(DOUBLE_QUOTE_MODE)
 ;
 
 OPERATOR:
-	'='
-	| '<'
-	| '<='
-	| '>='
-	| '>'
-	| '!='
-	| '->'
-	| [Rr][Ee][Ff][Ee][Rr][Ee][Nn][Cc][Ee]([Ss]|WHITE_SPACE_f? [Tt][Oo]) (WHITE_SPACE_f? A {_input.LA(1) == ' '}?)? {setText("->");}
+    '='
+    | '<'
+    | '<='
+    | '>='
+    | '>'
+    | '!='
+    | '->'
+    | [Rr][Ee][Ff][Ee][Rr][Ee][Nn][Cc][Ee]([Ss]|WHITE_SPACE_f? [Tt][Oo]) (WHITE_SPACE_f? A {_input.LA(1) == ' '}?)? {setText("->");}
 ;
 
 LIKE:
-	[Ll][Ii][Kk][Ee] WHITE_SPACE_f?
+    [Ll][Ii][Kk][Ee] WHITE_SPACE_f?
 ;
 
 IS_NULL:
-	IS_f WHITE_SPACE_f NULL_f WHITE_SPACE_f?
+    IS_f WHITE_SPACE_f NULL_f WHITE_SPACE_f?
 ;
 
 IS_NOT_NULL:
-	IS_f WHITE_SPACE_f NOT_f WHITE_SPACE_f NULL_f WHITE_SPACE_f?
+    IS_f WHITE_SPACE_f NOT_f WHITE_SPACE_f NULL_f WHITE_SPACE_f?
 ;
 
 fragment
 NULL_f:
-	[Nn][Uu][Ll][Ll]
+    [Nn][Uu][Ll][Ll]
 ;
 
 fragment
 DOES_f:
-	[Dd][Oo][Ee][Ss]
+    [Dd][Oo][Ee][Ss]
 ;
 
 fragment
 NOT_f:
-	[Nn][Oo][Tt]
+    [Nn][Oo][Tt]
 ;
 
 fragment
 DOESNT_f:
-	DOES_f WHITE_SPACE_f? NOT_f
-	| DOES_f [Nn] SINGLE_QUOTE [Tt]
+    DOES_f WHITE_SPACE_f? NOT_f
+    | DOES_f [Nn] SINGLE_QUOTE [Tt]
 ;
 
 fragment
 ISNT_f:
-	IS_f [Nn] SINGLE_QUOTE [Tt]
+    IS_f [Nn] SINGLE_QUOTE [Tt]
 ;
 
 fragment
 WERE_f:
-	[Ww][Ee][Rr][Ee]
+    [Ww][Ee][Rr][Ee]
 ;
 
 fragment
 WERENT_f:
-	WERE_f [Nn] SINGLE_QUOTE [Tt]
+    WERE_f [Nn] SINGLE_QUOTE [Tt]
 ;
 
 fragment
 HAVENT_f:
-	HAVE_f [Nn] SINGLE_QUOTE [Tt]
+    HAVE_f [Nn] SINGLE_QUOTE [Tt]
 ;
 
 fragment
 HADNT_f:
-	HAD_f [Nn] SINGLE_QUOTE [Tt]
+    HAD_f [Nn] SINGLE_QUOTE [Tt]
 ;
 
 fragment
 HAD_f:
-	[Hh][Aa][Dd]
+    [Hh][Aa][Dd]
 ;
-	
+
 fragment
 HAVE_f:
-	[Hh][Aa][Vv][Ee]
+    [Hh][Aa][Vv][Ee]
 ;
 
 fragment
 HAS_f:
-	[Hh][Aa][Ss]
+    [Hh][Aa][Ss]
 ;
 
 fragment
 HASNT_f:
-	HAS_f [Nn] SINGLE_QUOTE [Tt]
+    HAS_f [Nn] SINGLE_QUOTE [Tt]
 ;
 
 fragment
 BEEN_f:
-	[Bb][Ee][Ee][Nn]
+    [Bb][Ee][Ee][Nn]
 ;
 
 fragment
 HAVE_A_f:
-	HAVE_f (WHITE_SPACE? A)? 
+    HAVE_f (WHITE_SPACE? A)?
 ;
 
 fragment
 DO_f:
-	[Dd][Oo]
+    [Dd][Oo]
 ;
 
 fragment
 DONT_f:
-	DO_f NOT_f
-	| DO_f [Nn] SINGLE_QUOTE [Tt]
+    DO_f NOT_f
+    | DO_f [Nn] SINGLE_QUOTE [Tt]
 ;
 
 fragment
 WAS_f:
-	[Ww][Aa][Ss]
+    [Ww][Aa][Ss]
 ;
 
 fragment
 WASNT_f:
-	WAS_f [Nn] SINGLE_QUOTE [Tt]
+    WAS_f [Nn] SINGLE_QUOTE [Tt]
 ;
 
 NEGATION:
-	(
-		'!'
-		| DOESNT_f (WHITE_SPACE? HAVE_A_f)?
-		| DONT_f (WHITE_SPACE? HAVE_A_f)?
-		| HASNT_f (WHITE_SPACE? BEEN_f)?
-		| ISNT_f (WHITE_SPACE? BEEN_f)?
-		| NOT_f (WHITE_SPACE? BEEN_f)?
-		| WERENT_f (WHITE_SPACE? BEEN_f)?
-		| WASNT_f (WHITE_SPACE? BEEN_f)?
-		| HAVENT_f (WHITE_SPACE? BEEN_f)?
-		| HADNT_f (WHITE_SPACE? BEEN_f)?
-	) WHITE_SPACE_f?
-;	
+    (
+        '!'
+        | DOESNT_f (WHITE_SPACE? HAVE_A_f)?
+        | DONT_f (WHITE_SPACE? HAVE_A_f)?
+        | HASNT_f (WHITE_SPACE? BEEN_f)?
+        | ISNT_f (WHITE_SPACE? BEEN_f)?
+        | NOT_f (WHITE_SPACE? BEEN_f)?
+        | WERENT_f (WHITE_SPACE? BEEN_f)?
+        | WASNT_f (WHITE_SPACE? BEEN_f)?
+        | HAVENT_f (WHITE_SPACE? BEEN_f)?
+        | HADNT_f (WHITE_SPACE? BEEN_f)?
+    ) WHITE_SPACE_f?
+;
 
 WITH:
-	[Ww][Ii][Tt][Hh] WHITE_SPACE_f?
+    [Ww][Ii][Tt][Hh] WHITE_SPACE_f?
 ;
 
-THE: 
-	[Tt][Hh][Ee] WHITE_SPACE_f?
+THE:
+    [Tt][Hh][Ee] WHITE_SPACE_f?
 ;
 
 GREATEST:
-	[Gg][Rr][Ee][Aa][Tt][Ee][Ss][Tt] WHITE_SPACE_f?
+    [Gg][Rr][Ee][Aa][Tt][Ee][Ss][Tt] WHITE_SPACE_f?
 ;
 
 SMALLEST:
-	[Ss][Mm][Aa][Ll][Ll][Ee][Ss][Tt] WHITE_SPACE_f?
+    [Ss][Mm][Aa][Ll][Ll][Ee][Ss][Tt] WHITE_SPACE_f?
 ;
 
 A:
-	[Aa][Nn]? WHITE_SPACE_f?
+    [Aa][Nn]? WHITE_SPACE_f?
 ;
 
 ME:
-	[Mm][Ee] WHITE_SPACE_f?
+    [Mm][Ee] WHITE_SPACE_f?
 ;
 
 SOMEONE:
-	[Ss][Oo][Mm][Ee][Oo][Nn][Ee] WHITE_SPACE_f?
+    [Ss][Oo][Mm][Ee][Oo][Nn][Ee] WHITE_SPACE_f?
 ;
 
 ELSE:
-	[Ee][Ll][Ss][Ee] WHITE_SPACE_f?
+    [Ee][Ll][Ss][Ee] WHITE_SPACE_f?
 ;
 
 WHERE:
-	[Ww][Hh][Ee][Rr][Ee] WHITE_SPACE_f?
+    [Ww][Hh][Ee][Rr][Ee] WHITE_SPACE_f?
 ;
 
 WHICH:
-	[Ww][Hh][Ii][Cc][Hh] WHITE_SPACE_f?
+    [Ww][Hh][Ii][Cc][Hh] WHITE_SPACE_f?
 ;
 
 HAS_A:
-	(
-		(HAS_f | HAD_f | HAVE_f | WERE_f | WAS_f | IS_f)
-		(
-			(WHITE_SPACE_f? A)
-			| (WHITE_SPACE_f? BEEN_f)
-		)?
-	) WHITE_SPACE_f?
+    (
+        (HAS_f | HAD_f | HAVE_f | WERE_f | WAS_f | IS_f)
+        (
+            (WHITE_SPACE_f? A)
+            | (WHITE_SPACE_f? BEEN_f)
+        )?
+    ) WHITE_SPACE_f?
 ;
 
 PROPERTY:
-	[Pp][Rr][Oo][Pp][Ee][Rr][Tt]([Yy]|[Ii][Ee][Ss]) WHITE_SPACE_f?
+    [Pp][Rr][Oo][Pp][Ee][Rr][Tt]([Yy]|[Ii][Ee][Ss]) WHITE_SPACE_f?
 ;
 
-RECORDTYPE:	
-	[Rr][Ee][Cc][Oo][Rr][Dd][Tt][Yy][Pp][Ee]([Ss])? WHITE_SPACE_f?
+RECORDTYPE:
+    [Rr][Ee][Cc][Oo][Rr][Dd][Tt][Yy][Pp][Ee]([Ss])? WHITE_SPACE_f?
 ;
 
 RECORD:
-	[Rr][Ee][Cc][Oo][Rr][Dd]([Ss])? WHITE_SPACE_f?
+    [Rr][Ee][Cc][Oo][Rr][Dd]([Ss])? WHITE_SPACE_f?
 ;
 
 FILE:
-	[Ff][Ii][Ll][Ee]([Ss])? WHITE_SPACE_f?
+    [Ff][Ii][Ll][Ee]([Ss])? WHITE_SPACE_f?
 ;
 
 ENTITY:
@@ -349,99 +348,99 @@ ENTITY:
 ;
 
 QUERYTEMPLATE:
-	[Qq][Uu][Ee][Rr][yY][Tt][Ee][Mm][Pp][Ll][Aa][Tt][Ee] WHITE_SPACE_f?
+    [Qq][Uu][Ee][Rr][yY][Tt][Ee][Mm][Pp][Ll][Aa][Tt][Ee] WHITE_SPACE_f?
 ;
 
 fragment
 IS_f:
-	[Ii][Ss]
+    [Ii][Ss]
 ;
 
 fragment
 WHITE_SPACE_f:
-	[ \t\n\r]+
+    [ \t\n\r]+
 ;
 
 WHITE_SPACE:
-	[ \t\n\r]+
+    [ \t\n\r]+
 ;
 
 fragment
 DOUBLE_QUOTE:
-	'"'
+    '"'
 ;
 
 fragment
 SINGLE_QUOTE:
-	'\''
+    '\''
 ;
 
 REGEXP_MARKER:
-	'#'
+    '#'
 ;
 
 REGEXP_BEGIN:
-	'<<'
+    '<<'
 ;
 
 REGEXP_END:
-	'>>'
+    '>>'
 ;
 
-ID: 
-	[Ii][Dd] WHITE_SPACE_f?
+ID:
+    [Ii][Dd] WHITE_SPACE_f?
 ;
 
 SLASH:
-	'/'
+    '/'
 ;
 
 STAR:
-	'*'
+    '*'
 ;
 
 DOT:
-	'.'
+    '.'
 ;
 
 QMARK:
-	'?' WHITE_SPACE_f?
+    '?' WHITE_SPACE_f?
 ;
 
 BUT:
-	[Bb][Uu][Tt] WHITE_SPACE_f?
+    [Bb][Uu][Tt] WHITE_SPACE_f?
 ;
 
 ESC_REGEXP_END:
-	ESC_MARKER
-	'>>' WHITE_SPACE_f?
+    ESC_MARKER
+    '>>' WHITE_SPACE_f?
 ;
 
 ESC_STAR:
-	ESC_MARKER
-	'*' WHITE_SPACE_f?
+    ESC_MARKER
+    '*' WHITE_SPACE_f?
 ;
 
 ESC_BS:
-	ESC_MARKER
-	'\\' WHITE_SPACE_f?
+    ESC_MARKER
+    '\\' WHITE_SPACE_f?
 ;
 
-fragment 
+fragment
 ESC_MARKER:
-	'\\'
+    '\\'
 ;
 
 TODAY:
-	[Tt][Oo][Dd][Aa][Yy] WHITE_SPACE_f?
+    [Tt][Oo][Dd][Aa][Yy] WHITE_SPACE_f?
 ;
 
 HYPHEN:
-	'-'
+    '-'
 ;
 
 COLON:
-	':'
+    ':'
 ;
 
 NUM:
@@ -451,53 +450,53 @@ NUM:
 ;
 
 NUM_f:
-	('0'..'9')+
+    ('0'..'9')+
 ;
 
 TXT:
-	('a'..'z' | 'A'..'Z' | '0'..'9' | '_' | '-' {_input.LA(1) != '>'}? | '+' | '&' | ';' | ',' | '$' | ':' | '%' | '^' | '~' {_input.LA(1) != '='}? | '`' | '´' | 'ö' | 'ä' | 'ß' | 'ü' | 'Ö' | 'Ä' | 'Ü' | '@' | '[' | ']' | '{' | '}' )+
+    ('a'..'z' | 'A'..'Z' | '0'..'9' | '_' | '-' {_input.LA(1) != '>'}? | '+' | '&' | ';' | ',' | '$' | ':' | '%' | '^' | '~' {_input.LA(1) != '='}? | '`' | '´' | 'ö' | 'ä' | 'ß' | 'ü' | 'Ö' | 'Ä' | 'Ü' | '@' | '[' | ']' | '{' | '}' )+
 ;
 
 UNKNOWN_CHAR: . ;
 
 mode SINGLE_QUOTE_MODE;
 
-	SINGLE_QUOTE_ESCAPED_CHAR:
-		ESC_MARKER
-		( '\'' | '\\' | '*' )
-	;
-
-	SINGLE_QUOTE_END:
-		'\'' -> mode(DEFAULT_MODE)
-	;
-	
-	SINGLE_QUOTE_STAR:
-		'*'
-	;
-
-	SINGLE_QUOTE_ANY_CHAR:
-		~('\''|'\\'|'*')+
-	;
-	
+    SINGLE_QUOTE_ESCAPED_CHAR:
+        ESC_MARKER
+        ( '\'' | '\\' | '*' )
+    ;
+
+    SINGLE_QUOTE_END:
+        '\'' -> mode(DEFAULT_MODE)
+    ;
+
+    SINGLE_QUOTE_STAR:
+        '*'
+    ;
+
+    SINGLE_QUOTE_ANY_CHAR:
+        ~('\''|'\\'|'*')+
+    ;
+
 mode DOUBLE_QUOTE_MODE;
 
-	DOUBLE_QUOTE_ESCAPED_CHAR:
-		ESC_MARKER
-		( '"' | '\\' | '*' )
-	;
+    DOUBLE_QUOTE_ESCAPED_CHAR:
+        ESC_MARKER
+        ( '"' | '\\' | '*' )
+    ;
+
+    DOUBLE_QUOTE_END:
+        '"' -> mode(DEFAULT_MODE)
+    ;
+
+    DOUBLE_QUOTE_STAR:
+        '*'
+    ;
 
-	DOUBLE_QUOTE_END:
-		'"' -> mode(DEFAULT_MODE)
-	;
-	
-	DOUBLE_QUOTE_STAR:
-		'*'
-	;
+    DOUBLE_QUOTE_ANY_CHAR:
+        ~('"'|'\\'|'*')+
+    ;
 
-	DOUBLE_QUOTE_ANY_CHAR:
-		~('"'|'\\'|'*')+
-	;
-	
 
 mode SELECT_MODE;
 
@@ -508,14 +507,11 @@ mode SELECT_MODE;
     SELECT_DOT:
         '.'
     ;
-    
+
     SELECT_COMMA:
         ','
     ;
-    
+
     SELECTOR_TXT:
-        . 
+        .
     ;
-    
-    
-    
diff --git a/src/main/java/org/caosdb/server/query/CQLParser.g4 b/src/main/java/org/caosdb/server/query/CQLParser.g4
index 6aea8c55f006cd9127d68f0c09e1654d9f9842a9..c4311ac1cbe03f490b79456e091cc0e73b94c506 100644
--- a/src/main/java/org/caosdb/server/query/CQLParser.g4
+++ b/src/main/java/org/caosdb/server/query/CQLParser.g4
@@ -1,10 +1,10 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
- * Copyright (C) 2020 Indiscale GmbH <info@indiscale.com>
+ * Copyright (C) 2019-2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2019-2021 Timm Fitschen <t.fitschen@indiscale.com>
  * Copyright (C) 2020 Florian Spreckelsen <f.spreckelsen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
@@ -19,8 +19,6 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
 parser grammar CQLParser;
 
@@ -32,38 +30,38 @@ options { tokenVocab = CQLLexer; }
 }
 
 cq returns [Query.Type t, List<Query.Selection> s, Query.Pattern e, Query.Role r, EntityFilterInterface filter, VersionFilter v]
-	@init{
-	    $s = null;
-		$e = null;
-		$r = null;
-		$v = VersionFilter.UNVERSIONED;
-		$filter = null;
-	}
+    @init{
+        $s = null;
+        $e = null;
+        $r = null;
+        $v = VersionFilter.UNVERSIONED;
+        $filter = null;
+    }
 :
 
-	(
-	   SELECT prop_sel {$s = $prop_sel.s;} FROM {$t = Query.Type.FIND;}
-	   | FIND {$t = Query.Type.FIND;}
-	   | COUNT {$t = Query.Type.COUNT;})
-	(version {$v = $version.v;})?
-	( role {$r = $role.r;} )?
-	(
-		entity_filter {$filter = $entity_filter.filter;}
-		|
-		entity WHITE_SPACE?{$e = $entity.ep;}
-		(
-			entity_filter {$filter = $entity_filter.filter;}
-		)?
-	)?
-	EOF
+    (
+       SELECT prop_sel {$s = $prop_sel.s;} FROM {$t = Query.Type.FIND;}
+       | FIND {$t = Query.Type.FIND;}
+       | COUNT {$t = Query.Type.COUNT;})
+    (version {$v = $version.v;})?
+    ( role {$r = $role.r;} )?
+    (
+        entity_filter {$filter = $entity_filter.filter;}
+        |
+        entity WHITE_SPACE?{$e = $entity.ep;}
+        (
+            entity_filter {$filter = $entity_filter.filter;}
+        )?
+    )?
+    EOF
 ;
 
 version returns [VersionFilter v]
-	@init{
-		$v = null;
-	}
+    @init{
+        $v = null;
+    }
 :
-	ANY_VERSION_OF {$v = VersionFilter.ANY_VERSION;}
+    ANY_VERSION_OF {$v = VersionFilter.ANY_VERSION;}
 ;
 
 prop_sel returns [List<Query.Selection> s]
@@ -83,110 +81,110 @@ selector_txt:
     SELECTOR_TXT+
 ;
 
-role returns [Query.Role r]: 
-	RECORDTYPE {$r = Query.Role.RECORDTYPE;}
-	| RECORD {$r = Query.Role.RECORD;}
-	| PROPERTY {$r = Query.Role.PROPERTY;}
-	| FILE {$r = Query.Role.FILE;}
-	| QUERYTEMPLATE {$r = Query.Role.QUERYTEMPLATE;}
-	| ENTITY {$r = Query.Role.ENTITY;}
+role returns [Query.Role r]:
+    RECORDTYPE {$r = Query.Role.RECORDTYPE;}
+    | RECORD {$r = Query.Role.RECORD;}
+    | PROPERTY {$r = Query.Role.PROPERTY;}
+    | FILE {$r = Query.Role.FILE;}
+    | QUERYTEMPLATE {$r = Query.Role.QUERYTEMPLATE;}
+    | ENTITY {$r = Query.Role.ENTITY;}
 ;
 
 entity_filter returns [EntityFilterInterface filter]
-	@init{
-		$filter = null;
-	}
+    @init{
+        $filter = null;
+    }
 :
-	which_exp
-	(
-		(
-			LPAREN WHITE_SPACE?
-			( 
-				filter_expression {$filter = $filter_expression.efi;}
-				| conjunction {$filter = $conjunction.c;} 
-				| disjunction {$filter = $disjunction.d;}
-			)
-			RPAREN
-		) | (
-			filter_expression {$filter = $filter_expression.efi;}
-			| conjunction {$filter = $conjunction.c;}
-			| disjunction {$filter = $disjunction.d;}
-		)
-	)?
+    which_exp
+    (
+        (
+            LPAREN WHITE_SPACE?
+            (
+                filter_expression {$filter = $filter_expression.efi;}
+                | conjunction {$filter = $conjunction.c;}
+                | disjunction {$filter = $disjunction.d;}
+            )
+            RPAREN
+        ) | (
+            filter_expression {$filter = $filter_expression.efi;}
+            | conjunction {$filter = $conjunction.c;}
+            | disjunction {$filter = $disjunction.d;}
+        )
+    )?
 ;
 
 which_exp:
-	WHICH (HAS_A (PROPERTY)?)?
-	| HAS_A (PROPERTY)?
-	| WITH (A (PROPERTY)?)?
-	| WHERE
-	| DOT WHITE_SPACE?
+    WHICH (HAS_A (PROPERTY)?)?
+    | HAS_A (PROPERTY)?
+    | WITH (A (PROPERTY)?)?
+    | WHERE
+    | DOT WHITE_SPACE?
 ;
 
 filter_expression returns [EntityFilterInterface efi]
 :
-	backreference (subproperty {((Backreference) $backreference.ref).setSubProperty($subproperty.subp);})? {$efi = $backreference.ref;}
-	| idfilter {$efi=$idfilter.filter;}
-	| storedat {$efi=$storedat.filter;}
-	| transaction {$efi=$transaction.filter;}
-	| ( pov (subproperty {((POV) $pov.filter).setSubProperty($subproperty.subp);})? {$efi = $pov.filter;} )
-	| subproperty {$efi=$subproperty.subp;}
-	| negation {$efi=$negation.n;}
+    backreference (subproperty {((Backreference) $backreference.ref).setSubProperty($subproperty.subp);})? {$efi = $backreference.ref;}
+    | idfilter {$efi=$idfilter.filter;}
+    | storedat {$efi=$storedat.filter;}
+    | transaction {$efi=$transaction.filter;}
+    | ( pov (subproperty {((POV) $pov.filter).setSubProperty($subproperty.subp);})? {$efi = $pov.filter;} )
+    | subproperty {$efi=$subproperty.subp;}
+    | negation {$efi=$negation.n;}
 ;
 
 idfilter returns [IDFilter filter] locals [String o, String v, String a]
 @init{
-	$a = null;
-	$o = null;
-	$v = null;
-} 
+    $a = null;
+    $o = null;
+    $v = null;
+}
 @after{
-	$filter = new IDFilter($o,$v,$a);
+    $filter = new IDFilter($o,$v,$a);
 }
 :
-	(minmax {$a=$minmax.agg;})??
-	ID 
-	(
-		OPERATOR {$o = $OPERATOR.text;}
-		WHITE_SPACE?
-		value {$v = $value.str;}
-	)?
+    (minmax {$a=$minmax.agg;})??
+    ID
+    (
+        OPERATOR {$o = $OPERATOR.text;}
+        WHITE_SPACE?
+        value {$v = $value.str;}
+    )?
 ;
 
 transaction returns [TransactionFilter filter] locals [String type, TransactionFilter.Transactor user, String time, String time_op]
 @init{
-	$time = null;
-	$user = null;
-	$type = null;
-	$time_op = null;
+    $time = null;
+    $user = null;
+    $type = null;
+    $time_op = null;
 }
 @after{
-	$filter = new TransactionFilter($type,$user,$time,$time_op);
+    $filter = new TransactionFilter($type,$user,$time,$time_op);
 }
 :
-	(
-		( INSERTED | CREATED ) {$type = TransactionFilter.INSERTION;}
-		| ( UPDATED ) {$type = TransactionFilter.UPDATE;}
-	)
-	
-	(
-		transactor (transaction_time {$time = $transaction_time.tqp; $time_op = $transaction_time.op;})? {$user = $transactor.t;}
-		| transaction_time (transactor {$user = $transactor.t;})? {$time = $transaction_time.tqp; $time_op = $transaction_time.op;}
-	)
+    (
+        ( INSERTED | CREATED ) {$type = TransactionFilter.INSERTION;}
+        | ( UPDATED ) {$type = TransactionFilter.UPDATE;}
+    )
+
+    (
+        transactor (transaction_time {$time = $transaction_time.tqp; $time_op = $transaction_time.op;})? {$user = $transactor.t;}
+        | transaction_time (transactor {$user = $transactor.t;})? {$time = $transaction_time.tqp; $time_op = $transaction_time.op;}
+    )
 ;
 
 transactor returns [TransactionFilter.Transactor t]
 :
-	BY 
-	( 
-		SOMEONE ELSE BUT ME {$t = TransactionFilter.neq_currentTransactor();}
-		| SOMEONE ELSE BUT entity {$t = TransactionFilter.neq_foreignTransactor($entity.ep);}
-		| SOMEONE ELSE BUT username {$t = TransactionFilter.neq_foreignTransactor($username.ep);}
-		| SOMEONE ELSE {$t = TransactionFilter.neq_currentTransactor();}
-		| ME {$t = TransactionFilter.eq_currentTransactor();}
-		| entity {$t = TransactionFilter.eq_foreignTransactor($entity.ep);}
-		| username {$t = TransactionFilter.eq_foreignTransactor($username.ep);}
-	)
+    BY
+    (
+        SOMEONE ELSE BUT ME {$t = TransactionFilter.neq_currentTransactor();}
+        | SOMEONE ELSE BUT entity {$t = TransactionFilter.neq_foreignTransactor($entity.ep);}
+        | SOMEONE ELSE BUT username {$t = TransactionFilter.neq_foreignTransactor($username.ep);}
+        | SOMEONE ELSE {$t = TransactionFilter.neq_currentTransactor();}
+        | ME {$t = TransactionFilter.eq_currentTransactor();}
+        | entity {$t = TransactionFilter.eq_foreignTransactor($entity.ep);}
+        | username {$t = TransactionFilter.eq_foreignTransactor($username.ep);}
+    )
 ;
 
 username returns [Query.Pattern ep] locals [int type]
@@ -205,14 +203,14 @@ transaction_time returns [String tqp, String op]
      $op = "(";
 }
 :
-	(
-	    AT  {$op = "=";}
+    (
+        AT  {$op = "=";}
         | (ON | IN)
         | (
-        	BEFORE {$op = "<";}
-          	| UNTIL {$op = "<=";}
-          	| AFTER {$op = ">";}
-          	| SINCE {$op = ">=";}
+            BEFORE {$op = "<";}
+              | UNTIL {$op = "<=";}
+              | AFTER {$op = ">";}
+              | SINCE {$op = ">=";}
         )
     )?
     (
@@ -222,283 +220,283 @@ transaction_time returns [String tqp, String op]
 ;
 
 /*
-* not fully compliant with iso 8601 (TODO) 
+* not fully compliant with iso 8601 (TODO)
 */
 datetime
 :
-	NUM // year 
-	(
-		HYPHEN NUM // mon
-		( 
-			HYPHEN NUM // day of mon
-			(
-				(m=TXT {$m.text.equals("T")}?)?// compliance with iso datetime
-				NUM // hour
-				(
-					COLON NUM // minut 
-					(
-						COLON NUM // sec 
-						(
-							DOT NUM // millisec
-						)?
-					)?
-				)?
-			)?
-		)?
-	)?	
+    NUM // year
+    (
+        HYPHEN NUM // mon
+        (
+            HYPHEN NUM // day of mon
+            (
+                (m=TXT {$m.text.equals("T")}?)?// compliance with iso datetime
+                NUM // hour
+                (
+                    COLON NUM // minut
+                    (
+                        COLON NUM // sec
+                        (
+                            DOT NUM // millisec
+                        )?
+                    )?
+                )?
+            )?
+        )?
+    )?
 ;
 
 pov returns [POV filter] locals [Query.Pattern p, String o, String v, String a]
-	@init{
-		$p = null;
-		$o = null;
-		$v = null;
-		$a = null;
-	}
-	@after{
-		$filter = new POV($p,$o,$v,$a);
-	}
+    @init{
+        $p = null;
+        $o = null;
+        $v = null;
+        $a = null;
+    }
+    @after{
+        $filter = new POV($p,$o,$v,$a);
+    }
 :
-	(
-		property {$p = $property.pp; $a=$property.agg;} 
-		(
-			( 
-			  LIKE {$o = $LIKE.text.trim();}
-			  ( like_pattern {$v = $like_pattern.ep.toString();}
-			    | value {$v = $value.str;} )
-			  | OPERATOR {$o = $OPERATOR.text;} WHITE_SPACE? value {$v = $value.str;}
-			)
-			| IS_NULL {$o = "0";}
-			| IS_NOT_NULL {$o = "!0";}
-			| IN value {$o = "("; $v=$value.str;}
-			| NEGATION IN value {$o = "!("; $v=$value.str;}
-		)?
-	) 
-	| 
-	(
-		( LIKE {$o = $LIKE.text;}
-		  ( like_pattern {$v = $like_pattern.ep.toString();}
-		    | value {$v = $value.str;} )
-		)
-		| ( OPERATOR {$o = $OPERATOR.text;} WHITE_SPACE? value {$v = $value.str;}
-		  ( AS_A
-			property {$p = $property.pp;} )?
-		)
-	)
-	WHITE_SPACE?
+    (
+        property {$p = $property.pp; $a=$property.agg;}
+        (
+            (
+              LIKE {$o = $LIKE.text.trim();}
+              ( like_pattern {$v = $like_pattern.ep.toString();}
+                | value {$v = $value.str;} )
+              | OPERATOR {$o = $OPERATOR.text;} WHITE_SPACE? value {$v = $value.str;}
+            )
+            | IS_NULL {$o = "0";}
+            | IS_NOT_NULL {$o = "!0";}
+            | IN value {$o = "("; $v=$value.str;}
+            | NEGATION IN value {$o = "!("; $v=$value.str;}
+        )?
+    )
+    |
+    (
+        ( LIKE {$o = $LIKE.text;}
+          ( like_pattern {$v = $like_pattern.ep.toString();}
+            | value {$v = $value.str;} )
+        )
+        | ( OPERATOR {$o = $OPERATOR.text;} WHITE_SPACE? value {$v = $value.str;}
+          ( AS_A
+            property {$p = $property.pp;} )?
+        )
+    )
+    WHITE_SPACE?
 ;
 
 
 subproperty returns [SubProperty subp] locals [String p]
 @init{
-	$p = null;
-	$subp = null;
+    $p = null;
+    $subp = null;
 }
 :
-	entity_filter {$subp = new SubProperty($entity_filter.filter);}
+    entity_filter {$subp = new SubProperty($entity_filter.filter);}
 ;
 
 backreference returns [Backreference ref] locals [Query.Pattern e, Query.Pattern p]
-	@init{
-		$e = null;
-		$p = null;
-	}
-	@after{
-		$ref = new Backreference($e, $p); 
-	}
+    @init{
+        $e = null;
+        $p = null;
+    }
+    @after{
+        $ref = new Backreference($e, $p);
+    }
 :
-	IS_REFERENCED
-	(BY A? entity {$e=$entity.ep;})?
-	( 
-		WHITE_SPACE?
-		AS_A
-		property {$p=$property.pp;}
-	)?
-	WHITE_SPACE?
+    IS_REFERENCED
+    (BY A? entity {$e=$entity.ep;})?
+    (
+        WHITE_SPACE?
+        AS_A
+        property {$p=$property.pp;}
+    )?
+    WHITE_SPACE?
 ;
 
 storedat returns [StoredAt filter] locals [String loc]
-	@init{
-		$loc = null;
-	}
-	@after{
-		$filter = new StoredAt($loc);
-	}
+    @init{
+        $loc = null;
+    }
+    @after{
+        $filter = new StoredAt($loc);
+    }
 :
-	IS_STORED_AT
-	location {$loc = $location.str;}
-	WHITE_SPACE?
+    IS_STORED_AT
+    location {$loc = $location.str;}
+    WHITE_SPACE?
 ;
 
 conjunction returns [Conjunction c] locals [Conjunction dummy]
-	@init{
-		$c = new Conjunction();
-	}
+    @init{
+        $c = new Conjunction();
+    }
 :
-	(
-		f1 = filter_expression {$c.add($f1.efi);}
-		|
-		LPAREN WHITE_SPACE?
-		(
-			f4 = filter_expression {$c.add($f4.efi);}
-			| disjunction {$c.add($disjunction.d);}
-			| c3=conjunction {$c.addAll($c3.c);}
-		)
-		RPAREN
-	) 
-	( 
-		WHITE_SPACE?
-		AND
-		(
-			( which_exp | A (PROPERTY)?? )
-		)? 
-		( 
-			f2 = filter_expression {$c.add($f2.efi);}
-			| (
-				LPAREN WHITE_SPACE?
-				( 
-					f3 = filter_expression {$c.add($f3.efi);}
-					| disjunction {$c.add($disjunction.d);}
-					| c2=conjunction {$c.addAll($c2.c);}
-				)
-				RPAREN
-			)
-		)
-	)+
+    (
+        f1 = filter_expression {$c.add($f1.efi);}
+        |
+        LPAREN WHITE_SPACE?
+        (
+            f4 = filter_expression {$c.add($f4.efi);}
+            | disjunction {$c.add($disjunction.d);}
+            | c3=conjunction {$c.addAll($c3.c);}
+        )
+        RPAREN
+    )
+    (
+        WHITE_SPACE?
+        AND
+        (
+            ( which_exp | A (PROPERTY)?? )
+        )?
+        (
+            f2 = filter_expression {$c.add($f2.efi);}
+            | (
+                LPAREN WHITE_SPACE?
+                (
+                    f3 = filter_expression {$c.add($f3.efi);}
+                    | disjunction {$c.add($disjunction.d);}
+                    | c2=conjunction {$c.addAll($c2.c);}
+                )
+                RPAREN
+            )
+        )
+    )+
 ;
 
 disjunction returns [Disjunction d]
-	@init{
-		$d = new Disjunction();
-	}
+    @init{
+        $d = new Disjunction();
+    }
 :
-	(
-		f1 = filter_expression {$d.add($f1.efi);}
-		|
-		LPAREN WHITE_SPACE?
-		(
-			f4 = filter_expression {$d.add($f4.efi);}
-			| conjunction {$d.add($conjunction.c);}
-			| d3 = disjunction {$d.addAll($d3.d);} 
-		)
-		RPAREN
-	)
-	(
-		OR
-		(
-			( which_exp | A (PROPERTY)? )
-		)?
-		(
-			f2 = filter_expression {$d.add($f2.efi);}
-			| (
-				LPAREN WHITE_SPACE?
-				( 
-					f3 = filter_expression {$d.add($f3.efi);}
-					| conjunction {$d.add($conjunction.c);}
-					| d2 = disjunction {$d.addAll($d2.d);} 
-				)
-				RPAREN
-			)
-		)
-	)+
+    (
+        f1 = filter_expression {$d.add($f1.efi);}
+        |
+        LPAREN WHITE_SPACE?
+        (
+            f4 = filter_expression {$d.add($f4.efi);}
+            | conjunction {$d.add($conjunction.c);}
+            | d3 = disjunction {$d.addAll($d3.d);}
+        )
+        RPAREN
+    )
+    (
+        OR
+        (
+            ( which_exp | A (PROPERTY)? )
+        )?
+        (
+            f2 = filter_expression {$d.add($f2.efi);}
+            | (
+                LPAREN WHITE_SPACE?
+                (
+                    f3 = filter_expression {$d.add($f3.efi);}
+                    | conjunction {$d.add($conjunction.c);}
+                    | d2 = disjunction {$d.addAll($d2.d);}
+                )
+                RPAREN
+            )
+        )
+    )+
 ;
 
 negation returns [Negation n]
-	@init{
-	}
-:	
-	NEGATION
-	(
-		f1 = filter_expression {$n = new Negation($f1.efi);}
-		| ( 
-			LPAREN WHITE_SPACE?
-			(	
-				f2 = filter_expression {$n = new Negation($f2.efi);}
-				| disjunction {$n = new Negation($disjunction.d);}
-				| conjunction {$n = new Negation($conjunction.c);}
-			)
-			RPAREN
-		)
-	)
+    @init{
+    }
+:
+    NEGATION
+    (
+        f1 = filter_expression {$n = new Negation($f1.efi);}
+        | (
+            LPAREN WHITE_SPACE?
+            (
+                f2 = filter_expression {$n = new Negation($f2.efi);}
+                | disjunction {$n = new Negation($disjunction.d);}
+                | conjunction {$n = new Negation($conjunction.c);}
+            )
+            RPAREN
+        )
+    )
 ;
 
 entity returns [Query.Pattern ep]
-: 
-	regexp_pattern {$ep = $regexp_pattern.ep;}
-	| like_pattern {$ep = $like_pattern.ep;}
-	| ( double_quoted {$ep = $double_quoted.ep;} )
-	| ( single_quoted {$ep = $single_quoted.ep;} )
-	| ENTITY {$ep = new Query.Pattern((String) $text.trim(), Query.Pattern.TYPE_NORMAL);}
-	| ( ~(ENTITY |WHITE_SPACE | DOT) )+ {$ep = new Query.Pattern((String) $text.trim(), Query.Pattern.TYPE_NORMAL);}
+:
+    regexp_pattern {$ep = $regexp_pattern.ep;}
+    | like_pattern {$ep = $like_pattern.ep;}
+    | ( double_quoted {$ep = $double_quoted.ep;} )
+    | ( single_quoted {$ep = $single_quoted.ep;} )
+    | ENTITY {$ep = new Query.Pattern((String) $text.trim(), Query.Pattern.TYPE_NORMAL);}
+    | ( ~(ENTITY |WHITE_SPACE | DOT) )+ {$ep = new Query.Pattern((String) $text.trim(), Query.Pattern.TYPE_NORMAL);}
 ;
 
 regexp_pattern returns [Query.Pattern ep] locals [StringBuffer sb]
-	@init{
-		$sb = new StringBuffer();
-	}
+    @init{
+        $sb = new StringBuffer();
+    }
 :
-	REGEXP_BEGIN  
-	(ESC_REGEXP_END {$sb.append(">>");} |m=. {$sb.append($m.text);})*? 
-	REGEXP_END {$ep = new Query.Pattern((String) $sb.toString(), Query.Pattern.TYPE_REGEXP);}
+    REGEXP_BEGIN
+    (ESC_REGEXP_END {$sb.append(">>");} |m=. {$sb.append($m.text);})*?
+    REGEXP_END {$ep = new Query.Pattern((String) $sb.toString(), Query.Pattern.TYPE_REGEXP);}
 ;
 
 like_pattern returns [Query.Pattern ep] locals [StringBuffer sb]
-	@init{
-		$sb = new StringBuffer();
-	}
+    @init{
+        $sb = new StringBuffer();
+    }
 :
-	~(WHITE_SPACE|DOT|LIKE|OPERATOR|AS_A|AND|OR|IS_STORED_AT|IS_REFERENCED)*?
-	STAR
-	~(WHITE_SPACE|DOT|STAR)*?
-	{$ep = new Query.Pattern((String) $text, Query.Pattern.TYPE_LIKE);}
+    ~(WHITE_SPACE|DOT|LIKE|OPERATOR|AS_A|AND|OR|IS_STORED_AT|IS_REFERENCED)*?
+    STAR
+    ~(WHITE_SPACE|DOT|STAR)*?
+    {$ep = new Query.Pattern((String) $text, Query.Pattern.TYPE_LIKE);}
 ;
 
 property returns [Query.Pattern pp, String agg]locals [StringBuffer sb]
-	@init{
-		$sb = new StringBuffer();
-		$agg = null;
-	}
+    @init{
+        $sb = new StringBuffer();
+        $agg = null;
+    }
 :
-	(minmax {$agg=$minmax.agg;})??
-	(
+    (minmax {$agg=$minmax.agg;})??
+    (
         regexp_pattern {$pp = $regexp_pattern.ep;}
         | like_pattern {$pp = $like_pattern.ep;}
         | ( double_quoted {$pp = $double_quoted.ep;} )
         | ( single_quoted {$pp = $single_quoted.ep;} )
         | ((m=TXT | m=NUM | m=REGEXP_MARKER | m=ENTITY){$sb.append($m.text);})+  {$pp = new Query.Pattern($sb.toString(), Query.Pattern.TYPE_NORMAL);}
-	)
-	WHITE_SPACE?
+    )
+    WHITE_SPACE?
 ;
 
 minmax returns [String agg]
 :
-	(THE?? (
-		GREATEST {$agg="max";} 
-		| SMALLEST {$agg="min";} 
-	))
+    (THE?? (
+        GREATEST {$agg="max";}
+        | SMALLEST {$agg="min";}
+    ))
 ;
 
 value returns [String str]
-: 
-	number_with_unit {$str = $number_with_unit.text;}
-	| datetime {$str = $datetime.text;}
-	| atom {$str = $atom.ep.toString();}
-	WHITE_SPACE?
+:
+    number_with_unit {$str = $number_with_unit.text;}
+    | datetime {$str = $datetime.text;}
+    | atom {$str = $atom.ep.toString();}
+    WHITE_SPACE?
 ;
 
 number_with_unit
 :
-	HYPHEN??
-	NUM
-	(WHITE_SPACE?? unit)?
+    HYPHEN??
+    NUM
+    (WHITE_SPACE?? unit)?
 ;
 
 unit
 :
-	(~(WHITE_SPACE | WHICH | HAS_A | WITH | WHERE | DOT | AND | OR ))
-	(~(WHITE_SPACE))*
-	|
-	NUM SLASH (~(WHITE_SPACE))+
+    (~(WHITE_SPACE | WHICH | HAS_A | WITH | WHERE | DOT | AND | OR ))
+    (~(WHITE_SPACE))*
+    |
+    NUM SLASH (~(WHITE_SPACE))+
 ;
 
 location returns [String str]
@@ -511,46 +509,46 @@ location returns [String str]
 atom returns [Query.Pattern ep]
 :
     double_quoted {$ep = $double_quoted.ep;}
-	| single_quoted {$ep = $single_quoted.ep;}
-	| (~(WHITE_SPACE | DOT ))+  {$ep = new Query.Pattern($text, Query.Pattern.TYPE_NORMAL);}
+    | single_quoted {$ep = $single_quoted.ep;}
+    | (~(WHITE_SPACE | DOT ))+  {$ep = new Query.Pattern($text, Query.Pattern.TYPE_NORMAL);}
 ;
 
 single_quoted returns [Query.Pattern ep] locals [StringBuffer sb, int patternType]
-	@init{
-		$sb = new StringBuffer();
-		$patternType = Query.Pattern.TYPE_NORMAL;
-	}
-	@after{
-		$ep = new Query.Pattern($sb.toString(),$patternType);
-	}
+    @init{
+        $sb = new StringBuffer();
+        $patternType = Query.Pattern.TYPE_NORMAL;
+    }
+    @after{
+        $ep = new Query.Pattern($sb.toString(),$patternType);
+    }
 :
-	SINGLE_QUOTE_START
-	(
-		t = SINGLE_QUOTE_ESCAPED_CHAR {$sb.append($t.text.substring(1,$t.text.length()));}
-	|
-		r = SINGLE_QUOTE_STAR {$sb.append($r.text); $patternType = Query.Pattern.TYPE_LIKE;}
-	|
-		s = ~SINGLE_QUOTE_END {$sb.append($s.text);}
-	)*?
-	SINGLE_QUOTE_END
+    SINGLE_QUOTE_START
+    (
+        t = SINGLE_QUOTE_ESCAPED_CHAR {$sb.append($t.text.substring(1,$t.text.length()));}
+    |
+        r = SINGLE_QUOTE_STAR {$sb.append($r.text); $patternType = Query.Pattern.TYPE_LIKE;}
+    |
+        s = ~SINGLE_QUOTE_END {$sb.append($s.text);}
+    )*?
+    SINGLE_QUOTE_END
 ;
 
 double_quoted returns [Query.Pattern ep] locals [StringBuffer sb, int patternType]
-	@init{
-		$sb = new StringBuffer();
-		$patternType = Query.Pattern.TYPE_NORMAL;
-	}
-	@after{
-		$ep = new Query.Pattern($sb.toString(),$patternType);
-	}
+    @init{
+        $sb = new StringBuffer();
+        $patternType = Query.Pattern.TYPE_NORMAL;
+    }
+    @after{
+        $ep = new Query.Pattern($sb.toString(),$patternType);
+    }
 :
-	DOUBLE_QUOTE_START
-	(
-		t = DOUBLE_QUOTE_ESCAPED_CHAR {$sb.append($t.text.substring(1,$t.text.length()));}
-	|
-		r = DOUBLE_QUOTE_STAR {$sb.append($r.text); $patternType = Query.Pattern.TYPE_LIKE;}
-	|
-		s = ~DOUBLE_QUOTE_END {$sb.append($s.text);}
-	)*?
-	DOUBLE_QUOTE_END
+    DOUBLE_QUOTE_START
+    (
+        t = DOUBLE_QUOTE_ESCAPED_CHAR {$sb.append($t.text.substring(1,$t.text.length()));}
+    |
+        r = DOUBLE_QUOTE_STAR {$sb.append($r.text); $patternType = Query.Pattern.TYPE_LIKE;}
+    |
+        s = ~DOUBLE_QUOTE_END {$sb.append($s.text);}
+    )*?
+    DOUBLE_QUOTE_END
 ;
diff --git a/src/main/java/org/caosdb/server/query/Query.java b/src/main/java/org/caosdb/server/query/Query.java
index 058fc619e021b96e1ac3d83750d7ed84c5acc2c0..dca63841df3ee1a5efa4148d24e3e8eb68cd2f50 100644
--- a/src/main/java/org/caosdb/server/query/Query.java
+++ b/src/main/java/org/caosdb/server/query/Query.java
@@ -1,9 +1,10 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019-2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2019-2021 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -17,8 +18,6 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
 package org.caosdb.server.query;
 
diff --git a/src/main/java/org/caosdb/server/resource/transaction/EntityResource.java b/src/main/java/org/caosdb/server/resource/transaction/EntityResource.java
index 704fcfb9aa92e56ad963365d763f70fa7feb470e..7003dfc1d9657def48da4295a189e72730ef9fa8 100644
--- a/src/main/java/org/caosdb/server/resource/transaction/EntityResource.java
+++ b/src/main/java/org/caosdb/server/resource/transaction/EntityResource.java
@@ -1,5 +1,4 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
@@ -19,8 +18,6 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
 package org.caosdb.server.resource.transaction;
 
diff --git a/src/main/java/org/caosdb/server/resource/transaction/RetrieveEntityResource.java b/src/main/java/org/caosdb/server/resource/transaction/RetrieveEntityResource.java
index 5eb9db72f69f8f054dbb6f02b970d09e09ff366c..578eca4c855ed067aae7a9f2e18463808558b94d 100644
--- a/src/main/java/org/caosdb/server/resource/transaction/RetrieveEntityResource.java
+++ b/src/main/java/org/caosdb/server/resource/transaction/RetrieveEntityResource.java
@@ -1,5 +1,4 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
@@ -19,8 +18,6 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
 package org.caosdb.server.resource.transaction;
 
diff --git a/src/main/java/org/caosdb/server/transaction/Retrieve.java b/src/main/java/org/caosdb/server/transaction/Retrieve.java
index 19f840e92a1a6812e2ae2610ed3ace23b7e3cee7..250df0423869ae97a766da2fc54131e04d5257ca 100644
--- a/src/main/java/org/caosdb/server/transaction/Retrieve.java
+++ b/src/main/java/org/caosdb/server/transaction/Retrieve.java
@@ -31,7 +31,7 @@ import org.caosdb.server.entity.xml.SetFieldStrategy;
 import org.caosdb.server.entity.xml.ToElementStrategy;
 import org.caosdb.server.entity.xml.ToElementable;
 import org.caosdb.server.jobs.ScheduledJob;
-import org.caosdb.server.jobs.core.Mode;
+import org.caosdb.server.jobs.core.JobFailureSeverity;
 import org.caosdb.server.jobs.core.RemoveDuplicates;
 import org.caosdb.server.jobs.core.ResolveNames;
 import org.caosdb.server.permissions.EntityPermission;
@@ -53,14 +53,14 @@ public class Retrieve extends Transaction<RetrieveContainer> {
     // resolve names
     {
       final ResolveNames r = new ResolveNames();
-      r.init(Mode.SHOULD, null, this);
+      r.init(JobFailureSeverity.WARN, null, this);
       ScheduledJob scheduledJob = getSchedule().add(r);
       getSchedule().runJob(scheduledJob);
     }
 
     {
       final RemoveDuplicates job = new RemoveDuplicates();
-      job.init(Mode.MUST, null, this);
+      job.init(JobFailureSeverity.ERROR, null, this);
       ScheduledJob scheduledJob = getSchedule().add(job);
       getSchedule().runJob(scheduledJob);
     }
diff --git a/src/main/java/org/caosdb/server/transaction/Transaction.java b/src/main/java/org/caosdb/server/transaction/Transaction.java
index 99c305fa17b37878cc5f3277c93623a915be4ea3..fe0b63f333e0151aed161ce3fa3914794e5f3d52 100644
--- a/src/main/java/org/caosdb/server/transaction/Transaction.java
+++ b/src/main/java/org/caosdb/server/transaction/Transaction.java
@@ -1,11 +1,10 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
- * Copyright (C) 2020 Indiscale GmbH <info@indiscale.com>
- * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2019-2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2019-2021 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -19,8 +18,6 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
 package org.caosdb.server.transaction;
 
@@ -45,7 +42,7 @@ import org.caosdb.server.jobs.TransactionStage;
 import org.caosdb.server.jobs.core.AccessControl;
 import org.caosdb.server.jobs.core.CheckDatatypePresent;
 import org.caosdb.server.jobs.core.CheckEntityACLRoles;
-import org.caosdb.server.jobs.core.Mode;
+import org.caosdb.server.jobs.core.JobFailureSeverity;
 import org.caosdb.server.jobs.core.PickUp;
 import org.caosdb.server.permissions.EntityACL;
 import org.caosdb.server.utils.AbstractObservable;
@@ -96,13 +93,17 @@ public abstract class Transaction<C extends TransactionContainer> extends Abstra
    */
   protected void makeSchedule() throws Exception {
     // load flag jobs
-    final Job loadContainerFlags = Job.getJob("LoadContainerFlagJobs", Mode.MUST, null, this);
+    final Job loadContainerFlags =
+        Job.getJob("LoadContainerFlagJobs", JobFailureSeverity.ERROR, null, this);
     final ScheduledJob scheduledJob = this.schedule.add(loadContainerFlags);
     this.schedule.runJob(scheduledJob);
 
     // AccessControl
-    this.schedule.add(Job.getJob(AccessControl.class.getSimpleName(), Mode.MUST, null, this));
-    this.schedule.add(Job.getJob(CheckEntityACLRoles.class.getSimpleName(), Mode.MUST, null, this));
+    this.schedule.add(
+        Job.getJob(AccessControl.class.getSimpleName(), JobFailureSeverity.ERROR, null, this));
+    this.schedule.add(
+        Job.getJob(
+            CheckEntityACLRoles.class.getSimpleName(), JobFailureSeverity.ERROR, null, this));
 
     // load permanent container jobs
     this.schedule.addAll(Job.loadPermanentContainerJobs(this));
@@ -113,12 +114,12 @@ public abstract class Transaction<C extends TransactionContainer> extends Abstra
 
       // additionally load datatype job
       if (e.hasValue()) {
-        this.schedule.add(new CheckDatatypePresent().init(Mode.MUST, e, this));
+        this.schedule.add(new CheckDatatypePresent().init(JobFailureSeverity.ERROR, e, this));
       }
 
       // load pickup job if necessary
       if (e.hasFileProperties() && e.getFileProperties().isPickupable()) {
-        this.schedule.add(new PickUp().init(Mode.MUST, e, this));
+        this.schedule.add(new PickUp().init(JobFailureSeverity.ERROR, e, this));
       }
     }
   }
diff --git a/src/main/java/org/caosdb/server/transaction/WriteTransaction.java b/src/main/java/org/caosdb/server/transaction/WriteTransaction.java
index 670ff8bd6f4e78c6f20500dc057fc0e432872255..62a9beea1921f34dd3646bcc78ed9086d05e6098 100644
--- a/src/main/java/org/caosdb/server/transaction/WriteTransaction.java
+++ b/src/main/java/org/caosdb/server/transaction/WriteTransaction.java
@@ -1,11 +1,10 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
- * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
- * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2019-2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2019-2021 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -19,8 +18,6 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
 package org.caosdb.server.transaction;
 
diff --git a/src/main/java/org/caosdb/server/utils/ConfigurationException.java b/src/main/java/org/caosdb/server/utils/ConfigurationException.java
index 517c11677c73d9624b93df18fb5196e2f4578024..df04ac169f3c8113443a69795840a50774bef0b1 100644
--- a/src/main/java/org/caosdb/server/utils/ConfigurationException.java
+++ b/src/main/java/org/caosdb/server/utils/ConfigurationException.java
@@ -26,7 +26,11 @@ public class ConfigurationException extends RuntimeException {
 
   private static final long serialVersionUID = -8973694831724941007L;
 
-  public ConfigurationException(String reason) {
+  public ConfigurationException(final String reason) {
     super(reason);
   }
+
+  public ConfigurationException(final String reason, final Throwable cause) {
+    super(reason, cause);
+  }
 }
diff --git a/src/test/java/org/caosdb/server/database/DatabaseAccessManagerTest.java b/src/test/java/org/caosdb/server/database/DatabaseAccessManagerTest.java
index 0ba085b1fd050578b50e443613f82167890ea61f..1e8f6cc707299f5b0102ad774d127630dee51c08 100644
--- a/src/test/java/org/caosdb/server/database/DatabaseAccessManagerTest.java
+++ b/src/test/java/org/caosdb/server/database/DatabaseAccessManagerTest.java
@@ -1,9 +1,10 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019-2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2019-2021 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -17,8 +18,6 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
 package org.caosdb.server.database;
 
diff --git a/src/test/java/org/caosdb/server/database/backend/transaction/BackendTransactionTest.java b/src/test/java/org/caosdb/server/database/backend/transaction/BackendTransactionTest.java
deleted file mode 100644
index 93402bf0d06c487451d9219b4487f301eb855f49..0000000000000000000000000000000000000000
--- a/src/test/java/org/caosdb/server/database/backend/transaction/BackendTransactionTest.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.caosdb.server.database.backend.transaction;
-
-import static org.junit.Assert.assertEquals;
-
-import org.caosdb.server.database.BackendTransaction;
-import org.caosdb.server.entity.DeleteEntity;
-import org.caosdb.server.entity.InsertEntity;
-import org.caosdb.server.entity.RetrieveEntity;
-import org.caosdb.server.entity.Role;
-import org.caosdb.server.entity.UpdateEntity;
-import org.jdom2.Element;
-import org.junit.Test;
-
-public class BackendTransactionTest {
-
-  @Test
-  public void testGetTransactionType() {
-    BackendTransaction transaction = new InsertTransactionHistory(null, null, null, null);
-    assertEquals("Retrieve", transaction.getTransactionType(new RetrieveEntity("test")));
-    assertEquals("Insert", transaction.getTransactionType(new InsertEntity("test", Role.Record)));
-    assertEquals("Delete", transaction.getTransactionType(new DeleteEntity(1234)));
-    assertEquals("Update", transaction.getTransactionType(new UpdateEntity(new Element("Record"))));
-  }
-}
diff --git a/src/test/java/org/caosdb/server/jobs/JobConfigTest.java b/src/test/java/org/caosdb/server/jobs/JobConfigTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..70917b53553c6b94b283ed1e39b0ee81db9d466c
--- /dev/null
+++ b/src/test/java/org/caosdb/server/jobs/JobConfigTest.java
@@ -0,0 +1,31 @@
+package org.caosdb.server.jobs;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import org.caosdb.server.CaosDBServer;
+import org.caosdb.server.entity.DeleteEntity;
+import org.caosdb.server.entity.InsertEntity;
+import org.caosdb.server.entity.RetrieveEntity;
+import org.caosdb.server.entity.Role;
+import org.caosdb.server.entity.UpdateEntity;
+import org.jdom2.Element;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class JobConfigTest {
+
+  @BeforeClass
+  public static void setup() throws IOException {
+    CaosDBServer.initServerProperties();
+  }
+
+  @Test
+  public void testGetTransactionType() {
+    final JobConfig jobConfig = JobConfig.getInstance();
+    assertEquals("Retrieve", jobConfig.getTransactionType(new RetrieveEntity("test")));
+    assertEquals("Insert", jobConfig.getTransactionType(new InsertEntity("test", Role.Record)));
+    assertEquals("Delete", jobConfig.getTransactionType(new DeleteEntity(1234)));
+    assertEquals("Update", jobConfig.getTransactionType(new UpdateEntity(new Element("Record"))));
+  }
+}
diff --git a/src/test/java/org/caosdb/server/jobs/ScheduleTest.java b/src/test/java/org/caosdb/server/jobs/ScheduleTest.java
index cef0ec3e076f38893ada35bbad881ccfe8a02331..9aa8e6f66788911f2c4314c44549d543277398d2 100644
--- a/src/test/java/org/caosdb/server/jobs/ScheduleTest.java
+++ b/src/test/java/org/caosdb/server/jobs/ScheduleTest.java
@@ -24,10 +24,18 @@ package org.caosdb.server.jobs;
 
 import static org.junit.Assert.fail;
 
+import java.io.IOException;
+import org.caosdb.server.CaosDBServer;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 public class ScheduleTest {
 
+  @BeforeClass
+  public static void setup() throws IOException {
+    CaosDBServer.initServerProperties();
+  }
+
   /** The problem of ticket #297 was that a CHECK job was executed during the ROLL_BACK stage. */
   @Test
   public void testTicket297() {
diff --git a/src/test/java/org/caosdb/server/jobs/core/TestInsertFilesInDir.java b/src/test/java/org/caosdb/server/jobs/core/TestInsertFilesInDir.java
index ae5df853ba5d29e681cd0fb0e5ec8eee6ec3462c..5d81ac368f65c559eb666f003f34da522f191f21 100644
--- a/src/test/java/org/caosdb/server/jobs/core/TestInsertFilesInDir.java
+++ b/src/test/java/org/caosdb/server/jobs/core/TestInsertFilesInDir.java
@@ -4,16 +4,23 @@ import static org.junit.Assert.assertTrue;
 
 import java.io.File;
 import java.io.IOException;
+import org.caosdb.server.CaosDBServer;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 public class TestInsertFilesInDir {
 
+  @BeforeClass
+  public static void setup() throws IOException {
+    CaosDBServer.initServerProperties();
+  }
+
   @Test
   public void testExclude() throws IOException {
-    InsertFilesInDir job = new InsertFilesInDir();
+    final InsertFilesInDir job = new InsertFilesInDir();
     job.init(null, null, null);
     job.parseValue("-e ^.*test.*$ test");
-    File testFile = new File("test.dat");
+    final File testFile = new File("test.dat");
     assertTrue(job.isExcluded(testFile));
   }
 }
diff --git a/src/test/java/org/caosdb/server/query/QueryTest.java b/src/test/java/org/caosdb/server/query/QueryTest.java
index 26e43ec6c65414808c72b9e04b95c1d67011fc9a..8f3d4946c36d795e5e7bd9048a66f86cc0290a0b 100644
--- a/src/test/java/org/caosdb/server/query/QueryTest.java
+++ b/src/test/java/org/caosdb/server/query/QueryTest.java
@@ -1,3 +1,22 @@
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
 package org.caosdb.server.query;
 
 import static org.junit.Assert.assertEquals;
diff --git a/src/test/java/org/caosdb/server/query/TestCQL.java b/src/test/java/org/caosdb/server/query/TestCQL.java
index 154977a909a9d34ac2034c1ef352b81e7b2a3cb7..cf54bf71b69ea5f539744bd4bbe30fd1c37edcf4 100644
--- a/src/test/java/org/caosdb/server/query/TestCQL.java
+++ b/src/test/java/org/caosdb/server/query/TestCQL.java
@@ -1,9 +1,10 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2019-2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2019-2021 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -17,8 +18,6 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
 package org.caosdb.server.query;