diff --git a/src/main/java/caosdb/server/CaosDBServer.java b/src/main/java/caosdb/server/CaosDBServer.java
index 2586c2df7a388061a0986a4ff3a40f94b8657f1b..c68ae31c1fbfe8487bce082e545222ec9647b188 100644
--- a/src/main/java/caosdb/server/CaosDBServer.java
+++ b/src/main/java/caosdb/server/CaosDBServer.java
@@ -49,12 +49,11 @@ import caosdb.server.resource.ScriptingResource;
 import caosdb.server.resource.ServerLogsResource;
 import caosdb.server.resource.ServerPropertiesResource;
 import caosdb.server.resource.SharedFileResource;
-import caosdb.server.resource.TestCaseFileSystemResource;
-import caosdb.server.resource.TestCaseResource;
 import caosdb.server.resource.ThumbnailsResource;
 import caosdb.server.resource.UserResource;
 import caosdb.server.resource.UserRolesResource;
 import caosdb.server.resource.Webinterface;
+import caosdb.server.resource.WebinterfaceBuildNumber;
 import caosdb.server.resource.transaction.EntityResource;
 import caosdb.server.terminal.CaosDBTerminal;
 import caosdb.server.terminal.StatsPanel;
@@ -581,39 +580,8 @@ public class CaosDBServer extends Application {
           }
         };
 
-    // -- Section only for debug mode --
-    if (isDebugMode()) {
-      baseRouter.attach("/TestCase/", DefaultResource.class);
-
-      final Variable pathVariable =
-          baseRouter
-              .attach(
-                  "/TestCase/FileSystem/{path}",
-                  baseRouter.createFinder(TestCaseFileSystemResource.class))
-              .getTemplate()
-              .getDefaultVariable();
-      pathVariable.setRequired(false);
-      pathVariable.setType(Variable.TYPE_URI_PATH);
-      pathVariable.setDefaultValue("");
-
-      baseRouter
-          .attach("/TestCase/Thumbnails/{path}", ThumbnailsResource.class)
-          .getTemplate()
-          .getDefaultVariable()
-          .setType(Variable.TYPE_URI_PATH);
-
-      baseRouter
-          .attach("/TestCase/webinterface/{path}", Webinterface.class)
-          .getTemplate()
-          .getDefaultVariable()
-          .setType(Variable.TYPE_URI_PATH);
-      baseRouter.attach("/TestCase/Entity", TestCaseResource.class);
-      baseRouter.attach("/TestCase/Entity/", TestCaseResource.class);
-      baseRouter.attach("/TestCase/Entity/{specifier}", TestCaseResource.class);
-    }
-    // -- End of debug section --
-
     // These routes can be used without logging in:
+    baseRouter.attach("/webinterface/version/build", WebinterfaceBuildNumber.class);
     baseRouter
         .attach("/webinterface/{path}", Webinterface.class)
         .getTemplate()
diff --git a/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java b/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java
index 948b4eab2fda1c906f4acf2802c0d83184b875bc..ea4e65f0a60d72cb5da6cb03b2cec44848dbc3c6 100644
--- a/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java
+++ b/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java
@@ -33,6 +33,7 @@ import caosdb.server.accessControl.UserSources;
 import caosdb.server.database.backend.implementation.MySQL.ConnectionException;
 import caosdb.server.entity.Message;
 import caosdb.server.utils.ServerMessages;
+import caosdb.server.utils.WebinterfaceUtils;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.UnsupportedEncodingException;
@@ -55,7 +56,6 @@ import org.jdom2.JDOMException;
 import org.jdom2.input.SAXBuilder;
 import org.restlet.data.Form;
 import org.restlet.data.Header;
-import org.restlet.data.MediaType;
 import org.restlet.data.Parameter;
 import org.restlet.data.Status;
 import org.restlet.representation.Representation;
@@ -81,7 +81,12 @@ public abstract class AbstractCaosDBServerResource extends ServerResource {
   private String[] requestedItems = null;
   private ArrayList<Integer> requestedIDs = new ArrayList<Integer>();
   private ArrayList<String> requestedNames = new ArrayList<String>();
-  private String xslScript = "webcaosdb.xsl";
+  private WebinterfaceUtils utils;
+
+  /** Return the {@link WebinterfaceUtils} instance for this resource. */
+  public WebinterfaceUtils getUtils() {
+    return this.utils;
+  }
 
   public static class xmlNotWellFormedException extends Exception {
     private static final long serialVersionUID = -6836378704013776849L;
@@ -116,6 +121,8 @@ public abstract class AbstractCaosDBServerResource extends ServerResource {
       getRequest().setEntity(r);
     }
 
+    this.utils = WebinterfaceUtils.getInstance(getHostRef());
+
     this.timestamp = getRequest().getDate().getTime();
 
     this.sRID = getRequest().getAttributes().get("SRID").toString();
@@ -325,11 +332,7 @@ public abstract class AbstractCaosDBServerResource extends ServerResource {
   }
 
   protected String getXSLScript() {
-    return this.xslScript;
-  }
-
-  protected void setXSLScript(final String s) {
-    this.xslScript = s;
+    return this.utils.getWebinterfaceURI("webcaosdb.xsl");
   }
 
   /** Wrap an element for an "OK" response. */
@@ -337,18 +340,28 @@ public abstract class AbstractCaosDBServerResource extends ServerResource {
     return ok(new Document(root));
   }
 
+  /**
+   * Return a JDomRepresentation of the document and leave the {@link Response}'s {@link Status}
+   * where it is - which is usually 200 - OK when this method happens to be called.
+   */
   protected JdomRepresentation ok(final Document doc) {
-    return new JdomRepresentation(doc, MediaType.TEXT_XML, "  ", getReference(), getXSLScript());
+    return new JdomRepresentation(doc, "  ", getXSLScript());
   }
 
+  /**
+   * Return a Representation containing the error message and set the {@link Response}'s {@link
+   * Status}.
+   *
+   * @param m - the error message.
+   * @param status - the response status
+   * @return A Representation of the error.
+   */
   protected Representation error(final Message m, final Status status) {
     final Document doc = new Document();
     final Element root = generateRootElement();
     root.addContent(m.toElement().setName("Error"));
     doc.setRootElement(root);
-    return error(
-        new JdomRepresentation(doc, MediaType.TEXT_XML, "  ", getReference(), getXSLScript()),
-        status);
+    return error(new JdomRepresentation(doc, "  ", getXSLScript()), status);
   }
 
   protected Representation error(Representation entity, Status status) {
@@ -364,12 +377,19 @@ public abstract class AbstractCaosDBServerResource extends ServerResource {
     return error((Representation) null, status);
   }
 
+  /**
+   * Return a Representation containing the warning message but leave the {@link Response}'s {@link
+   * Status} where it is - which is usually 200 - OK when this method happens to be called.
+   *
+   * @param m - the warning message.
+   * @return A Representation of the warning.
+   */
   protected JdomRepresentation warning(final Message m) {
     final Document doc = new Document();
     final Element root = generateRootElement();
     root.addContent(m.toElement().setName("Warning"));
     doc.setRootElement(root);
-    return new JdomRepresentation(doc, MediaType.TEXT_XML, "  ", getReference(), getXSLScript());
+    return new JdomRepresentation(doc, "  ", getXSLScript());
   }
 
   protected Representation noWellFormedNess() {
diff --git a/src/main/java/caosdb/server/resource/JdomRepresentation.java b/src/main/java/caosdb/server/resource/JdomRepresentation.java
index 2443b95c20b3f659d6a2f9e88c0dd9fabb563088..04fbdac3fdd13f9e5c4f025cb2364386deac71f0 100644
--- a/src/main/java/caosdb/server/resource/JdomRepresentation.java
+++ b/src/main/java/caosdb/server/resource/JdomRepresentation.java
@@ -56,30 +56,21 @@ public class JdomRepresentation extends WriterRepresentation {
 
   private final Document document;
   private final String indent;
-  private final Reference reference;
-  private final String xslPath;
 
   /**
    * Constructor.
    *
-   * @param mediaType This should be TEXT_XML, but we'll leave it open to use others.
    * @param document A JDOM Document parsed from elsewhere.
-   * @param indent To make that representation better to read by a human.
-   * @param reference
+   * @param indent Usually a number of whitespaces for better human readbility.
    * @param xslPath A String containing a meaningful path to a xslt file.
    */
-  public JdomRepresentation(
-      final Document document,
-      final MediaType mediaType,
-      final String indent,
-      final Reference reference,
-      final String xslPath) {
-    super(mediaType);
-    this.xslPath = xslPath;
-    this.reference = reference;
+  public JdomRepresentation(final Document document, final String indent, final String xslPath) {
+    super(MediaType.TEXT_XML);
     this.indent = indent;
     this.document = document;
-    addStyleSheet();
+    if (xslPath != null && document != null) {
+      addStyleSheet(document, xslPath);
+    }
   }
 
   public static final String getWebUIRef(final Reference reference) {
@@ -89,14 +80,10 @@ public class JdomRepresentation extends WriterRepresentation {
   }
 
   /** adds the xslt processing instruction to the document. */
-  protected final void addStyleSheet() {
-    if (this.document != null) {
-      final ProcessingInstruction pi =
-          new ProcessingInstruction(
-              "xml-stylesheet",
-              "type=\"text/xsl\" href=\"" + getWebUIRef(this.reference) + this.xslPath + "\" ");
-      this.document.getContent().add(0, pi);
-    }
+  protected final void addStyleSheet(Document document, String xslPath) {
+    final ProcessingInstruction pi =
+        new ProcessingInstruction("xml-stylesheet", "type=\"text/xsl\" href=\"" + xslPath + "\" ");
+    document.getContent().add(0, pi);
   }
 
   @Override
diff --git a/src/main/java/caosdb/server/resource/ScriptingResource.java b/src/main/java/caosdb/server/resource/ScriptingResource.java
index 57ada4aa7b4ed2a588e7de6e50c85de1b7fb6670..b23a9593918ead75a6b38216838995e09ac67bc7 100644
--- a/src/main/java/caosdb/server/resource/ScriptingResource.java
+++ b/src/main/java/caosdb/server/resource/ScriptingResource.java
@@ -214,6 +214,7 @@ public class ScriptingResource extends AbstractCaosDBServerResource {
   public int callScript(
       List<String> commandLine, Integer timeoutMs, List<FileProperties> files, Object authToken)
       throws Message {
+    // TODO getUser().checkPermission("SCRIPTING:EXECUTE:" + commandLine.get(0));
     caller =
         new ServerSideScriptingCaller(
             commandLine.toArray(new String[commandLine.size()]), timeoutMs, files, authToken);
diff --git a/src/main/java/caosdb/server/resource/ServerPropertiesResource.java b/src/main/java/caosdb/server/resource/ServerPropertiesResource.java
index 9b878e3338a9d0b0c74704acb412ac92ceb1f0c4..347919d9c7d0f604734790f2ea09b5e2bac54042 100644
--- a/src/main/java/caosdb/server/resource/ServerPropertiesResource.java
+++ b/src/main/java/caosdb/server/resource/ServerPropertiesResource.java
@@ -13,7 +13,11 @@ public class ServerPropertiesResource extends AbstractCaosDBServerResource {
   @Override
   protected void doInit() {
     super.doInit();
-    setXSLScript("xsl/server_properties.xsl");
+  }
+
+  @Override
+  protected String getXSLScript() {
+    return getUtils().getWebinterfaceURI("xsl/server_properties.xsl");
   }
 
   @Override
diff --git a/src/main/java/caosdb/server/resource/TestCaseFileSystemResource.java b/src/main/java/caosdb/server/resource/TestCaseFileSystemResource.java
deleted file mode 100644
index 903ef0d5aca9a91eaeaff3693b7f77ef51e6cc46..0000000000000000000000000000000000000000
--- a/src/main/java/caosdb/server/resource/TestCaseFileSystemResource.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * ** header v3.0
- * This file is a part of the CaosDB Project.
- *
- * Copyright (C) 2018 Research Group Biomedical Physics,
- * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
- */
-package caosdb.server.resource;
-
-import caosdb.server.accessControl.Principal;
-import caosdb.server.entity.FileProperties;
-import caosdb.server.utils.FileUtils;
-import java.io.File;
-import org.jdom2.Element;
-
-public class TestCaseFileSystemResource extends FileSystemResource {
-
-  @Override
-  protected void doInit() {
-    super.doInit();
-    setXSLScript("webcaosdb.xsl");
-  }
-
-  private static final String BASEPATH = "./testfiles/";
-
-  @Override
-  protected File getFile(final String path) throws Exception {
-    final File f = new File(BASEPATH + path);
-    return (f != null && f.exists() ? f : null);
-  }
-
-  @Override
-  protected String getEntityID(final String path) throws Exception {
-    return "123412341234";
-  }
-
-  @Override
-  protected Element generateRootElement() {
-    final Element retRoot = new Element("Response");
-
-    if (getUser() != null && getUser().isAuthenticated()) {
-      retRoot.setAttribute("username", ((Principal) getUser().getPrincipal()).getUsername());
-      retRoot.setAttribute("realm", ((Principal) getUser().getPrincipal()).getRealm());
-    }
-    retRoot.setAttribute("srid", getSRID());
-    if (this.getCRID() != null) {
-      retRoot.setAttribute("crid", this.getCRID());
-    }
-    retRoot.setAttribute("timestamp", getTimestamp().toString());
-    retRoot.setAttribute("baseuri", getRootRef().toString() + "/TestCase/");
-    return retRoot;
-  }
-
-  public static FileProperties getFileProperties(final String path) {
-    final File f = new File(BASEPATH + path);
-    return new FileProperties(FileUtils.getChecksum(f), path, f.length());
-  }
-}
diff --git a/src/main/java/caosdb/server/resource/TestCaseResource.java b/src/main/java/caosdb/server/resource/TestCaseResource.java
deleted file mode 100644
index e8aef1f00e57335185b7b204c8de081570e6e188..0000000000000000000000000000000000000000
--- a/src/main/java/caosdb/server/resource/TestCaseResource.java
+++ /dev/null
@@ -1,481 +0,0 @@
-/*
- * ** header v3.0
- * This file is a part of the CaosDB Project.
- *
- * Copyright (C) 2018 Research Group Biomedical Physics,
- * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
- */
-package caosdb.server.resource;
-
-import caosdb.datetime.DateTimeFactory2;
-import caosdb.datetime.UTCDateTime;
-import caosdb.server.CaosDBException;
-import caosdb.server.database.backend.implementation.MySQL.ConnectionException;
-import caosdb.server.datatype.AbstractCollectionDatatype;
-import caosdb.server.datatype.AbstractDatatype;
-import caosdb.server.datatype.BooleanDatatype;
-import caosdb.server.datatype.BooleanValue;
-import caosdb.server.datatype.CollectionValue;
-import caosdb.server.datatype.DateTimeDatatype;
-import caosdb.server.datatype.DoubleDatatype;
-import caosdb.server.datatype.GenericValue;
-import caosdb.server.datatype.IntegerDatatype;
-import caosdb.server.datatype.ListDatatype;
-import caosdb.server.datatype.ReferenceDatatype2;
-import caosdb.server.datatype.ReferenceValue;
-import caosdb.server.datatype.TextDatatype;
-import caosdb.server.datatype.Value;
-import caosdb.server.entity.Entity;
-import caosdb.server.entity.EntityInterface;
-import caosdb.server.entity.MagicTypes;
-import caosdb.server.entity.Message;
-import caosdb.server.entity.Message.MessageType;
-import caosdb.server.entity.RetrieveEntity;
-import caosdb.server.entity.Role;
-import caosdb.server.entity.StatementStatus;
-import caosdb.server.entity.container.PropertyContainer;
-import caosdb.server.entity.wrapper.Parent;
-import caosdb.server.entity.wrapper.Property;
-import caosdb.server.entity.xml.ToElementable;
-import caosdb.server.query.Query;
-import caosdb.server.query.Query.ParsingException;
-import caosdb.server.utils.ServerMessages;
-import caosdb.server.utils.TransactionLogMessage;
-import java.io.IOException;
-import java.security.NoSuchAlgorithmException;
-import java.sql.SQLException;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.GregorianCalendar;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import org.jdom2.Document;
-import org.jdom2.Element;
-import org.restlet.representation.Representation;
-
-public class TestCaseResource extends AbstractCaosDBServerResource {
-
-  @Override
-  protected void doInit() {
-    super.doInit();
-    setXSLScript("webcaosdb.xsl");
-  }
-
-  @Override
-  protected Element generateRootElement() {
-    final Element retRoot = new Element("Response");
-
-    retRoot.setAttribute("username", "TestUser");
-    retRoot.setAttribute("realm", "PAM");
-    retRoot.setAttribute("srid", getSRID());
-    if (this.getCRID() != null) {
-      retRoot.setAttribute("crid", this.getCRID());
-    }
-    retRoot.setAttribute("timestamp", getTimestamp().toString());
-    retRoot.setAttribute("baseuri", getRootRef().toString() + "/TestCase/");
-    return retRoot;
-  }
-
-  @Override
-  protected synchronized Representation httpGetInChildClass()
-      throws ConnectionException, IOException, SQLException, CaosDBException,
-          NoSuchAlgorithmException, Exception {
-
-    final Element rootElem = generateRootElement();
-    final Document doc = new Document();
-
-    final Collection<ToElementable> selectedTestCases = getSelectedTestCase();
-    rootElem.setAttribute("count", Integer.toString(selectedTestCases.size()));
-
-    int from = 0;
-    int len = Integer.MAX_VALUE;
-    if (getFlags().containsKey("query")) {
-      final String queryStr = getFlags().get("query");
-      if (queryStr != null && !queryStr.isEmpty()) {
-        final Query query = new Query(queryStr);
-        try {
-          query.parse();
-          query.addToElement(rootElem);
-        } catch (final ParsingException e) {
-          query.addToElement(rootElem);
-          rootElem.addContent(ServerMessages.QUERY_PARSING_ERROR.toElement());
-          doc.setRootElement(rootElem);
-          return ok(doc);
-        }
-      }
-    }
-    if (getFlags().containsKey("P")) {
-      final String[] split = getFlags().get("P").split("L");
-      from = Integer.valueOf(split[0]);
-      len = Integer.valueOf(split[1]);
-    }
-    int i = 0;
-    for (final ToElementable e : selectedTestCases) {
-      if (i++ >= from && i <= from + len) {
-        e.addToElement(rootElem);
-      }
-    }
-    // testEntities = null;
-    // entityCounter = 1;
-
-    doc.setRootElement(rootElem);
-    return ok(doc);
-  }
-
-  private Collection<ToElementable> getSelectedTestCase() {
-    final HashMap<String, ToElementable> testCases = getTestCase();
-    if (getFlags().containsKey("query")) {
-      final String queryStr = getFlags().get("query");
-      if (queryStr.startsWith("FIND Annotation WHICH REFERENCES")) {
-        final LinkedList<ToElementable> ret = new LinkedList<ToElementable>();
-        for (final ToElementable e : testCases.values()) {
-          if (e instanceof Entity
-              && ((Entity) e).hasParents()
-              && ((Entity) e).getParents().get(0).getName().equals("Annotation")) {
-            ret.add(e);
-          }
-        }
-        return ret;
-      }
-    }
-    if (getFlags().containsKey("all")) {
-      final LinkedList<ToElementable> ret = new LinkedList<ToElementable>();
-      for (final ToElementable e : testCases.values()) {
-        if (e instanceof Entity) {
-          if (getFlags().get("all").equalsIgnoreCase("Entity")) {
-            ret.add(e);
-          } else {
-            final Entity entity = (Entity) e;
-            if (entity.getRole().toString().equalsIgnoreCase(getFlags().get("all"))) {
-              ret.add(e);
-            }
-          }
-        }
-      }
-      return ret;
-    }
-    if (!getRequestedIDs().isEmpty() || !getRequestedNames().isEmpty()) {
-      final LinkedList<ToElementable> ret = new LinkedList<ToElementable>();
-      for (final Integer id : getRequestedIDs()) {
-        for (final ToElementable e : testCases.values()) {
-          if (e instanceof Entity) {
-            if (((Entity) e).getId().equals(id)) {
-              ret.add(e);
-            }
-          }
-        }
-      }
-      for (final String name : getRequestedNames()) {
-        for (final ToElementable e : testCases.values()) {
-          if (e instanceof Entity) {
-            if (((Entity) e).getName().equals(name)) {
-              ret.add(e);
-            }
-          }
-        }
-      }
-      return ret;
-    }
-    return testCases.values();
-  }
-
-  private HashMap<String, ToElementable> getTestCase() {
-    if (testEntities == null) {
-      testEntities = new HashMap<String, ToElementable>();
-
-      final Entity lp1 =
-          createProperty(
-              "LIST"
-                  + AbstractCollectionDatatype.LEFT_DELIMITER
-                  + "TEXT"
-                  + AbstractCollectionDatatype.RIGHT_DELIMITER);
-      final Entity lp2 =
-          createProperty(
-              "LIST"
-                  + AbstractCollectionDatatype.LEFT_DELIMITER
-                  + "INTEGER"
-                  + AbstractCollectionDatatype.RIGHT_DELIMITER,
-              "meter");
-      final Entity lp4 =
-          createProperty(
-              "LIST"
-                  + AbstractCollectionDatatype.LEFT_DELIMITER
-                  + "BOOLEAN"
-                  + AbstractCollectionDatatype.RIGHT_DELIMITER);
-      final Entity tp1 = createProperty("TEXT");
-      final Entity dp1 = createProperty("DOUBLE", "meter");
-      final Entity dtp1 = createProperty("DATETIME");
-      final Entity tp2 = createProperty("TEXT");
-      final Entity ip1 = createProperty("INTEGER", "meter");
-      final Entity bp1 = createProperty("BOOLEAN");
-      final Entity fp1 = createProperty("FILE");
-
-      final Entity rt1 = createRecordType(new Entity[] {}, new Entity[] {});
-      final Entity rt2 = createRecordType(new Entity[] {}, new Entity[] {tp2, ip1, bp1});
-
-      final Entity rp1 = createProperty(rt2);
-      final Entity lp3 =
-          createProperty(
-              "LIST"
-                  + AbstractCollectionDatatype.LEFT_DELIMITER
-                  + rt2.getName()
-                  + AbstractCollectionDatatype.RIGHT_DELIMITER);
-      final Entity rt3 =
-          createRecordType(
-              new Entity[] {rt1}, new Entity[] {lp1, tp1, dp1, dtp1, rp1, lp2, lp3, lp4});
-
-      createRecordFromRecordType(rt2);
-      createRecordFromRecordType(rt3);
-
-      createFileRecord("lorem_ipsum.txt");
-      createFileRecord("testsubfolder/lorem_ipsum.txt");
-      final Entity pngFile = createFileRecord("testimg.png");
-      createFileRecord("testimg.jpg");
-      createFileRecord("testimg.svg");
-      createFileRecord("testimg.gif");
-
-      createRecordWithPngFile(pngFile, fp1, rt1);
-
-      /* Test Error, Warning, Info */
-      createError();
-      createWarning();
-      createInfo();
-
-      /* Entities with Error, Warning, Info */
-      createRecordFromRecordType(rt2)
-          .addError(
-              new Message(
-                  MessageType.Error,
-                  666,
-                  "Description of this error",
-                  "Further information about this error."));
-      createRecordFromRecordType(rt2)
-          .addWarning(
-              new Message(
-                  MessageType.Warning,
-                  346,
-                  "Description of this warning",
-                  "Further information about this warning."));
-      createRecordFromRecordType(rt2)
-          .addInfo(
-              new Message(
-                  MessageType.Info,
-                  0,
-                  "Description of this info",
-                  "Further information about this info."));
-
-      createAnnotation();
-    }
-    return testEntities;
-  }
-
-  private void createAnnotation() {
-    final Entity annotationOf = createEntity(Role.Property, "annotationOf");
-    annotationOf.setDatatype("REFERENCE");
-    final Entity comment = createEntity(Role.Property, "comment");
-    comment.setDatatype("TEXT");
-    final Entity annotation = createEntity(Role.RecordType, "Annotation");
-    annotation.addProperty(new Property(annotationOf));
-    annotation.addProperty(new Property(comment));
-
-    final Entity rec = createRecordFromRecordType(annotation);
-    rec.addTransactionLog(
-        new TransactionLogMessage(
-            "INSERT",
-            rec,
-            "Adam",
-            UTCDateTime.SystemMillisToUTCDateTime(System.currentTimeMillis())));
-  }
-
-  private Entity createRecordWithPngFile(
-      final Entity pngFile, final Entity property, final Entity parent) {
-    final Entity entity = createRecordFromRecordType(parent);
-    final Property newP = new Property(property.toElement());
-    newP.setValue(new ReferenceValue(pngFile));
-    entity.addProperty(newP);
-    return entity;
-  }
-
-  private Entity createFileRecord(final String path) {
-    final Entity entity = createEntity(Role.File, "TestFile");
-    entity.setFileProperties(TestCaseFileSystemResource.getFileProperties(path));
-    return entity;
-  }
-
-  private Entity createProperty(final Entity rt2) {
-    final Entity entity = createProperty("REFERENCE");
-    entity.setDatatype(rt2.getName());
-    return entity;
-  }
-
-  private Value generateValue(final AbstractDatatype dt) {
-    if (dt instanceof IntegerDatatype) {
-      return new GenericValue(1337);
-    } else if (dt instanceof DoubleDatatype) {
-      return new GenericValue(3.14);
-    } else if (dt instanceof DateTimeDatatype) {
-      return DateTimeFactory2.valueOf(new GregorianCalendar());
-    } else if (dt instanceof BooleanDatatype) {
-      return BooleanValue.valueOf(entityCounter % 2 == 0);
-    } else if (dt instanceof TextDatatype) {
-      return new GenericValue("Don't panic.");
-    } else if (dt instanceof ReferenceDatatype2) {
-      final ReferenceDatatype2 dt2 = (ReferenceDatatype2) dt;
-      dt2.getId();
-      for (final ToElementable e : testEntities.values()) {
-        if (e instanceof Entity && ((Entity) e).getRole() == Role.Record) {
-          return new ReferenceValue((Entity) e);
-        }
-      }
-    } else if (dt instanceof ListDatatype) {
-      final AbstractDatatype dt2 = ((ListDatatype) dt).getDatatype();
-      final CollectionValue v = new CollectionValue();
-      for (int i = 0; i < 25; i++) {
-        v.add(generateValue(dt2));
-      }
-      return v;
-    }
-    return null;
-  }
-
-  private Entity createRecordFromRecordType(final Entity rt2) {
-    final Entity entity = createEntity(Role.Record, "TestRecord");
-    addParentsAndProperties(entity, rt2, rt2.getProperties(), "FIX");
-    for (final Property p : entity.getProperties()) {
-      p.setValue(generateValue(p.getDatatype()));
-    }
-    return entity;
-  }
-
-  private void addParentsAndProperties(
-      final Entity entity,
-      final Entity parent,
-      final PropertyContainer properties,
-      final String importance) {
-    final List<Entity> par = new LinkedList<Entity>();
-    par.add(parent);
-    addParentsAndProperties(entity, par, properties, importance);
-  }
-
-  private void addParentsAndProperties(
-      final Entity entity,
-      final Collection<? extends EntityInterface> parents,
-      final Collection<? extends EntityInterface> properties,
-      final String importance) {
-    for (final EntityInterface p : properties) {
-      final Property newP = new Property(p.toElement());
-      if (importance != null) {
-        newP.setStatementStatus(StatementStatus.valueOf(importance));
-      }
-      entity.addProperty(newP);
-    }
-    for (final EntityInterface p : parents) {
-      entity.addParent(new Parent(p.toElement()));
-    }
-  }
-
-  private Message createError() {
-    return createMessage(
-        MessageType.Error.toString(),
-        "This is a description of the error",
-        "Here is additional info about the error.",
-        666);
-  }
-
-  private Message createWarning() {
-    return createMessage(
-        MessageType.Warning.toString(),
-        "This is a description of the warning",
-        "Here is additional info about the warning.",
-        333);
-  }
-
-  private Message createInfo() {
-    return createMessage(
-        MessageType.Info.toString(),
-        "Here is useful information about the last activity of the user.",
-        "Here is additional information.",
-        null);
-  }
-
-  private Message createMessage(
-      String type, final String description, final String body, final Integer code) {
-    final Message m = new Message(type, code, description, body);
-    if (testEntities.containsKey(type)) {
-      int i = 0;
-      while (testEntities.containsKey(type + "-" + i)) {
-        i++;
-      }
-      type += "-" + i;
-    }
-    testEntities.put(type, m);
-    return m;
-  }
-
-  private static HashMap<String, ToElementable> testEntities = null;
-  private static int entityCounter = 1;
-
-  private Entity createProperty(final String datatype) {
-    return createProperty(datatype, null);
-  }
-
-  private Entity createRecordType(final Entity[] parents, final Entity[] properties) {
-    final Entity entity = createEntity(Role.RecordType, "TestRecordType");
-    addParentsAndProperties(entity, parents, properties, "OBLIGATORY");
-    return entity;
-  }
-
-  private void addParentsAndProperties(
-      final Entity entity,
-      final EntityInterface[] parents,
-      final EntityInterface[] properties,
-      final String importance) {
-    addParentsAndProperties(entity, Arrays.asList(parents), Arrays.asList(properties), importance);
-  }
-
-  private Entity createProperty(final String datatype, final String unit) {
-    final Entity entity = createEntity(Role.Property, "Test" + datatype + "Property");
-    entity.setDatatype(datatype);
-    if (unit != null) {
-      final EntityInterface magicUnit = MagicTypes.UNIT.getEntity();
-      final Property unitProp = new Property();
-      unitProp.setId(magicUnit.getId());
-      unitProp.setValue(new GenericValue(unit));
-      entity.addProperty(unitProp);
-    }
-    return entity;
-  }
-
-  private Entity createEntity(final Role role, String name) {
-    final Entity entity = new RetrieveEntity(entityCounter++);
-    entity.setRole(role);
-    entity.setDescription(
-        "An informative description of the idea, meaning, and purpose of this entity.");
-    if (testEntities.containsKey(name)) {
-      int i = 0;
-      while (testEntities.containsKey(name + "-" + i)) {
-        i++;
-      }
-      name += "-" + i;
-    }
-    testEntities.put(name, entity);
-    entity.setName(name);
-    return entity;
-  }
-}
diff --git a/src/main/java/caosdb/server/resource/UserResource.java b/src/main/java/caosdb/server/resource/UserResource.java
index 5cae0da2efa4716293407497fd896bf0b880f233..b77ff8a49035d2be930a3f957e743ed2f0dae067 100644
--- a/src/main/java/caosdb/server/resource/UserResource.java
+++ b/src/main/java/caosdb/server/resource/UserResource.java
@@ -42,7 +42,6 @@ import org.jdom2.Document;
 import org.jdom2.Element;
 import org.jdom2.JDOMException;
 import org.restlet.data.Form;
-import org.restlet.data.MediaType;
 import org.restlet.data.Status;
 import org.restlet.representation.Representation;
 
@@ -84,7 +83,7 @@ public class UserResource extends AbstractCaosDBServerResource {
     }
 
     doc.setRootElement(rootElem);
-    return new JdomRepresentation(doc, MediaType.TEXT_XML, "  ", getReference(), getXSLScript());
+    return ok(doc);
   }
 
   @Override
@@ -125,7 +124,7 @@ public class UserResource extends AbstractCaosDBServerResource {
 
       rootElem.addContent(t.getUserElement());
       doc.setRootElement(rootElem);
-      return new JdomRepresentation(doc, MediaType.TEXT_XML, "  ", getReference(), getXSLScript());
+      return ok(doc);
     } catch (final Message m) {
       if (m == ServerMessages.ACCOUNT_DOES_NOT_EXIST) {
         return error(m, Status.CLIENT_ERROR_NOT_FOUND);
@@ -177,7 +176,7 @@ public class UserResource extends AbstractCaosDBServerResource {
 
     rootElem.addContent(t.getUserElement());
     doc.setRootElement(rootElem);
-    return new JdomRepresentation(doc, MediaType.TEXT_XML, "  ", getReference(), getXSLScript());
+    return ok(doc);
   }
 
   @Override
@@ -200,6 +199,6 @@ public class UserResource extends AbstractCaosDBServerResource {
     rootElem.addContent(t.getUserElement());
 
     doc.setRootElement(rootElem);
-    return new JdomRepresentation(doc, MediaType.TEXT_XML, "  ", getReference(), getXSLScript());
+    return ok(doc);
   }
 }
diff --git a/src/main/java/caosdb/server/resource/Webinterface.java b/src/main/java/caosdb/server/resource/Webinterface.java
index 7343fc5ff681a56f1dcba633bd7f3de4ba27f813..f05ae4e6b9a98aba1c925677e0fc8e90a74e57e6 100644
--- a/src/main/java/caosdb/server/resource/Webinterface.java
+++ b/src/main/java/caosdb/server/resource/Webinterface.java
@@ -24,6 +24,7 @@ package caosdb.server.resource;
 
 import caosdb.server.CaosDBServer;
 import caosdb.server.ServerProperties;
+import caosdb.server.utils.WebinterfaceUtils;
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -42,13 +43,14 @@ import org.restlet.util.Series;
 
 public class Webinterface extends ServerResource {
 
+  private WebinterfaceUtils utils;
+
   @Override
   protected void doInit() throws ResourceException {
+    this.utils = WebinterfaceUtils.getInstance(getHostRef());
     super.doInit();
   }
 
-  private static final File PUBLIC_DIRECTORY = new File("caosdb-webui/public/").getAbsoluteFile();
-
   @Get
   public Representation deliver() throws IOException {
     final String path = (String) getRequest().getAttributes().get("path");
@@ -57,23 +59,15 @@ public class Webinterface extends ServerResource {
       getResponse().setStatus(Status.CLIENT_ERROR_NOT_FOUND);
       return null;
     }
-    final File file = new File(this.PUBLIC_DIRECTORY.getAbsolutePath() + "/" + path);
-    // TODO   if (!FileUtils.isSubDirSymlinkSave(file, this.PUBLIC_DIRECTORY.getParentFile())) {
-    //      getResponse().setStatus(Status.CLIENT_ERROR_FORBIDDEN);
-    //      return null;
-    //    }
-    if (!file.exists()) {
+
+    final File file = utils.getPublicFile(path);
+
+    if (file == null) {
       getResponse().setStatus(Status.CLIENT_ERROR_NOT_FOUND);
       return null;
     }
 
-    Series<Header> headers = getRequest().getHeaders();
-    if (headers == null) {
-      headers = new Series<Header>(Header.class);
-      getResponse().getAttributes().put(HeaderConstants.ATTRIBUTE_HEADERS, headers);
-    }
-    headers.set("Access-Control-Allow-Origin", getHostRef().toString());
-
+    // TODO move to FileUtils and use a third-party library
     final MediaType mt =
         path.endsWith(".json")
             ? MediaType.APPLICATION_JSON
@@ -91,6 +85,13 @@ public class Webinterface extends ServerResource {
 
     final FileRepresentation ret = new FileRepresentation(file, mt);
 
+    Series<Header> headers = getRequest().getHeaders();
+    if (headers == null) {
+      headers = new Series<Header>(Header.class);
+      getResponse().getAttributes().put(HeaderConstants.ATTRIBUTE_HEADERS, headers);
+    }
+    headers.set("Access-Control-Allow-Origin", getHostRef().toString());
+
     List<CacheDirective> cacheDirectives = new ArrayList<>();
     cacheDirectives.add(
         new CacheDirective(
diff --git a/src/main/java/caosdb/server/resource/WebinterfaceBuildNumber.java b/src/main/java/caosdb/server/resource/WebinterfaceBuildNumber.java
new file mode 100644
index 0000000000000000000000000000000000000000..b61a61846f188d16ba7f13c61dd47d422e93e89f
--- /dev/null
+++ b/src/main/java/caosdb/server/resource/WebinterfaceBuildNumber.java
@@ -0,0 +1,60 @@
+/**
+ * ** header v3.0 This file is a part of the CaosDB Project.
+ *
+ * <p>Copyright (c) 2019 IndiScale GmbH Copyright (c) 2019 Daniel Hornung <d.hornung@indiscale.com>
+ *
+ * <p>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.
+ *
+ * <p>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.
+ *
+ * <p>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/>.
+ *
+ * <p>** end header
+ */
+package caosdb.server.resource;
+
+import caosdb.server.utils.WebinterfaceUtils;
+import org.restlet.data.Status;
+import org.restlet.resource.Get;
+import org.restlet.resource.ServerResource;
+
+/**
+ * This {@link ServerResource} exposes the current build number of the web interface.
+ *
+ * <p>It is mainly used for testing and debugging. A User can determine the current build number of
+ * the web interface.
+ *
+ * @author Timm Fitschen (t.fitschen@indiscale.com)
+ */
+public class WebinterfaceBuildNumber extends ServerResource {
+
+  private WebinterfaceUtils utils;
+
+  public WebinterfaceBuildNumber() {
+    this.utils = WebinterfaceUtils.getInstance(getHostRef());
+  }
+
+  /**
+   * Return the current build number of the web interface.
+   *
+   * <p>If the build number could not be determined the server responds with 404 - Not Found.
+   * Reasons for this include an out-dated web interface, the web interface has not been build yet
+   * or the web interface is not available at all.
+   *
+   * @return the current build number.
+   */
+  @Get("text")
+  public String getBuildNumber() {
+    String buildNumber = utils.getBuildNumber();
+
+    if (buildNumber == null) {
+      setStatus(Status.CLIENT_ERROR_NOT_FOUND, "Build number could not be determined.");
+    }
+    return buildNumber;
+  }
+}
diff --git a/src/main/java/caosdb/server/resource/transaction/EntityResource.java b/src/main/java/caosdb/server/resource/transaction/EntityResource.java
index f55c2b7ae0cb736305086ae131c3a3eeac1ba0ab..54882bcab6211bdbbd6921871ef4709413738543 100644
--- a/src/main/java/caosdb/server/resource/transaction/EntityResource.java
+++ b/src/main/java/caosdb/server/resource/transaction/EntityResource.java
@@ -29,7 +29,6 @@ import caosdb.server.entity.container.InsertContainer;
 import caosdb.server.entity.container.RetrieveContainer;
 import caosdb.server.entity.container.UpdateContainer;
 import caosdb.server.resource.AbstractCaosDBServerResource;
-import caosdb.server.resource.JdomRepresentation;
 import caosdb.server.resource.transaction.handlers.FileUploadHandler;
 import caosdb.server.resource.transaction.handlers.IDHandler;
 import caosdb.server.resource.transaction.handlers.RequestHandler;
@@ -120,7 +119,7 @@ public class EntityResource extends AbstractCaosDBServerResource {
     final Element rootElem = generateRootElement();
     entityContainer.addToElement(rootElem);
     doc.setRootElement(rootElem);
-    return new JdomRepresentation(doc, MediaType.TEXT_XML, "  ", getReference(), getXSLScript());
+    return ok(doc);
   }
 
   @Override
@@ -143,7 +142,7 @@ public class EntityResource extends AbstractCaosDBServerResource {
     entityContainer.addToElement(rootElem);
     doc.setRootElement(rootElem);
 
-    return new JdomRepresentation(doc, MediaType.TEXT_XML, "  ", getReference(), getXSLScript());
+    return ok(doc);
   }
 
   @Override
@@ -167,7 +166,8 @@ public class EntityResource extends AbstractCaosDBServerResource {
     final Element rootElem = generateRootElement();
     entityContainer.addToElement(rootElem);
     doc.setRootElement(rootElem);
-    return new JdomRepresentation(doc, MediaType.TEXT_XML, "  ", getReference(), getXSLScript());
+
+    return ok(doc);
   }
 
   @Override
@@ -191,6 +191,7 @@ public class EntityResource extends AbstractCaosDBServerResource {
     final Element rootElem = generateRootElement();
     entityContainer.addToElement(rootElem);
     doc.setRootElement(rootElem);
-    return new JdomRepresentation(doc, MediaType.TEXT_XML, "  ", getReference(), getXSLScript());
+
+    return ok(doc);
   }
 }
diff --git a/src/main/java/caosdb/server/transaction/Transaction.java b/src/main/java/caosdb/server/transaction/Transaction.java
index de1e5e279ee4b1cd252dda0fdf6bb23e8f93e57f..3e511257b239627c61064acc5d011d79516bbb08 100644
--- a/src/main/java/caosdb/server/transaction/Transaction.java
+++ b/src/main/java/caosdb/server/transaction/Transaction.java
@@ -191,7 +191,6 @@ public abstract class Transaction<C extends TransactionContainer> extends Abstra
               this.getClass().getSimpleName() + ".commit", System.currentTimeMillis() - t1);
 
     } catch (final Exception e) {
-      e.printStackTrace();
       rollBack();
       throw e;
     } finally {
diff --git a/src/main/java/caosdb/server/utils/WebinterfaceUtils.java b/src/main/java/caosdb/server/utils/WebinterfaceUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..ecf0b08630e58662c0abad5c5fe71a0c5b1a8889
--- /dev/null
+++ b/src/main/java/caosdb/server/utils/WebinterfaceUtils.java
@@ -0,0 +1,293 @@
+/**
+ * ** header v3.0 This file is a part of the CaosDB Project.
+ *
+ * <p>Copyright (c) 2019 IndiScale GmbH Copyright (c) 2019 Daniel Hornung <d.hornung@indiscale.com>
+ *
+ * <p>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.
+ *
+ * <p>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.
+ *
+ * <p>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/>.
+ *
+ * <p>** end header
+ */
+package caosdb.server.utils;
+
+import caosdb.server.CaosDBServer;
+import caosdb.server.ServerProperties;
+import caosdb.server.resource.AbstractCaosDBServerResource;
+import caosdb.server.resource.Webinterface;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Stream;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.restlet.data.Reference;
+
+/**
+ * This class is responsible for two things:
+ * <li>Resolving relative paths of files of the web interface to {@link File} objects.
+ * <li>Generating URIs to public files of the web interface from relative string paths.
+ *
+ *     <p>Especially the {@link AbstractCaosDBServerResource} and the {@link Webinterface} use this
+ *     class for these tasks.
+ *
+ * @author Timm Fitschen (t.fitschen@indiscale.com)
+ */
+public class WebinterfaceUtils {
+
+  private Logger logger = LogManager.getLogger(this.getClass());
+  public static final String DEFAULT_WEBUI_PUBLIC = "caosdb-webui/public";
+  public static final String DEFAULT_BUILD_NUMBER_FILE = ".build_number";
+  public static final String DEFAULT_ROUTE = "webinterface";
+  private final String host;
+  private final String publicDir;
+  private final String route;
+  private final Path buildNumberFile;
+  private final String contextRoot;
+  private String buildNumber = null;
+  private long buildNumberDate;
+  private static final Map<String, WebinterfaceUtils> instances = new HashMap<>();
+
+  /**
+   * Retrieve an instance of {@link WebinterfaceUtils} for the host. The instance can be shared with
+   * other callers.
+   *
+   * @param host
+   * @return a shared instance of {@link WebinterfaceUtils}.
+   */
+  public static WebinterfaceUtils getInstance(Reference host) {
+    return getInstance(host.getHostIdentifier());
+  }
+
+  /**
+   * Retrieve an instance of {@link WebinterfaceUtils} for the host. The instance can be shared with
+   * other callers.
+   *
+   * @param host
+   * @return a shared instance of {@link WebinterfaceUtils}.
+   */
+  public static WebinterfaceUtils getInstance(String host) {
+    synchronized (instances) {
+      WebinterfaceUtils ret = instances.get(host);
+      if (ret == null) {
+        ret = new WebinterfaceUtils(host);
+        instances.put(host, ret);
+      }
+      return ret;
+    }
+  }
+
+  /**
+   * Create a utility class for creating references to resources of the web interface.
+   *
+   * @param host
+   * @param contextRoot
+   * @param publicDir
+   * @param route
+   * @param buildNumberFile
+   */
+  WebinterfaceUtils(
+      String host, String contextRoot, String publicDir, String route, String buildNumberFile) {
+    this.host = host;
+    this.contextRoot = contextRoot;
+    this.publicDir = publicDir;
+    this.route = route;
+    this.buildNumberFile = getPublicRootPath().resolve(buildNumberFile);
+  }
+
+  /**
+   * Create a utility class for creating references to resources of the web interface.
+   *
+   * <p>The route to the web interface, the build number file and the public folder of the web
+   * interface build directory are set to default values ({@link #DEFAULT_BUILD_NUMBER_FILE}, {@link
+   * #DEFAULT_ROUTE}, and {@link #DEFAULT_WEBUI_PUBLIC}).
+   *
+   * <p>The contextRoute defaults to the server property {@link ServerProperties.KEY_CONTEXT_ROOT}.
+   *
+   * @param host - the host of the web interface.
+   * @see {@link WebinterfaceUtils#WebinterfaceUtils(String, String, String, String)}.
+   */
+  WebinterfaceUtils(Reference host) {
+    this(host.getHostIdentifier());
+  }
+
+  /**
+   * Create a utility class for creating references to resources of the web interface.
+   *
+   * <p>The route to the web interface, the build number file and the public folder of the web
+   * interface build directory are set to default values ({@link #DEFAULT_BUILD_NUMBER_FILE}, {@link
+   * #DEFAULT_ROUTE}, and {@link #DEFAULT_WEBUI_PUBLIC}).
+   *
+   * <p>The contextRoute defaults to the server property {@link ServerProperties.KEY_CONTEXT_ROOT}.
+   *
+   * @param host - the host of the web interface.
+   * @see {@link WebinterfaceUtils#WebinterfaceUtils(String, String, String, String)}.
+   */
+  WebinterfaceUtils(String host) {
+    this(
+        host,
+        CaosDBServer.getServerProperty(ServerProperties.KEY_CONTEXT_ROOT),
+        DEFAULT_WEBUI_PUBLIC,
+        DEFAULT_ROUTE,
+        DEFAULT_BUILD_NUMBER_FILE);
+  }
+
+  /**
+   * Return the server root reference.
+   *
+   * <p>The server root is composed of the scheme (https://) the host name (example.com) the port if
+   * necessary (:10443) and the context root of the application (beta).
+   *
+   * @return the server root reference e.g. https://example.com:10443/beta.
+   */
+  public String getServerRootURI() {
+    if (contextRoot == null || contextRoot.length() == 0) {
+      return host;
+    }
+    return host + "/" + contextRoot;
+  }
+
+  /**
+   * Return the root reference for the web interface.
+   *
+   * <p>The root reference for the web interface is the server root reference ({@link
+   * #getServerRootURI()}) plus the route to the resource which routes to the individual resources
+   * of the web interface ({@link Webinterface}).
+   *
+   * @return root reference for the web interface.
+   */
+  public String getWebinterfaceRootURI() {
+    return getServerRootURI() + "/" + this.route + "/" + getBuildNumber();
+  }
+
+  /**
+   * Return the absolute path of the root of the public directory of the webui.
+   *
+   * @return Public directory of webui.
+   */
+  public Path getPublicRootPath() {
+    return Paths.get(this.publicDir).toAbsolutePath();
+  }
+
+  /**
+   * return the Path of the build number file.
+   *
+   * @return
+   */
+  public Path getBuildNumberFile() {
+    return this.buildNumberFile;
+  }
+
+  /**
+   * Determine the build number of the webui. Returns null if the build number could not be
+   * determined.
+   *
+   * <p>Because opening and reading the {@link #buildNumberFile} content is expensive, the
+   * buildNumber is cached.
+   *
+   * <p>The cached buildNumber invalidates when the files modified time stamp is younger than the
+   * {@link #buildNumberDate}. The validity of the cache is checked once every minute with a {@link
+   * CronJob}.
+   *
+   * @return Build number or null.
+   */
+  public String getBuildNumber() {
+    validateBuildNumber();
+    if (this.buildNumber == null) {
+      this.buildNumberDate = System.currentTimeMillis();
+      this.buildNumber = readBuildNumber();
+    }
+    return this.buildNumber;
+  }
+
+  /**
+   * Return the latest buildNumber, freshly read from the buildNumberfile.
+   *
+   * @return fresh build number.
+   */
+  public String readBuildNumber() {
+    Path buildNumberFile = getBuildNumberFile();
+    StringBuilder contentBuilder = new StringBuilder();
+    try (Stream<String> stream = Files.lines(buildNumberFile, StandardCharsets.UTF_8)) {
+      stream.forEach(s -> contentBuilder.append(s));
+    } catch (IOException e) {
+      logger.error(e);
+      return null;
+    }
+    return contentBuilder.toString();
+  }
+
+  /**
+   * Return the public file or null if the file does not exists or is not in the public directory of
+   * the web interface.
+   *
+   * <p>This method is used to resolve requests for files of the web interface.
+   *
+   * @param path
+   * @return The file or null
+   */
+  public File getPublicFile(String path) {
+    Path publicFilePath = getPublicFilePath(path);
+    if (publicFilePath != null) {
+      File publicFile = publicFilePath.toFile();
+      if (publicFile.exists() && !publicFile.isDirectory() && publicFile.canRead())
+        return publicFile;
+    }
+    return null;
+  }
+
+  /**
+   * Return the absolute path to the public file denoted by the path.
+   *
+   * <p>This method is used to resolve requests for files of the web interface.
+   *
+   * @param path - path to the file of the web interface, relative to the public directory of the
+   *     web interface.
+   * @return The absolute path of the web interface file.
+   */
+  public Path getPublicFilePath(String path) {
+    Path root = getPublicRootPath();
+    Path resolved = root.resolve(path).toAbsolutePath().normalize();
+    if (resolved.startsWith(root)) {
+      return resolved;
+    }
+    return null;
+  }
+
+  /**
+   * Return a URI to a public file of the web interface.
+   *
+   * <p>This method is used to generate references for *.xsl files or other resources of the web
+   * interface.
+   *
+   * @param publicFile - the path of the public file, relative to the public directory of the web
+   *     interface.
+   * @return a URI for the public file of the web interface.
+   */
+  public String getWebinterfaceURI(String publicFile) {
+    return getWebinterfaceRootURI() + "/" + publicFile;
+  }
+
+  /**
+   * Check the validity of the buildNumber and invalidate the buildNumber if the buildNumberDate is
+   * older than the {@link File#lastModified} time stamp of the buildNumberFile.
+   */
+  public void validateBuildNumber() {
+    if (buildNumber != null
+        && getBuildNumberFile().toFile().lastModified() > this.buildNumberDate) {
+      this.buildNumber = null;
+    }
+  }
+}
diff --git a/src/test/java/caosdb/server/resource/TestScriptingResource.java b/src/test/java/caosdb/server/resource/TestScriptingResource.java
index 8ea1cc2e888ceacd51213ef1d1516ede498a8267..353a2ea220984705b2612e01fe8460d5c2d7b457 100644
--- a/src/test/java/caosdb/server/resource/TestScriptingResource.java
+++ b/src/test/java/caosdb/server/resource/TestScriptingResource.java
@@ -178,6 +178,7 @@ public class TestScriptingResource {
     request.setRootRef(new Reference("bla"));
     request.getAttributes().put("SRID", "asdf1234");
     request.setDate(new Date());
+    request.setHostRef("bla");
     resource.init(null, request, new Response(null));
 
     resource.handle();
diff --git a/src/test/java/caosdb/server/utils/WebinterfaceUtilsTest.java b/src/test/java/caosdb/server/utils/WebinterfaceUtilsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4ff5d7185d43704e4bc97ef435a5c853e58e3eb8
--- /dev/null
+++ b/src/test/java/caosdb/server/utils/WebinterfaceUtilsTest.java
@@ -0,0 +1,45 @@
+package caosdb.server.utils;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import caosdb.server.CaosDBServer;
+import java.io.IOException;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.restlet.data.Reference;
+
+public class WebinterfaceUtilsTest {
+
+  @Rule public ExpectedException exceptionRule = ExpectedException.none();
+
+  @BeforeClass
+  public static void setup() throws IOException {
+    CaosDBServer.initServerProperties();
+  }
+
+  @Test
+  public void testGetWebinterfaceReference() {
+    WebinterfaceUtils utils = new WebinterfaceUtils(new Reference("https://host:2345/some_path"));
+    String buildNumber = utils.getBuildNumber();
+    String ref = utils.getWebinterfaceURI("sub");
+    assertEquals("https://host:2345/webinterface/" + buildNumber + "/sub", ref);
+  }
+
+  @Test
+  public void testGetPublicFile() {
+    WebinterfaceUtils utils = new WebinterfaceUtils(new Reference("https://host:2345/some_path"));
+    assertNull(utils.getPublicFile("../"));
+  }
+
+  @Test
+  public void testGetPublicFilePath() {
+    WebinterfaceUtils utils = new WebinterfaceUtils(new Reference("https://host:2345/some_path"));
+    assertNull(utils.getPublicFilePath("../"));
+    assertNotNull(utils.getPublicFilePath("./"));
+    assertNotNull(utils.getPublicFilePath("bla"));
+  }
+}