searchkrill git merge
diff --git a/config/kustvakt2.conf b/config/kustvakt2.conf
new file mode 100644
index 0000000..70de25a
--- /dev/null
+++ b/config/kustvakt2.conf
@@ -0,0 +1,80 @@
+### Common options
+# read index from:
+lucene.indexDir	=	/Users/hanl/Projects/prep_corpus2
+## lucene.indexDir	=	/home/hanl/Projects/prep_corpus
+## lucene.indexDir	=	/data/indices
+
+
+# Lucene Backend properties
+lucene.properties = true
+lucene.index.commit.count = 134217000
+lucene.index.commit.log = log/korap.commit.log
+
+# Not active at the moment:
+lucene.index.search.count.default = 25
+lucene.index.search.count.max = 100
+lucene.index.search.context.left.type = token
+lucene.index.search.context.left.default = 6
+lucene.index.search.context.left.max = 12
+lucene.index.search.context.right.type = token
+lucene.index.search.context.right.default = 6
+lucene.index.search.context.right.max = 12
+
+
+### Options for server mode ###
+service.port	=	8070
+
+## email configuration
+mail.host = mail.ids-mannheim.de
+mail.from = no-reply@korap.ids-mannheim.de
+
+## frontend parameter  - deprecated
+korap.frontend.url = http://localhost:8080
+## korap.frontend.url = http://klinux10.ids-mannheim.de:8080/korap-frontend/app
+
+korap.ql=Cosmas2, Poliqarp, CQL, ANNIS
+korap.issuer=http://korap1:8080/api
+
+## configuration options
+log4jconfig     = ./config/log4j.properties
+
+# directory to reference jsp and html files for static content
+webapp.dir=webapp
+
+
+## NEW!!
+users.config=/Users/hanl/Projects/KorAP-project/KorAP-modules/KorAP-REST/config/users.c
+policies.config=/Users/hanl/Projects/KorAP-project/KorAP-modules/KorAP-REST/config/policies.c
+
+## options referring to the security module!
+
+## token expiration time in minutes!
+## decpricated, no function uses this anymore
+security.absoluteTimeoutDuration = 45M
+
+security.tokenTTL=72H
+security.shortTokenTTL=12H
+
+security.idleTimeoutDuration = 25M
+security.multipleLogIn = true
+security.loginAttemptNum = 3
+security.authAttemptTTL = 45M
+
+security.encryption.loadFactor = 8
+security.validation.stringLength = 150
+security.validation.emailLength = 50
+security.encryption.algo=BCRYPT
+security.sharedSecret=nHim5JB-YqkX7sS55jayGBnga8WmqgpkzieGe8UhojE
+security.adminToken=f61d02c04a0f18d60172f7b990955824
+
+# change to persistent id or create one (could be list of values, which would be joined!)
+security.shibUserMapping=eppn
+
+# specify a default client!
+
+## applicable: rewrite, foundry, filter, deny
+security.rewrite.strategies=filter, foundry, rewrite
+
+## name of the policy condition that registering users can be added by default
+## if none given, none is applied
+security.group.public=public
\ No newline at end of file
diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml
index 3054681..9ebc5a9 100644
--- a/dependency-reduced-pom.xml
+++ b/dependency-reduced-pom.xml
@@ -10,6 +10,17 @@
   <build>

     <plugins>

       <plugin>

+        <artifactId>maven-jar-plugin</artifactId>

+        <executions>

+          <execution>

+            <phase>package</phase>

+            <goals>

+              <goal>test-jar</goal>

+            </goals>

+          </execution>

+        </executions>

+      </plugin>

+      <plugin>

         <artifactId>maven-surefire-plugin</artifactId>

         <version>2.9</version>

         <configuration>

@@ -34,7 +45,7 @@
             <configuration>

               <transformers>

                 <transformer>

-                  <mainClass>de.ids_mannheim.korap.web.Kustvakt</mainClass>

+                  <mainClass>de.ids_mannheim.korap.web.KustvaktServer</mainClass>

                 </transformer>

                 <transformer>

                   <resource>META-INF/spring.handlers</resource>

@@ -77,6 +88,44 @@
   </repositories>

   <dependencies>

     <dependency>

+      <groupId>com.sun.jersey.jersey-test-framework</groupId>

+      <artifactId>jersey-test-framework-core</artifactId>

+      <version>1.19</version>

+      <scope>test</scope>

+      <exclusions>

+        <exclusion>

+          <artifactId>javax.servlet-api</artifactId>

+          <groupId>javax.servlet</groupId>

+        </exclusion>

+        <exclusion>

+          <artifactId>jersey-client</artifactId>

+          <groupId>com.sun.jersey</groupId>

+        </exclusion>

+      </exclusions>

+    </dependency>

+    <dependency>

+      <groupId>com.sun.jersey.jersey-test-framework</groupId>

+      <artifactId>jersey-test-framework-grizzly</artifactId>

+      <version>1.19</version>

+      <scope>test</scope>

+      <exclusions>

+        <exclusion>

+          <artifactId>grizzly-servlet-webserver</artifactId>

+          <groupId>com.sun.grizzly</groupId>

+        </exclusion>

+        <exclusion>

+          <artifactId>jersey-grizzly</artifactId>

+          <groupId>com.sun.jersey</groupId>

+        </exclusion>

+      </exclusions>

+    </dependency>

+    <dependency>

+      <groupId>junit</groupId>

+      <artifactId>junit</artifactId>

+      <version>4.11</version>

+      <scope>test</scope>

+    </dependency>

+    <dependency>

       <groupId>com.restfuse</groupId>

       <artifactId>com.eclipsesource.restfuse</artifactId>

       <version>1.0.0</version>

@@ -88,46 +137,6 @@
         </exclusion>

       </exclusions>

     </dependency>

-    <dependency>

-      <groupId>com.jayway.restassured</groupId>

-      <artifactId>rest-assured</artifactId>

-      <version>2.4.0</version>

-      <scope>provided</scope>

-      <exclusions>

-        <exclusion>

-          <artifactId>groovy</artifactId>

-          <groupId>org.codehaus.groovy</groupId>

-        </exclusion>

-        <exclusion>

-          <artifactId>groovy-xml</artifactId>

-          <groupId>org.codehaus.groovy</groupId>

-        </exclusion>

-        <exclusion>

-          <artifactId>httpclient</artifactId>

-          <groupId>org.apache.httpcomponents</groupId>

-        </exclusion>

-        <exclusion>

-          <artifactId>httpmime</artifactId>

-          <groupId>org.apache.httpcomponents</groupId>

-        </exclusion>

-        <exclusion>

-          <artifactId>hamcrest-library</artifactId>

-          <groupId>org.hamcrest</groupId>

-        </exclusion>

-        <exclusion>

-          <artifactId>tagsoup</artifactId>

-          <groupId>org.ccil.cowan.tagsoup</groupId>

-        </exclusion>

-        <exclusion>

-          <artifactId>json-path</artifactId>

-          <groupId>com.jayway.restassured</groupId>

-        </exclusion>

-        <exclusion>

-          <artifactId>xml-path</artifactId>

-          <groupId>com.jayway.restassured</groupId>

-        </exclusion>

-      </exclusions>

-    </dependency>

   </dependencies>

   <properties>

     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

diff --git a/logs/audit.log b/logs/audit.log
new file mode 100644
index 0000000..591c4a4
--- /dev/null
+++ b/logs/audit.log
@@ -0,0 +1,48 @@
+SERVICE AUDIT : 
+MichaelHanl@Tue Aug 11 15:50:35 CEST 2015
+Status 105; Args None; 
+SERVICE AUDIT : 
+MichaelHanl@Tue Aug 11 15:50:35 CEST 2015
+Status 105; Args None; 
+SERVICE AUDIT : 
+MichaelHanl@Tue Aug 11 15:50:35 CEST 2015
+Status 105; Args None; 
+SERVICE AUDIT : 
+MichaelHanl@Tue Aug 11 15:50:35 CEST 2015
+Status 105; Args None; 
+SERVICE AUDIT : 
+MichaelHanl@Tue Aug 11 15:50:35 CEST 2015
+Status 105; Args None; 
+SERVICE AUDIT : 
+MichaelHanl@Tue Aug 11 15:50:35 CEST 2015
+Status 105; Args None; 
+SERVICE AUDIT : 
+MichaelHanl@Tue Aug 11 15:50:35 CEST 2015
+Status 105; Args None; 
+SERVICE AUDIT : 
+MichaelHanl@Tue Aug 11 15:50:35 CEST 2015
+Status 105; Args None; 
+SERVICE AUDIT : 
+MichaelHanl@Tue Aug 11 15:50:35 CEST 2015
+Status 105; Args None; 
+SERVICE AUDIT : 
+MichaelHanl@Tue Aug 11 15:50:35 CEST 2015
+Status 105; Args None; 
+SERVICE AUDIT : 
+MichaelHanl@Tue Aug 11 15:50:35 CEST 2015
+Status 105; Args None; 
+SERVICE AUDIT : 
+MichaelHanl@Tue Aug 11 15:50:35 CEST 2015
+Status 105; Args None; 
+SERVICE AUDIT : 
+MichaelHanl@Tue Aug 11 15:50:35 CEST 2015
+Status 105; Args None; 
+SERVICE AUDIT : 
+MichaelHanl@Tue Aug 11 15:50:35 CEST 2015
+Status 105; Args None; 
+SERVICE AUDIT : 
+MichaelHanl@Tue Aug 11 15:50:35 CEST 2015
+Status 105; Args None; 
+SERVICE AUDIT : 
+MichaelHanl@Tue Aug 11 15:50:35 CEST 2015
+Status 105; Args None; 
diff --git a/logs/default_audit.log b/logs/default_audit.log
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/logs/default_audit.log
diff --git a/pom.xml b/pom.xml
index 0db5741..858e997 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>de.ids_mannheim.korap</groupId>
     <artifactId>Kustvakt-core</artifactId>
-    <version>0.2</version>
+    <version>0.4</version>
     <packaging>jar</packaging>
     <name>Kustvakt core</name>
     <description>Kustvakt core, basic rest api for testing purposes and default
@@ -17,7 +17,44 @@
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     </properties>
     <build>
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+                <filtering>true</filtering>
+                <includes>
+                    <include>**/*.prop</include>
+                    <include>**/*.xml</include>
+                    <include>**/*.conf</include>
+                    <include>**/*.properties</include>
+                </includes>
+            </resource>
+        </resources>
+        <testResources>
+            <testResource>
+                <directory>src/main/resources</directory>
+                <filtering>true</filtering>
+                <includes>
+                    <include>**/*.prop</include>
+                    <include>**/*.xml</include>
+                    <include>**/*.conf</include>
+                    <include>**/*.properties</include>
+                </includes>
+            </testResource>
+        </testResources>
         <plugins>
+            <!-- build tests jar, so extensions can use fastjerseytest class to build rest tests -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>test-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-surefire-plugin</artifactId>
@@ -44,11 +81,58 @@
                             <goal>shade</goal>
                         </goals>
                         <configuration>
+                            <finalName>
+                            Kustvakt-core-${project.version}-light
+                            </finalName>
                             <transformers>
                                 <transformer
                                         implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                     <mainClass>
-                                        de.ids_mannheim.korap.web.Kustvakt
+                                        de.ids_mannheim.korap.web.KustvaktLightServer
+                                    </mainClass>
+                                </transformer>
+                                <transformer
+                                        implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+                                    <resource>META-INF/spring.handlers
+                                    </resource>
+                                </transformer>
+                                <transformer
+                                        implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+                                    <resource>META-INF/spring.schemas
+                                    </resource>
+                                </transformer>
+                            </transformers>
+                            <!-- Additional configuration. -->
+                            <!-- apparently there is a securityexception -->
+                            <filters>
+                                <filter>
+                                    <artifact>*:*</artifact>
+                                    <excludes>
+                                        <exclude>META-INF/*.SF</exclude>
+                                        <exclude>META-INF/*.DSA
+                                        </exclude>
+                                        <exclude>META-INF/*.RSA
+                                        </exclude>
+                                    </excludes>
+                                </filter>
+                            </filters>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>full</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <finalName>
+                                Kustvakt-core-${project.version}-full
+                            </finalName>
+                            <transformers>
+                                <transformer
+                                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                                    <mainClass>
+                                        de.ids_mannheim.korap.web.KustvaktServer
                                     </mainClass>
                                 </transformer>
                                 <transformer
@@ -105,6 +189,18 @@
             <version>1.8</version>
         </dependency>
         <dependency>
+            <groupId>com.sun.jersey.jersey-test-framework</groupId>
+            <artifactId>jersey-test-framework-core</artifactId>
+            <version>1.19</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.jersey.jersey-test-framework</groupId>
+            <artifactId>jersey-test-framework-grizzly</artifactId>
+            <version>1.19</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
             <version>1.7.5</version>
@@ -128,11 +224,13 @@
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <version>4.11</version>
+            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>
             <version>0.11.8</version>
+            <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>joda-time</groupId>
@@ -150,16 +248,24 @@
                 </exclusion>
             </exclusions>
         </dependency>
-        <dependency>
-            <groupId>com.sun.jersey</groupId>
-            <artifactId>jersey-grizzly2</artifactId>
-            <version>1.8</version>
-        </dependency>
-        <dependency>
-            <groupId>com.sun.grizzly</groupId>
-            <artifactId>grizzly-servlet-webserver</artifactId>
-            <version>1.9.18-i</version>
-        </dependency>
+        <!--
+                <dependency>
+                    <groupId>com.sun.jersey</groupId>
+                    <artifactId>jersey-grizzly2</artifactId>
+                    <version>1.8</version>
+                </dependency>
+                <dependency>
+                    <groupId>com.sun.grizzly</groupId>
+                    <artifactId>grizzly-servlet-webserver</artifactId>
+                    <version>1.9.18-i</version>
+                </dependency>
+                -->
+
+        <!--<dependency>-->
+        <!--<groupId>org.xerial</groupId>-->
+        <!--<artifactId>sqlite-jdbc</artifactId>-->
+        <!--<version>3.7.2</version>-->
+        <!--</dependency>-->
         <dependency>
             <groupId>com.nimbusds</groupId>
             <artifactId>nimbus-jose-jwt</artifactId>
@@ -182,18 +288,20 @@
             <artifactId>reflections</artifactId>
             <version>0.9.9-RC1</version>
         </dependency>
+        <!-- deprecated? -->
         <dependency>
             <groupId>com.restfuse</groupId>
             <artifactId>com.eclipsesource.restfuse</artifactId>
             <version>1.0.0</version>
             <scope>provided</scope>
         </dependency>
-        <dependency>
-            <groupId>com.jayway.restassured</groupId>
-            <artifactId>rest-assured</artifactId>
-            <version>2.4.0</version>
-            <scope>provided</scope>
-        </dependency>
+        <!-- deprecated -->
+        <!--<dependency>-->
+        <!--<groupId>com.jayway.restassured</groupId>-->
+        <!--<artifactId>rest-assured</artifactId>-->
+        <!--<version>2.4.0</version>-->
+        <!--<scope>provided</scope>-->
+        <!--</dependency>-->
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-core</artifactId>
@@ -222,11 +330,54 @@
             <artifactId>spring-aspects</artifactId>
             <version>4.0.5.RELEASE</version>
         </dependency>
+
+        <!-- copied from extension -->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-jdbc</artifactId>
+            <version>4.1.6.RELEASE</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-tx</artifactId>
+            <version>4.1.6.RELEASE</version>
+        </dependency>
+        <dependency>
+            <groupId>org.flywaydb</groupId>
+            <artifactId>flyway-core</artifactId>
+            <version>3.2.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>net.sf.ehcache</groupId>
+            <artifactId>ehcache-core</artifactId>
+            <version>2.6.5</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.oltu.oauth2</groupId>
+            <artifactId>org.apache.oltu.oauth2.authzserver</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+
+        <!-- end copy -->
+
         <dependency>
             <groupId>commons-collections</groupId>
             <artifactId>commons-collections</artifactId>
             <version>3.2.1</version>
         </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-server</artifactId>
+            <version>8.1.8.v20121106</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-servlet</artifactId>
+            <version>8.1.8.v20121106</version>
+        </dependency>
+
         <!--
         not part of public release
         <dependency>
diff --git a/src/.DS_Store b/src/.DS_Store
index 16a96aa..f62e292 100644
--- a/src/.DS_Store
+++ b/src/.DS_Store
Binary files differ
diff --git a/src/main/.DS_Store b/src/main/.DS_Store
index d94e183..d4fd3e7 100644
--- a/src/main/.DS_Store
+++ b/src/main/.DS_Store
Binary files differ
diff --git a/src/main/java/de/ids_mannheim/korap/auditing/AuditRecord.java b/src/main/java/de/ids_mannheim/korap/auditing/AuditRecord.java
index 28f915d..8e7169c 100644
--- a/src/main/java/de/ids_mannheim/korap/auditing/AuditRecord.java
+++ b/src/main/java/de/ids_mannheim/korap/auditing/AuditRecord.java
@@ -8,6 +8,7 @@
 import lombok.Setter;
 
 import java.util.Arrays;
+import java.util.Date;
 
 /**
  * @author hanl
@@ -18,7 +19,7 @@
 @Setter
 public class AuditRecord {
 
-    // fixme: handle via status codes
+    // todo: handle via status codes
     @Deprecated
     public enum Operation {
         GET, INSERT, UPDATE, DELETE, CREATE
@@ -100,16 +101,19 @@
         return userAgent + "@" + IP;
     }
 
+    // fixme: add id, useragent
     @Override
     public String toString() {
-        return "Record{" +
-                "account='" + userid + '\'' +
-                ", category=" + category +
-                ", loc='" + loc + '\'' +
-                ", timestamp=" + timestamp +
-                ", status='" + status + '\'' +
-                ", field_1='" + field_1 + '\'' +
-                '}';
+        StringBuilder b = new StringBuilder();
+        b.append(category.toString().toLowerCase() + " audit : ")
+                .append(userid + "@" + new Date(timestamp)).append("\n")
+                .append("Status " + status).append("; ");
+
+        if (this.args != null)
+            b.append("Args " + field_1).append("; ");
+        if (this.loc != null)
+            b.append("Location " + loc).append("; ");
+        return b.toString();
     }
 
     @Override
diff --git a/src/main/java/de/ids_mannheim/korap/config/AuditingHandler.java b/src/main/java/de/ids_mannheim/korap/config/AuditingHandler.java
deleted file mode 100644
index 30d08f1..0000000
--- a/src/main/java/de/ids_mannheim/korap/config/AuditingHandler.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package de.ids_mannheim.korap.config;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * @author hanl
- * @date 17/06/2015
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
-public @interface AuditingHandler {
-}
diff --git a/src/main/java/de/ids_mannheim/korap/config/AuthenticationHandler.java b/src/main/java/de/ids_mannheim/korap/config/AuthenticationHandler.java
deleted file mode 100644
index d712490..0000000
--- a/src/main/java/de/ids_mannheim/korap/config/AuthenticationHandler.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package de.ids_mannheim.korap.config;
-
-/**
- * @author hanl
- * @date 17/06/2015
- */
-public @interface AuthenticationHandler {
-}
diff --git a/src/main/java/de/ids_mannheim/korap/config/BeanConfiguration.java b/src/main/java/de/ids_mannheim/korap/config/BeanConfiguration.java
index 11b8b60..028a9f4 100644
--- a/src/main/java/de/ids_mannheim/korap/config/BeanConfiguration.java
+++ b/src/main/java/de/ids_mannheim/korap/config/BeanConfiguration.java
@@ -1,10 +1,15 @@
 package de.ids_mannheim.korap.config;
 
-import de.ids_mannheim.korap.interfaces.AuditingIface;
+import de.ids_mannheim.korap.interfaces.*;
+import de.ids_mannheim.korap.web.utils.KustvaktResponseHandler;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.support.ClassPathXmlApplicationContext;
 import org.springframework.context.support.FileSystemXmlApplicationContext;
 
+import java.util.HashSet;
+import java.util.Set;
+
 /**
  * User: hanl
  * Date: 10/9/13
@@ -12,44 +17,141 @@
  */
 public class BeanConfiguration {
 
-    private static final String config_file = "default-config.xml";
+    private static final String config_file = "light-config.xml";
+    public static final String KUSTVAKT_DB = "kustvakt_db";
 
-    private static ApplicationContext context = null;
+    public static final String KUSTVAKT_ENCRYPTION = "kustvakt_encryption";
+    public static final String KUSTVAKT_AUDITING = "kustvakt_auditing";
+    public static final String KUSTVAKT_CONFIG = "kustvakt_config";
 
-    public static void loadClasspathContext(String... files) {
-        if (context == null && files.length == 0)
-            context = new ClassPathXmlApplicationContext(config_file);
-        else if (context == null)
-            context = new ClassPathXmlApplicationContext(files);
+    private static BeanHolderHelper beans;
+
+    public static void setCustomBeansHolder(BeanHolderHelper holder) {
+        ApplicationContext context = beans.context;
+        holder.context = context;
+        BeanConfiguration.beans = holder;
     }
 
-    public static void loadFileContext(String filepath) {
-        if (context == null)
-            context = new FileSystemXmlApplicationContext("file:" + filepath);
+    public static BeanHolderHelper getBeans() {
+        return BeanConfiguration.beans;
     }
 
-    public static <T extends KustvaktConfiguration> T getConfiguration() {
-        return (T) getBean("config");
-    }
-
-    public static <T extends KustvaktConfiguration> T getConfiguration(
-            Class<T> clazz) {
-        return getBean(clazz);
+    @Deprecated
+    public static void loadAuthenticationProviders() {
+        Set<Class<? extends AuthenticationIface>> set = KustvaktClassLoader
+                .loadSubTypes(AuthenticationIface.class);
+        Set<AuthenticationIface> set2 = new HashSet<>();
+        for (Class<? extends AuthenticationIface> i : set) {
+            try {
+                set2.add(i.newInstance());
+            }catch (InstantiationException | IllegalAccessException e) {
+                e.printStackTrace();
+            }
+        }
+        try {
+            getBeans().getAuthenticationManager().setProviders(set2);
+        }catch (RuntimeException e) {
+            // do nothing
+        }
     }
 
     public static boolean hasContext() {
-        return context != null;
+        return beans != null;
     }
 
-    protected static <T> T getBean(Class<T> clazz) {
-        return context.getBean(clazz);
+    public static void loadClasspathContext(String... files) {
+        if (beans == null) {
+            ApplicationContext context;
+            if (files.length == 0)
+                context = new ClassPathXmlApplicationContext(config_file);
+            else
+                context = new ClassPathXmlApplicationContext(files);
+            BeanConfiguration.beans = new BeanHolderHelper(context);
+        }
     }
 
-    protected static <T> T getBean(String name) {
-        return (T) context.getBean(name);
+    public static void loadFileContext(String filepath) {
+        if (beans == null) {
+            ApplicationContext context = new FileSystemXmlApplicationContext(
+                    "file:" + filepath);
+            BeanConfiguration.beans = new BeanHolderHelper(context);
+        }
     }
 
-    public static AuditingIface getAuditingProvider() {
-        return (AuditingIface) context.getBean("auditingProvider");
+    public static void closeApplication() {
+        beans.finish();
+    }
+
+    //todo: set response handler
+    @Deprecated
+    public static KustvaktResponseHandler getResponseHandler() {
+        return null;
+    }
+
+    public static class BeanHolderHelper {
+
+        private ApplicationContext context = null;
+        private DefaultHandler handler;
+
+        public BeanHolderHelper() {
+            this.handler = new DefaultHandler();
+        }
+
+        private BeanHolderHelper(ApplicationContext context) {
+            this();
+            this.context = context;
+        }
+
+        protected <T> T getBean(Class<T> clazz) {
+            if (context != null) {
+                try {
+                    return context.getBean(clazz);
+                }catch (NoSuchBeanDefinitionException e) {
+                    // do nothing
+                }
+            }
+            return this.handler.getDefault(clazz);
+        }
+
+        protected <T> T getBean(String name) {
+            if (context != null) {
+                try {
+                    return (T) context.getBean(name);
+                }catch (NoSuchBeanDefinitionException e) {
+                    // do nothing
+                }
+            }
+            return (T) this.handler.getDefault(name);
+        }
+
+        public AuditingIface getAuditingProvider() {
+            return (AuditingIface) context.getBean(KUSTVAKT_AUDITING);
+        }
+
+        public <T extends KustvaktConfiguration> T getConfiguration() {
+            return (T) getBean(KUSTVAKT_CONFIG);
+        }
+
+        public PersistenceClient getPersistenceClient() {
+            return getBean(KUSTVAKT_DB);
+        }
+
+        public AuthenticationManagerIface getAuthenticationManager() {
+            throw new RuntimeException("!Stub");
+        }
+
+        public EntityHandlerIface getUserDBHandler() {
+            throw new RuntimeException("!Stub");
+        }
+
+        public EncryptionIface getEncryption() {
+            return getBean(KUSTVAKT_ENCRYPTION);
+        }
+
+        public void finish() {
+            this.getAuditingProvider().finish();
+            context = null;
+        }
+
     }
 }
diff --git a/src/main/java/de/ids_mannheim/korap/config/Configurable.java b/src/main/java/de/ids_mannheim/korap/config/Configurable.java
new file mode 100644
index 0000000..af656a2
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/config/Configurable.java
@@ -0,0 +1,13 @@
+package de.ids_mannheim.korap.config;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * @author hanl
+ * @date 27/07/2015
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Configurable {
+    String value();
+}
diff --git a/src/main/java/de/ids_mannheim/korap/config/DefaultHandler.java b/src/main/java/de/ids_mannheim/korap/config/DefaultHandler.java
new file mode 100644
index 0000000..38af9f3
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/config/DefaultHandler.java
@@ -0,0 +1,54 @@
+package de.ids_mannheim.korap.config;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author hanl
+ * @date 17/06/2015
+ */
+public class DefaultHandler {
+
+    private Map<String, Object> defaults;
+
+    public DefaultHandler() {
+        this.defaults = new HashMap<>();
+        loadClasses();
+    }
+
+    private void loadClasses() {
+        Set<Class<?>> cls = KustvaktClassLoader
+                .loadFromAnnotation(Configurable.class);
+        for (Class clazz : cls) {
+            Configurable c = (Configurable) clazz
+                    .getAnnotation(Configurable.class);
+            try {
+                this.defaults.put(c.value(), clazz.newInstance());
+            }catch (InstantiationException | IllegalAccessException e) {
+                throw new RuntimeException("Could not instantiate class");
+            }
+        }
+    }
+
+    public Object getDefault(String name) {
+        return this.defaults.get(name);
+    }
+
+    public <T> T getDefault(Class<T> tClass) {
+        for (Object o : this.defaults.values()) {
+            if (o.getClass().equals(tClass))
+                return (T) o;
+        }
+        return null;
+    }
+
+    public void remove(String name) {
+        this.defaults.remove(name);
+    }
+
+    @Override
+    public String toString() {
+        return defaults.toString();
+    }
+}
diff --git a/src/main/java/de/ids_mannheim/korap/config/EncryptionHandler.java b/src/main/java/de/ids_mannheim/korap/config/EncryptionHandler.java
deleted file mode 100644
index b0a28ac..0000000
--- a/src/main/java/de/ids_mannheim/korap/config/EncryptionHandler.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package de.ids_mannheim.korap.config;
-
-/**
- * @author hanl
- * @date 18/06/2015
- */
-public @interface EncryptionHandler {
-}
diff --git a/src/main/java/de/ids_mannheim/korap/config/KustvaktClassLoader.java b/src/main/java/de/ids_mannheim/korap/config/KustvaktClassLoader.java
index 4cc7f12..ed3b832 100644
--- a/src/main/java/de/ids_mannheim/korap/config/KustvaktClassLoader.java
+++ b/src/main/java/de/ids_mannheim/korap/config/KustvaktClassLoader.java
@@ -14,6 +14,10 @@
     private static final Reflections reflections = new Reflections(
             "de.ids_mannheim.korap");
 
+
+    private KustvaktClassLoader() {
+
+    }
     /**
      * loads interface implementations in current classpath
      *
@@ -21,7 +25,7 @@
      * @param <T>
      * @return
      */
-    public static <T> Set<Class<? extends T>> load(Class<T> iface) {
+    public static <T> Set<Class<? extends T>> loadSubTypes(Class<T> iface) {
         return reflections.getSubTypesOf(iface);
     }
 
diff --git a/src/main/java/de/ids_mannheim/korap/config/KustvaktConfiguration.java b/src/main/java/de/ids_mannheim/korap/config/KustvaktConfiguration.java
index bac1486..49eb4df 100644
--- a/src/main/java/de/ids_mannheim/korap/config/KustvaktConfiguration.java
+++ b/src/main/java/de/ids_mannheim/korap/config/KustvaktConfiguration.java
@@ -1,6 +1,6 @@
 package de.ids_mannheim.korap.config;
 
-import de.ids_mannheim.korap.utils.KorAPLogger;
+import de.ids_mannheim.korap.utils.KustvaktLogger;
 import lombok.Getter;
 import org.apache.log4j.PropertyConfigurator;
 import org.slf4j.Logger;
@@ -14,7 +14,7 @@
 import java.util.Properties;
 
 /**
- * if configuration class is extended, load method should be overriden
+ * if configuration class is extended, loadSubTypes method should be overriden
  *
  * @author hanl
  * @date 05/02/2014
@@ -23,7 +23,7 @@
 @Getter
 public class KustvaktConfiguration {
 
-    private final Logger jlog = KorAPLogger
+    private final Logger jlog = KustvaktLogger
             .initiate(KustvaktConfiguration.class);
     private String indexDir;
     private int port;
@@ -115,7 +115,7 @@
     }
 
     private void loadLog4jLogger(String log4jconfig) {
-        /** load log4j configuration file programmatically */
+        /** loadSubTypes log4j configuration file programmatically */
         Properties log4j = new Properties();
         try {
             if (!log4jconfig.equals("")) {
diff --git a/src/main/java/de/ids_mannheim/korap/config/ParamFields.java b/src/main/java/de/ids_mannheim/korap/config/ParamFields.java
new file mode 100644
index 0000000..f796ca7
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/config/ParamFields.java
@@ -0,0 +1,41 @@
+package de.ids_mannheim.korap.config;
+
+import lombok.Getter;
+
+import java.util.Collection;
+import java.util.HashMap;
+
+/**
+ * @author hanl
+ * @date 21/07/2015
+ */
+// could also be an array or list!
+public class ParamFields extends HashMap<String, ParamFields.Param> {
+
+    public void add(Param param) {
+        this.put(Param.class.getName(), param);
+    }
+
+    public <T extends Param> T get(Class<T> cl) {
+        return (T) this.get(cl.getName());
+    }
+
+    public <T extends Param> T remove(Class<T> cl) {
+        return (T) this.remove(cl.getName());
+    }
+
+    public void addAll(Collection<Param> params) {
+        for (Param p : params)
+            super.put(p.getClass().getName(), p);
+    }
+
+    @Getter
+    public abstract static class Param {
+
+        public boolean hasValues(){
+            return false;
+        }
+
+    }
+
+}
diff --git a/src/main/java/de/ids_mannheim/korap/config/PluginHandler.java b/src/main/java/de/ids_mannheim/korap/config/PluginHandler.java
deleted file mode 100644
index 2944aa1..0000000
--- a/src/main/java/de/ids_mannheim/korap/config/PluginHandler.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package de.ids_mannheim.korap.config;
-
-import de.ids_mannheim.korap.interfaces.AuditingIface;
-import de.ids_mannheim.korap.interfaces.AuthenticationManagerIface;
-import de.ids_mannheim.korap.interfaces.EncryptionIface;
-import de.ids_mannheim.korap.interfaces.EntityHandlerIface;
-
-import java.lang.annotation.Annotation;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * @author hanl
- * @date 17/06/2015
- */
-public class PluginHandler {
-
-    private static Map<Class<? extends Annotation>, Class> interfaces;
-    private Map<Class<? extends Annotation>, Object> plugins;
-
-    // add resource handler annotation
-    static {
-        interfaces = new HashMap<>();
-        interfaces.put(AuditingHandler.class, AuditingIface.class);
-        interfaces.put(UserDbHandler.class, EntityHandlerIface.class);
-        interfaces.put(AuthenticationHandler.class,
-                AuthenticationManagerIface.class);
-        interfaces.put(EncryptionHandler.class, EncryptionIface.class);
-        //todo:
-        interfaces.put(ResourceHandler.class, ResourceHandler.class);
-    }
-
-    public PluginHandler() {
-        this.plugins = new HashMap<>();
-        this.load();
-    }
-
-    public void load() {
-        for (Map.Entry<Class<? extends Annotation>, Class> en : new HashSet<>(
-                interfaces.entrySet())) {
-            Set<Class<?>> set = KustvaktClassLoader
-                    .loadFromAnnotation(en.getKey());
-            if (set.size() > 1)
-                throw new UnsupportedOperationException(
-                        "handler declaration not unique!");
-            else if (set.size() == 0)
-                interfaces.remove(en.getKey());
-        }
-    }
-
-    public void addInterface(Class<? extends Annotation> anno, Class<?> iface) {
-        interfaces.put(anno, iface);
-    }
-
-    public void registerPlugin(Object ob) {
-        for (Map.Entry<Class<? extends Annotation>, Class> en : interfaces
-                .entrySet()) {
-            if (en.getValue().isInstance(ob))
-                this.plugins.put(en.getKey(), ob);
-        }
-    }
-
-    public Object getPluginInstance(Class<? extends Annotation> anno) {
-        Object o = this.plugins.get(anno);
-        if (o == null)
-            return new NullPointerException(
-                    "no plugin defined for type " + anno.toString());
-        return o;
-    }
-
-    @Override
-    public String toString() {
-        System.out.println("PRINT INTERFACES " + interfaces.toString());
-        return plugins.toString();
-    }
-}
diff --git a/src/main/java/de/ids_mannheim/korap/config/ProtectedResource.java b/src/main/java/de/ids_mannheim/korap/config/ProtectedResource.java
deleted file mode 100644
index 2d24c8b..0000000
--- a/src/main/java/de/ids_mannheim/korap/config/ProtectedResource.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package de.ids_mannheim.korap.config;
-
-/**
- * @author hanl
- * @date 18/06/2015
- */
-public @interface ProtectedResource {
-}
diff --git a/src/main/java/de/ids_mannheim/korap/config/ResourceHandler.java b/src/main/java/de/ids_mannheim/korap/config/ResourceHandler.java
deleted file mode 100644
index aae143f..0000000
--- a/src/main/java/de/ids_mannheim/korap/config/ResourceHandler.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package de.ids_mannheim.korap.config;
-
-/**
- * @author hanl
- * @date 18/06/2015
- */
-public @interface ResourceHandler {
-}
diff --git a/src/main/java/de/ids_mannheim/korap/config/UserDbHandler.java b/src/main/java/de/ids_mannheim/korap/config/UserDbHandler.java
deleted file mode 100644
index 304f16c..0000000
--- a/src/main/java/de/ids_mannheim/korap/config/UserDbHandler.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package de.ids_mannheim.korap.config;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * @author hanl
- * @date 17/06/2015
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
-public @interface UserDbHandler {
-}
diff --git a/src/main/java/de/ids_mannheim/korap/exceptions/KorAPException.java b/src/main/java/de/ids_mannheim/korap/exceptions/KorAPException.java
deleted file mode 100644
index d3435f7..0000000
--- a/src/main/java/de/ids_mannheim/korap/exceptions/KorAPException.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package de.ids_mannheim.korap.exceptions;
-
-import lombok.Getter;
-import lombok.Setter;
-
-/**
- * @author hanl
- * @date 11/12/2013
- */
-//fixme: redundant with baseexception
-@Deprecated
-@Setter
-@Getter
-public class KorAPException extends BaseException {
-
-    private String userid;
-
-    public KorAPException(Integer status) {
-        super(status);
-    }
-
-    public KorAPException(Object userid, Integer status) {
-        this(status);
-        this.userid = String.valueOf(userid);
-    }
-
-    public KorAPException(Object userid, Integer status, String message,
-            String entity) {
-        super(status, message, entity);
-        this.userid = String.valueOf(userid);
-    }
-
-    public KorAPException(Integer status, String message, String entity) {
-        super(status, message, entity);
-    }
-
-    public KorAPException(Throwable cause, Integer status) {
-        super(cause, status);
-    }
-
-    public KorAPException(String message, Throwable cause, Integer status) {
-        super(message, cause, status);
-    }
-
-    @Override
-    public String toString() {
-        return "Excpt{" +
-                "status=" + getStatusCode() +
-                ", message=" + getMessage() +
-                ", args=" + getEntity() +
-                ", userid=" + userid +
-                '}';
-    }
-}
diff --git a/src/main/java/de/ids_mannheim/korap/exceptions/KustvaktException.java b/src/main/java/de/ids_mannheim/korap/exceptions/KustvaktException.java
new file mode 100644
index 0000000..1c0c5aa
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/exceptions/KustvaktException.java
@@ -0,0 +1,61 @@
+package de.ids_mannheim.korap.exceptions;
+
+import de.ids_mannheim.korap.auditing.AuditRecord;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author hanl
+ * @date 11/12/2013
+ */
+//fixme: redundant with baseexception
+@Deprecated
+@Setter
+@Getter
+public class KustvaktException extends Exception {
+
+    protected List<AuditRecord> records = new ArrayList<>();
+    private String userid;
+    private Integer statusCode;
+    private String entity;
+
+    public KustvaktException(Integer status) {
+        this.statusCode = status;
+    }
+
+    public KustvaktException(Object userid, Integer status) {
+        this(status);
+        this.userid = String.valueOf(userid);
+    }
+
+    public KustvaktException(Object userid, Integer status, String message,
+            String entity) {
+        super(status, message, entity);
+        this.userid = String.valueOf(userid);
+    }
+
+    public KustvaktException(Integer status, String message, String entity) {
+        super(status, message, entity);
+    }
+
+    public KustvaktException(Throwable cause, Integer status) {
+        super(cause, status);
+    }
+
+    public KustvaktException(String message, Throwable cause, Integer status) {
+        super(message, cause, status);
+    }
+
+    @Override
+    public String toString() {
+        return "Excpt{" +
+                "status=" + getStatusCode() +
+                ", message=" + getMessage() +
+                ", args=" + getEntity() +
+                ", userid=" + userid +
+                '}';
+    }
+}
diff --git a/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java b/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
index 6db7b0e..bb2753b 100644
--- a/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
+++ b/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
@@ -35,6 +35,7 @@
     public static final Integer NAME_EXISTS = 207;
     public static final Integer PASSWORD_RESET_FAILED = 208;
     // fixme: ?!
+    @Deprecated
     public static final Integer AUTHENTICATION_DENIED = 209;
 
     public static final Integer LOGIN_SUCCESSFUL = 210;
diff --git a/src/main/java/de/ids_mannheim/korap/handlers/BatchBuilder.java b/src/main/java/de/ids_mannheim/korap/handlers/BatchBuilder.java
new file mode 100644
index 0000000..17f3616
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/handlers/BatchBuilder.java
@@ -0,0 +1,70 @@
+package de.ids_mannheim.korap.handlers;
+
+import de.ids_mannheim.korap.utils.KustvaktLogger;
+import org.slf4j.Logger;
+import org.springframework.dao.DataAccessException;
+import org.springframework.jdbc.core.JdbcOperations;
+import org.springframework.jdbc.core.RowMapper;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author hanl
+ * @date 24/03/2014
+ */
+public class BatchBuilder {
+
+    private static final int SINGLE_BATCH = 1;
+    private static final int SMALL_BATCH = 4;
+    private static final int SMALL_MEDIUM_BATCH = 6;
+    private static final int MEDIUM_BATCH = 8;
+    private static final int LARGE_BATCH = 12;
+    private Logger log = KustvaktLogger.initiate(BatchBuilder.class);
+
+    private JdbcOperations operations;
+
+    public BatchBuilder(JdbcOperations operations) {
+        this.operations = operations;
+    }
+
+    public <T> List<T> selectFromIDs(String query, Collection ids, RowMapper<T> mapper) {
+        List l = new ArrayList(ids);
+        int size = ids.size();
+        List<T> values = new ArrayList<>();
+        while (size > 0) {
+            int batchSize = SINGLE_BATCH;
+            if (size >= LARGE_BATCH)
+                batchSize = LARGE_BATCH;
+            else if (size >= MEDIUM_BATCH)
+                batchSize = MEDIUM_BATCH;
+            else if (size >= SMALL_MEDIUM_BATCH)
+                batchSize = SMALL_MEDIUM_BATCH;
+            else if (size >= SMALL_BATCH)
+                batchSize = SMALL_BATCH;
+            size -= batchSize;
+            StringBuilder inClause = new StringBuilder();
+            for (int i = 0; i < batchSize; i++) {
+                inClause.append('?');
+                inClause.append(',');
+            }
+            inClause.deleteCharAt(inClause.length() - 1);
+            String sql = query + " (" + inClause.toString() + ");";
+            Object[] args = new Object[batchSize];
+            List d = new ArrayList();
+            for (int idx = 0; idx < batchSize; idx++) {
+                args[idx] = l.get(idx);
+                d.add(idx, args[idx]);
+            }
+            l.removeAll(d);
+            try {
+                values.addAll(this.operations.query(sql, args, mapper));
+            } catch (DataAccessException e) {
+                log.error("Exception during database retrieval", e);
+            }
+
+        }
+        return values;
+    }
+}
diff --git a/src/main/java/de/ids_mannheim/korap/handlers/CollectionDao.java b/src/main/java/de/ids_mannheim/korap/handlers/CollectionDao.java
new file mode 100644
index 0000000..7f4fb22
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/handlers/CollectionDao.java
@@ -0,0 +1,167 @@
+package de.ids_mannheim.korap.handlers;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.ext.interfaces.ResourceOperationIface;
+import de.ids_mannheim.korap.ext.resource.KorAPResource;
+import de.ids_mannheim.korap.ext.resource.VirtualCollection;
+import de.ids_mannheim.korap.interfaces.PersistenceClient;
+import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.utils.KustvaktLogger;
+import org.slf4j.Logger;
+import org.springframework.dao.DataAccessException;
+import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author hanl
+ * @date 11/01/2014
+ */
+
+//todo?! cache with ehcache and put token/sentence/paragraph numbers into cache
+public class CollectionDao
+        implements ResourceOperationIface<VirtualCollection> {
+
+    private static Logger log = KustvaktLogger.initiate(CollectionDao.class);
+    private BatchBuilder batchBuilder;
+    protected final NamedParameterJdbcTemplate jdbcTemplate;
+
+    public CollectionDao(PersistenceClient client) {
+        this.jdbcTemplate = (NamedParameterJdbcTemplate) client.getSource();
+        this.batchBuilder = new BatchBuilder(
+                this.jdbcTemplate.getJdbcOperations());
+    }
+
+    @Override
+    public Class<VirtualCollection> getType() {
+        return VirtualCollection.class;
+    }
+
+    // fixme: persistentid can be done, persistence is achieved by specifing a date until which documents
+    // are to be included. this excludes documents that are part of the "sperreinträge"
+    public <T extends KorAPResource> T findbyId(String id, User user)
+            throws KustvaktException {
+        MapSqlParameterSource source = new MapSqlParameterSource();
+        source.addValue("id", id);
+        source.addValue("user", user.getId());
+        final String sql = "select * from coll_store where persistentID=:id and userID=:user;";
+        try {
+            return (T) this.jdbcTemplate.queryForObject(sql, source,
+                    new RowMapperFactory.CollectionMapper());
+        }catch (DataAccessException e) {
+            log.error("Exception during database get for id '" + id + "'", e);
+            return null;
+        }
+    }
+
+    public VirtualCollection findbyId(Integer id, User user)
+            throws KustvaktException {
+        MapSqlParameterSource source = new MapSqlParameterSource();
+        source.addValue("id", id);
+        final String sql = "select * from coll_store where id=:id";
+        try {
+            return this.jdbcTemplate.queryForObject(sql, source,
+                    new RowMapperFactory.CollectionMapper());
+        }catch (DataAccessException e) {
+            log.error("Exception during database get for id '" + id + "'", e);
+            return null;
+        }
+    }
+
+    public int updateResource(VirtualCollection resource, User user)
+            throws KustvaktException {
+        MapSqlParameterSource np = new MapSqlParameterSource();
+        np.addValue("id", resource.getPersistentID());
+        np.addValue("qy", resource.getQuery());
+        np.addValue("name", resource.getName());
+        np.addValue("desc", resource.getDescription());
+        final String sql = "UPDATE coll_store SET query=:qy, name=:name, description=:desc WHERE persistentID=:id;";
+        try {
+            return this.jdbcTemplate.update(sql, np);
+        }catch (DataAccessException e) {
+            log.error("Exception during database update for id '" + resource
+                    .getId() + "'", e);
+            throw new KustvaktException(e, StatusCodes.CONNECTION_ERROR);
+        }
+    }
+
+    public int[] updateResources(List<VirtualCollection> resources, User user)
+            throws KustvaktException {
+        MapSqlParameterSource[] sources = new MapSqlParameterSource[resources
+                .size()];
+        final String sql = "UPDATE coll_store SET query=:qy, name=:name, description=:desc WHERE persistentID=:id;";
+        int i = 0;
+        for (VirtualCollection c : resources) {
+            MapSqlParameterSource np = new MapSqlParameterSource();
+            np.addValue("id", c.getPersistentID());
+            np.addValue("qy", c.getQuery());
+            np.addValue("name", c.getName());
+            np.addValue("desc", c.getDescription());
+            sources[i++] = np;
+        }
+        try {
+            return this.jdbcTemplate.batchUpdate(sql, sources);
+        }catch (DataAccessException e) {
+            log.error("Exception during database update", e);
+            throw new KustvaktException(e, StatusCodes.CONNECTION_ERROR);
+        }
+    }
+
+    @Override
+    public int storeResource(VirtualCollection resource, User user)
+            throws KustvaktException {
+        if (resource.getQuery().length() > 3) {
+            MapSqlParameterSource np = new MapSqlParameterSource();
+            np.addValue("query", resource.getQuery());
+            np.addValue("pid", resource.getPersistentID());
+            np.addValue("name", resource.getName());
+            np.addValue("desc", resource.getDescription());
+            np.addValue("us", user.getId());
+
+            final String sql =
+                    "INSERT INTO coll_store (persistentID, name, description, userID, query) "
+                            + "VALUES (:pid, :name, :desc, :us, :query);";
+            try {
+                return this.jdbcTemplate.update(sql, np);
+            }catch (DataAccessException e) {
+                log.error("Exception during database store for id '" + resource
+                        .getId() + "'", e);
+                throw new KustvaktException(e, StatusCodes.CONNECTION_ERROR);
+            }
+        }else
+            throw new KustvaktException(StatusCodes.ILLEGAL_ARGUMENT,
+                    "invalid query parameter", resource.getQuery());
+    }
+
+    public int deleteResource(String id, User user) throws KustvaktException {
+        //todo: foreign key and on delete cascade does not work currently!
+        MapSqlParameterSource np = new MapSqlParameterSource();
+        np.addValue("id", id);
+        np.addValue("user", user.getId());
+        //        final String sql = "DELETE FROM coll_store cs inner join r_store rs on rs.id=cs.id WHERE rs.persistentID=:id;";
+        final String sql = "DELETE FROM coll_store where persistentID=:id and user=:user;";
+        try {
+            return this.jdbcTemplate.update(sql, np);
+        }catch (DataAccessException e) {
+            log.error("Exception during database delete for id '" + id + "'",
+                    e);
+            throw new KustvaktException(e, StatusCodes.CONNECTION_ERROR);
+        }
+    }
+
+    //todo: adjust to resource id input (batch operation!)
+    // fixme: test
+    public List<VirtualCollection> getResources(Collection<Object> resources,
+            User user) throws KustvaktException {
+        final String sql1 = "SELECT * from coll_store where id in";
+        //        final String sql =
+        //                "SELECT rs.*, rt.name_path, cs.query FROM r_store as rs inner join r_tree as rt on rs.id=rt.childID "
+        //                        + "inner join coll_store as cs on cs.id=rs.id WHERE rs.id in";
+        return batchBuilder.selectFromIDs(sql1, resources,
+                new RowMapperFactory.CollectionMapper());
+    }
+
+}
diff --git a/src/main/java/de/ids_mannheim/korap/handlers/DocumentDao.java b/src/main/java/de/ids_mannheim/korap/handlers/DocumentDao.java
new file mode 100644
index 0000000..111c282
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/handlers/DocumentDao.java
@@ -0,0 +1,183 @@
+package de.ids_mannheim.korap.handlers;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.ext.interfaces.ResourceOperationIface;
+import de.ids_mannheim.korap.ext.resource.Document;
+import de.ids_mannheim.korap.interfaces.PersistenceClient;
+import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.utils.BooleanUtils;
+import org.springframework.dao.DataAccessException;
+import org.springframework.jdbc.core.ResultSetExtractor;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author hanl
+ * @date 05/11/2014
+ */
+// todo: testing!
+// todo: error handling
+public class DocumentDao implements ResourceOperationIface<Document> {
+
+    private NamedParameterJdbcTemplate jdbcTemplate;
+
+    public DocumentDao(PersistenceClient client) {
+        this.jdbcTemplate = (NamedParameterJdbcTemplate) client.getSource();
+    }
+
+    @Override
+    public Class<Document> getType() {
+        return Document.class;
+    }
+
+    @Override
+    public Document findbyId(Integer id, User user) throws KustvaktException {
+        MapSqlParameterSource s = new MapSqlParameterSource();
+        s.addValue("id", id);
+        String sql = "select * from doc_store where id=:id";
+        try {
+            return this.jdbcTemplate
+                    .query(sql, s, new ResultSetExtractor<Document>() {
+                        @Override
+                        public Document extractData(ResultSet rs)
+                                throws SQLException, DataAccessException {
+                            Document doc = new Document(
+                                    rs.getString("persistentID"));
+                            doc.setId(rs.getInt("id"));
+                            doc.setCreated(
+                                    rs.getTimestamp("created").getTime());
+                            doc.setDisabled(rs.getBoolean("disabled"));
+                            return doc;
+                        }
+                    });
+        }catch (DataAccessException e) {
+            throw new KustvaktException(StatusCodes.CONNECTION_ERROR);
+        }
+    }
+
+    // document id, consisting of corpus sigle, substring key and document number
+    @Override
+    public Document findbyId(String id, User user) throws KustvaktException {
+        MapSqlParameterSource s = new MapSqlParameterSource();
+        s.addValue("id", id);
+        String sql = "select * from doc_store where persistentID=:id";
+
+        try {
+            return this.jdbcTemplate
+                    .query(sql, s, new ResultSetExtractor<Document>() {
+                        @Override
+                        public Document extractData(ResultSet rs)
+                                throws SQLException, DataAccessException {
+                            Document doc = new Document(
+                                    rs.getString("persistentID"));
+                            doc.setId(rs.getInt("id"));
+                            doc.setCreated(
+                                    rs.getTimestamp("created").getTime());
+                            doc.setDisabled(rs.getBoolean("disabled"));
+                            return doc;
+                        }
+                    });
+        }catch (DataAccessException e) {
+            throw new KustvaktException(StatusCodes.CONNECTION_ERROR);
+        }
+    }
+
+    @Override
+    public List<Document> getResources(Collection<Object> ids, User user)
+            throws KustvaktException {
+        return null;
+    }
+
+    @Override
+    public int updateResource(Document document, User user)
+            throws KustvaktException {
+        MapSqlParameterSource source = new MapSqlParameterSource();
+        source.addValue("pid", document.getPersistentID());
+        source.addValue("dis", BooleanUtils.getBoolean(document.isDisabled()));
+        final String sql = "UPDATE doc_store set disabled=:dis where persistentID=:pid;";
+        return this.jdbcTemplate.update(sql, source);
+    }
+
+    @Override
+    public int[] updateResources(List<Document> resources, User user)
+            throws KustvaktException {
+        return new int[0];
+    }
+
+    public List<Document> findbyCorpus(String corpus, int offset, int index)
+            throws KustvaktException {
+        MapSqlParameterSource source = new MapSqlParameterSource();
+        source.addValue("corpus", corpus + "%");
+        source.addValue("offset", (offset * index));
+        source.addValue("limit", offset);
+        final String sql = "select * from doc_store where (persistentID like :corpus) limit :offset, :limit";
+        try {
+            return this.jdbcTemplate
+                    .query(sql, source, new RowMapper<Document>() {
+                        @Override
+                        public Document mapRow(ResultSet rs, int rowNum)
+                                throws SQLException {
+                            Document doc = new Document(
+                                    rs.getString("persistentID"));
+                            doc.setId(rs.getInt("id"));
+                            doc.setDisabled(rs.getBoolean("disabled"));
+                            return doc;
+                        }
+                    });
+        }catch (DataAccessException e) {
+            throw new KustvaktException(StatusCodes.CONNECTION_ERROR);
+        }
+    }
+
+    public List<String> findbyCorpus(String corpus, boolean disabled)
+            throws KustvaktException {
+        MapSqlParameterSource s = new MapSqlParameterSource();
+        s.addValue("corpus", corpus + "%");
+        s.addValue("dis", BooleanUtils.getBoolean(disabled));
+        String sql = "SELECT persistentID FROM doc_store WHERE (persistentID like :corpus) AND disabled=:dis;";
+        try {
+            return this.jdbcTemplate.queryForList(sql, s, String.class);
+        }catch (DataAccessException e) {
+            e.printStackTrace();
+            throw new KustvaktException(StatusCodes.CONNECTION_ERROR);
+        }
+    }
+
+    // parent is disabled here
+    @Override
+    public int storeResource(Document resource, User user)
+            throws KustvaktException {
+        MapSqlParameterSource s = new MapSqlParameterSource();
+        s.addValue("id", resource.getPersistentID());
+        s.addValue("corpus", resource.getCorpus());
+        s.addValue("dis", BooleanUtils.getBoolean(resource.isDisabled()));
+
+        String sql = "INSERT INTO doc_store (persistentID, disabled) VALUES (:id, :dis)";
+        try {
+            return this.jdbcTemplate.update(sql, s);
+        }catch (DataAccessException e) {
+            throw new KustvaktException(StatusCodes.ILLEGAL_ARGUMENT,
+                    "illegal argument given", resource.getPersistentID());
+        }
+    }
+
+    @Override
+    public int deleteResource(String id, User user) throws KustvaktException {
+        MapSqlParameterSource s = new MapSqlParameterSource();
+        s.addValue("id", id);
+        String sql = "delete from doc_store where persistentID=:id;";
+        try {
+            return this.jdbcTemplate.update(sql, s);
+        }catch (DataAccessException e) {
+            throw new KustvaktException(StatusCodes.CONNECTION_ERROR);
+        }
+
+    }
+}
diff --git a/src/main/java/de/ids_mannheim/korap/handlers/EntityDao.java b/src/main/java/de/ids_mannheim/korap/handlers/EntityDao.java
new file mode 100644
index 0000000..fa29e59
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/handlers/EntityDao.java
@@ -0,0 +1,475 @@
+package de.ids_mannheim.korap.handlers;
+
+import de.ids_mannheim.korap.config.ParamFields;
+import de.ids_mannheim.korap.config.URIParam;
+import de.ids_mannheim.korap.exceptions.EmptyResultException;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.exceptions.dbException;
+import de.ids_mannheim.korap.interfaces.EntityHandlerIface;
+import de.ids_mannheim.korap.interfaces.PersistenceClient;
+import de.ids_mannheim.korap.user.*;
+import de.ids_mannheim.korap.utils.BooleanUtils;
+import de.ids_mannheim.korap.utils.KustvaktLogger;
+import de.ids_mannheim.korap.utils.TimeUtils;
+import org.slf4j.Logger;
+import org.springframework.dao.DataAccessException;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
+import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
+import org.springframework.jdbc.support.GeneratedKeyHolder;
+import org.springframework.jdbc.support.KeyHolder;
+
+import java.sql.Date;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author hanl
+ * @date 13/01/2014
+ */
+public class EntityDao implements EntityHandlerIface {
+
+    private static Logger jlog = KustvaktLogger.initiate(EntityDao.class);
+    private NamedParameterJdbcTemplate jdbcTemplate;
+
+    public EntityDao(PersistenceClient client) {
+        this.jdbcTemplate = (NamedParameterJdbcTemplate) client.getSource();
+    }
+
+    @Override
+    public UserSettings getUserSettings(Integer userid) throws
+            KustvaktException {
+        MapSqlParameterSource np = new MapSqlParameterSource();
+        np.addValue("us", userid);
+        final String sql =
+                "SELECT user.* FROM user_settings as user inner join korap_users as a "
+                        + "on a.id=user.user_id WHERE user.user_id=:us";
+        try {
+            Map s = this.jdbcTemplate.queryForMap(sql, np);
+            return UserSettings.fromObjectMap(s);
+        }catch (EmptyResultDataAccessException ex) {
+            return new UserSettings();
+        }catch (DataAccessException e) {
+            jlog.error("Could not retrieve user settings for user: " + userid,
+                    e);
+            throw new dbException(userid, "user_settings",
+                    StatusCodes.DB_GET_FAILED, userid.toString());
+        }
+    }
+
+    @Override
+    public int updateSettings(UserSettings settings) throws KustvaktException {
+        final String sql =
+                "UPDATE user_settings SET fileNameForExport=:fileNameForExport,"
+                        +
+                        "leftContextItemForExport=:leftContextItemForExport," +
+                        "leftContextSizeForExport=:leftContextSizeForExport,locale=:locale,leftContextItem=:leftContextItem,"
+                        +
+                        "leftContextSize=:leftContextSize," +
+                        "rightContextItem=:rightContextItem,rightContextItemForExport=:rightContextItemForExport,"
+                        +
+                        "rightContextSize=:rightContextSize," +
+                        "POSFoundry=:defaultPOSfoundry, lemmaFoundry=:defaultLemmafoundry, constFoundry=:defaultConstfoundry, "
+                        +
+                        "relFoundry=:defaultRelfoundry, " +
+                        "rightContextSizeForExport=:rightContextSizeForExport,selectedCollection=:selectedCollection,queryLanguage=:queryLanguage,"
+                        +
+                        "pageLength=:pageLength,metadataQueryExpertModus=:metadataQueryExpertModus,collectData=:collectData "
+                        +
+                        "WHERE user_id=:userID";
+        try {
+            return this.jdbcTemplate
+                    .update(sql, new BeanPropertySqlParameterSource(settings));
+        }catch (DataAccessException e) {
+            jlog.error("Could not update user settings for user: " + settings
+                    .getUserID(), e);
+            throw new dbException(settings.getUserID(), "userSettings",
+                    StatusCodes.DB_UPDATE_FAILED, settings.toString());
+            //            throw new KorAPException(e, StatusCodes.CONNECTION_ERROR);
+        }
+    }
+
+    /**
+     * @param settings
+     * @throws KustvaktException
+     */
+
+    private void createSettings(UserSettings settings) throws
+            KustvaktException {
+        final String sql =
+                "INSERT INTO user_settings (user_id, fileNameForExport,leftContextItemForExport,"
+                        +
+                        "leftContextSizeForExport,locale,leftContextItem,leftContextSize,"
+                        +
+                        "rightContextItem,rightContextItemForExport,rightContextSize,"
+                        +
+                        "rightContextSizeForExport,selectedCollection,queryLanguage,"
+                        +
+                        "pageLength,metadataQueryExpertModus, POSFoundry, lemmaFoundry, constFoundry, relFoundry, collectData) "
+                        +
+                        "VALUES (:userID, :fileNameForExport, :leftContextItemForExport, "
+                        +
+                        ":leftContextSizeForExport, :locale, :leftContextItem, :leftContextSize, "
+                        +
+                        ":rightContextItem,:rightContextItemForExport, :rightContextSize, "
+                        +
+                        ":rightContextSizeForExport, :selectedCollection, :queryLanguage, "
+                        +
+                        ":pageLength, :metadataQueryExpertModus, :defaultPOSfoundry, "
+                        +
+                        ":defaultLemmafoundry, :defaultConstfoundry, :defaultRelfoundry, :collectData);";
+
+        try {
+            if (settings == null)
+                throw new KustvaktException(StatusCodes.MISSING_ARGUMENTS,
+                        "no settings provided", "user settings");
+            this.jdbcTemplate
+                    .update(sql, new BeanPropertySqlParameterSource(settings));
+        }catch (DataAccessException e) {
+            jlog.error("Could not create user settings for user: " + settings
+                    .getUserID(), e);
+            throw new dbException(settings.getUserID(), "userSettings",
+                    StatusCodes.DB_INSERT_FAILED, settings.toString());
+        }
+    }
+
+    @Override
+    public UserDetails getUserDetails(Integer userid) throws KustvaktException {
+        final String sql = "SELECT us.* FROM user_details as us inner join korap_users as ku on ku.id=us.user_id WHERE us.user_id=:user";
+        MapSqlParameterSource np = new MapSqlParameterSource();
+        np.addValue("user", userid);
+
+        try {
+            return this.jdbcTemplate
+                    .queryForObject(sql, np, new RowMapper<UserDetails>() {
+                        @Override
+                        public UserDetails mapRow(ResultSet rs, int i)
+                                throws SQLException {
+                            UserDetails d = new UserDetails();
+                            d.setUserID(rs.getInt("user_id"));
+                            d.setAddress(rs.getString("address"));
+                            d.setCountry(rs.getString("country"));
+                            d.setEmail(rs.getString("email"));
+                            d.setFirstName(rs.getString("firstName"));
+                            d.setLastName(rs.getString("lastName"));
+                            d.setGender(rs.getString("gender"));
+                            d.setInstitution(rs.getString("institution"));
+                            d.setPhone(rs.getString("phone"));
+                            d.setPrivateUsage(rs.getBoolean("privateUsage"));
+                            return d;
+                        }
+                    });
+        }catch (EmptyResultDataAccessException ex) {
+            //todo: create audit record?
+            return null;
+        }catch (DataAccessException e) {
+            jlog.error("Could not retrieve user details for user: " + userid,
+                    e);
+            throw new dbException(userid, "userDetails",
+                    StatusCodes.DB_GET_FAILED, userid.toString());
+            //            throw new KorAPException(e, StatusCodes.CONNECTION_ERROR);
+        }
+    }
+
+    @Override
+    public int updateUserDetails(UserDetails details) throws KustvaktException {
+        final String up =
+                "UPDATE user_details SET firstName=:firstName, lastName=:lastName, "
+                        +
+                        "gender=:gender, phone=:phone, institution=:institution, "
+                        +
+                        "email=:email, address=:address, country=:country, privateUsage=:privateUsage "
+                        +
+                        "WHERE userID=:userID;";
+        try {
+            return this.jdbcTemplate
+                    .update(up, new BeanPropertySqlParameterSource(details));
+        }catch (DataAccessException e) {
+            jlog.error("Could not retrieve user details for user: " + details
+                    .getUserID(), e);
+            //            throw new KorAPException(e, StatusCodes.CONNECTION_ERROR);
+            throw new dbException(details.getUserID(), "userDetails",
+                    StatusCodes.DB_UPDATE_FAILED, details.toString());
+        }
+    }
+
+    private void createUserDetails(UserDetails details) throws
+            KustvaktException {
+        final String up =
+                "INSERT INTO user_details (user_id, firstName, lastName, gender, phone, institution, "
+                        +
+                        "email, address, country, privateUsage) VALUES (:userID, :firstName, :lastName, :gender, "
+                        +
+                        ":phone, :institution, :email, :address, :country, :privateUsage);";
+        try {
+            if (details == null)
+                throw new KustvaktException(StatusCodes.MISSING_ARGUMENTS,
+                        "no details provided", "user details");
+            this.jdbcTemplate
+                    .update(up, new BeanPropertySqlParameterSource(details));
+        }catch (DataAccessException e) {
+            jlog.error("Could not create user details for user: " + details
+                    .getUserID(), e);
+            throw new dbException(details.getUserID(), "userDetails",
+                    StatusCodes.DB_INSERT_FAILED, details.toString());
+        }
+    }
+
+    // usersettings are fetched plus basic account info, no details, since i rarely use them anyway!
+    @Override
+    public User getAccount(String username)
+            throws KustvaktException {
+        Map<String, String> namedParameters = Collections
+                .singletonMap("username", username);
+        final String sql = "select a.* from korap_users as a where a.username=:username;";
+        User user;
+        try {
+            user = this.jdbcTemplate.queryForObject(sql, namedParameters,
+                    new RowMapperFactory.UserMapper());
+        }catch (EmptyResultDataAccessException ae) {
+            jlog.error("No user found for name '{}'", username);
+            throw new EmptyResultException(username);
+        }catch (DataAccessException e) {
+            jlog.error("Could not retrieve user for name: " + username, e);
+            throw new dbException(username, "korap_users",
+                    StatusCodes.DB_GET_FAILED, username);
+        }
+        // todo: set null!?!
+        //todo: test this
+        return user;
+    }
+
+    @Override
+    public int updateAccount(User user) throws KustvaktException {
+        MapSqlParameterSource np = new MapSqlParameterSource();
+        final String query;
+        if (user instanceof KorAPUser) {
+            KorAPUser k = (KorAPUser) user;
+            np.addValue("ali", k.getAccountLink());
+            np.addValue("alo", k.isAccountLocked());
+            if (k.getPassword() != null)
+                np.addValue("ps", k.getPassword());
+            URIParam param = k.getField(URIParam.class);
+            if (param != null) {
+                np.addValue("frag", param.getUriFragment());
+                np.addValue("exp", new Date(param.getUriExpiration()));
+            }
+            np.addValue("id", k.getId());
+
+            query = "UPDATE korap_users SET accountLock=:alo," +
+                    "accountLink=:ali, password=:ps," +
+                    "uri_fragment=:frag," +
+                    "uri_expiration=:exp WHERE id=:id";
+        }else if (user instanceof ShibUser) {
+            ShibUser s = (ShibUser) user;
+            //todo:
+            //            np.addValue("ali", s.getAccountLink());
+            np.addValue("ali", null);
+            np.addValue("edu", s.getAffiliation());
+            np.addValue("id", s.getId());
+            np.addValue("cn", s.getCn());
+            np.addValue("mail", s.getMail());
+
+            query = "UPDATE shibusers SET AccountLink=:ali" +
+                    " eduPersonScopedAffiliation=:edu" +
+                    "mail=:mail, cn=:cn WHERE id=:id";
+        }else
+            return -1;
+        try {
+            return this.jdbcTemplate.update(query, np);
+        }catch (DataAccessException e) {
+            jlog.error(
+                    "Could not update user account for user: " + user.getId(),
+                    e);
+            //            throw new KorAPException(e, StatusCodes.CONNECTION_ERROR);
+            throw new dbException(user.getId(), "korap_users",
+                    StatusCodes.DB_UPDATE_FAILED, user.toString());
+        }
+    }
+
+    @Override
+    public int createAccount(User user) throws KustvaktException {
+        final String query;
+        MapSqlParameterSource np = new MapSqlParameterSource();
+
+        if (user instanceof KorAPUser) {
+            final KorAPUser k = (KorAPUser) user;
+
+            URIParam param = k.getField(URIParam.class);
+            np.addValue("us", k.getUsername());
+            np.addValue("alo", k.isAccountLocked());
+            np.addValue("ali", k.getAccountLink());
+            np.addValue("ps", k.getPassword());
+            if (param != null) {
+                np.addValue("uri", param.getUriFragment());
+                np.addValue("urie", new Date(param.getUriExpiration()));
+            }else {
+                np.addValue("uri", null);
+                np.addValue("urie", null);
+            }
+
+            np.addValue("acr", k.getAccountCreation());
+            np.addValue("id", k.getId());
+
+            if (user.getId() != -1)
+                query = "INSERT INTO korap_users (id, username, accountLock, " +
+                        "accountLink, password, uri_fragment, " +
+                        "accountCreation, " +
+                        "uri_expiration) VALUES (:id, :us, :alo, :ali, " +
+                        ":ps, :uri, :acr, :urie);";
+            else
+                query = "INSERT INTO korap_users (username, accountLock, " +
+                        "accountLink, password, uri_fragment, " +
+                        "accountCreation, " +
+                        "uri_expiration) VALUES (:us, :alo, :ali, " +
+                        ":ps, :uri, :acr, :urie);";
+
+            //fixme: still applicable?
+        }else if (user instanceof ShibUser) {
+            ShibUser s = (ShibUser) user;
+            query = "INSERT INTO shibusers (username, type, accountLink " +
+                    "eduPersonScopedAffiliation, accountCreation, cn, mail) " +
+                    "VALUES (:us, :type, :ali, " +
+                    ":edu, :acr, :cn, :mail, :logs, :logft);";
+            np.addValue("us", s.getUsername());
+            //            np.addValue("ali", s.getAccountLink());
+            np.addValue("ali", null);
+            np.addValue("edu", s.getAffiliation());
+            np.addValue("mail", s.getMail());
+            np.addValue("type", user.getType());
+            np.addValue("cn", s.getCn());
+            np.addValue("acr", new Date(TimeUtils.getNow().getMillis()));
+
+            //todo: disable after first intro
+        }else if (user instanceof DemoUser) {
+            query = "INSERT INTO korap_users (username, type, accountLock, " +
+                    "password, uri_fragment, " +
+                    "accountCreation, uri_expiration) VALUES (:us, :type, :alo, "
+                    +
+                    ":ps, :uri, :acr, :urie);";
+
+            np.addValue("us", user.getUsername());
+            np.addValue("type", user.getType());
+            //            np.addValue("ali", user.getAccountLink());
+            np.addValue("ali", null);
+            np.addValue("alo", user.isAccountLocked());
+            np.addValue("urie", new Date(0));
+            np.addValue("ps", DemoUser.PASSPHRASE);
+            np.addValue("uri", "");
+            np.addValue("acr", new Date(TimeUtils.getNow().getMillis()));
+        }else
+            return -1;
+
+        KeyHolder holder = new GeneratedKeyHolder();
+
+        try {
+            int r = this.jdbcTemplate
+                    .update(query, np, holder, new String[] { "id" });
+            user.setId(holder.getKey().intValue());
+
+            if (user.getDetails() == null)
+                user.setDetails(new UserDetails());
+            if (user.getSettings() == null)
+                user.setSettings(new UserSettings());
+
+            this.createUserDetails(user.getDetails());
+            this.createSettings(user.getSettings());
+            return r;
+        }catch (DataAccessException e) {
+            e.printStackTrace();
+            jlog.error("Could not create user account with username: {}",
+                    user.getUsername());
+            throw new dbException(user.getUsername(), "korap_users",
+                    StatusCodes.NAME_EXISTS, user.getUsername());
+        }
+    }
+
+    @Override
+    public int deleteAccount(final Integer userid) throws KustvaktException {
+        MapSqlParameterSource s = new MapSqlParameterSource();
+        s.addValue("user", userid);
+
+        try {
+            int r;
+            r = this.jdbcTemplate
+                    .update("DELETE FROM korap_users WHERE id=:user", s);
+            //            if (user instanceof KorAPUser)
+            //                r = this.jdbcTemplate
+            //                        .update("DELETE FROM korap_users WHERE username=:user",
+            //                                s);
+            //            else if (user instanceof ShibUser)
+            //                r = this.jdbcTemplate
+            //                        .update("DELETE FROM shibusers WHERE username=:user",
+            //                                s);
+            //            else
+            //                r = -1;
+            return r;
+        }catch (DataAccessException e) {
+            jlog.error("Could not delete account for user: " + userid, e);
+            //            throw new KorAPException(e, StatusCodes.CONNECTION_ERROR);
+            throw new dbException(userid, "korap_users",
+                    StatusCodes.DB_DELETE_FAILED, userid.toString());
+        }
+
+    }
+
+    @Override
+    public int resetPassphrase(String username, String uriToken,
+            String passphrase) throws KustvaktException {
+        MapSqlParameterSource np = new MapSqlParameterSource();
+        final String query = "UPDATE korap_users SET " +
+                "uri_expiration=0, password=:pass WHERE uri_fragment=:uri AND uri_expiration > :now "
+                + "AND username=:us AND uri_expiration > :now;";
+        np.addValue("uri", uriToken);
+        np.addValue("now", new Date(TimeUtils.getNow().getMillis()));
+        np.addValue("pass", passphrase);
+        np.addValue("us", username);
+        try {
+            return this.jdbcTemplate.update(query, np);
+        }catch (DataAccessException e) {
+            jlog.error("Could not reset password for name: " + username, e);
+            throw new dbException(username, "korap_users",
+                    StatusCodes.DB_UPDATE_FAILED, username, uriToken,
+                    passphrase);
+        }
+    }
+
+    @Override
+    public int activateAccount(String username, String uriToken)
+            throws KustvaktException {
+        MapSqlParameterSource np = new MapSqlParameterSource();
+        final String query = "UPDATE korap_users SET uriFragment='', " +
+                "uri_expiration=0, accountLock=:lock WHERE uri_fragment=:uri AND username=:us AND "
+                +
+                "uri_expiration > :now;";
+        np.addValue("uri", uriToken);
+        np.addValue("now", new Date(TimeUtils.getNow().getMillis()));
+        np.addValue("us", username);
+        np.addValue("lock", BooleanUtils.getBoolean(false));
+        try {
+            return this.jdbcTemplate.update(query, np);
+        }catch (DataAccessException e) {
+            jlog.error("Could not confirm registration for name " + username,
+                    e);
+            throw new dbException(username, "korap_users",
+                    StatusCodes.DB_UPDATE_FAILED, username, uriToken);
+        }
+    }
+
+    //todo:
+    public List getAccountLinks(User user) {
+
+        return Collections.emptyList();
+    }
+
+    //todo:
+    public void setAccountParameters(User user) {
+        ParamFields fields = user.getFields();
+    }
+}
diff --git a/src/main/java/de/ids_mannheim/korap/handlers/JDBCAuditing.java b/src/main/java/de/ids_mannheim/korap/handlers/JDBCAuditing.java
new file mode 100644
index 0000000..9cfbb3d
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/handlers/JDBCAuditing.java
@@ -0,0 +1,113 @@
+package de.ids_mannheim.korap.handlers;
+
+import de.ids_mannheim.korap.auditing.AuditRecord;
+import de.ids_mannheim.korap.interfaces.AuditingIface;
+import de.ids_mannheim.korap.interfaces.PersistenceClient;
+import de.ids_mannheim.korap.user.User;
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
+import org.springframework.jdbc.core.namedparam.SqlParameterSource;
+
+import java.sql.Timestamp;
+import java.util.List;
+
+/**
+ * @author hanl
+ * @date 13/01/2014
+ */
+public class JDBCAuditing extends AuditingIface {
+
+    private NamedParameterJdbcTemplate template;
+
+    public JDBCAuditing(PersistenceClient client) {
+        this.template = (NamedParameterJdbcTemplate) client.getSource();
+    }
+
+    @Override
+    public <T extends AuditRecord> List<T> retrieveRecords(
+            AuditRecord.CATEGORY category, DateTime day, DateTime until,
+            boolean dayOnly, int limit) {
+        MapSqlParameterSource p = new MapSqlParameterSource();
+        p.addValue("limit", limit);
+        p.addValue("cat", category.toString());
+
+        String sql =
+                "select * from audit_records where aud_timestamp > :today AND"
+                        + " aud_timestamp < :tomorr AND aud_category=:cat limit :limit;";
+
+        if (dayOnly) {
+            LocalDate today = day.toLocalDate();
+            DateTime start = today.toDateTimeAtStartOfDay(day.getZone());
+            DateTime end = today.plusDays(1)
+                    .toDateTimeAtStartOfDay(day.getZone());
+            p.addValue("today", start.getMillis());
+            p.addValue("tomorr", end.getMillis());
+        }else {
+            p.addValue("today", day.getMillis());
+            p.addValue("tomorr", until.getMillis());
+        }
+        return (List<T>) this.template
+                .query(sql, p, new RowMapperFactory.AuditMapper());
+    }
+
+    @Override
+    public <T extends AuditRecord> List<T> retrieveRecords(
+            AuditRecord.CATEGORY category, User user, int limit) {
+        MapSqlParameterSource p = new MapSqlParameterSource();
+        p.addValue("limit", limit);
+        p.addValue("us", user.getUsername());
+        p.addValue("cat", category.toString());
+
+        String sql =
+                "select * from audit_records where aud_category=:cat and aud_user=:us "
+                        + "order by aud_timestamp desc limit :limit;";
+
+        return (List<T>) this.template
+                .query(sql, p, new RowMapperFactory.AuditMapper());
+    }
+
+    @Override
+    public <T extends AuditRecord> List<T> retrieveRecords(LocalDate day,
+            int hitMax) {
+        return null;
+    }
+
+    @Override
+    public <T extends AuditRecord> List<T> retrieveRecords(String userID,
+            LocalDate start, LocalDate end, int hitMax) {
+        return null;
+    }
+
+    @Override
+    public void apply() {
+        String sql;
+        sql = "INSERT INTO audit_records (aud_target, aud_category, aud_user, aud_location, aud_timestamp, "
+                + "aud_status, aud_field_1, aud_args) "
+                + "VALUES (:target, :category, :account, :loc, :timestamp, :status, :field, :args);";
+        List<AuditRecord> records = getRecordsToSave();
+        SqlParameterSource[] s = new SqlParameterSource[records.size()];
+        for (int i = 0; i < records.size(); i++) {
+            AuditRecord rec = records.get(i);
+            MapSqlParameterSource source = new MapSqlParameterSource();
+            source.addValue("category", rec.getCategory().toString());
+            source.addValue("account", rec.getUserid());
+            source.addValue("target", rec.getTarget());
+            source.addValue("loc", rec.getLoc());
+            source.addValue("timestamp", new Timestamp(rec.getTimestamp()));
+            source.addValue("status", rec.getStatus());
+            source.addValue("field", rec.getField_1());
+            source.addValue("args", rec.getArgs());
+            s[i] = source;
+        }
+        this.template.batchUpdate(sql, s);
+        records.clear();
+    }
+
+    @Override
+    public void finish() {
+
+    }
+
+}
diff --git a/src/main/java/de/ids_mannheim/korap/handlers/JDBCClient.java b/src/main/java/de/ids_mannheim/korap/handlers/JDBCClient.java
new file mode 100644
index 0000000..1b277d6
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/handlers/JDBCClient.java
@@ -0,0 +1,84 @@
+package de.ids_mannheim.korap.handlers;
+
+import de.ids_mannheim.korap.interfaces.PersistenceClient;
+import de.ids_mannheim.korap.utils.BooleanUtils;
+import lombok.AccessLevel;
+import lombok.Data;
+import lombok.Setter;
+import org.springframework.core.io.InputStreamResource;
+import org.springframework.core.io.Resource;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
+import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
+
+import javax.sql.DataSource;
+import java.io.IOException;
+import java.io.InputStream;
+import java.sql.SQLException;
+import java.util.HashMap;
+
+/**
+ * @author hanl
+ * @date 13/01/2014
+ */
+@Data
+public class JDBCClient extends PersistenceClient<NamedParameterJdbcTemplate> {
+
+    @Setter(AccessLevel.NONE)
+    private DataSource dataSource;
+
+    public JDBCClient(DataSource datasource) {
+        NamedParameterJdbcTemplate template = new NamedParameterJdbcTemplate(
+                datasource);
+        template.setCacheLimit(500);
+        super.setSource(template);
+        this.dataSource = datasource;
+    }
+
+    public JDBCClient(DataSource dataSource, Resource resource)
+            throws IOException {
+        this(dataSource);
+        this.setSchema(resource.getInputStream());
+    }
+
+    @Override
+    public void setSchema(InputStream stream) throws IOException {
+        super.setSchema(stream);
+    }
+
+    @Override
+    public boolean checkDatabase() {
+        NamedParameterJdbcTemplate tmp = this.getSource();
+        try {
+            // todo: use a table that doesnt change!!!!!
+            tmp.queryForObject("select count(id) from korap_users limit 1;",
+                    new HashMap<String, Object>(), Integer.class);
+        }catch (Exception e) {
+            System.out.println("No database schema found!");
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public void setDatabase(String name) {
+        super.setDatabase(name);
+        BooleanUtils.dbname = name;
+    }
+
+    // get schema file from configuration and create database
+    @Override
+    public void createDatabase() {
+        if (!checkDatabase()) {
+            final ResourceDatabasePopulator rdp = new ResourceDatabasePopulator();
+            rdp.addScript(new InputStreamResource(this.getSchema()));
+            rdp.setSeparator("$$");
+            try {
+                rdp.populate(this.dataSource.getConnection());
+            }catch (SQLException e) {
+                // do nothing
+                e.printStackTrace();
+            }
+        }
+    }
+
+}
diff --git a/src/main/java/de/ids_mannheim/korap/handlers/OAuth2Handler.java b/src/main/java/de/ids_mannheim/korap/handlers/OAuth2Handler.java
new file mode 100644
index 0000000..71ecbaf
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/handlers/OAuth2Handler.java
@@ -0,0 +1,49 @@
+package de.ids_mannheim.korap.handlers;
+
+import de.ids_mannheim.korap.config.AuthCodeInfo;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.interfaces.PersistenceClient;
+import de.ids_mannheim.korap.user.User;
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.CacheManager;
+import net.sf.ehcache.Element;
+
+/**
+ * @author hanl
+ * @date 04/05/2015
+ */
+public class OAuth2Handler extends OAuthDb {
+
+    private Cache cache;
+
+    public OAuth2Handler(PersistenceClient client) {
+        super(client);
+        this.cache = CacheManager.getInstance().getCache("auth_codes");
+    }
+
+    public AuthCodeInfo getAuthorization(String code) {
+        Element e = this.cache.get(code);
+        if (e != null)
+            return (AuthCodeInfo) e.getObjectValue();
+        return null;
+    }
+
+    public void authorize(AuthCodeInfo code, User user) throws
+            KustvaktException {
+        code.setUserId(user.getId());
+        cache.put(new Element(code.getCode(), code));
+    }
+
+    public boolean addToken(String code, String token, int ttl)
+            throws KustvaktException {
+        Element e = cache.get(code);
+        if (e != null) {
+            AuthCodeInfo info = (AuthCodeInfo) e.getObjectValue();
+            cache.remove(code);
+            return super.addToken(token, info.getUserId(), info.getClientId(),
+                    info.getScopes(), ttl);
+        }
+        return false;
+    }
+
+}
diff --git a/src/main/java/de/ids_mannheim/korap/handlers/OAuthDb.java b/src/main/java/de/ids_mannheim/korap/handlers/OAuthDb.java
new file mode 100644
index 0000000..b0bee5a
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/handlers/OAuthDb.java
@@ -0,0 +1,267 @@
+package de.ids_mannheim.korap.handlers;
+
+import de.ids_mannheim.korap.config.ClientInfo;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.exceptions.dbException;
+import de.ids_mannheim.korap.interfaces.PersistenceClient;
+import de.ids_mannheim.korap.user.Attributes;
+import de.ids_mannheim.korap.user.TokenContext;
+import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.utils.BooleanUtils;
+import de.ids_mannheim.korap.utils.KustvaktLogger;
+import de.ids_mannheim.korap.utils.TimeUtils;
+import edu.emory.mathcs.backport.java.util.Collections;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.dao.DataAccessException;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.util.List;
+
+/**
+ * Created by hanl on 7/14/14.
+ */
+public class OAuthDb {
+
+    private static final Logger errorLogger = LoggerFactory
+            .getLogger(KustvaktLogger.ERROR_LOG);
+    private NamedParameterJdbcTemplate jdbcTemplate;
+
+    public OAuthDb(PersistenceClient client) {
+        this.jdbcTemplate = (NamedParameterJdbcTemplate) client.getSource();
+    }
+
+    public ClientInfo getClient(String clientid) {
+        MapSqlParameterSource s = new MapSqlParameterSource();
+        s.addValue("cl", clientid);
+        String sql = "select * from oauth2_client where client_id=:cl;";
+
+        try {
+            return this.jdbcTemplate
+                    .queryForObject(sql, s, new RowMapper<ClientInfo>() {
+                        @Override
+                        public ClientInfo mapRow(ResultSet rs, int rowNum)
+                                throws SQLException {
+                            ClientInfo info = new ClientInfo(
+                                    rs.getString("client_id"),
+                                    rs.getString("client_secret"));
+                            info.setId(rs.getInt("id"));
+                            info.setClient_type(rs.getString("client_type"));
+                            info.setRedirect_uri(rs.getString("redirect_uri"));
+                            info.setUrl(rs.getString("url"));
+                            info.setConfidential(
+                                    rs.getBoolean("is_confidential"));
+                            return info;
+                        }
+                    });
+        }catch (EmptyResultDataAccessException ex) {
+            errorLogger.error("no client found", ex.fillInStackTrace());
+            return null;
+        }
+    }
+
+    // fixme: what to delete? difference client/application table?
+    public boolean revokeToken(String token) throws KustvaktException {
+        String sql = "delete from oauth2_access_token WHERE access_token=:token;";
+        MapSqlParameterSource s = new MapSqlParameterSource();
+        s.addValue("token", token);
+        try {
+            return this.jdbcTemplate.update(sql, s) == 1;
+        }catch (DataAccessException e) {
+            errorLogger
+                    .error("token could not be revoked", e.fillInStackTrace());
+            return false;
+        }
+    }
+
+    public boolean revokeAuthorization(ClientInfo info, User user) {
+        MapSqlParameterSource source = new MapSqlParameterSource();
+        source.addValue("us", user.getId());
+        source.addValue("cls", info.getClient_secret());
+        source.addValue("clid", info.getClient_id());
+
+        String tokens =
+                "delete from oauth2_access_token where user_id=:us and client_id in "
+                        + "(select client_id from oauth2_client where client_id=:clid and client_secret=:cls);";
+
+        try {
+            this.jdbcTemplate.update(tokens, source);
+        }catch (DataAccessException e) {
+            errorLogger
+                    .error("authorization could not be revoked for user '{}'",
+                            user.getUsername());
+            return false;
+        }
+        //fixme: if int row not updated, false!!
+        return true;
+    }
+
+    public boolean addToken(String token, Integer userid, String client_id,
+            String scopes, int expiration) throws KustvaktException {
+        MapSqlParameterSource s = new MapSqlParameterSource();
+        s.addValue("token", token);
+        s.addValue("ex",
+                new Timestamp(TimeUtils.plusSeconds(expiration).getMillis()));
+        s.addValue("us", userid);
+        s.addValue("sc", scopes);
+        s.addValue("st", BooleanUtils.getBoolean(true));
+        s.addValue("cli", client_id);
+        String sql =
+                "insert into oauth2_access_token (access_token, scopes, client_id, user_id, expiration, status) "
+                        + "values (:token, :sc, :cli, :us, :ex, :st);";
+        try {
+            return this.jdbcTemplate.update(sql, s) == 1;
+        }catch (DataAccessException e) {
+            e.printStackTrace();
+            errorLogger
+                    .error("token '{}' could not be added for user '{}'", token,
+                            userid);
+            return false;
+        }
+    }
+
+    // returns the first token to find
+    public String getToken(String client_id, Integer userid) {
+        String sql =
+                "select access_token from oauth2_access_token where user_id=:uid"
+                        + " and status=1 and client_id=:cli limit 1;";
+        MapSqlParameterSource s = new MapSqlParameterSource();
+        s.addValue("uid", userid);
+        s.addValue("cli", client_id);
+        try {
+            return this.jdbcTemplate.queryForObject(sql, s, String.class);
+        }catch (EmptyResultDataAccessException ex) {
+            errorLogger.error("no token found for user '{}'", userid);
+            return null;
+        }catch (DataAccessException ex) {
+            errorLogger.error("token retrieval failed for user '{}'", userid);
+            return null;
+        }
+    }
+
+    public List<ClientInfo> getAuthorizedClients(Integer userid) {
+        String sql =
+                "select cl.* from oauth2_client as cl where cl.client_id in (select cd.client_id from oauth2_access_token as cd "
+                        + "where cd.user_id=:user) or cl.is_confidential=:conf;";
+
+        MapSqlParameterSource s = new MapSqlParameterSource();
+        s.addValue("user", userid);
+        s.addValue("conf", BooleanUtils.getBoolean(true));
+        try {
+            // secret is not returned for this function
+            return this.jdbcTemplate.query(sql, s, new RowMapper<ClientInfo>() {
+
+                @Override
+                public ClientInfo mapRow(ResultSet rs, int rowNum)
+                        throws SQLException {
+                    ClientInfo info = new ClientInfo(rs.getString("client_id"),
+                            "*****");
+                    info.setConfidential(rs.getBoolean("is_confidential"));
+                    info.setUrl(rs.getString("url"));
+                    info.setId(rs.getInt("id"));
+                    info.setRedirect_uri(rs.getString("redirect_uri"));
+                    return info;
+                }
+            });
+        }catch (DataAccessException e) {
+            errorLogger.error("Data access error", e);
+            return Collections.emptyList();
+        }
+
+    }
+
+    public TokenContext getContext(final String token) throws
+            KustvaktException {
+        String sql =
+                "select ko.username, oa.expiration, oa.scopes from oauth2_access_token as oa inner join korap_users as ko "
+                        + "on ko.id=oa.user_id where oa.access_token=:token and oa.expiration > :now;";
+        MapSqlParameterSource s = new MapSqlParameterSource();
+        s.addValue("token", token);
+        s.addValue("now", new Timestamp(TimeUtils.getNow().getMillis()));
+
+        try {
+            TokenContext context = this.jdbcTemplate
+                    .queryForObject(sql, s, new RowMapper<TokenContext>() {
+                        @Override
+                        public TokenContext mapRow(ResultSet rs, int rowNum)
+                                throws SQLException {
+                            long exp = rs.getTimestamp("expiration").getTime();
+                            TokenContext c = new TokenContext(
+                                    rs.getString(Attributes.USERNAME));
+                            c.setExpirationTime(exp);
+                            c.setToken(token);
+                            c.setTokenType(Attributes.OAUTH2_AUTHORIZATION);
+                            c.addContextParameter(Attributes.SCOPES,
+                                    rs.getString(Attributes.SCOPES));
+                            return c;
+                        }
+                    });
+            return context;
+        }catch (EmptyResultDataAccessException ee) {
+            errorLogger.error("no context found for token '{}'", token);
+            revokeToken(token);
+            throw new KustvaktException(StatusCodes.EXPIRED, "token", token);
+        }catch (DataAccessException e) {
+            errorLogger.error("token context retrieval failed for '{}'", token);
+            throw new KustvaktException(StatusCodes.ILLEGAL_ARGUMENT,
+                    "invalid token", token);
+        }
+
+    }
+
+    // subsequently delete all access and auth code tokens associated!
+    public void removeClient(ClientInfo info, User user) throws
+            KustvaktException {
+        MapSqlParameterSource p = new MapSqlParameterSource();
+        p.addValue("url", info.getUrl());
+        p.addValue("cls", info.getClient_secret());
+        p.addValue("clid", info.getClient_id());
+        String sql =
+                "delete from oauth2_client where client_id=:clid and client_secret=:cls and"
+                        + " url=:url;";
+        try {
+            this.jdbcTemplate.update(sql, p);
+        }catch (DataAccessException e) {
+            e.printStackTrace();
+            errorLogger
+                    .error("removing client '{}' failed", info.getClient_id());
+            throw new dbException(new KustvaktException(user.getId(),
+                    StatusCodes.ILLEGAL_ARGUMENT, "arguments given not valid",
+                    info.toJSON()), StatusCodes.CLIENT_REMOVAL_FAILURE,
+                    info.toJSON());
+
+        }
+    }
+
+    public void registerClient(ClientInfo info, User user)
+            throws KustvaktException {
+        MapSqlParameterSource p = new MapSqlParameterSource();
+        p.addValue("clid", info.getClient_id());
+        p.addValue("con", info.isConfidential());
+        p.addValue("cls", info.getClient_secret());
+        p.addValue("clt", info.getClient_type());
+        p.addValue("url", info.getUrl());
+        p.addValue("r_url", info.getRedirect_uri());
+        String sql =
+                "insert into oauth2_client (client_id, client_secret, client_type, url, is_confidential, redirect_uri) "
+                        + "VALUES (:clid, :cls, :clt, :url, :con, :r_url);";
+        try {
+            this.jdbcTemplate.update(sql, p);
+        }catch (DataAccessException e) {
+            e.printStackTrace();
+            errorLogger.error("registering client '{}' failed",
+                    info.getClient_id());
+            throw new dbException(new KustvaktException(user.getId(),
+                    StatusCodes.ILLEGAL_ARGUMENT, "arguments given not valid",
+                    info.toJSON()), StatusCodes.CLIENT_REGISTRATION_FAILURE,
+                    info.toJSON());
+        }
+    }
+}
diff --git a/src/main/java/de/ids_mannheim/korap/handlers/ResourceDao.java b/src/main/java/de/ids_mannheim/korap/handlers/ResourceDao.java
new file mode 100644
index 0000000..6039c79
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/handlers/ResourceDao.java
@@ -0,0 +1,172 @@
+package de.ids_mannheim.korap.handlers;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.exceptions.dbException;
+import de.ids_mannheim.korap.ext.interfaces.ResourceOperationIface;
+import de.ids_mannheim.korap.ext.resource.KorAPResource;
+import de.ids_mannheim.korap.ext.resource.ResourceFactory;
+import de.ids_mannheim.korap.interfaces.PersistenceClient;
+import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.utils.KustvaktLogger;
+import de.ids_mannheim.korap.utils.TimeUtils;
+import org.slf4j.Logger;
+import org.springframework.dao.DataAccessException;
+import org.springframework.dao.IncorrectResultSizeDataAccessException;
+import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
+import org.springframework.jdbc.support.GeneratedKeyHolder;
+import org.springframework.jdbc.support.KeyHolder;
+
+import java.sql.Timestamp;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Created by hanl on 7/21/14.
+ */
+//todo: auditing // testing
+public class ResourceDao<T extends KorAPResource>
+        implements ResourceOperationIface<T> {
+
+    private static Logger log = KustvaktLogger.initiate(ResourceDao.class);
+    protected final NamedParameterJdbcTemplate jdbcTemplate;
+
+    public ResourceDao(PersistenceClient client) {
+        this.jdbcTemplate = (NamedParameterJdbcTemplate) client.getSource();
+    }
+
+    @Override
+    public Class<T> getType() {
+        return (Class<T>) KorAPResource.class;
+    }
+
+    @Override
+    public List<T> getResources(Collection<Object> ids, User user)
+            throws KustvaktException {
+        return null;
+    }
+
+    @Override
+    public int updateResource(T resource, User user) throws KustvaktException {
+        MapSqlParameterSource source = new MapSqlParameterSource();
+        source.addValue("id", resource.getPersistentID());
+        source.addValue("name", resource.getName());
+        source.addValue("desc", resource.getDescription());
+        final String sql = "UPDATE resource_store set name=:name, description=:desc where persistent_id=:id;";
+        try {
+            return this.jdbcTemplate.update(sql, source);
+        }catch (DataAccessException e) {
+            log.error("Exception during database update for id '" + resource
+                    .getPersistentID() + "'", e);
+            throw new dbException(user.getId(), "resource_store",
+                    StatusCodes.DB_UPDATE_FAILED, resource.toString());
+        }
+    }
+
+    @Override
+    public int[] updateResources(List<T> resources, User user)
+            throws KustvaktException {
+        return new int[1];
+    }
+
+    @Override
+    public <T extends KorAPResource> T findbyId(String id, User user)
+            throws KustvaktException {
+        MapSqlParameterSource source = new MapSqlParameterSource();
+        source.addValue("pid", id);
+        String sql =
+                "SELECT rs.*, rt.name_path FROM resource_store as rs inner join resource_tree as rt"
+                        + " on rs.id=rt.child_id WHERE rs.persistent_id=:pid group by rs.id;";
+        try {
+            return (T) this.jdbcTemplate.queryForObject(sql, source,
+                    new RowMapperFactory.ResourceMapper());
+        }catch (DataAccessException e) {
+            return null;
+        }
+    }
+
+    public KorAPResource findbyPath(String path, User user)
+            throws KustvaktException {
+        MapSqlParameterSource source = new MapSqlParameterSource();
+        source.addValue("path", path);
+        String sql = "SELECT rs.*, rt.name_path FROM resource_store as rs inner join resource_tree as rt on rs.id=rt.child_id WHERE rt.name_path=:path;";
+        try {
+            return this.jdbcTemplate.queryForObject(sql, source,
+                    new RowMapperFactory.ResourceMapper());
+        }catch (DataAccessException e) {
+            if (e instanceof IncorrectResultSizeDataAccessException)
+                throw new KustvaktException(StatusCodes.ILLEGAL_ARGUMENT,
+                        "invalid request path given!", path);
+            return null;
+        }
+    }
+
+    @Override
+    public <T extends KorAPResource> T findbyId(Integer id, User user)
+            throws KustvaktException {
+        MapSqlParameterSource source = new MapSqlParameterSource();
+        source.addValue("id", id);
+        String sql =
+                "SELECT rs.*, rt.name_path FROM resource_store as rs inner join resource_tree as rt on rs.id=rt.child_id "
+                        + "WHERE rs.id=:id group by rs.id order by rt.depth desc;";
+        try {
+            return (T) this.jdbcTemplate.queryForObject(sql, source,
+                    new RowMapperFactory.ResourceMapper());
+        }catch (DataAccessException e) {
+            return null;
+        }
+    }
+
+    @Override
+    public int storeResource(T resource, User user) throws KustvaktException {
+        MapSqlParameterSource source = new MapSqlParameterSource();
+        KeyHolder holder = new GeneratedKeyHolder();
+        // parent_id necessary so trigger can be used for tree insert!
+        final String sql, parid;
+        if (resource.getParentID() == null) {
+            sql = "INSERT INTO resource_store (name, parent_id, persistent_id, description, creator, type, created) "
+                    + "VALUES (:name, :parent, :pid, :desc, :ow, :type, :created);";
+            parid = null;
+        }else {
+            sql = "INSERT INTO resource_store (name, parent_id, persistent_id, description, creator, type, created) "
+                    + "select :name, id, :pid, :desc, :ow, :type, :created from resource_store where persistent_id=:parent;";
+            parid = resource.getParentID();
+        }
+
+        source.addValue("name", resource.getName());
+        source.addValue("pid", resource.getPersistentID());
+        source.addValue("parent", parid);
+        source.addValue("ow", resource.getOwner());
+        source.addValue("desc", resource.getDescription());
+        source.addValue("type",
+                ResourceFactory.getResourceMapping(resource.getClass()));
+        source.addValue("created",
+                new Timestamp(TimeUtils.getNow().getMillis()));
+        try {
+            this.jdbcTemplate
+                    .update(sql, source, holder, new String[] { "id" });
+        }catch (DataAccessException e) {
+            log.error("Exception during database store for id '" + resource
+                    .getPersistentID() + "'", e);
+            throw new dbException(user.getId(), "resource_store",
+                    StatusCodes.DB_INSERT_FAILED, resource.toString());
+        }
+        resource.setId(holder.getKey().intValue());
+        return resource.getId();
+    }
+
+    @Override
+    public int deleteResource(String id, User user) throws KustvaktException {
+        MapSqlParameterSource source = new MapSqlParameterSource();
+        source.addValue("id", id);
+        final String sql = "DELETE FROM resource_store WHERE persistent_id=:id;";
+        try {
+            return this.jdbcTemplate.update(sql, source);
+        }catch (DataAccessException e) {
+            e.printStackTrace();
+            throw new dbException(user.getId(), "resource_store",
+                    StatusCodes.DB_DELETE_FAILED, id);
+        }
+    }
+}
diff --git a/src/main/java/de/ids_mannheim/korap/handlers/RowMapperFactory.java b/src/main/java/de/ids_mannheim/korap/handlers/RowMapperFactory.java
new file mode 100644
index 0000000..c5cc374
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/handlers/RowMapperFactory.java
@@ -0,0 +1,139 @@
+package de.ids_mannheim.korap.handlers;
+
+import de.ids_mannheim.korap.auditing.AuditRecord;
+import de.ids_mannheim.korap.config.URIParam;
+import de.ids_mannheim.korap.resources.KustvaktResource;
+import de.ids_mannheim.korap.resources.ResourceFactory;
+import de.ids_mannheim.korap.resources.VirtualCollection;
+import de.ids_mannheim.korap.user.Attributes;
+import de.ids_mannheim.korap.user.KorAPUser;
+import de.ids_mannheim.korap.user.ShibUser;
+import de.ids_mannheim.korap.user.User;
+import org.springframework.jdbc.core.RowMapper;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Map;
+
+/**
+ * @author hanl
+ * @date 14/01/2014
+ */
+public class RowMapperFactory {
+
+    public static class UserMapMapper implements RowMapper<Map> {
+
+        @Override
+        public Map mapRow(ResultSet rs, int rowNum) throws SQLException {
+            User user = new UserMapper().mapRow(rs, rowNum);
+            return user.toMap();
+        }
+    }
+
+    public static class UserMapper implements RowMapper<User> {
+
+        @Override
+        public User mapRow(ResultSet rs, int rowNum) throws SQLException {
+            User user;
+            switch (rs.getInt("type")) {
+                case 0:
+                    user = getKorAP(rs);
+                    break;
+                case 1:
+                    user = getShib(rs);
+                    break;
+                default:
+                    user = User.UserFactory.getDemoUser();
+                    user.setId(rs.getInt("id"));
+                    user.setAccountCreation(
+                            rs.getTimestamp(Attributes.ACCOUNT_CREATION)
+                                    .getTime());
+                    return user;
+            }
+            return user;
+        }
+
+        private KorAPUser getKorAP(ResultSet rs) throws SQLException {
+            KorAPUser user = User.UserFactory
+                    .getUser(rs.getString(Attributes.USERNAME));
+            user.setPassword(rs.getString(Attributes.PASSWORD));
+            user.setId(rs.getInt(Attributes.ID));
+            user.setAccountLocked(rs.getBoolean(Attributes.ACCOUNTLOCK));
+            user.setAccountCreation(
+                    rs.getTimestamp(Attributes.ACCOUNT_CREATION).getTime());
+            user.setAccountLink(rs.getString(Attributes.ACCOUNTLINK));
+
+            URIParam param = new URIParam(rs.getString(Attributes.URI_FRAGMENT),
+                    rs.getDate(Attributes.URI_EXPIRATION).getTime());
+            user.addField(param);
+            return user;
+        }
+
+        private ShibUser getShib(ResultSet rs) throws SQLException {
+            ShibUser user = User.UserFactory
+                    .getShibInstance(rs.getString(Attributes.USERNAME),
+                            rs.getString(Attributes.MAIL),
+                            rs.getString(Attributes.CN));
+            user.setId(rs.getInt(Attributes.ID));
+            return user;
+        }
+
+    }
+
+    public static class AuditMapper implements RowMapper<AuditRecord> {
+
+        @Override
+        public AuditRecord mapRow(ResultSet rs, int rowNum)
+                throws SQLException {
+            AuditRecord r = new AuditRecord(
+                    AuditRecord.CATEGORY.valueOf(rs.getString("aud_category")));
+            r.setUserid(rs.getString("aud_user"));
+            r.setField_1(rs.getString("aud_field_1"));
+            r.setTimestamp(rs.getTimestamp("aud_timestamp").getTime());
+            r.setId(rs.getInt("aud_id"));
+            r.setStatus(rs.getInt("aud_status"));
+            r.setLoc(rs.getString("aud_location"));
+            return r;
+        }
+    }
+
+    public static class ResourceMapper implements RowMapper<KustvaktResource> {
+
+        @Override
+        public KustvaktResource mapRow(ResultSet rs, int rowNum)
+                throws SQLException {
+            KustvaktResource r = ResourceFactory.getResource(rs.getInt("type"));
+            if (r != null) {
+                r.setId(rs.getInt("id"));
+                r.setOwner(rs.getInt("creator"));
+                r.setName(rs.getString("name"));
+                r.setDescription(rs.getString("description"));
+                r.setCreated(rs.getTimestamp("created").getTime());
+                r.setPath(rs.getString("name_path"));
+                r.setPersistentID(rs.getString("persistent_id"));
+            }
+            return r;
+        }
+    }
+
+    // todo: ??!
+    public static class CollectionMapper
+            implements RowMapper<VirtualCollection> {
+
+        @Override
+        public VirtualCollection mapRow(ResultSet rs, int i)
+                throws SQLException {
+            VirtualCollection c = ResourceFactory
+                    .getCollection(rs.getInt("id"), false);
+            c.setPersistentID(rs.getString("persistentId"));
+            c.setCreated(rs.getTimestamp("created").getTime());
+            c.setName(rs.getString("name"));
+            c.setDescription(rs.getString("description"));
+            c.setOwner(rs.getInt("userId"));
+            c.setQuery(rs.getString("query"));
+            c.checkNull();
+            return c;
+        }
+    }
+
+}
diff --git a/src/main/java/de/ids_mannheim/korap/interfaces/AuditingIface.java b/src/main/java/de/ids_mannheim/korap/interfaces/AuditingIface.java
index 873f435..18a616f 100644
--- a/src/main/java/de/ids_mannheim/korap/interfaces/AuditingIface.java
+++ b/src/main/java/de/ids_mannheim/korap/interfaces/AuditingIface.java
@@ -2,6 +2,7 @@
 
 import de.ids_mannheim.korap.auditing.AuditRecord;
 import de.ids_mannheim.korap.user.User;
+import edu.emory.mathcs.backport.java.util.Collections;
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 
@@ -14,10 +15,11 @@
  * Time: 10:45 AM
  */
 //fixme: move table to different database!
-public abstract class AuditingIface implements Runnable {
+public abstract class AuditingIface {
 
     protected static int BATCH_SIZE = 15;
-    private final List<AuditRecord> records = new ArrayList<>(BATCH_SIZE + 5);
+    private final List<AuditRecord> records = Collections
+            .synchronizedList(new ArrayList<>(BATCH_SIZE + 5));
     private final List<AuditRecord> buffer = new ArrayList<>(BATCH_SIZE + 5);
 
     public abstract <T extends AuditRecord> List<T> retrieveRecords(
@@ -37,7 +39,12 @@
         if (buffer.size() > BATCH_SIZE) {
             records.clear();
             records.addAll(buffer);
-            run();
+            new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    apply();
+                }
+            }).start();
             buffer.clear();
         }
         if (buffer.size() <= BATCH_SIZE)
@@ -53,8 +60,12 @@
             addAndRun(rec);
     }
 
+    public abstract void apply();
+
     protected List<AuditRecord> getRecordsToSave() {
         return this.records;
     }
 
+
+    public abstract void finish();
 }
diff --git a/src/main/java/de/ids_mannheim/korap/interfaces/AuthenticationManagerIface.java b/src/main/java/de/ids_mannheim/korap/interfaces/AuthenticationManagerIface.java
index 4021836..45e5143 100644
--- a/src/main/java/de/ids_mannheim/korap/interfaces/AuthenticationManagerIface.java
+++ b/src/main/java/de/ids_mannheim/korap/interfaces/AuthenticationManagerIface.java
@@ -1,11 +1,9 @@
 package de.ids_mannheim.korap.interfaces;
 
-import de.ids_mannheim.korap.config.KustvaktClassLoader;
 import de.ids_mannheim.korap.exceptions.KorAPException;
 import de.ids_mannheim.korap.user.*;
 
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
@@ -19,21 +17,6 @@
 
     public AuthenticationManagerIface() {
         this.providers = new HashMap<>();
-        loadProviders();
-    }
-
-    private void loadProviders() {
-        Set<Class<? extends AuthenticationIface>> set = KustvaktClassLoader
-                .load(AuthenticationIface.class);
-        Set<AuthenticationIface> set2 = new HashSet<>();
-        for (Class<? extends AuthenticationIface> i : set) {
-            try {
-                set2.add(i.newInstance());
-            }catch (InstantiationException | IllegalAccessException e) {
-                e.printStackTrace();
-            }
-        }
-        this.setProviders(set2);
     }
 
     public void setProviders(Set<AuthenticationIface> providers) {
@@ -88,6 +71,14 @@
     public abstract Object[] validateResetPasswordRequest(String username,
             String email) throws KorAPException;
 
+    public abstract void resetPassword(String uriFragment, String username,
+            String newPassphrase) throws KorAPException;
+
     public abstract void confirmRegistration(String uriFragment,
             String username) throws KorAPException;
+
+    @Override
+    public String toString() {
+        return "provider list: " + this.providers.toString();
+    }
 }
diff --git a/src/main/java/de/ids_mannheim/korap/interfaces/EntityHandlerIface.java b/src/main/java/de/ids_mannheim/korap/interfaces/EntityHandlerIface.java
index 01cab35..fb8048b 100644
--- a/src/main/java/de/ids_mannheim/korap/interfaces/EntityHandlerIface.java
+++ b/src/main/java/de/ids_mannheim/korap/interfaces/EntityHandlerIface.java
@@ -12,33 +12,33 @@
  * Time: 11:04 AM
  */
 public interface EntityHandlerIface {
-    public UserSettings getUserSettings(Integer userid) throws KorAPException;
+    UserSettings getUserSettings(Integer userid) throws KorAPException;
 
-    public int updateSettings(UserSettings settings) throws KorAPException;
+    int updateSettings(UserSettings settings) throws KorAPException;
 
-    public UserDetails getUserDetails(Integer userid) throws KorAPException;
+    UserDetails getUserDetails(Integer userid) throws KorAPException;
 
-    public int updateUserDetails(UserDetails details) throws KorAPException;
+    int updateUserDetails(UserDetails details) throws KorAPException;
 
-    //    public List<UserQuery> getUserQueries(User user) throws KorAPException;
+    //    List<UserQuery> getUserQueries(User user) throws KorAPException;
 
-    //    public UserQuery getUserQuery(String id) throws KorAPException;
+    //    UserQuery getUserQuery(String id) throws KorAPException;
 
-    //    public void updateUserQueries(User user, List<UserQuery> newOnes) throws KorAPException;
+    //    void updateUserQueries(User user, List<UserQuery> newOnes) throws KorAPException;
 
-    public User getAccount(String username) throws
+    User getAccount(String username) throws
             EmptyResultException, KorAPException;
 
-    public int updateAccount(User user) throws KorAPException;
+    int updateAccount(User user) throws KorAPException;
 
-    public int createAccount(User user) throws KorAPException;
+    int createAccount(User user) throws KorAPException;
 
-    public int deleteAccount(Integer userid) throws KorAPException;
+    int deleteAccount(Integer userid) throws KorAPException;
 
-    public int resetPassphrase(String username, String uriToken,
+    int resetPassphrase(String username, String uriToken,
             String passphrase) throws KorAPException;
 
-    public int activateAccount(String username, String uriToken)
+    int activateAccount(String username, String uriToken)
             throws KorAPException;
 
 }
diff --git a/src/main/java/de/ids_mannheim/korap/interfaces/PersistenceClient.java b/src/main/java/de/ids_mannheim/korap/interfaces/PersistenceClient.java
new file mode 100644
index 0000000..07d53c7
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/interfaces/PersistenceClient.java
@@ -0,0 +1,53 @@
+package de.ids_mannheim.korap.interfaces;
+
+import lombok.Getter;
+
+import java.io.*;
+
+@Getter
+public abstract class PersistenceClient<SOURCE> {
+
+    private SOURCE source;
+    private TYPE type;
+    @Deprecated
+    protected String database;
+    private InputStream schema;
+
+    public PersistenceClient(String database, TYPE type) {
+        this.type = type;
+        this.database = database;
+    }
+
+    public PersistenceClient(TYPE type) {
+        this.type = type;
+    }
+
+    public PersistenceClient() {
+    }
+
+    protected void setSource(SOURCE conn) {
+        this.source = conn;
+    }
+
+    public void setDatabase(String name) {
+        this.database = name;
+    }
+
+    public void setSchema(String schema_path) throws FileNotFoundException {
+        this.schema = new FileInputStream(new File(schema_path));
+    }
+
+    // for spring configuration
+    @Deprecated
+    public void setSchema(InputStream schema) throws IOException {
+        this.schema = schema;
+    }
+
+    public abstract boolean checkDatabase() throws Exception;
+
+    public abstract void createDatabase() throws IOException;
+
+    public enum TYPE {
+        SQL, CASSANDRA
+    }
+}
diff --git a/src/main/java/de/ids_mannheim/korap/interfaces/defaults/DefaultAuditing.java b/src/main/java/de/ids_mannheim/korap/interfaces/defaults/DefaultAuditing.java
index 27bb3a1..9f5a453 100644
--- a/src/main/java/de/ids_mannheim/korap/interfaces/defaults/DefaultAuditing.java
+++ b/src/main/java/de/ids_mannheim/korap/interfaces/defaults/DefaultAuditing.java
@@ -1,6 +1,8 @@
 package de.ids_mannheim.korap.interfaces.defaults;
 
 import de.ids_mannheim.korap.auditing.AuditRecord;
+import de.ids_mannheim.korap.config.BeanConfiguration;
+import de.ids_mannheim.korap.config.Configurable;
 import de.ids_mannheim.korap.interfaces.AuditingIface;
 import de.ids_mannheim.korap.user.User;
 import org.joda.time.DateTime;
@@ -16,6 +18,7 @@
  * @author hanl
  * @date 05/06/2015
  */
+@Configurable(BeanConfiguration.KUSTVAKT_AUDITING)
 public class DefaultAuditing extends AuditingIface {
 
     private FileOutputStream stream;
@@ -24,7 +27,7 @@
         try {
             File f = new File("logs");
             f.mkdirs();
-            stream = new FileOutputStream(new File(f, "audit.log"));
+            stream = new FileOutputStream(new File(f, "default_audit.log"));
         }catch (FileNotFoundException e) {
             e.printStackTrace();
         }
@@ -34,33 +37,43 @@
     public <T extends AuditRecord> List<T> retrieveRecords(
             AuditRecord.CATEGORY category, DateTime day, DateTime until,
             boolean exact, int limit) {
-        throw new UnsupportedOperationException("operation not supported!");
+        throw new UnsupportedOperationException("Operation not supported!");
     }
 
     @Override
     public <T extends AuditRecord> List<T> retrieveRecords(
             AuditRecord.CATEGORY category, User user, int limit) {
-        throw new UnsupportedOperationException("operation not supported!");
+        throw new UnsupportedOperationException("Operation not supported!");
     }
 
     @Override
     public <T extends AuditRecord> List<T> retrieveRecords(LocalDate day,
             int hitMax) {
-        throw new UnsupportedOperationException("operation not supported!");
+        throw new UnsupportedOperationException("Operation not supported!");
     }
 
     @Override
     public <T extends AuditRecord> List<T> retrieveRecords(String userID,
             LocalDate start, LocalDate end, int hitMax) {
-        throw new UnsupportedOperationException("operation not supported!");
+        throw new UnsupportedOperationException("Operation not supported!");
     }
 
     @Override
-    public void run() {
+    public void apply() {
+        List<AuditRecord> rcs = getRecordsToSave();
         try {
-            for (AuditRecord r : getRecordsToSave())
+            for (AuditRecord r : rcs)
                 stream.write((r.toString() + "\n").getBytes());
+        }catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void finish() {
+        try {
             stream.flush();
+            stream.close();
         }catch (IOException e) {
             e.printStackTrace();
         }
diff --git a/src/main/java/de/ids_mannheim/korap/interfaces/defaults/DefaultEncryption.java b/src/main/java/de/ids_mannheim/korap/interfaces/defaults/DefaultEncryption.java
index 64f3b5e..a79c82f 100644
--- a/src/main/java/de/ids_mannheim/korap/interfaces/defaults/DefaultEncryption.java
+++ b/src/main/java/de/ids_mannheim/korap/interfaces/defaults/DefaultEncryption.java
@@ -1,5 +1,7 @@
 package de.ids_mannheim.korap.interfaces.defaults;
 
+import de.ids_mannheim.korap.config.BeanConfiguration;
+import de.ids_mannheim.korap.config.Configurable;
 import de.ids_mannheim.korap.exceptions.KorAPException;
 import de.ids_mannheim.korap.interfaces.EncryptionIface;
 import de.ids_mannheim.korap.user.User;
@@ -14,7 +16,7 @@
  * @author hanl
  * @date 05/06/2015
  */
-//todo: init!
+@Configurable(BeanConfiguration.KUSTVAKT_ENCRYPTION)
 public class DefaultEncryption implements EncryptionIface {
 
     private SecureRandom randomizer;
diff --git a/src/main/java/de/ids_mannheim/korap/resource/LayerMapper.java b/src/main/java/de/ids_mannheim/korap/resource/LayerMapper.java
index ff2b2cb..b51229c 100644
--- a/src/main/java/de/ids_mannheim/korap/resource/LayerMapper.java
+++ b/src/main/java/de/ids_mannheim/korap/resource/LayerMapper.java
@@ -1,6 +1,5 @@
 package de.ids_mannheim.korap.resource;
 
-import de.ids_mannheim.korap.config.BeanConfiguration;
 import de.ids_mannheim.korap.config.KustvaktConfiguration;
 import de.ids_mannheim.korap.user.UserSettings;
 
@@ -13,13 +12,13 @@
     private UserSettings settings;
     private KustvaktConfiguration config;
 
-    public LayerMapper(UserSettings settings) {
+    public LayerMapper(KustvaktConfiguration config, UserSettings settings) {
         this.settings = settings;
-        this.config = BeanConfiguration.getConfiguration();
+        this.config = config;
     }
 
-    public LayerMapper() {
-        this.config = BeanConfiguration.getConfiguration();
+    public LayerMapper(KustvaktConfiguration config) {
+        this.config = config;
     }
 
     /**
diff --git a/src/main/java/de/ids_mannheim/korap/resource/LayerProcessor.java b/src/main/java/de/ids_mannheim/korap/resource/LayerProcessor.java
index 90b6319..952e308 100644
--- a/src/main/java/de/ids_mannheim/korap/resource/LayerProcessor.java
+++ b/src/main/java/de/ids_mannheim/korap/resource/LayerProcessor.java
@@ -2,6 +2,7 @@
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
 
 import java.util.Iterator;
 
@@ -13,8 +14,8 @@
 
     private LayerMapper mapper;
 
-    public LayerProcessor() {
-        this.mapper = new LayerMapper();
+    public LayerProcessor(KustvaktConfiguration config) {
+        this.mapper = new LayerMapper(config);
     }
 
     @Override
diff --git a/src/main/java/de/ids_mannheim/korap/resource/RewriteProcessor.java b/src/main/java/de/ids_mannheim/korap/resource/RewriteProcessor.java
index 6031261..4d2a2a4 100644
--- a/src/main/java/de/ids_mannheim/korap/resource/RewriteProcessor.java
+++ b/src/main/java/de/ids_mannheim/korap/resource/RewriteProcessor.java
@@ -1,7 +1,6 @@
 package de.ids_mannheim.korap.resource;
 
 import com.fasterxml.jackson.databind.JsonNode;
-import de.ids_mannheim.korap.config.BeanConfiguration;
 import de.ids_mannheim.korap.config.KustvaktConfiguration;
 import de.ids_mannheim.korap.utils.JsonUtils;
 
@@ -17,10 +16,10 @@
     private KustvaktConfiguration config;
     private List<NodeProcessor> processors;
 
-    public RewriteProcessor() {
-        this.config = BeanConfiguration.getConfiguration();
+    public RewriteProcessor(KustvaktConfiguration config) {
+        this.config = config;
         this.processors = new ArrayList<>();
-        addProcessor(new LayerProcessor());
+        addProcessor(new LayerProcessor(config));
     }
 
     public JsonNode process(JsonNode node) {
diff --git a/src/main/java/de/ids_mannheim/korap/resource/rewrite/CollectionCleanupFilter.java b/src/main/java/de/ids_mannheim/korap/resource/rewrite/CollectionCleanupFilter.java
new file mode 100644
index 0000000..93db8ae
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/resource/rewrite/CollectionCleanupFilter.java
@@ -0,0 +1,79 @@
+package de.ids_mannheim.korap.resource.rewrite;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import de.ids_mannheim.korap.utils.JsonUtils;
+
+import java.util.Iterator;
+
+/**
+ * @author hanl
+ * @date 28/07/2015
+ */
+//todo: test
+public class CollectionCleanupFilter extends RewriteQuery {
+
+    // track path to operand
+    private StringBuilder builder;
+
+    public CollectionCleanupFilter() {
+        this.builder = new StringBuilder();
+    }
+
+    @Override
+    public JsonNode rewrite(KoralNode node) {
+        if (node.rawNode().has("collection")) {
+            JsonNode coll = node.rawNode().path("collection");
+            process(coll);
+        }
+        return null;
+    }
+
+    private JsonNode process(JsonNode root) {
+        if (root.isObject()) {
+            if (root.has("operands")) {
+                JsonNode node = root.at("/operands");
+                Iterator<JsonNode> it = node.elements();
+                while (it.hasNext()) {
+                    JsonNode n = it.next();
+                    JsonNode s = process(n);
+                    if (s == null)
+                        it.remove();
+                }
+            }
+
+            JsonNode sub = processNodes(root);
+            if (!root.equals(sub)) {
+                if (sub == null) {
+                    //can't do anything here -- fixme: edge case?!
+                    return null;
+                }else if (sub.isObject()) {
+                    ObjectNode ob = (ObjectNode) root;
+                    ob.removeAll();
+                    ob.putAll((ObjectNode) sub);
+                }
+
+            }
+        }
+        return root;
+    }
+
+    // return null deletes node, if node return replace at level -1
+    private JsonNode processNodes(JsonNode jsonNode) {
+        if (jsonNode.isObject()) {
+            if (jsonNode.has("operands")) {
+                JsonNode node = jsonNode.at("/operands");
+                int count = node.size();
+                if (count == 1) {
+                    // move to super node if any
+                    return node.path(0);
+                }else if (count == 0) {
+                    // remove container
+                    return null;
+                }
+                return jsonNode;
+            }
+        }
+        return JsonUtils.createArrayNode();
+    }
+}
diff --git a/src/main/java/de/ids_mannheim/korap/resource/rewrite/CollectionConstraint.java b/src/main/java/de/ids_mannheim/korap/resource/rewrite/CollectionConstraint.java
new file mode 100644
index 0000000..a4cf771
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/resource/rewrite/CollectionConstraint.java
@@ -0,0 +1,29 @@
+package de.ids_mannheim.korap.resource.rewrite;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import de.ids_mannheim.korap.user.User;
+
+/**
+ * @author hanl
+ * @date 03/07/2015
+ */
+public class CollectionConstraint extends RewriteNode {
+
+    @Override
+    public JsonNode rewrite(KoralNode koralnode) {
+        JsonNode node = koralnode.rawNode();
+        if (node.at("/@type").asText().equals("koral:doc")) {
+            if (node.at("/key").asText().equals("corpusID") && !check(node,
+                    koralnode.getUser())) {
+                koralnode.removeNode();
+                // todo: add message that node was removed!
+            }
+        }
+        return node;
+    }
+
+    private boolean check(JsonNode node, User user) {
+        return false;
+    }
+
+}
diff --git a/src/main/java/de/ids_mannheim/korap/resource/rewrite/FoundryInject.java b/src/main/java/de/ids_mannheim/korap/resource/rewrite/FoundryInject.java
new file mode 100644
index 0000000..58e3ff2
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/resource/rewrite/FoundryInject.java
@@ -0,0 +1,39 @@
+package de.ids_mannheim.korap.resource.rewrite;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.resource.LayerMapper;
+
+/**
+ * @author hanl
+ * @date 30/06/2015
+ */
+public class FoundryInject extends RewriteNode {
+
+    private KustvaktConfiguration config;
+
+    public FoundryInject(KustvaktConfiguration config) {
+        this.config = config;
+    }
+
+    @Override
+    public JsonNode rewrite(KoralNode node) {
+        LayerMapper mapper;
+        if (node.hasUser())
+            mapper = new LayerMapper(config, node.getUser().getSettings());
+        else
+            mapper = new LayerMapper(config);
+
+        if (node.rawNode().path("@type").asText().equals("koral:term") && !node
+                .rawNode().has("foundry")) {
+            String layer;
+            if (node.rawNode().has("layer"))
+                layer = node.rawNode().path("layer").asText();
+            else
+                layer = node.rawNode().path("key").asText();
+            String foundry = mapper.findFoundry(layer);
+            node.set("foundry", foundry);
+        }
+        return node.rawNode();
+    }
+}
diff --git a/src/main/java/de/ids_mannheim/korap/resource/rewrite/KoralNode.java b/src/main/java/de/ids_mannheim/korap/resource/rewrite/KoralNode.java
new file mode 100644
index 0000000..e708504
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/resource/rewrite/KoralNode.java
@@ -0,0 +1,145 @@
+package de.ids_mannheim.korap.resource.rewrite;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.utils.JsonUtils;
+
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author hanl
+ * @date 04/07/2015
+ */
+public abstract class KoralNode {
+    private JsonNode node;
+    private KoralRewriteBuilder builder;
+    private boolean toRemove;
+    private final User user;
+
+    private KoralNode(JsonNode node) {
+        this(node, null);
+    }
+
+    public KoralNode(JsonNode node, User user) {
+        this.node = node;
+        this.builder = new KoralRewriteBuilder();
+        this.toRemove = false;
+        this.user = user;
+    }
+
+    public boolean hasUser() {
+        return this.user != null;
+    }
+
+    public User getUser() {
+        return this.user;
+    }
+
+    public static KoralNode getNode(JsonNode node) {
+        return new KoralNode(node) {
+        };
+    }
+
+    public void set(String name, Object value) {
+
+        if (this.node.isObject()) {
+            ObjectNode node = (ObjectNode) this.node;
+            if (value instanceof String)
+                node.put(name, (String) value);
+            else if (value instanceof Integer)
+                node.put(name, (Integer) value);
+            builder.setOperation("injection");
+            builder.build(this.node);
+        }else
+            throw new UnsupportedOperationException(
+                    "node doesn't support this operation");
+    }
+
+    public void remove(Object identifier) {
+        boolean set = false;
+        if (this.node.isObject() && identifier instanceof String) {
+            ObjectNode n = (ObjectNode) this.node;
+            n.remove((String) identifier);
+            set = true;
+        }else if (this.node.isArray() && identifier instanceof Integer) {
+            ArrayNode n = (ArrayNode) this.node;
+            n.remove((Integer) identifier);
+            set = true;
+        }
+        if (set) {
+            builder.setOperation("deletion");
+            builder.build(this.node);
+        }
+    }
+
+    public void replace(String name, String value) {
+        if (this.node.isObject() && this.node.has(name)) {
+            ObjectNode n = (ObjectNode) this.node;
+            n.put(name, value);
+            builder.setOperation("override");
+            builder.build(this.node);
+        }
+    }
+
+    public JsonNode rawNode() {
+        return this.node;
+    }
+
+    public void removeNode() {
+        this.toRemove = true;
+    }
+
+    public boolean toRemove() {
+        return this.toRemove;
+    }
+
+    private static class KoralRewriteBuilder {
+
+        private Map<String, String> map;
+
+        public KoralRewriteBuilder() {
+            this.map = new LinkedHashMap<>();
+            this.map.put("@type", "koral:rewrite");
+            this.map.put("src", "Kustvakt");
+        }
+
+        public KoralRewriteBuilder setOperation(String op) {
+            if (!op.startsWith("operation:"))
+                op = "operation:" + op;
+            this.map.put("operation", op);
+            return this;
+        }
+
+        public KoralRewriteBuilder setScope(String scope) {
+            this.map.put("scope", scope);
+            return this;
+        }
+
+        public JsonNode build(JsonNode node) {
+            if (this.map.get("operation") == null)
+                throw new UnsupportedOperationException(
+                        "operation not set properly");
+
+            if (node.has("rewrites")) {
+                ArrayNode n = (ArrayNode) node.path("rewrites");
+                n.add(JsonUtils.valueToTree(this.map));
+            }else {
+                ObjectNode n = (ObjectNode) node;
+                List l = new LinkedList<>();
+                l.add(JsonUtils.valueToTree(this.map));
+                n.put("rewrites", JsonUtils.valueToTree(l));
+            }
+            return node;
+        }
+
+    }
+}
+
+
+
+
diff --git a/src/main/java/de/ids_mannheim/korap/resource/rewrite/MetaConstraint.java b/src/main/java/de/ids_mannheim/korap/resource/rewrite/MetaConstraint.java
new file mode 100644
index 0000000..4660235
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/resource/rewrite/MetaConstraint.java
@@ -0,0 +1,24 @@
+package de.ids_mannheim.korap.resource.rewrite;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * @author hanl
+ * @date 04/07/2015
+ */
+public class MetaConstraint extends RewriteQuery {
+
+    public MetaConstraint() {
+        super();
+    }
+
+    @Override
+    public JsonNode rewrite(KoralNode node) {
+        if (node.rawNode().has("meta")) {
+            JsonNode meta = node.rawNode().path("meta");
+            //todo: check meta parameter
+            System.out.println("HAVE TO CHECK THE META ENTRIES");
+        }
+        return node.rawNode();
+    }
+}
diff --git a/src/main/java/de/ids_mannheim/korap/resource/rewrite/PublicCollection.java b/src/main/java/de/ids_mannheim/korap/resource/rewrite/PublicCollection.java
new file mode 100644
index 0000000..d6685ea
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/resource/rewrite/PublicCollection.java
@@ -0,0 +1,20 @@
+package de.ids_mannheim.korap.resource.rewrite;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * @author hanl
+ * @date 04/07/2015
+ */
+public class PublicCollection extends RewriteQuery {
+
+    @Override
+    public JsonNode rewrite(KoralNode node) {
+        JsonNode subnode = node.rawNode();
+        if (!subnode.at("/collection").findValuesAsText("key")
+                .contains("corpusID")) {
+            //todo: inject public collection node
+        }
+        return subnode;
+    }
+}
diff --git a/src/main/java/de/ids_mannheim/korap/resource/rewrite/RewriteHandler.java b/src/main/java/de/ids_mannheim/korap/resource/rewrite/RewriteHandler.java
new file mode 100644
index 0000000..5676817
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/resource/rewrite/RewriteHandler.java
@@ -0,0 +1,87 @@
+package de.ids_mannheim.korap.resource.rewrite;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.utils.JsonUtils;
+import de.ids_mannheim.korap.utils.KustvaktLogger;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+
+/**
+ * @author hanl
+ * @date 30/06/2015
+ */
+//todo: load rewritenode and rewritequery automatically from classpath, but namespaced from package
+public class RewriteHandler {
+
+    private static Logger jlog = KustvaktLogger.initiate(RewriteHandler.class);
+    private Collection<RewriteNode> node_processors;
+    private Collection<RewriteQuery> query_processors;
+
+    public RewriteHandler() {
+        this.node_processors = new HashSet<>();
+        this.query_processors = new HashSet<>();
+        // add defaults?!
+    }
+
+    public void add(RewriteNode node) {
+        this.node_processors.add(node);
+    }
+
+    public void add(RewriteQuery node) {
+        this.query_processors.add(node);
+    }
+
+    public void clear() {
+        this.node_processors.clear();
+        this.query_processors.clear();
+    }
+
+    private boolean process(JsonNode root, User user) {
+        if (root.isObject()) {
+            if (root.has("operands")) {
+                JsonNode node = root.at("/operands");
+                Iterator<JsonNode> it = node.elements();
+                while (it.hasNext()) {
+                    JsonNode n = it.next();
+                    if (!process(n, user))
+                        it.remove();
+                }
+            }else if (root.has("wrap")) {
+                JsonNode node = root.at("/wrap");
+                //todo: remove object nodes as well
+                process(node, user);
+            }else
+                return processNode(root, user, this.node_processors);
+        }
+        return true;
+    }
+
+    public JsonNode apply(JsonNode root, User user) {
+        for (JsonNode n : root)
+            process(n, user);
+        processNode(root, user, this.query_processors);
+        return root;
+    }
+
+    public String apply(String json, User user) {
+        return JsonUtils.toJSON(apply(JsonUtils.readTree(json), user));
+    }
+
+    private static boolean processNode(JsonNode node, User user,
+            Collection<? extends RewriteTask> tasks) {
+        KoralNode knode = KoralNode.getNode(node);
+        for (RewriteTask task : tasks) {
+            jlog.debug("running node in processor " + node);
+            jlog.debug("on processor " + task.getClass().toString());
+            task.rewrite(knode);
+            if (knode.toRemove())
+                break;
+        }
+        return !knode.toRemove();
+    }
+
+}
diff --git a/src/main/java/de/ids_mannheim/korap/resource/rewrite/RewriteNode.java b/src/main/java/de/ids_mannheim/korap/resource/rewrite/RewriteNode.java
new file mode 100644
index 0000000..cb0a06c
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/resource/rewrite/RewriteNode.java
@@ -0,0 +1,20 @@
+package de.ids_mannheim.korap.resource.rewrite;
+
+/**
+ * node rewrites get injected typically object nodes that are subject to altering.
+ * Be aware that node rewrites are processed before query rewrites. Thus query rewrite may override previous node rewrites
+ *
+ * {@link de.ids_mannheim.korap.resource.rewrite.RewriteNode} rewrite support the deletion of the respective node by simply setting the node invalid in KoralNode
+ *
+ * @author hanl
+ * @date 03/07/2015
+ */
+public abstract class RewriteNode extends RewriteTask {
+
+    public RewriteNode() {
+        super();
+    }
+
+
+
+}
diff --git a/src/main/java/de/ids_mannheim/korap/resource/rewrite/RewriteQuery.java b/src/main/java/de/ids_mannheim/korap/resource/rewrite/RewriteQuery.java
new file mode 100644
index 0000000..99bfb34
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/resource/rewrite/RewriteQuery.java
@@ -0,0 +1,18 @@
+package de.ids_mannheim.korap.resource.rewrite;
+
+/**
+ * query rewrites get injected the entire query from root containing all child nodes
+ * <p/>
+ * {@link de.ids_mannheim.korap.resource.rewrite.RewriteQuery} does not allow the deletion of the root node or subnode through KoralNode.
+ * The {@link de.ids_mannheim.korap.resource.rewrite.RewriteHandler} will igonore respecitve invalid requests
+ *
+ * @author hanl
+ * @date 03/07/2015
+ */
+public abstract class RewriteQuery extends RewriteTask {
+
+    public RewriteQuery() {
+        super();
+    }
+
+}
diff --git a/src/main/java/de/ids_mannheim/korap/resource/rewrite/RewriteTask.java b/src/main/java/de/ids_mannheim/korap/resource/rewrite/RewriteTask.java
new file mode 100644
index 0000000..ed5ad34
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/resource/rewrite/RewriteTask.java
@@ -0,0 +1,17 @@
+package de.ids_mannheim.korap.resource.rewrite;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import lombok.Getter;
+
+/**
+ * @author hanl
+ * @date 30/06/2015
+ */
+@Getter
+public abstract class RewriteTask {
+
+    protected RewriteTask() {
+    }
+
+    public abstract JsonNode rewrite(KoralNode node);
+}
diff --git a/src/main/java/de/ids_mannheim/korap/resource/rewrite/TreeConstraint.java b/src/main/java/de/ids_mannheim/korap/resource/rewrite/TreeConstraint.java
new file mode 100644
index 0000000..e285114
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/resource/rewrite/TreeConstraint.java
@@ -0,0 +1,57 @@
+package de.ids_mannheim.korap.resource.rewrite;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * #ELEM(W ANA=N)
+ * <p/>
+ * {
+ * "@context": "http://korap.ids-mannheim.de/ns/koral/0.3/context.jsonld",
+ * "errors": [],
+ * "warnings": [],
+ * "messages": [],
+ * "collection": {},
+ * "query": {
+ * "@type": "koral:span",
+ * "key": "w",
+ * "attr": {
+ * "@type": "koral:term",
+ * "layer": "p",
+ * "key": "N",
+ * "match": "match:eq"
+ * }
+ * },
+ * "meta": {}
+ * }
+ * <p/>
+ * <p/>
+ * email reference:
+ * Hallo Michael,
+ * mir fiel gestern bei der neuen KoralQuery Serialisierung noch ein Fall
+ * für default-Werte ein, die zumindest für viele Beispiele, die wir haben,
+ * relevant ist: Wenn ein koral:term in einem koral:span gewrappt ist, dann
+ * kann er eventuell nur einen Schlüssel haben ("s" oder "p" von "<s>" oder
+ * "<p>". In diesem Fall wäre der default layer "s" und die default foundry
+ * "base". (Im alten KoralQuery wurden spans nicht gewrappt - der Fall
+ * sollte aber erstmal weiter unterstützt werden.)
+ * Viele Grüße,
+ * Nils
+ *
+ * @author hanl
+ * @date 02/07/2015
+ */
+public class TreeConstraint extends RewriteQuery {
+
+    private String pointer;
+
+    public TreeConstraint(String constraint_pointer) {
+        this.pointer = constraint_pointer;
+    }
+
+    @Override
+    public JsonNode rewrite(KoralNode node) {
+        System.out.println("FIND PATH " + node.rawNode().findParent(pointer));
+
+        return node.rawNode();
+    }
+}
diff --git a/src/main/java/de/ids_mannheim/korap/user/Attributes.java b/src/main/java/de/ids_mannheim/korap/user/Attributes.java
index 0bba5b7..55110f8 100644
--- a/src/main/java/de/ids_mannheim/korap/user/Attributes.java
+++ b/src/main/java/de/ids_mannheim/korap/user/Attributes.java
@@ -27,6 +27,7 @@
 
     public static final String TYPE = "type";
 
+    public static final String ID = "ID";
     public static final String UID = "accountID";
     public static final String USERNAME = "username";
     public static final String PASSWORD = "password";
@@ -42,8 +43,8 @@
     public static final String ACCOUNTLOCK = "accountLock";
     public static final String ACCOUNTLINK = "accountLink";
     public static final String URI = "uri";
-    public static final String URI_FRAGMENT = "URIFragment";
-    public static final String URI_EXPIRATION = "uriExpiration";
+    public static final String URI_FRAGMENT = "uri_fragment";
+    public static final String URI_EXPIRATION = "uri_expiration";
     public static final String PRIVATE_USAGE = "privateUsage";
 
     /**
diff --git a/src/main/java/de/ids_mannheim/korap/user/KorAPUser.java b/src/main/java/de/ids_mannheim/korap/user/KorAPUser.java
index 5eb6932..ab1c164 100644
--- a/src/main/java/de/ids_mannheim/korap/user/KorAPUser.java
+++ b/src/main/java/de/ids_mannheim/korap/user/KorAPUser.java
@@ -1,6 +1,6 @@
 package de.ids_mannheim.korap.user;
 
-import de.ids_mannheim.korap.utils.KorAPLogger;
+import de.ids_mannheim.korap.utils.KustvaktLogger;
 import lombok.Getter;
 import lombok.Setter;
 import org.slf4j.Logger;
@@ -8,7 +8,7 @@
 @Getter
 @Setter
 public class KorAPUser extends User {
-    private static Logger jlog = KorAPLogger.initiate(KorAPUser.class);
+    private static Logger jlog = KustvaktLogger.initiate(KorAPUser.class);
     private static final long serialVersionUID = -7108308497625884584L;
 
     //fixme: accountlink to shibboleth account
diff --git a/src/main/java/de/ids_mannheim/korap/user/User.java b/src/main/java/de/ids_mannheim/korap/user/User.java
index 2b28edd..7de02b4 100644
--- a/src/main/java/de/ids_mannheim/korap/user/User.java
+++ b/src/main/java/de/ids_mannheim/korap/user/User.java
@@ -1,6 +1,7 @@
 package de.ids_mannheim.korap.user;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import de.ids_mannheim.korap.config.ParamFields;
 import de.ids_mannheim.korap.utils.JsonUtils;
 import de.ids_mannheim.korap.utils.TimeUtils;
 import lombok.Data;
@@ -23,16 +24,16 @@
     private Long accountCreation;
     private boolean isAccountLocked;
     private int type;
-    private Map<String, Object> fields;
+    private ParamFields fields;
     private UserSettings settings;
     private UserDetails details;
     private List<UserQuery> queries;
 
     protected User() {
+        this.fields = new ParamFields();
         this.accountCreation = TimeUtils.getNow().getMillis();
         this.isAccountLocked = false;
         this.username = "";
-        this.fields = new HashMap<>();
         this.id = -1;
     }
 
@@ -46,12 +47,12 @@
         this.username = username;
     }
 
-    public void setField(String key, Object value) {
-        this.fields.put(key, value);
+    public void addField(ParamFields.Param param) {
+        this.fields.add(param);
     }
 
-    public Object getField(String key) {
-        return this.fields.get(key);
+    public <T extends ParamFields.Param> T getField(Class<T> cl) {
+        return this.fields.get(cl);
     }
 
     //todo: repair transfer
diff --git a/src/main/java/de/ids_mannheim/korap/user/UserSettings.java b/src/main/java/de/ids_mannheim/korap/user/UserSettings.java
index 365e69a..ebb85ed 100644
--- a/src/main/java/de/ids_mannheim/korap/user/UserSettings.java
+++ b/src/main/java/de/ids_mannheim/korap/user/UserSettings.java
@@ -279,7 +279,7 @@
                 (String) m.get(Attributes.DEFAULT_REL_FOUNDRY));
     }
 
-    //load from configuration?
+    //loadSubTypes from configuration?
     private void setupDefaultSettings() {
         this.setFileNameForExport("export");
         //        this.setItemForSimpleAnnotation(0);
diff --git a/src/main/java/de/ids_mannheim/korap/utils/CollectionQueryBuilder3.java b/src/main/java/de/ids_mannheim/korap/utils/CollectionQueryBuilder3.java
index 1b52306..4d012b2 100644
--- a/src/main/java/de/ids_mannheim/korap/utils/CollectionQueryBuilder3.java
+++ b/src/main/java/de/ids_mannheim/korap/utils/CollectionQueryBuilder3.java
@@ -3,13 +3,10 @@
 import de.ids_mannheim.korap.query.serialize.CollectionQueryProcessor;
 
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 /**
- * convenience builder class for collection query -- new one
+ * convenience builder class for collection query
  *
  * @author hanl
  * @date 16/09/2014
@@ -30,14 +27,21 @@
         this.rq = new LinkedList<>();
     }
 
-
     public CollectionQueryBuilder3 addSegment(String field, String value) {
         String f = field + "=" + value;
         this.builder.append(f);
         return this;
     }
 
-    public CollectionQueryBuilder3 add(String query) {
+    /**
+     * element can be a more complex sub query like (textClass=freizeit & corpusID=WPD)
+     *
+     * @param query will be parenthised in order to make sub query element
+     * @return
+     */
+    public CollectionQueryBuilder3 addSub(String query) {
+        if (!query.startsWith("(") && !query.endsWith(")"))
+            query = "(" + query + ")";
         this.builder.append(query);
         return this;
     }
@@ -52,29 +56,33 @@
         return this;
     }
 
-    public CollectionQueryBuilder3 addResource(String collection) {
+    public CollectionQueryBuilder3 addRaw(String collection) {
         try {
-            List v = JsonUtils.read(collection, LinkedList.class);
-            this.rq.addAll(v);
-        } catch (IOException e) {
+            Map v = JsonUtils.read(collection, HashMap.class);
+            v.get("collection");
+        }catch (IOException e) {
             throw new IllegalArgumentException("Conversion went wrong!");
         }
         return this;
     }
 
-    public List getRequest() {
-        List list = new ArrayList();
-        if (!this.rq.isEmpty())
-            list.addAll(this.rq);
-        CollectionQueryProcessor tree = new CollectionQueryProcessor(this.verbose);
+    public Map getRequest() {
+        //todo: adding another resource query doesnt work
+
+        CollectionQueryProcessor tree = new CollectionQueryProcessor(
+                this.verbose);
         tree.process(this.builder.toString());
-        list.add(tree.getRequestMap());
-        return list;
+
+        Map request = tree.getRequestMap();
+        if (!this.rq.isEmpty()) {
+            List coll = (List) request.get("collection");
+            coll.addAll(this.rq);
+        }
+        return request;
     }
 
     public String toJSON() {
         return JsonUtils.toJSON(getRequest());
     }
 
-
 }
diff --git a/src/main/java/de/ids_mannheim/korap/utils/KorAPLogger.java b/src/main/java/de/ids_mannheim/korap/utils/KustvaktLogger.java
similarity index 93%
rename from src/main/java/de/ids_mannheim/korap/utils/KorAPLogger.java
rename to src/main/java/de/ids_mannheim/korap/utils/KustvaktLogger.java
index 5fc876f..90f3c23 100644
--- a/src/main/java/de/ids_mannheim/korap/utils/KorAPLogger.java
+++ b/src/main/java/de/ids_mannheim/korap/utils/KustvaktLogger.java
@@ -9,7 +9,7 @@
  * @date 28/03/2014
  */
 
-public class KorAPLogger implements Logger {
+public class KustvaktLogger implements Logger {
     // debugging flag, info, warn, error will always be logged though!
     public static boolean DEBUG = false;
 
@@ -17,28 +17,29 @@
     public static final String SECURITY_LOG = "securityLog";
 
     //fixme:
-    public static final KorAPLogger ERROR_LOGGER = KorAPLogger
+    public static final KustvaktLogger ERROR_LOGGER = KustvaktLogger
             .initiate(ERROR_LOG);
-    public static final KorAPLogger QUERY_LOGGER = KorAPLogger.initiate("ql");
+    public static final KustvaktLogger QUERY_LOGGER = KustvaktLogger
+            .initiate("ql");
 
     @Deprecated
-    public static final KorAPLogger SECURITY_LOGGER = KorAPLogger
+    public static final KustvaktLogger SECURITY_LOGGER = KustvaktLogger
             .initiate("security");
     private Logger log;
 
-    public static KorAPLogger initiate(Class cl) {
-        KorAPLogger l = new KorAPLogger();
+    public static KustvaktLogger initiate(Class cl) {
+        KustvaktLogger l = new KustvaktLogger();
         l.log = LoggerFactory.getLogger(cl);
         return l;
     }
 
-    public static KorAPLogger initiate(String name) {
-        KorAPLogger l = new KorAPLogger();
+    public static KustvaktLogger initiate(String name) {
+        KustvaktLogger l = new KustvaktLogger();
         l.log = LoggerFactory.getLogger(name);
         return l;
     }
 
-    private KorAPLogger() {
+    private KustvaktLogger() {
     }
 
     @Override
diff --git a/src/main/java/de/ids_mannheim/korap/utils/ServiceVersion.java b/src/main/java/de/ids_mannheim/korap/utils/ServiceVersion.java
new file mode 100755
index 0000000..1233c15
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/utils/ServiceVersion.java
@@ -0,0 +1,33 @@
+package de.ids_mannheim.korap.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+/**
+ * @author hanl
+ * @date 23/01/2014
+ */
+public class ServiceVersion {
+
+    private ServiceVersion() {
+    }
+
+    public static String getAPIVersion() {
+        String path = "/version.prop";
+        InputStream stream = ServiceVersion.class.getResourceAsStream(path);
+        if (stream == null)
+            return "UNKNOWN";
+
+        Properties props = new Properties();
+        try {
+            props.load(stream);
+            stream.close();
+            return (String) props.get("version");
+        }catch (IOException e) {
+            return "UNKNOWN";
+        }
+
+    }
+
+}
diff --git a/src/main/java/de/ids_mannheim/korap/utils/StringUtils.java b/src/main/java/de/ids_mannheim/korap/utils/StringUtils.java
index a7120aa..ac2c1a7 100644
--- a/src/main/java/de/ids_mannheim/korap/utils/StringUtils.java
+++ b/src/main/java/de/ids_mannheim/korap/utils/StringUtils.java
@@ -1,12 +1,13 @@
 package de.ids_mannheim.korap.utils;
 
-import org.apache.commons.lang3.StringEscapeUtils;
+import org.apache.commons.lang.StringEscapeUtils;
 import org.slf4j.Logger;
 
 import java.util.*;
 
 public class StringUtils {
-    private final static Logger jlog = KorAPLogger.initiate(StringUtils.class);
+    private final static Logger jlog = KustvaktLogger
+            .initiate(StringUtils.class);
 
     private static final String SLASH = "/";
     private static final String SEP = ";";
@@ -153,11 +154,11 @@
     }
 
     public static String normalizeHTML(String value) {
-        return StringEscapeUtils.escapeHtml4(value);
+        return StringEscapeUtils.escapeHtml(value);
     }
 
     public static String decodeHTML(String value) {
-        return StringEscapeUtils.unescapeHtml4(value);
+        return StringEscapeUtils.unescapeHtml(value);
     }
 
     /**
diff --git a/src/main/java/de/ids_mannheim/korap/utils/TimeUtils.java b/src/main/java/de/ids_mannheim/korap/utils/TimeUtils.java
index b4f1d30..3627a4d 100644
--- a/src/main/java/de/ids_mannheim/korap/utils/TimeUtils.java
+++ b/src/main/java/de/ids_mannheim/korap/utils/TimeUtils.java
@@ -25,7 +25,7 @@
 
     private static DecimalFormat df = new DecimalFormat("#.#############");
     private static final DateTimeZone dtz = DateTimeZone.forID("Europe/Berlin");
-    private static Logger jlog = KorAPLogger.initiate(TimeUtils.class);
+    private static Logger jlog = KustvaktLogger.initiate(TimeUtils.class);
 
     public static int convertTimeToSeconds(String expirationVal) {
         expirationVal = expirationVal.trim();
diff --git a/src/main/java/de/ids_mannheim/korap/web/Kustvakt.java b/src/main/java/de/ids_mannheim/korap/web/Kustvakt.java
deleted file mode 100644
index f5c14f4..0000000
--- a/src/main/java/de/ids_mannheim/korap/web/Kustvakt.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package de.ids_mannheim.korap.web;
-
-import com.sun.grizzly.http.embed.GrizzlyWebServer;
-import com.sun.grizzly.http.servlet.ServletAdapter;
-import com.sun.jersey.spi.container.servlet.ServletContainer;
-import de.ids_mannheim.korap.config.BeanConfiguration;
-import de.ids_mannheim.korap.utils.KorAPLogger;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-
-/**
- * @author hanl
- * @date 01/06/2015
- */
-public class Kustvakt {
-
-    private static Integer PORT = -1;
-    private static String CONFIG = null;
-
-    public static void main(String[] args) throws Exception {
-        attributes(args);
-        BeanConfiguration.loadClasspathContext();
-
-        if (CONFIG != null) {
-            BeanConfiguration.getConfiguration()
-                    .setPropertiesAsStream(new FileInputStream(new File(CONFIG)));
-
-        }
-        grizzlyServer(PORT);
-    }
-
-    public static void grizzlyServer(int port) throws IOException {
-        if (port == -1)
-            port = BeanConfiguration.getConfiguration().getPort();
-        System.out.println("Starting grizzly on port " + port + " ...");
-        GrizzlyWebServer gws = new GrizzlyWebServer(port);
-        ServletAdapter jerseyAdapter = new ServletAdapter();
-        jerseyAdapter
-                .addInitParameter("com.sun.jersey.config.property.packages",
-                        "de.ids_mannheim.korap.web.service");
-        jerseyAdapter.setContextPath("/api");
-        jerseyAdapter.setServletInstance(new ServletContainer());
-
-        gws.addGrizzlyAdapter(jerseyAdapter, new String[] { "/api" });
-        gws.start();
-    }
-
-    private static void attributes(String[] args) {
-        for (int i = 0; i < args.length; i++) {
-            switch ((args[i])) {
-                case "--debug":
-                    KorAPLogger.DEBUG = true;
-                    break;
-                case "--config":
-                    CONFIG = args[i + 1];
-                    break;
-                case "--port":
-                    PORT = Integer.valueOf(args[i + 1]);
-                    break;
-            }
-        }
-    }
-
-}
diff --git a/src/main/java/de/ids_mannheim/korap/web/KustvaktLightServer.java b/src/main/java/de/ids_mannheim/korap/web/KustvaktLightServer.java
new file mode 100644
index 0000000..65f21c3
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/web/KustvaktLightServer.java
@@ -0,0 +1,117 @@
+package de.ids_mannheim.korap.web;
+
+import com.sun.jersey.api.core.PackagesResourceConfig;
+import com.sun.jersey.spi.container.servlet.ServletContainer;
+import de.ids_mannheim.korap.config.BeanConfiguration;
+import lombok.Getter;
+import lombok.Setter;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.bio.SocketConnector;
+import org.eclipse.jetty.server.ssl.SslSocketConnector;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+/**
+ * @author hanl
+ * @date 01/06/2015
+ */
+public class KustvaktLightServer {
+
+    public static void main(String[] args) throws Exception {
+        KustvaktArgs kargs = readAttributes(args);
+
+        if (kargs.config != null)
+            BeanConfiguration.loadFileContext(kargs.config);
+        else
+            BeanConfiguration.loadClasspathContext();
+
+        kargs.setRootClasses(
+                new String[] { "de.ids_mannheim.korap.web.service" });
+        startServer(kargs);
+    }
+
+    public static KustvaktArgs readAttributes(String[] args) {
+        KustvaktArgs kargs = new KustvaktArgs();
+        for (int i = 0; i < args.length; i++) {
+            switch ((args[i])) {
+                case "--debug":
+                    kargs.setDebug(true);
+                    break;
+                case "--config":
+                    kargs.setConfig(args[i + 1]);
+                    break;
+                case "--port":
+                    kargs.setPort(Integer.valueOf(args[i + 1]));
+                    break;
+            }
+        }
+        return kargs;
+    }
+
+    public static void startServer(KustvaktArgs kargs) {
+        if (kargs.port == -1)
+            kargs.setPort(
+                    BeanConfiguration.getBeans().getConfiguration().getPort());
+
+        System.out.println(
+                "Starting Kustvakt Service on port '" + kargs.port + "'");
+        try {
+            // from http://wiki.eclipse.org/Jetty/Tutorial/Embedding_Jetty
+            Server server = new Server();
+            ServletContextHandler contextHandler = new ServletContextHandler(
+                    ServletContextHandler.NO_SESSIONS);
+            contextHandler.setContextPath("/");
+
+            SocketConnector connector = new SocketConnector();
+            connector.setPort(kargs.port);
+            connector.setMaxIdleTime(60000);
+
+            // http://stackoverflow.com/questions/9670363/how-do-i-programmatically-configure-jersey-to-use-jackson-for-json-deserializa
+            final PackagesResourceConfig prc = new PackagesResourceConfig(
+                    kargs.rootClasses);
+            // from http://stackoverflow.com/questions/7421574/embedded-jetty-with-jersey-or-resteasy
+            contextHandler
+                    .addServlet(new ServletHolder(new ServletContainer(prc)),
+                            "/api/*");
+
+            server.setHandler(contextHandler);
+
+            if (kargs.sslContext != null) {
+                SslSocketConnector sslConnector = new SslSocketConnector(
+                        kargs.sslContext);
+                sslConnector.setPort(8443);
+                sslConnector.setMaxIdleTime(60000);
+                server.setConnectors(
+                        new Connector[] { connector, sslConnector });
+            }else
+                server.setConnectors(new Connector[] { connector });
+
+            server.start();
+            server.join();
+        }catch (Exception e) {
+            e.printStackTrace();
+        }
+
+    }
+
+    @Setter
+    public static class KustvaktArgs {
+
+        private boolean debug;
+        @Getter
+        private String config;
+        private int port;
+        private SslContextFactory sslContext;
+        private String[] rootClasses;
+
+        public KustvaktArgs() {
+            this.port = -1;
+            this.sslContext = null;
+            this.debug = false;
+            this.config = null;
+        }
+    }
+
+}
diff --git a/src/main/java/de/ids_mannheim/korap/web/SearchKrill.java b/src/main/java/de/ids_mannheim/korap/web/SearchKrill.java
index dd9fdd1..ec3dd54 100644
--- a/src/main/java/de/ids_mannheim/korap/web/SearchKrill.java
+++ b/src/main/java/de/ids_mannheim/korap/web/SearchKrill.java
@@ -1,15 +1,13 @@
 // Connector to the Lucene Backend
 package de.ids_mannheim.korap.web;
 
-import com.fasterxml.jackson.databind.JsonNode;
 import de.ids_mannheim.korap.Krill;
 import de.ids_mannheim.korap.KrillCollection;
 import de.ids_mannheim.korap.KrillIndex;
 import de.ids_mannheim.korap.response.Match;
 import de.ids_mannheim.korap.response.Result;
 import de.ids_mannheim.korap.util.QueryException;
-import de.ids_mannheim.korap.utils.JsonUtils;
-import de.ids_mannheim.korap.utils.KorAPLogger;
+import de.ids_mannheim.korap.utils.KustvaktLogger;
 import org.apache.lucene.store.MMapDirectory;
 import org.slf4j.Logger;
 
@@ -24,8 +22,9 @@
  * @author Nils Diewald
  */
 public class SearchKrill {
-    private final static Logger qlog = KorAPLogger.initiate("queryLogger");
-    private final static Logger log = KorAPLogger.initiate(SearchKrill.class);
+    private final static Logger qlog = KustvaktLogger.initiate("queryLogger");
+    private final static Logger log = KustvaktLogger
+            .initiate(SearchKrill.class);
     // Temporary
     String indexDir = "/data/prep_corpus/index/";
     String i = "/Users/hanl/Projects/prep_corpus";
@@ -42,16 +41,17 @@
             File f = new File(path);
             log.info("Loading index from " + path);
             if (!f.exists()) {
-                KorAPLogger.ERROR_LOGGER.error("Index not found!");
+                KustvaktLogger.ERROR_LOGGER.error("Index not found!");
                 System.exit(-1);
             }
             this.index = new KrillIndex(new MMapDirectory(new File(path)));
-        } catch (IOException e) {
-            KorAPLogger.ERROR_LOGGER
-                    .error("Unable to load index: {}", e.getMessage());
+        }catch (IOException e) {
+            KustvaktLogger.ERROR_LOGGER
+                    .error("Unable to loadSubTypes index: {}", e.getMessage());
         }
-    };
+    }
 
+    ;
 
     /**
      * Search in the Lucene index.
@@ -66,8 +66,9 @@
         Result kr = new Result();
         kr.addError(601, "Unable to find index");
         return kr.toJsonString();
-    };
+    }
 
+    ;
 
     /**
      * Search in the Lucene index and return matches as token lists.
@@ -83,7 +84,9 @@
         Result kr = new Result();
         kr.addError(601, "Unable to find index");
         return kr.toJsonString();
-    };
+    }
+
+    ;
 
     /**
      * Get info on a match - by means of a richly annotated html snippet.
@@ -95,17 +98,20 @@
         if (this.index != null) {
             try {
                 return this.index.getMatch(id).toJsonString();
-            } catch (QueryException qe) {
+            }catch (QueryException qe) {
                 Match km = new Match();
                 km.addError(qe.getErrorCode(), qe.getMessage());
                 return km.toJsonString();
             }
-        };
+        }
+        ;
 
         Match km = new Match();
         km.addError(601, "Unable to find index");
         return km.toJsonString();
-    };
+    }
+
+    ;
 
     public String getMatch(String id, List<String> foundries,
             List<String> layers, boolean includeSpans,
@@ -117,18 +123,20 @@
                         .getMatchInfo(id, "tokens", true, foundries, layers,
                                 includeSpans, includeHighlights,
                                 sentenceExpansion).toJsonString();
-            } catch (QueryException qe) {
+            }catch (QueryException qe) {
                 Match km = new Match();
                 km.addError(qe.getErrorCode(), qe.getMessage());
                 return km.toJsonString();
             }
-        };
+        }
+        ;
 
         Match km = new Match();
         km.addError(601, "Unable to find index");
         return km.toJsonString();
-    };
+    }
 
+    ;
 
     /**
      * Get info on a match - by means of a richly annotated html snippet.
@@ -139,7 +147,7 @@
      * @param includeSpans      Should spans be included (or only token infos)?
      * @param includeHighlights Should highlight markup be included?
      */
-    public String getMatch (String id, String foundry, String layer,
+    public String getMatch(String id, String foundry, String layer,
             boolean includeSpans, boolean includeHighlights,
             boolean sentenceExpansion) {
 
@@ -161,18 +169,20 @@
                 return this.index.getMatchInfo(id, "tokens", foundry, layer,
                         includeSpans, includeHighlights, sentenceExpansion)
                         .toJsonString();
-            } catch (QueryException qe) {
+            }catch (QueryException qe) {
                 Match km = new Match();
                 km.addError(qe.getErrorCode(), qe.getMessage());
                 return km.toJsonString();
             }
-        };
+        }
+        ;
 
         Match km = new Match();
         km.addError(601, "Unable to find index");
         return km.toJsonString();
-    };
+    }
 
+    ;
 
     /**
      * Get statistics on (virtual) collections.
@@ -180,7 +190,7 @@
      * @param json JSON-LD string with potential meta filters.
      */
     @Deprecated
-    public String getStatistics (String json) {
+    public String getStatistics(String json) {
         qlog.trace(json);
 
         if (this.index == null) {
@@ -204,9 +214,10 @@
             tokens = kc.numberOf("tokens");
             sentences = kc.numberOf("sentences");
             paragraphs = kc.numberOf("paragraphs");
-        } catch (IOException e) {
+        }catch (IOException e) {
             e.printStackTrace();
-        };
+        }
+        ;
 
         // Build json response
         StringBuilder sb = new StringBuilder("{");
@@ -214,12 +225,12 @@
                 .append(tokens).append(",\"sentences\":").append(sentences)
                 .append(",\"paragraphs\":").append(paragraphs).append("}");
         return sb.toString();
-    };
 
+    }
 
-    public String getMatchId (String type, String docid, String tofrom) {
+    public String getMatchId(String type, String docid, String tofrom) {
         return new StringBuilder().append("match-").append(type).append("!")
                 .append(type).append("_").append(docid).append("-")
                 .append(tofrom).toString();
-    };
+    }
 };
diff --git a/src/main/java/de/ids_mannheim/korap/web/service/LightService.java b/src/main/java/de/ids_mannheim/korap/web/service/light/LightService.java
similarity index 80%
rename from src/main/java/de/ids_mannheim/korap/web/service/LightService.java
rename to src/main/java/de/ids_mannheim/korap/web/service/light/LightService.java
index 29599a3..2f300be 100644
--- a/src/main/java/de/ids_mannheim/korap/web/service/LightService.java
+++ b/src/main/java/de/ids_mannheim/korap/web/service/light/LightService.java
@@ -1,16 +1,15 @@
-package de.ids_mannheim.korap.web.service;
+package de.ids_mannheim.korap.web.service.light;
 
 import com.sun.jersey.core.util.MultivaluedMapImpl;
 import de.ids_mannheim.korap.config.BeanConfiguration;
 import de.ids_mannheim.korap.config.KustvaktConfiguration;
 import de.ids_mannheim.korap.config.QueryBuilderUtil;
-import de.ids_mannheim.korap.exceptions.KorAPException;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.exceptions.StatusCodes;
 import de.ids_mannheim.korap.query.serialize.MetaQueryBuilder;
 import de.ids_mannheim.korap.query.serialize.QuerySerializer;
 import de.ids_mannheim.korap.resource.RewriteProcessor;
-import de.ids_mannheim.korap.utils.CollectionQueryBuilder;
-import de.ids_mannheim.korap.utils.KorAPLogger;
+import de.ids_mannheim.korap.utils.KustvaktLogger;
 import de.ids_mannheim.korap.web.ClientsHandler;
 import de.ids_mannheim.korap.web.SearchKrill;
 import de.ids_mannheim.korap.web.TRACE;
@@ -18,7 +17,10 @@
 import org.slf4j.Logger;
 
 import javax.ws.rs.*;
-import javax.ws.rs.core.*;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
@@ -27,38 +29,43 @@
  * @author hanl
  * @date 29/01/2014
  */
-//todd test functions
 @Path("v0.1" + "/")
 @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
 public class LightService {
 
-    private static Logger jlog = KorAPLogger.initiate(LightService.class);
+    private static Logger jlog = KustvaktLogger.initiate(LightService.class);
 
     private SearchKrill searchKrill;
     private ClientsHandler graphDBhandler;
     private RewriteProcessor processor;
+    private KustvaktConfiguration config;
 
     public LightService() {
-        this.searchKrill = new SearchKrill(
-                BeanConfiguration.getConfiguration().getIndexDir());
+        this.config = BeanConfiguration.getBeans().getConfiguration();
+        this.searchKrill = new SearchKrill(config.getIndexDir());
         UriBuilder builder = UriBuilder.fromUri("http://10.0.10.13").port(9997);
         this.graphDBhandler = new ClientsHandler(builder.build());
-        this.processor = new RewriteProcessor();
+        this.processor = new RewriteProcessor(this.config);
     }
 
+    /**
+     * @param query
+     * @return response
+     */
     @POST
     @Path("colloc")
     public Response getCollocationBase(@QueryParam("q") String query) {
         String result;
         try {
             result = graphDBhandler.getResponse("distCollo", "q", query);
-        }catch (KorAPException e) {
+        }catch (KustvaktException e) {
             throw KustvaktResponseHandler.throwit(e);
         }
         return Response.ok(result).build();
     }
 
     // todo
+    @Deprecated
     public Response postMatchFavorite() {
         return Response.ok().build();
     }
@@ -83,8 +90,8 @@
         meta.setSpanContext(context);
         meta.addEntry("cutOff", cutoff);
         ss.setMeta(meta);
-        //todo: test this
-        ss.setCollection(cq);
+        if (cq != null)
+            ss.setCollection(cq);
         return Response.ok(processor.process(ss.toJSON())).build();
     }
 
@@ -92,31 +99,28 @@
     @Path("search")
     public Response queryRaw(@QueryParam("engine") String engine,
             String jsonld) {
-        KustvaktConfiguration.BACKENDS eng = BeanConfiguration
-                .getConfiguration().chooseBackend(engine);
         jsonld = processor.process(jsonld);
         // todo: should be possible to add the meta part to the query serialization
         jlog.info("Serialized search: {}", jsonld);
 
         // fixme: to use the systemarchitecture pointcut thingis, searchkrill must be injected via
         String result = searchKrill.search(jsonld);
-        KorAPLogger.QUERY_LOGGER.trace("The result set: {}", result);
+        KustvaktLogger.QUERY_LOGGER.trace("The result set: {}", result);
         return Response.ok(result).build();
     }
 
     @GET
     @Path("search")
-    public Response searchbyNameAll(@Context SecurityContext securityContext,
-            @QueryParam("q") String q, @QueryParam("ql") String ql,
-            @QueryParam("v") String v, @QueryParam("context") String ctx,
+    public Response searchbyNameAll(@QueryParam("q") String q,
+            @QueryParam("ql") String ql, @QueryParam("v") String v,
+            @QueryParam("context") String ctx,
             @QueryParam("cutoff") Boolean cutoff,
             @QueryParam("count") Integer pageLength,
             @QueryParam("offset") Integer pageIndex,
             @QueryParam("page") Integer pageInteger,
             @QueryParam("fields") Set<String> fields,
             @QueryParam("cq") String cq, @QueryParam("engine") String engine) {
-        KustvaktConfiguration.BACKENDS eng = BeanConfiguration
-                .getConfiguration().chooseBackend(engine);
+        KustvaktConfiguration.BACKENDS eng = this.config.chooseBackend(engine);
 
         String result;
         QuerySerializer serializer = new QuerySerializer().setQuery(q, ql, v);
@@ -143,12 +147,12 @@
                     String.valueOf(meta.getSpanContext().getRight_size()));
             try {
                 result = this.graphDBhandler.getResponse(map, "distKwic");
-            }catch (KorAPException e) {
+            }catch (KustvaktException e) {
                 throw KustvaktResponseHandler.throwit(e);
             }
         }else
             result = searchKrill.search(query);
-        KorAPLogger.QUERY_LOGGER.trace("The result set: {}", result);
+        KustvaktLogger.QUERY_LOGGER.trace("The result set: {}", result);
         return Response.ok(result).build();
     }
 
@@ -172,32 +176,30 @@
      * @param engine
      * @return
      */
-    //fixme: does not use policyrewrite!
+    //fixme: search in collection /collection/collection-id/search
     @GET
     @Path("/{type}/{id}/search")
-    public Response searchbyName(@QueryParam("q") String query,
+    public Response searchbyName(@PathParam("id") String id,
+            @PathParam("type") String type, @QueryParam("q") String query,
             @QueryParam("ql") String ql, @QueryParam("v") String v,
             @QueryParam("context") String ctx,
             @QueryParam("cutoff") Boolean cutoff,
             @QueryParam("count") Integer pageLength,
             @QueryParam("offset") Integer pageIndex,
-            @QueryParam("page") Integer pageInteger, @PathParam("id") String id,
-            @PathParam("type") String type, @QueryParam("cq") String cq,
-            @QueryParam("raw") Boolean raw,
+            @QueryParam("page") Integer pageInteger,
+            @QueryParam("cq") String cq, @QueryParam("raw") Boolean raw,
             @QueryParam("engine") String engine) {
         // ref is a virtual collection id!
-        KustvaktConfiguration.BACKENDS eng = BeanConfiguration
-                .getConfiguration().chooseBackend(engine);
+        KustvaktConfiguration.BACKENDS eng = this.config.chooseBackend(engine);
         raw = raw == null ? false : raw;
         MetaQueryBuilder meta = QueryBuilderUtil
                 .defaultMetaBuilder(pageIndex, pageInteger, pageLength, ctx,
                         cutoff);
         if (!raw) {
-            QuerySerializer s = new QuerySerializer().setQuery(query, ql, v);
-
             // should only apply to CQL queries
             //                meta.addEntry("itemsPerResource", 1);
-            s.setMeta(meta);
+            QuerySerializer s = new QuerySerializer().setQuery(query, ql, v)
+                    .setMeta(meta);
             query = processor.process(s.toJSON());
         }
         String result;
@@ -219,12 +221,12 @@
                 result = searchKrill.search(query);
 
         }catch (Exception e) {
-            KorAPLogger.ERROR_LOGGER
+            KustvaktLogger.ERROR_LOGGER
                     .error("Exception for serialized query: " + query, e);
             throw KustvaktResponseHandler.throwit(500, e.getMessage(), null);
         }
 
-        KorAPLogger.QUERY_LOGGER.trace("The result set: {}", result);
+        KustvaktLogger.QUERY_LOGGER.trace("The result set: {}", result);
         return Response.ok(result).build();
     }
 
@@ -232,10 +234,11 @@
     @POST
     @Path("stats")
     public Response getStats(String json) {
-        CollectionQueryBuilder builder = new CollectionQueryBuilder();
-        builder.addResource(json);
+        //        CollectionQueryBuilder builder = new CollectionQueryBuilder();
+        //        builder.addResource(json);
 
-        String stats = searchKrill.getStatistics(builder.toCollections());
+        // todo: policy override in extension!
+        String stats = searchKrill.getStatistics(json);
         if (stats.contains("-1"))
             throw KustvaktResponseHandler.throwit(StatusCodes.EMPTY_RESULTS);
 
diff --git a/src/main/java/de/ids_mannheim/korap/web/utils/KustvaktResponseHandler.java b/src/main/java/de/ids_mannheim/korap/web/utils/KustvaktResponseHandler.java
index f99a520..c35ce5b 100644
--- a/src/main/java/de/ids_mannheim/korap/web/utils/KustvaktResponseHandler.java
+++ b/src/main/java/de/ids_mannheim/korap/web/utils/KustvaktResponseHandler.java
@@ -1,7 +1,6 @@
 package de.ids_mannheim.korap.web.utils;
 
 import de.ids_mannheim.korap.auditing.AuditRecord;
-import de.ids_mannheim.korap.config.BeanConfiguration;
 import de.ids_mannheim.korap.exceptions.BaseException;
 import de.ids_mannheim.korap.exceptions.KorAPException;
 import de.ids_mannheim.korap.exceptions.StatusCodes;
@@ -19,12 +18,18 @@
  */
 public class KustvaktResponseHandler {
 
-    private static AuditingIface auditing = BeanConfiguration
-            .getAuditingProvider();
+    private static AuditingIface auditing;
+
+    public static void init(AuditingIface iface) {
+        if (auditing == null)
+            auditing = iface;
+    }
 
     private static void register(List<AuditRecord> records) {
         if (auditing != null && !records.isEmpty())
             auditing.audit(records);
+        else if (auditing == null)
+            throw new RuntimeException("Auditing handler must be set!");
     }
 
     public static WebApplicationException throwit(BaseException e) {
@@ -53,7 +58,7 @@
     }
 
     private static String buildNotification(BaseException e) {
-        KustvaktResponseHandler.register(e.getRecords());
+        register(e.getRecords());
         return buildNotification(e.getStatusCode(), e.getMessage(),
                 e.getEntity());
     }
@@ -73,5 +78,4 @@
                                 "Basic realm=Kustvakt Authentication Service")
                         .entity(buildNotification(e)).build());
     }
-
 }
diff --git a/src/main/resources/.DS_Store b/src/main/resources/.DS_Store
index 5008ddf..51b3007 100644
--- a/src/main/resources/.DS_Store
+++ b/src/main/resources/.DS_Store
Binary files differ
diff --git a/src/main/resources/ESAPI.properties b/src/main/resources/ESAPI.properties
new file mode 100644
index 0000000..b822b21
--- /dev/null
+++ b/src/main/resources/ESAPI.properties
@@ -0,0 +1,188 @@
+#
+# OWASP Enterprise Security API (ESAPI) Properties file -- PRODUCTION Version
+# 
+# This file is part of the Open Web de.ids_mannheim.korap.news.Application Security Project (OWASP)
+# Enterprise Security API (ESAPI) project. For details, please see
+# http://www.owasp.org/index.php/ESAPI.
+#
+# Copyright (c) 2008,2009 - The OWASP Foundation
+#
+# DISCUSS: This may cause a major backwards compatibility issue, etc. but
+#		   from a name space perspective, we probably should have prefaced
+#		   all the property names with ESAPI or at least OWASP. Otherwise
+#		   there could be problems is someone loads this properties file into
+#		   the System properties.  We could also put this file into the
+#		   esapi.jar file (perhaps as a ResourceBundle) and then allow an external
+#		   ESAPI properties be defined that would overwrite these defaults.
+#		   That keeps the application's properties relatively simple as usually
+#		   they will only want to override a few properties. If looks like we
+#		   already support multiple override levels of this in the
+#		   DefaultSecurityConfiguration class, but I'm suggesting placing the
+#		   defaults in the esapi.jar itself. That way, if the jar is signed,
+#		   we could detect if those properties had been tampered with. (The
+#		   code to isSystem the jar signatures is pretty simple... maybe 70-90 LOC,
+#		   but off course there is an execution penalty (similar to the way
+#		   that the separate sunjce.jar used to be when a class from it was
+#		   first loaded). Thoughts?
+###############################################################################
+
+ESAPI.Randomizer=org.owasp.esapi.reference.DefaultRandomizer
+ESAPI.Validator=org.owasp.esapi.reference.DefaultValidator
+
+#===========================================================================
+# ESAPI Encoder
+#
+# ESAPI canonicalizes input before validation to prevent bypassing filters with encoded attacks.
+# Failure to canonicalize input is a very common mistake when implementing validation schemes.
+# Canonicalization is automatic when using the ESAPI Validator, but you can also use the
+# following code to canonicalize data.
+#
+#      ESAPI.Encoder().canonicalize( "%22hello world&#x22;" );
+#  
+# Multiple encoding is when a single encoding format is applied multiple times. Allowing
+# multiple encoding is strongly discouraged.
+Encoder.AllowMultipleEncoding=false
+
+# Mixed encoding is when multiple different encoding formats are applied, or when 
+# multiple formats are nested. Allowing multiple encoding is strongly discouraged.
+Encoder.AllowMixedEncoding=false
+
+# The default list of codecs to apply when canonicalizing untrusted data. The list should include the codecs
+# for all downstream interpreters or decoders. For example, if the data is likely to end up in a URL, HTML, or
+# inside JavaScript, then the list of codecs below is appropriate. The order of the list is not terribly important.
+Encoder.DefaultCodecList=HTMLEntityCodec,PercentCodec,JavaScriptCodec
+
+
+#===========================================================================
+# ESAPI Encryption
+#
+# The ESAPI Encryptor provides basic cryptographic functions with a simplified API.
+# To get started, generate a new key using java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor
+# There is not currently any support for key rotation, so be careful when changing your key and salt as it
+# will invalidate all signed, encrypted, and hashed data.
+#
+# WARNING: Not all combinations of algorithms and key lengths are supported.
+# If you choose to use a key length greater than 128, you MUST download the
+# unlimited strength policy files and install in the lib directory of your JRE/JDK.
+# See http://java.sun.com/javase/downloads/index.jsp for more information.
+#
+# Backward compatibility with ESAPI Java 1.4 is supported by the two deprecated API
+# methods, Encryptor.encrypt(String) and Encryptor.decrypt(String). However, whenever
+# possible, these methods should be avoided as they use ECB cipher mode, which in almost
+# all circumstances a poor choice because of it's weakness. CBC cipher mode is the default
+# for the new Encryptor encrypt / decrypt methods for ESAPI Java 2.0.  In general, you
+# should only use this compatibility setting if you have persistent data encrypted with
+# version 1.4 and even then, you should ONLY set this compatibility mode UNTIL
+# you have decrypted all of your old encrypted data and then re-encrypted it with
+# ESAPI 2.0 using CBC mode. If you have some reason to mix the deprecated 1.4 mode
+# with the new 2.0 methods, make sure that you use the same cipher algorithm for both
+# (256-bit AES was the default for 1.4; 128-bit is the default for 2.0; see below for
+# more details.) Otherwise, you will have to use the new 2.0 encrypt / decrypt methods
+# where you can specify a SecretKey. (Note that if you are using the 256-bit AES,
+# that requires downloading the special jurisdiction policy files mentioned above.)
+#
+#		***** IMPORTANT: Do NOT forget to replace these with your own values! *****
+# To calculate these values, you can run:
+#		java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor
+#
+#Encryptor.MasterKey=
+## default key
+#Encryptor.MasterSalt=434fsdferbs7sdf5sdf+d23=a
+
+#==============================================================
+Encryptor.MasterKey=Nowq7w96tBckpYCPkoBtjQ==
+Encryptor.MasterSalt=vRaKzzh7hLp9v3CXi7KDI/1yO3A=
+#==============================================================
+
+#===========================================================================
+# ESAPI Intrusion Detection
+#
+# Each event has a base to which .count, .interval, and .action are added
+# The IntrusionException will fire if we receive "count" events within "interval" seconds
+# The IntrusionDetector is configurable to take the following actions: log, logout, and disable
+#  (multiple actions separated by commas are allowed e.g. event.test.actions=log,disable
+#
+# Custom Events
+# Names must start with "event." as the base
+# Use IntrusionDetector.addEvent( "test" ) in your code to trigger "event.test" here
+# You can also disable intrusion detection completely by changing
+# the following parameter to true
+#
+IntrusionDetector.Disable=false
+#
+IntrusionDetector.event.test.count=2
+IntrusionDetector.event.test.interval=10
+IntrusionDetector.event.test.actions=disable,log
+
+# Exception Events
+# All EnterpriseSecurityExceptions are registered automatically
+# Call IntrusionDetector.getInstance().addException(e) for Exceptions that do not extend EnterpriseSecurityException
+# Use the fully qualified classname of the exception as the base
+
+# any intrusion is an attack
+IntrusionDetector.org.owasp.esapi.errors.IntrusionException.count=1
+IntrusionDetector.org.owasp.esapi.errors.IntrusionException.interval=1
+IntrusionDetector.org.owasp.esapi.errors.IntrusionException.actions=log,disable,logout
+
+# for test purposes
+# CHECKME: Shouldn't there be something in the property name itself that designates
+#		   that these are for testing???
+IntrusionDetector.org.owasp.esapi.errors.IntegrityException.count=10
+IntrusionDetector.org.owasp.esapi.errors.IntegrityException.interval=5
+IntrusionDetector.org.owasp.esapi.errors.IntegrityException.actions=log,disable,logout
+
+# rapid validation errors indicate scans or attacks in progress
+org.owasp.esapi.errors.ValidationException.count=10
+org.owasp.esapi.errors.ValidationException.interval=10
+org.owasp.esapi.errors.ValidationException.actions=log,logout
+
+# sessions jumping between hosts indicates session hijacking
+IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.count=2
+IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.interval=10
+IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.actions=log,logout
+
+
+#===========================================================================
+# ESAPI Validation
+#
+# The ESAPI Validator works on regular expressions with defined names. You can define names
+# either here, or you may define application specific patterns in a separate file defined below.
+# This allows enterprises to specify both organizational standards as well as application specific
+# validation rules.
+#
+Validator.ConfigurationFile=validation.properties
+
+# Validators used by ESAPI
+Validator.AccountName=^[a-zA-Z0-9]{3,20}$
+Validator.SystemCommand=^[a-zA-Z\\-\\/]{1,64}$
+Validator.RoleName=^[a-z]{1,20}$
+
+#the word TEST below should be changed to your application
+#name - only relative URL's are supported
+Validator.Redirect=^\\/test.*$
+
+# Global HTTP Validation Rules
+# Values with Base64 encoded data (e.g. encrypted state) will need at least [a-zA-Z0-9\/+=]
+Validator.HTTPScheme=^(http|https)$
+Validator.HTTPServerName=^[a-zA-Z0-9_.\\-]*$
+Validator.HTTPParameterName=^[a-zA-Z0-9_]{1,32}$
+Validator.HTTPParameterValue=^[a-zA-Z0-9.\\-\\/+=@_ ]*$
+Validator.HTTPCookieName=^[a-zA-Z0-9\\-_]{1,32}$
+Validator.HTTPCookieValue=^[a-zA-Z0-9\\-\\/+=_ ]*$
+Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,32}$
+Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$
+Validator.HTTPContextPath=^\\/?[a-zA-Z0-9.\\-\\/_]*$
+Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$
+Validator.HTTPPath=^[a-zA-Z0-9.\\-_]*$
+Validator.HTTPQueryString=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ %]*$
+Validator.HTTPURI=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$
+Validator.HTTPURL=^.*$
+Validator.HTTPJSESSIONID=^[A-Z0-9]{10,30}$
+
+# Validation of file related input
+Validator.FileName=^[a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
+Validator.DirectoryName=^[a-zA-Z0-9:/\\\\!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
+
+# Validation of dates. Controls whether or not 'lenient' dates are accepted.
+# See DataFormat.setLenient(boolean flag) for further details.
+Validator.AcceptLenientDates=false
diff --git a/src/main/resources/db/mysql/V1.0__initial_version.sql b/src/main/resources/db/mysql/V1.0__initial_version.sql
new file mode 100644
index 0000000..8e4c17b
--- /dev/null
+++ b/src/main/resources/db/mysql/V1.0__initial_version.sql
@@ -0,0 +1,326 @@
+
+-- rename all columns in new way!
+CREATE TABLE IF NOT EXISTS korapusers (
+    id INTEGER PRIMARY KEY AUTO_INCREMENT,
+    username VARCHAR(100) NOT NULL UNIQUE,
+    password VARCHAR(100) NOT NULL,
+    accountLock boolean NOT NULL,
+    accountCreation TIMESTAMP NOT NULL,
+    type INTEGER DEFAULT 0,
+    URI_PASS_Fragment VARCHAR(100),
+    URI_CONF_Fragment VARCHAR(100),
+    URI_Expiration TIMESTAMP,
+    loginSuccess INTEGER,
+    loginFailed INTEGER,
+    accountExpiration TIMESTAMP NOT NULL,
+    accountLink VARCHAR(100)
+);
+
+CREATE TABLE IF NOT EXISTS shibusers (
+    id INTEGER PRIMARY KEY AUTO_INCREMENT,
+    username VARCHAR(100) NOT NULL UNIQUE,
+    accountCreation TIMESTAMP NOT NULL,
+    type INTEGER DEFAULT 1,
+    loginSuccess INTEGER,
+    loginFailed INTEGER,
+    accountExpiration TIMESTAMP NOT NULL,
+    accountLink VARCHAR(100)
+);
+
+CREATE TABLE IF NOT EXISTS udetails (
+    Id INTEGER PRIMARY KEY AUTO_INCREMENT,
+    userID INTEGER NOT NULL UNIQUE,
+    firstName VARCHAR(100),
+    lastName VARCHAR(100),
+    gender VARCHAR(100),
+    phone VARCHAR(100),
+    institution VARCHAR(100),
+    email VARCHAR(100),
+    address VARCHAR(100),
+    country VARCHAR(100),
+    privateUsage BOOLEAN,
+    foreign key (userID)
+    references korapusers (id)
+    on delete cascade
+);
+
+CREATE TABLE IF NOT EXISTS usettings (
+    Id INTEGER PRIMARY KEY AUTO_INCREMENT,
+    userID INTEGER NOT NULL UNIQUE,
+    fileNameForExport VARCHAR(100),
+    itemForSimpleAnnotation INTEGER,
+    leftContextItemForExport VARCHAR(100),
+    leftContextSizeForExport INTEGER,
+    locale VARCHAR(100),
+    leftContextItem VARCHAR(100),
+    leftContextSize INTEGER,
+    rightContextItem VARCHAR(100),
+    rightContextItemForExport VARCHAR(100),
+    rightContextSize INTEGER,
+    rightContextSizeForExport INTEGER,
+    selectedCollection VARCHAR(100),
+    queryLanguage VARCHAR(100),
+    pageLength INTEGER,
+    metadataQueryExpertModus BOOLEAN,
+    searchSettingsTab INTEGER,
+    selectedGraphType INTEGER,
+    selectedSortType VARCHAR(100),
+    selectedViewForSearchResults VARCHAR(100),
+    POSFoundry VARCHAR(100),
+    lemmaFoundry VARCHAR(100),
+    constFoundry VARCHAR(100),
+    relFoundry VARCHAR(100),
+    collectData BOOLEAN,
+    foreign key (userID)
+    references korapusers (id)
+    on delete cascade
+);
+
+CREATE OR REPLACE VIEW allusers AS
+    SELECT 
+        id,
+        username,
+        password,
+        accountLock,
+        accountCreation,
+        type,
+        URI_PASS_Fragment,
+        URI_CONF_Fragment,
+        URI_Expiration,
+        loginSuccess,
+        loginFailed,
+        accountExpiration,
+        accountLink
+    from
+        korapusers 
+    UNION ALL SELECT 
+        id,
+        username,
+        NULL as password,
+        NULL as accountLock,
+        accountCreation,
+        type,
+        NULL as URI_PASS_Fragment,
+        NULL as URI_CONF_Fragment,
+        NULL as URI_Expiration,
+        loginSuccess,
+        loginFailed,
+        accountExpiration,
+        accountLink
+    from
+        shibusers;
+
+-- why unsigned?
+CREATE TABLE IF NOT EXISTS r_store (
+id INTEGER PRIMARY KEY AUTO_INCREMENT,
+persistent_id VARCHAR(100) NOT NULL UNIQUE,
+name VARCHAR(100),
+description VARCHAR(300),
+parent_id Integer unsigned null,
+created timestamp default current_timestamp,
+type INTEGER NOT NULL,
+creator INTEGER NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS uqueries (
+    id INTEGER PRIMARY KEY,
+    queryLanguage VARCHAR(100),
+    name VARCHAR(100),
+    query VARCHAR(200),
+    description VARCHAR(150),
+    foreign key (id)
+    references r_store(id)
+    on delete cascade
+);
+
+CREATE TABLE IF NOT EXISTS r_tree (
+parent_id INTEGER,
+child_id INTEGER,
+depth INTEGER,
+name_path VARCHAR(250),
+PRIMARY KEY (parent_id , child_id),
+foreign key (parent_id)
+references r_store (id)
+on delete cascade,
+foreign key (child_id)
+references r_store (id)
+on delete cascade
+);
+
+
+CREATE TABLE IF NOT EXISTS cstorage (
+    id INTEGER,
+    refCorpus VARCHAR(100),
+    query VARCHAR(500),
+    foreign key (id) references r_store(id)
+    on delete cascade);
+
+
+CREATE TABLE IF NOT EXISTS matchInfo (id INTEGER PRIMARY KEY AUTO_INCREMENT, userid BIGINT NOT NULL,
+matchInfo VARCHAR(100));
+
+CREATE TABLE IF NOT EXISTS resourceRecords (
+    AUD_ID INTEGER PRIMARY KEY AUTO_INCREMENT,
+    AUD_RESOURCE VARCHAR(100),
+    AUD_USER VARCHAR(100),
+    AUD_LOC VARCHAR(100),
+    AUD_OP VARCHAR(100),
+    AUD_TIMESTAMP TIMESTAMP,
+    AUD_FAILURE VARCHAR(100)
+);
+
+CREATE TABLE IF NOT EXISTS databaseRecords (
+    AUD_ID INTEGER PRIMARY KEY AUTO_INCREMENT,
+    AUD_TARGET VARCHAR(100),
+    AUD_USER VARCHAR(100),
+    AUD_LOC VARCHAR(100),
+    AUD_OP VARCHAR(100),
+    AUD_TIMESTAMP TIMESTAMP,
+    AUD_FAILURE VARCHAR(100)
+);
+
+CREATE TABLE IF NOT EXISTS securityRecords (
+    AUD_ID INTEGER PRIMARY KEY AUTO_INCREMENT,
+    AUD_USER VARCHAR(100),
+    AUD_LOC VARCHAR(100),
+    AUD_OP VARCHAR(100),
+    AUD_TIMESTAMP TIMESTAMP,
+    AUD_FAILURE VARCHAR(100)
+);
+
+
+CREATE TABLE IF NOT EXISTS doc_trace (
+id VARCHAR(200) PRIMARY KEY,
+resource INTEGER,
+created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+foreign key (resource)
+references r_store(id)
+);
+
+-- last_modified timestamp ON UPDATE CURRENT_TIMESTAMP,
+CREATE TABLE IF NOT EXISTS p_store (
+    id INTEGER PRIMARY KEY AUTO_INCREMENT,
+    target_id BIGINT NOT NULL,
+    created TIMESTAMP,
+    creator INTEGER NOT NULL,
+    posix SMALLINT NOT NULL,
+    expire TIMESTAMP NULL,
+    enable TIMESTAMP NULL,
+    iprange varchar(200)
+);
+
+CREATE TABLE IF NOT EXISTS conditionDef (
+    id INTEGER PRIMARY KEY AUTO_INCREMENT,
+    groupRef VARCHAR(100) NOT NULL,
+    policyid INTEGER NOT NULL
+);
+
+
+CREATE TABLE IF NOT EXISTS groupStore (
+    name VARCHAR(100) PRIMARY KEY,
+    description VARCHAR(200),
+    sym_use INTEGER DEFAULT -1,
+    export VARCHAR(30) DEFAULT NULL,
+    query_only VARCHAR(30) DEFAULT NULL,
+    licence INTEGER DEFAULT -1,
+    -- basically every resource we have is an academic resource, thus a non-commercial use is infered!
+    commercial BOOLEAN DEFAULT FALSE
+);
+
+CREATE TABLE IF NOT EXISTS groupUsers (
+    id INTEGER PRIMARY KEY AUTO_INCREMENT,
+    userID BIGINT NOT NULL,
+    groupRef VARCHAR(100) NOT NULL,
+    admin BOOLEAN NOT NULL DEFAULT FALSE,
+    FOREIGN KEY (groupRef)
+        REFERENCES groupStore (name) on delete cascade
+);
+
+CREATE TABLE IF NOT EXISTS paramStore (
+    id INTEGER PRIMARY KEY AUTO_INCREMENT,
+    p_key VARCHAR(100) NOT NULL,
+    p_value VARCHAR(150) NOT NULL,
+    resource INTEGER DEFAULT -1,
+    pid INTEGER DEFAULT -1,
+    FOREIGN KEY (resource)
+        REFERENCES r_store(id)
+    on delete cascade,
+    FOREIGN KEY (pid)
+        REFERENCES p_store(id)
+    on delete cascade
+);
+
+CREATE TABLE IF NOT EXISTS paramMapping (
+    id INTEGER PRIMARY KEY AUTO_INCREMENT,
+    policyID INTEGER NOT NULL,
+    paramID INTEGER NOT NULL,
+    value VARCHAR(100) NOT NULL,
+    flag BOOLEAN NOT NULL DEFAULT FALSE,
+    FOREIGN KEY (paramID)
+        REFERENCES paramStore (id),
+    FOREIGN KEY (policyID)
+        REFERENCES p_store (id)
+);
+
+create or replace view p_view as
+select
+    po.id as pid,
+    po.target_id as id,
+    rs.persistent_id as persistent_id,
+    rs.name as name,
+    rs.type as type,
+    c.groupref as groupref,
+    po.posix as perm,
+    po.creator as creator,
+    po.expire as expire,
+    po.enable as enable,
+    po.iprange as iprange
+from
+p_store as po
+inner join
+conditionDef as c ON c.policyid = po.id
+inner join
+r_store as rs ON rs.id = po.target_id
+union all select
+              - 1 as pid,
+              rs.id as id,
+              rs.persistent_id as persistent_id,
+              rs.name as name,
+              type as type,
+              'self' as groupref,
+              127 as perm,
+              creator,
+              NULL as expire,
+              rs.created as enable,
+              null as iprange
+          from
+          r_store as rs;
+
+
+-- indices
+create trigger delete_policy after delete on r_store
+for each row delete from p_store where target_id=OLD.id;
+
+DELIMITER //
+CREATE TRIGGER tree_entry_insert AFTER INSERT ON r_store FOR EACH ROW BEGIN
+	INSERT INTO r_tree (parent_id, child_id, depth, name_path)
+	VALUES (NEW.id, NEW.id, 0, NEW.name);
+	INSERT INTO r_tree (parent_id, child_id, depth, name_path)
+	SELECT parent_id, NEW.id, rt.depth + 1, concat(name_path,"/",NEW.name) FROM r_tree WHERE child_id = NEW.parent_id;
+END; //
+
+DELIMITER ;
+
+-- todo: are this automatically adapted when refactoring?
+CREATE INDEX group_index ON groupUsers(userid);
+CREATE INDEX policy_index ON conditionDef(policyid);
+CREATE UNIQUE INDEX r_tree_index ON r_tree (parent_id, depth, child_id);
+CREATE UNIQUE INDEX para_unique ON paramStore (p_key, p_value);
+
+-- foreign key constraints
+
+
+
+
+
+
diff --git a/src/main/resources/db/mysql/V1.1__mysql_refactoring.sql b/src/main/resources/db/mysql/V1.1__mysql_refactoring.sql
new file mode 100644
index 0000000..4cc3b21
--- /dev/null
+++ b/src/main/resources/db/mysql/V1.1__mysql_refactoring.sql
@@ -0,0 +1,139 @@
+-- alter table korapusers TO korap_users;
+rename table usettings TO user_settings;
+alter table user_settings drop column itemForSimpleAnnotation;
+alter table user_settings drop column searchSettingsTab;
+alter table user_settings drop column selectedGraphType;
+alter table user_settings drop column selectedSortType;
+alter table user_settings drop column selectedViewForSearchResults;
+rename table udetails to user_details;
+rename table uqueries to user_queries;
+rename table korapusers to korap_users;
+rename table shibusers to shib_users;
+rename table matchInfo to match_info;
+
+alter table korap_users change column URI_PASS_Fragment uri_fragment VARCHAR(100);
+alter table korap_users change column URI_Expiration uri_expiration TIMESTAMP;
+drop view allusers;
+
+rename table r_store TO resource_store;
+rename table r_tree TO resource_tree;
+
+rename table groupStore TO group_store;
+rename table groupUsers TO group_users;
+rename table paramStore TO param_store;
+rename table paramMapping TO param_map;
+
+-- todo: what about the moving of the entries?
+-- rather rename than drop!
+-- drop table cstorage;
+-- todo: test
+rename table cstorage to coll_store;
+alter table coll_store add column (
+created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+userID INTEGER);
+alter table coll_store drop column refCorpus;
+
+
+-- do not recreate -- maintain data
+--CREATE TABLE IF NOT EXISTS coll_store (
+--id INTEGER PRIMARY KEY AUTO_INCREMENT,
+--persistentID VARCHAR(150) UNIQUE,
+--name VARCHAR(150),
+--description VARCHAR(200),
+--query VARCHAR(500),
+--created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+--userID INTEGER,
+--foreign key(userID)
+--references korap_users(id)
+--on delete cascade
+--);
+
+
+drop table doc_trace;
+
+CREATE TABLE IF NOT EXISTS doc_store (
+id VARCHAR(230) PRIMARY KEY,
+persistent_id VARCHAR(230) UNIQUE,
+created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+disabled BOOLEAN default true
+);
+
+
+
+rename table p_store to policy_store;
+rename table conditionDef to group_ref;
+alter table group_ref change groupRef group_id VARCHAR(100) NOT NULL;
+alter table group_ref change policyId policy_id VARCHAR(100) NOT NULL;
+
+drop view p_view;
+create or replace view policy_view as
+select
+    po.id as pid,
+    po.target_id as id,
+    rs.persistent_id as persistent_id,
+    rs.name as name,
+    rs.type as type,
+    c.group_id as group_id,
+    po.posix as perm,
+    po.creator as creator,
+    po.expire as expire,
+    po.enable as enable,
+    po.iprange as iprange
+from
+policy_store as po
+inner join
+group_ref as c ON c.policy_id = po.id
+inner join
+resource_store as rs ON rs.id = po.target_id
+union all select
+              - 1 as pid,
+              rs.id as id,
+              rs.persistent_id as persistent_id,
+              rs.name as name,
+              type as type,
+              'self' as groupId,
+              127 as perm,
+              creator,
+              NULL as expire,
+              rs.created as enable,
+              null as iprange
+          from
+          resource_store as rs;
+
+
+drop table resourceRecords;
+drop table databaseRecords;
+drop table securityRecords;
+
+CREATE TABLE IF NOT EXISTS audit_records (
+id INTEGER PRIMARY KEY AUTO_INCREMENT,
+aud_category VARCHAR(100),
+aud_target VARCHAR(100),
+aud_user VARCHAR(100),
+aud_location VARCHAR(100),
+aud_operation VARCHAR(100),
+aud_field_1 VARCHAR(400),
+aud_timestamp TIMESTAMP,
+aud_failure VARCHAR(100)
+);
+
+
+
+drop trigger tree_entry_insert;
+
+DELIMITER //
+CREATE TRIGGER tree_entry_insert AFTER INSERT ON resource_store FOR EACH ROW BEGIN
+	INSERT INTO resource_tree (parent_id, child_id, depth, name_path)
+	VALUES (NEW.id, NEW.id, 0, NEW.name);
+	INSERT INTO resource_tree (parent_id, child_id, depth, name_path)
+	SELECT parent_id, NEW.id, depth + 1, concat(name_path,"/",NEW.name)
+	FROM resource_tree WHERE child_id = NEW.parent_id;
+END; //
+
+DELIMITER ;
+
+
+drop trigger delete_policy;
+
+create trigger delete_policy after delete on resource_store
+for each row delete from policy_store where target_id=OLD.id;
\ No newline at end of file
diff --git a/src/main/resources/db/mysql/V1.2__oauth2_tables_mysql.sql b/src/main/resources/db/mysql/V1.2__oauth2_tables_mysql.sql
new file mode 100644
index 0000000..5e9c968
--- /dev/null
+++ b/src/main/resources/db/mysql/V1.2__oauth2_tables_mysql.sql
@@ -0,0 +1,63 @@
+
+-- oauth2 db tables
+create table oauth2_client (
+client_id VARCHAR(100) UNIQUE PRIMARY KEY,
+client_secret VARCHAR(200),
+redirect_uri VARCHAR(250),
+client_type VARCHAR(200),
+is_confidential BOOLEAN DEFAULT FALSE,
+url VARCHAR(200) UNIQUE
+);
+
+create table oauth2_auth_codes (
+id INTEGER PRIMARY KEY AUTO_INCREMENT,
+client_id VARCHAR(100),
+auth_code VARCHAR(250),
+status INTEGER DEFAULT 1,
+scopes VARCHAR (150),
+created TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+
+-- define scopes?!
+create table oauth2_client_authorization (
+id INTEGER PRIMARY KEY AUTO_INCREMENT,
+fk_client_id VARCHAR(100),
+user_id INTEGER,
+FOREIGN KEY (fk_client_id)
+REFERENCES oauth2_client(client_id)
+ON DELETE CASCADE
+);
+
+---- status 1 = valid, 0 = revoked
+--create table oauth2_access_token (
+--id INTEGER PRIMARY KEY AUTO_INCREMENT,
+--access_token VARCHAR(300),
+--auth_code VARCHAR(250),
+--userID INTEGER,
+--status INTEGER DEFAULT 1,
+--expiration TIMESTAMP,
+--scopes VARCHAR (150),
+--FOREIGN KEY (userID)
+--REFERENCES korap_users(id)
+--);
+
+-- status 1 = valid, 0 = revoked, -1 = disabled
+create table oauth2_access_token (
+id INTEGER PRIMARY KEY AUTO_INCREMENT,
+access_token VARCHAR(300),
+auth_code VARCHAR(250),
+client_id VARCHAR(100),
+user_id INTEGER,
+-- make boolean --
+status INTEGER DEFAULT 1,
+-- in case of code authorization, should match auth code scopes!
+-- use scopes for levelaccess descriptor level[rw],level[r]
+scopes VARCHAR(350),
+expiration TIMESTAMP,
+FOREIGN KEY (user_id)
+REFERENCES korap_users(id)
+ON DELETE CASCADE,
+FOREIGN KEY (client_id)
+REFERENCES oauth2_client(client_id)
+ON DELETE CASCADE
+);
diff --git a/src/main/resources/db/mysql/mysql_schema_comp.sql b/src/main/resources/db/mysql/mysql_schema_comp.sql
new file mode 100644
index 0000000..6ee11c5
--- /dev/null
+++ b/src/main/resources/db/mysql/mysql_schema_comp.sql
@@ -0,0 +1,314 @@
+CREATE TABLE IF NOT EXISTS korap_users (
+    id INTEGER PRIMARY KEY AUTO_INCREMENT,
+    username VARCHAR(100) NOT NULL UNIQUE,
+    password VARCHAR(100) NOT NULL,
+    accountLock boolean NOT NULL,
+    accountCreation TIMESTAMP NOT NULL,
+    type INTEGER DEFAULT 0,
+    uriFragment VARCHAR(100),
+    uriExpiration TIMESTAMP,
+    accountLink VARCHAR(100)
+)$$
+
+
+CREATE TABLE IF NOT EXISTS shib_users (
+    id INTEGER PRIMARY KEY AUTO_INCREMENT,
+    username VARCHAR(100) NOT NULL UNIQUE,
+    accountCreation TIMESTAMP NOT NULL,
+    type INTEGER DEFAULT 1,
+    loginSuccess INTEGER,
+    loginFailed INTEGER,
+    accountExpiration TIMESTAMP NOT NULL,
+    accountLink VARCHAR(100)
+)$$
+
+CREATE TABLE IF NOT EXISTS user_details (
+    Id INTEGER PRIMARY KEY AUTO_INCREMENT,
+    userID INTEGER NOT NULL UNIQUE,
+    firstName VARCHAR(100),
+    lastName VARCHAR(100),
+    gender VARCHAR(100),
+    phone VARCHAR(100),
+    institution VARCHAR(100),
+    email VARCHAR(100),
+    address VARCHAR(100),
+    country VARCHAR(100),
+    privateUsage BOOLEAN,
+    foreign key (userId)
+    references korap_users (id)
+    on delete cascade
+)$$
+
+CREATE TABLE IF NOT EXISTS user_settings (
+id INTEGER PRIMARY KEY AUTO_INCREMENT,
+userId INTEGER NOT NULL,
+fileNameForExport VARCHAR(100),
+leftContextItemForExport VARCHAR(100),
+leftContextSizeForExport INTEGER,
+locale VARCHAR(100),
+leftContextItem VARCHAR(100),
+leftContextSize INTEGER,
+rightContextItem VARCHAR(100),
+rightContextItemForExport VARCHAR(100),
+rightContextSize INTEGER,
+rightContextSizeForExport INTEGER,
+selectedCollection VARCHAR(100),
+queryLanguage VARCHAR(100),
+pageLength INTEGER,
+metadataQueryExpertModus BOOLEAN,
+POSFoundry VARCHAR(100),
+lemmaFoundry VARCHAR(100),
+constFoundry VARCHAR(100),
+relFoundry VARCHAR(100),
+collectData BOOLEAN DEFAULT TRUE,
+foreign key (userId)
+references korap_users (id)
+on delete cascade
+)$$
+
+CREATE TABLE IF NOT EXISTS user_queries (
+    id INTEGER PRIMARY KEY,
+    queryLanguage VARCHAR(100),
+    name VARCHAR(100),
+    query VARCHAR(200),
+    description VARCHAR(150),
+    foreign key (id)
+    references resource_store(id)
+    on delete cascade)$$
+
+
+CREATE TABLE IF NOT EXISTS resource_store (
+id INTEGER PRIMARY KEY AUTO_INCREMENT,
+persistentID VARCHAR(100) NOT NULL UNIQUE,
+name VARCHAR(100),
+description VARCHAR(300),
+parentID Integer unsigned null,
+created timestamp default current_timestamp,
+type INTEGER NOT NULL,
+creator INTEGER NOT NULL
+)$$
+
+
+CREATE TABLE IF NOT EXISTS resource_tree (
+parentID INTEGER,
+childID INTEGER,
+depth INTEGER,
+name_path VARCHAR(250),
+PRIMARY KEY (parentID , childID),
+foreign key (parentID)
+references resource_store (id)
+on delete cascade,
+foreign key (childID)
+references resource_store (id)
+on delete cascade
+)$$
+
+
+CREATE TABLE IF NOT EXISTS coll_store (
+id INTEGER PRIMARY KEY AUTO_INCREMENT,
+persistentID VARCHAR(150) UNIQUE,
+name VARCHAR(150),
+description VARCHAR(200),
+query VARCHAR(500),
+created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+userID INTEGER,
+foreign key(userID)
+references korap_users(id)
+on delete cascade
+)$$
+
+
+
+CREATE TABLE IF NOT EXISTS match_info (
+id INTEGER PRIMARY KEY AUTO_INCREMENT,
+userid BIGINT NOT NULL,
+match_info VARCHAR(100)
+)$$
+
+CREATE TABLE IF NOT EXISTS audit_records (
+id INTEGER PRIMARY KEY AUTO_INCREMENT,
+aud_category VARCHAR(100),
+aud_target VARCHAR(100),
+aud_user VARCHAR(100),
+aud_location VARCHAR(100),
+aud_operation VARCHAR(100),
+aud_field_1 VARCHAR(400),
+aud_timestamp TIMESTAMP,
+aud_failure VARCHAR(100)
+)$$
+
+CREATE TABLE IF NOT EXISTS doc_store (
+id VARCHAR(265) PRIMARY KEY,
+persistentID VARCHAR(265) UNIQUE,
+created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+disabled BOOLEAN default true
+)$$
+
+-- last_modified timestamp ON UPDATE CURRENT_TIMESTAMP,
+CREATE TABLE IF NOT EXISTS policy_store (
+    id INTEGER PRIMARY KEY AUTO_INCREMENT,
+    targetID BIGINT NOT NULL,
+    created TIMESTAMP,
+    creator INTEGER NOT NULL,
+    posix SMALLINT NOT NULL,
+    expire timestamp null,
+    enable timestamp not null,
+    iprange VARCHAR(200)
+)$$
+
+CREATE TABLE IF NOT EXISTS group_ref (
+    id INTEGER PRIMARY KEY AUTO_INCREMENT,
+    groupId VARCHAR(100) NOT NULL,
+    policyid INTEGER NOT NULL
+)$$
+
+
+CREATE TABLE IF NOT EXISTS group_store (
+    name VARCHAR(100) PRIMARY KEY,
+    description VARCHAR(200),
+    sym_use INTEGER DEFAULT -1,
+    export VARCHAR(30) DEFAULT NULL,
+    query_only VARCHAR(30) DEFAULT NULL,
+    licence INTEGER DEFAULT -1,
+    -- basically every resource we have is an academic resource, thus a non-commercial use is infered!
+    commercial BOOLEAN DEFAULT FALSE
+)$$
+
+CREATE TABLE IF NOT EXISTS group_users (
+    id INTEGER PRIMARY KEY AUTO_INCREMENT,
+    userID BIGINT NOT NULL,
+    groupId VARCHAR(100) NOT NULL,
+    admin BOOLEAN NOT NULL DEFAULT FALSE,
+    FOREIGN KEY (groupId)
+        REFERENCES groupStore (name) on delete cascade
+)$$
+
+CREATE TABLE IF NOT EXISTS param_store (
+    id INTEGER PRIMARY KEY AUTO_INCREMENT,
+    p_key VARCHAR(100) NOT NULL,
+    p_value VARCHAR(150) NOT NULL,
+    resource INTEGER DEFAULT -1,
+    pid INTEGER DEFAULT -1,
+    FOREIGN KEY (resource)
+        REFERENCES resource_store(id)
+    on delete cascade,
+    FOREIGN KEY (pid)
+        REFERENCES policy_store(id)
+    on delete cascade
+)$$
+
+CREATE TABLE IF NOT EXISTS param_map (
+    id INTEGER PRIMARY KEY AUTO_INCREMENT,
+    policyID INTEGER NOT NULL,
+    paramID INTEGER NOT NULL,
+    value VARCHAR(100) NOT NULL,
+    flag BOOLEAN NOT NULL DEFAULT FALSE,
+    FOREIGN KEY (paramID)
+        REFERENCES param_store (id),
+    FOREIGN KEY (policyID)
+        REFERENCES policy_store (id)
+)$$
+
+create or replace view policy_view as
+select
+    po.id as pid,
+    po.targetID as id,
+    rs.persistentID as persistentID,
+    rs.name as name,
+    rs.type as type,
+    c.groupId as groupId,
+    po.posix as perm,
+    po.creator as creator,
+    po.expire as expire,
+    po.enable as enable,
+    po.iprange as iprange    
+from
+policy_store as po
+inner join
+group_ref as c ON c.policyid = po.id
+inner join
+resource_store as rs ON rs.id = po.targetid
+union all select
+              - 1 as pid,
+              rs.id as id,
+              rs.persistentID as persistentID,
+              rs.name as name,
+              type as type,
+              'self' as groupId,
+              127 as perm,
+              creator,              
+              NULL as expire,
+              rs.created as enable,
+              null as iprange
+          from
+          resource_store as rs$$
+
+
+
+-- oauth2 db tables
+create table oauth2_client (
+id INTEGER PRIMARY KEY AUTO_INCREMENT,
+client_id VARCHAR(100),
+client_secret VARCHAR(200),
+redirect_uri VARCHAR(250),
+client_type VARCHAR(200),
+native BOOLEAN DEFAULT FALSE,
+url VARCHAR(200) UNIQUE
+)$$
+
+create table oauth2_auth_codes (
+id INTEGER PRIMARY KEY AUTO_INCREMENT,
+client_id VARCHAR(100),
+auth_code VARCHAR(250),
+status INTEGER DEFAULT 1,
+scopes VARCHAR (150),
+created TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+)$$
+
+create table oauth2_client_authorization (
+id INTEGER PRIMARY KEY AUTO_INCREMENT,
+client_id INTEGER,
+user_id INTEGER,
+-- define scopes?! --
+FOREIGN KEY (client_id) REFERENCES oauth2_client(client_id)
+)$$
+
+-- status 1 = valid, 0 = revoked
+create table oauth2_access_token (
+id INTEGER PRIMARY KEY AUTO_INCREMENT,
+access_token VARCHAR(300),
+auth_code VARCHAR(250),
+userId INTEGER,
+status INTEGER DEFAULT 1,
+expiration TIMESTAMP,
+FOREIGN KEY (userId)
+REFERENCES korap_users(id)
+)$$
+
+
+
+-- indices
+create trigger delete_policy after delete on resource_store
+for each row delete from policy_store where targetID=OLD.id$$
+
+CREATE TRIGGER insert_data AFTER INSERT ON resource_store FOR EACH ROW BEGIN
+	INSERT INTO resource_tree (parentID, childID, depth, name_path)
+	VALUES (NEW.id, NEW.id, 0, NEW.name);
+	INSERT INTO resource_tree (parentID, childID, depth, name_path)
+	SELECT parentID, NEW.id, depth + 1, concat(name_path,"/",NEW.name) FROM resource_tree WHERE childID = NEW.parentID;
+END$$
+
+CREATE INDEX group_index ON group_users(userid)$$
+CREATE INDEX policy_index ON group_ref(policyid)$$
+CREATE UNIQUE INDEX resource_tree_index on resource_tree (parentID, depth, childID)$$
+CREATE UNIQUE INDEX para_unique ON param_store (p_key, p_value)$$
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/db/sqlite/V1__Initial_version.sql b/src/main/resources/db/sqlite/V1__Initial_version.sql
new file mode 100644
index 0000000..fbe252d
--- /dev/null
+++ b/src/main/resources/db/sqlite/V1__Initial_version.sql
@@ -0,0 +1,374 @@
+CREATE TABLE IF NOT EXISTS korap_users (
+id INTEGER PRIMARY KEY AUTOINCREMENT,
+username VARCHAR(150) NOT NULL UNIQUE,
+password VARCHAR(100) NOT NULL,
+accountLock boolean NOT NULL,
+accountCreation TIMESTAMP NOT NULL,
+-- deprecate this
+type INTEGER DEFAULT 0,
+uri_fragment VARCHAR(100),
+uri_expiration TIMESTAMP,
+accountLink VARCHAR(100)
+);
+
+CREATE TABLE IF NOT EXISTS shib_users (
+id INTEGER PRIMARY KEY AUTOINCREMENT,
+username VARCHAR(150) NOT NULL UNIQUE,
+accountCreation TIMESTAMP NOT NULL,
+type INTEGER DEFAULT 1,
+loginSuccess INTEGER,
+loginFailed INTEGER,
+accountLink VARCHAR(100)
+);
+
+CREATE TABLE IF NOT EXISTS user_details (
+id INTEGER PRIMARY KEY AUTOINCREMENT,
+user_id INTEGER NOT NULL,
+firstName VARCHAR(100),
+lastName VARCHAR(100),
+gender VARCHAR(100),
+phone VARCHAR(100),
+institution VARCHAR(100),
+email VARCHAR(100),
+address VARCHAR(100),
+country VARCHAR(100),
+privateUsage BOOLEAN,
+foreign key (user_id)
+references korap_users (id)
+on delete cascade
+);
+
+CREATE TABLE IF NOT EXISTS user_settings (
+id INTEGER PRIMARY KEY AUTOINCREMENT,
+user_id INTEGER NOT NULL,
+fileNameForExport VARCHAR(100),
+leftContextItemForExport VARCHAR(100),
+leftContextSizeForExport INTEGER,
+locale VARCHAR(100),
+leftContextItem VARCHAR(100),
+leftContextSize INTEGER,
+rightContextItem VARCHAR(100),
+rightContextItemForExport VARCHAR(100),
+rightContextSize INTEGER,
+rightContextSizeForExport INTEGER,
+selectedCollection VARCHAR(100),
+queryLanguage VARCHAR(100),
+pageLength INTEGER,
+metadataQueryExpertModus BOOLEAN,
+POSFoundry VARCHAR(100),
+lemmaFoundry VARCHAR(100),
+constFoundry VARCHAR(100),
+relFoundry VARCHAR(100),
+collectData BOOLEAN,
+foreign key (user_id)
+references korap_users (id)
+on delete cascade
+);
+
+
+CREATE TABLE IF NOT EXISTS user_queries (
+id INTEGER PRIMARY KEY,
+queryLanguage VARCHAR(100),
+name VARCHAR(100),
+query VARCHAR(200),
+description VARCHAR(150),
+foreign key (id)
+references resource_store (id)
+on delete cascade
+);
+
+CREATE TABLE IF NOT EXISTS coll_store (
+id INTEGER PRIMARY KEY AUTOINCREMENT,
+persistent_id VARCHAR(150) UNIQUE,
+name VARCHAR(150),
+description VARCHAR(200),
+query VARCHAR(500),
+created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+user_id INTEGER,
+foreign key(user_id)
+references korap_users(id)
+on delete cascade
+);
+
+CREATE TABLE IF NOT EXISTS audit_records (
+aud_id INTEGER PRIMARY KEY AUTOINCREMENT,
+aud_category VARCHAR(100),
+aud_target VARCHAR(100),
+aud_user VARCHAR(100),
+aud_location VARCHAR(100),
+aud_field_1 VARCHAR(400),
+aud_args VARCHAR(400),
+aud_timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+aud_status VARCHAR(100)
+);
+
+-- deprecated
+CREATE TABLE IF NOT EXISTS match_info (
+id INTEGER PRIMARY KEY AUTOINCREMENT,
+userid BIGINT NOT NULL,
+matchInfo VARCHAR(100)
+);
+
+
+CREATE TABLE IF NOT EXISTS policy_store (
+id INTEGER PRIMARY KEY AUTOINCREMENT,
+target_id BIGINT NOT NULL,
+created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+creator INTEGER NOT NULL,
+posix SMALLINT NOT NULL,
+expire timestamp,
+enable timestamp NOT NULL,
+iprange varchar(200)
+);
+
+-- send disabled documents per corpus to backend, so they can be excluded from searching!
+CREATE TABLE IF NOT EXISTS doc_store (
+id INTEGER PRIMARY KEY AUTOINCREMENT,
+persistent_id VARCHAR(265) UNIQUE,
+created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+disabled BOOLEAN default true
+);
+
+CREATE TABLE IF NOT EXISTS group_ref (
+id INTEGER PRIMARY KEY AUTOINCREMENT,
+group_id VARCHAR(100) NOT NULL,
+policy_id INTEGER NOT NULL
+);
+
+-- question: grouping of users or grouping of resources required?
+CREATE TABLE IF NOT EXISTS group_store (
+name VARCHAR(100) PRIMARY KEY,
+description VARCHAR(200),
+sym_use INTEGER DEFAULT -1,
+export VARCHAR(30) DEFAULT NULL,
+query_only VARCHAR(30) DEFAULT NULL,
+licence INTEGER DEFAULT -1,
+-- basically every resource we have is an academic resource, thus a non-commercial use is infered!
+commercial BOOLEAN DEFAULT FALSE
+);
+
+CREATE TABLE IF NOT EXISTS group_users (
+id INTEGER PRIMARY KEY AUTOINCREMENT,
+user_id INTEGER NOT NULL,
+group_id VARCHAR(100) NOT NULL,
+admin BOOLEAN NOT NULL DEFAULT FALSE,
+FOREIGN KEY (user_id)
+REFERENCES korap_users(id)
+on delete cascade,
+FOREIGN KEY (group_id)
+REFERENCES group_store (name)
+on delete cascade
+);
+
+
+CREATE TABLE IF NOT EXISTS param_store (
+id INTEGER PRIMARY KEY AUTOINCREMENT,
+p_key VARCHAR(150) NOT NULL,
+p_value VARCHAR(200) NOT NULL,
+resource INTEGER NOT NULL DEFAULT -1,
+pid INTEGER NOT NULL DEFAULT -1,
+FOREIGN KEY (resource)
+REFERENCES resource_store(id)
+on delete cascade,
+FOREIGN KEY (pid)
+REFERENCES policy_store(id)
+on delete cascade
+);
+
+CREATE TABLE IF NOT EXISTS param_map (
+id INTEGER PRIMARY KEY AUTOINCREMENT,
+policyId INTEGER NOT NULL,
+paramId INTEGER NOT NULL,
+value VARCHAR(100) NOT NULL,
+flag BOOLEAN NOT NULL DEFAULT FALSE,
+FOREIGN KEY (paramId)
+   REFERENCES param_store (id),
+FOREIGN KEY (policyId)
+   REFERENCES policy_store (id)
+);
+
+CREATE TABLE IF NOT EXISTS resource_store (
+id INTEGER PRIMARY KEY AUTOINCREMENT,
+persistent_id VARCHAR(100) NOT NULL UNIQUE,
+name VARCHAR(100),
+description VARCHAR(300),
+parent_id Integer unsigned null,
+created timestamp default current_timestamp,
+type INTEGER NOT NULL,
+creator INTEGER NOT NULL
+);
+
+
+CREATE TABLE IF NOT EXISTS resource_tree (
+parent_id INTEGER,
+child_id INTEGER,
+depth INTEGER,
+name_path VARCHAR(250),
+PRIMARY KEY (parent_id, child_id),
+foreign key (parent_id)
+references resource_store (id)
+on delete cascade,
+foreign key (child_id)
+references resource_store (id)
+on delete cascade
+);
+
+
+-- todo: refactor native to confidential, add column application name
+-- remove id and make client_id primary key
+create table IF NOT EXISTS oauth2_client (
+id INTEGER PRIMARY KEY AUTOINCREMENT,
+client_id VARCHAR(100) NOT NULL,
+client_secret VARCHAR(200) NOT NULL,
+redirect_uri VARCHAR(250) NOT NULL,
+client_type VARCHAR(200),
+is_confidential BOOLEAN DEFAULT FALSE,
+url VARCHAR(200) UNIQUE
+);
+
+-- status 1 = valid, 0 = revoked, -1 = disabled
+create table IF NOT EXISTS oauth2_access_token (
+id INTEGER PRIMARY KEY AUTOINCREMENT,
+access_token VARCHAR(300),
+auth_code VARCHAR(250),
+client_id VARCHAR(100),
+user_id INTEGER,
+-- make boolean --
+status INTEGER DEFAULT 1,
+-- in case of code authorization, should match auth code scopes!
+-- use scopes for levelaccess descriptor level[rw],level[r]
+scopes VARCHAR(350),
+expiration TIMESTAMP,
+FOREIGN KEY (user_id)
+REFERENCES korap_users(id)
+);
+
+
+-- a bit confusing. 1. creator is policy creator, 2. creator is resource creator --> different implications
+-- insert resource data from resource_store alltogether, so i dont have to retrieve anything from there?!
+create view if not exists policy_view as
+select
+    po.id as pid,
+    po.target_id as id,
+    rs.persistent_id as persistent_id,
+    rs.name as name,
+    rs.type as type,
+    c.group_id as group_id,
+    po.posix as perm,
+    po.creator as creator,
+    po.expire as expire,
+    po.enable as enable,
+    po.iprange as iprange
+from policy_store as po
+inner join
+group_ref as c ON c.policy_id = po.id
+inner join
+resource_store as rs ON rs.id = po.target_id
+union all select
+              - 1 as pid,
+              rs.id as id,
+              rs.persistent_id as persistent_id,
+              rs.name as name,
+              type as type,
+              'self' as group_id,
+              127 as perm,
+              creator,
+              NULL as expire,
+              rs.created as enable,
+              null as iprange
+          from
+          resource_store as rs;
+
+-- deletes a group if the group has no longer members!
+create trigger if not exists group_ref_del after delete on group_ref
+when (select count(*) from group_ref where groupId=OLD.group_id) = 0
+begin delete from groupolicy_store where name=OLD.group_id; end;
+
+    -- create trigger relCr after insert on resource_store
+    -- when (select count(*) from r_tree where parent_id==NEW.id and
+    -- child_id==NEW.id) == 0
+    -- BEGIN
+    -- insert into r_tree (parent_id, child_id, depth)
+    -- VALUES (NEW.id, NEW.id, 0);
+    -- END;
+
+    -- 1. CONCAT(NEW.name,"/")
+    -- 2. CONCAT(name_path, NEW.name, "/")
+
+CREATE TRIGGER IF NOT EXISTS insert_data
+AFTER INSERT ON resource_store
+FOR EACH ROW BEGIN
+INSERT INTO resource_tree (parent_id, child_id, depth, name_path)
+VALUES (NEW.id, NEW.id, 0, NEW.name);
+
+INSERT INTO resource_tree (parent_id, child_id, depth, name_path)
+SELECT parent_id, NEW.id, depth + 1, name_path || "/" ||  NEW.name FROM resource_tree
+WHERE child_id = NEW.parent_id;
+END;
+
+create trigger if not exists delete_policy after delete on resource_store
+begin delete from policy_store where target_id=OLD.id; end;
+
+    -- 1. requirement: delete hierarchical from resource_store and r_tree -- done!
+    -- 2. todo: subsequently delete from resourcedao extensions if child of deleted resource!
+create trigger if not exists del_tree after delete on resource_store
+begin delete from resource_store where id in (select rs.id from resource_store as rs
+inner join resource_tree as rt on rt.child_id=rs.id where rt.parent_id=OLD.id);
+delete from resource_tree where parent_id=OLD.id; end;
+
+-- mysql on delete cascade todo: test
+create trigger if not exists del_user delete on korap_users
+begin
+    delete from user_settings where user_id=OLD.id;
+    delete from user_details where user_id=OLD.id;
+    delete from group_users where user_id=OLD.id;
+end;
+
+-- indices
+-- test unique index constraints
+create index group_index on group_users(user_id);
+create index policy_index on group_ref(policy_id);
+create index policy_target on policy_store(target_id);
+create unique index r_tree_index on resource_tree (parent_id, depth, child_id);
+create unique index para_unique on param_store (p_key, p_value);
+create unique index conditions on group_ref (policy_id, group_id);
+create unique index groups on group_users (user_id, group_id);
+
+
+-- deprecated
+-- flagr is a reference to the applicable conditions: export, licence
+create table if not exists policy_store2 (
+id integer primary key autoincrement,
+target_id bigint not null,
+creator bigint not null,
+perm integer default -1,
+enable boolean default true,
+master INTEGER UNIQUE default NULL,
+expire timestamp default null,
+iprange varchar(200),
+flagr integer,
+params integer,
+baseline boolean default false,
+FOREIGN KEY (master)
+    REFERENCES policy_store2(id),
+FOREIGN KEY (flagr)
+    REFERENCES flag_store (id),
+FOREIGN KEY (params)
+    REFERENCES param_store (id)
+);
+
+-- grouping is matched with a view where the user and groups are listed together
+create table if not exists flag_store (
+id integer primary key autoincrement,
+export boolean default true,
+sym_use integer default -1,
+grouping varchar(150),
+FOREIGN KEY (grouping)
+ REFERENCES groupings (grouping)
+);
+
+-- todo: ??!
+-- haveing the username as grouping only works with the unique identifier at username
+create view if not exists groupings_view
+as select id as user_id, username as grouping from korap_users
+union all select user_id, group_id as grouping from group_users;
diff --git a/src/main/resources/default-config.xml b/src/main/resources/default-config.xml
index f0c380f..023f671 100644
--- a/src/main/resources/default-config.xml
+++ b/src/main/resources/default-config.xml
@@ -1,25 +1,197 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:p="http://www.springframework.org/schema/p"
        xmlns:util="http://www.springframework.org/schema/util"
+       xmlns:aop="http://www.springframework.org/schema/aop"
+       xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns="http://www.springframework.org/schema/beans"
+       xmlns:cache="http://www.springframework.org/schema/cache"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
                             http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
+                            http://www.springframework.org/schema/tx
+                            http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
+                            http://www.springframework.org/schema/aop
+                            http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
+                            http://www.springframework.org/schema/cache
+                            http://www.springframework.org/schema/cache/spring-cache-4.0.xsd
+
+
                             http://www.springframework.org/schema/util
                             http://www.springframework.org/schema/util/spring-util-4.0.xsd">
 
-
+    <cache:annotation-driven/>
     <util:properties id="props" location="classpath:kustvakt.conf"/>
 
-    <bean id="auditingProvider"
-          class="de.ids_mannheim.korap.interfaces.defaults.DefaultAuditing">
+    <bean id="jdbc_props"
+          class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+        <property name="locations" value="classpath:jdbc.properties"/>
     </bean>
 
-    <bean id="config"
-          class="de.ids_mannheim.korap.config.KustvaktConfiguration">
+    <bean id='cacheManager'
+          class='org.springframework.cache.ehcache.EhCacheCacheManager'
+          p:cacheManager-ref='ehcache'/>
+
+    <bean id='ehcache'
+          class='org.springframework.cache.ehcache.EhCacheManagerFactoryBean'
+          p:configLocation='classpath:ehcache.xml'
+          p:shared='true'/>
+
+    <!-- props are injected from default-config.xml -->
+    <bean id="kustvakt_config"
+          class="de.ids_mannheim.korap.ext.config.ExtConfiguration">
         <property name="properties" ref="props"/>
     </bean>
 
-    <bean name="encryption"
-          class="de.ids_mannheim.korap.interfaces.defaults.DefaultEncryption">
+    <bean id="dataSource"
+          class="org.springframework.jdbc.datasource.SingleConnectionDataSource"
+          lazy-init="true">
+        <property name="driverClassName" value="${jdbc.driverClassName}"/>
+        <property name="url" value="${jdbc.url}"/>
+        <property name="username" value="${jdbc.username}"/>
+        <property name="password" value="${jdbc.password}"/>
+        <!-- relevant for single connection datasource and sqlite -->
+        <property name="suppressClose">
+            <value>true</value>
+        </property>
+    </bean>
+
+    <!-- to configure database for sqlite, mysql, etc. migrations -->
+    <bean id="flyway" class="org.flywaydb.core.Flyway" init-method="migrate">
+        <property name="baselineOnMigrate" value="false"/>
+        <property name="locations"
+                  value="classpath:${jdbc.schemaPath}"/>
+        <property name="dataSource" ref="dataSource"/>
+    </bean>
+
+    <bean id="kustvakt_db"
+          class="de.ids_mannheim.korap.ext.database.JDBCClient">
+        <constructor-arg index="0" ref="dataSource"/>
+        <!-- deprecated property -->
+        <property name="database" value="${jdbc.database}"/>
+    </bean>
+
+    <bean id="kustvakt_auditing"
+          class="de.ids_mannheim.korap.ext.database.JDBCAuditing">
+        <constructor-arg ref="kustvakt_db"/>
+    </bean>
+
+    <bean id="kustvakt_userdb"
+          class="de.ids_mannheim.korap.ext.database.EntityDao">
+        <constructor-arg ref="kustvakt_db"/>
+    </bean>
+
+    <bean id="collectionProvider"
+          class="de.ids_mannheim.korap.ext.database.CollectionDao">
+        <constructor-arg ref="kustvakt_db"/>
+    </bean>
+
+    <bean id="resourceProvider"
+          class="de.ids_mannheim.korap.ext.database.ResourceDao">
+        <constructor-arg ref="kustvakt_db"/>
+    </bean>
+
+    <bean id="kustvakt_policies"
+          class="de.ids_mannheim.korap.ext.security.dataAccess.PolicyDao">
+        <constructor-arg ref="kustvakt_db"/>
+    </bean>
+
+    <bean name="kustvakt_encryption"
+          class="de.ids_mannheim.korap.ext.security.encryption.KorAPEncryption">
+        <constructor-arg ref="kustvakt_config"/>
+    </bean>
+
+    <bean id="resourceHandler"
+          class="de.ids_mannheim.korap.ext.security.accessControl.ResourceHandler"/>
+
+
+    <!-- authentication providers to use -->
+
+    <bean id="api_auth"
+          class="de.ids_mannheim.korap.ext.security.authentication.APIAuthentication">
+        <constructor-arg
+                type="de.ids_mannheim.korap.config.KustvaktConfiguration"
+                ref="kustvakt_config"/>
+    </bean>
+
+    <bean id="openid_auth"
+          class="de.ids_mannheim.korap.ext.security.authentication.OpenIDconnectAuthentication">
+        <constructor-arg
+                type="de.ids_mannheim.korap.config.KustvaktConfiguration"
+                ref="kustvakt_config"/>
+        <constructor-arg
+                type="de.ids_mannheim.korap.interfaces.PersistenceClient"
+                ref="kustvakt_db"/>
+    </bean>
+
+    <bean id="basic_auth"
+          class="de.ids_mannheim.korap.ext.security.authentication.BasicHttpAuth"/>
+
+
+    <bean id="session_auth"
+          class="de.ids_mannheim.korap.ext.security.authentication.SessionAuthentication">
+        <constructor-arg
+                type="de.ids_mannheim.korap.config.KustvaktConfiguration"
+                ref="kustvakt_config"/>
+        <constructor-arg
+                type="de.ids_mannheim.korap.interfaces.EncryptionIface"
+                ref="kustvakt_encryption"/>
+    </bean>
+
+    <util:list id="auth_providers"
+               value-type="de.ids_mannheim.korap.interfaces.AuthenticationIface">
+        <ref bean="basic_auth"/>
+        <ref bean="session_auth"/>
+        <ref bean="api_auth"/>
+        <ref bean="openid_auth"/>
+    </util:list>
+
+    <!-- specify type for constructor argument -->
+    <bean id="kustvakt_authenticationmanager"
+          class="de.ids_mannheim.korap.ext.security.authentication.KustvaktAuthenticationManager">
+        <constructor-arg
+                type="de.ids_mannheim.korap.interfaces.EntityHandlerIface"
+                ref="kustvakt_userdb"/>
+        <constructor-arg type="de.ids_mannheim.korap.interfaces.EncryptionIface"
+                         ref="kustvakt_encryption"/>
+        <constructor-arg ref="kustvakt_config"/>
+        <constructor-arg type="de.ids_mannheim.korap.interfaces.AuditingIface"
+                         ref="kustvakt_auditing"/>
+        <!-- inject authentication providers to use -->
+        <property name="providers" ref="auth_providers"/>
+    </bean>
+
+    <!--
+    <util:list id="providers"
+               value-type="de.ids_mannheim.korap.ext.interfaces.ResourceOperationIface">
+        <ref bean="resourceProvider"/>
+        <ref bean="collectionProvider"/>
+    </util:list>
+-->
+
+    <!-- todo: if db interfaces not loaded via spring, does transaction even work then? -->
+    <!-- the transactional advice (i.e. what 'happens'; see the <aop:advisor/> bean below) -->
+    <tx:advice id="txAdvice" transaction-manager="txManager">
+        <!-- the transactional semantics... -->
+        <tx:attributes>
+            <!-- all methods starting with 'get' are read-only -->
+            <tx:method name="get*" read-only="true"
+                       rollback-for="KorAPException"/>
+            <!-- other methods use the default transaction settings (see below) -->
+            <tx:method name="*" rollback-for="KorAPException"/>
+        </tx:attributes>
+    </tx:advice>
+
+    <!-- ensure that the above transactional advice runs for any execution
+        of an operation defined by the service interface -->
+    <aop:config>
+        <aop:pointcut id="service"
+                      expression="execution(* de.ids_mannheim.korap.interfaces.*.*(..))"/>
+        <aop:advisor advice-ref="txAdvice" pointcut-ref="service"/>
+    </aop:config>
+
+    <!-- similarly, don't forget the PlatformTransactionManager -->
+    <bean id="txManager"
+          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
+        <property name="dataSource" ref="dataSource"/>
     </bean>
 </beans>
\ No newline at end of file
diff --git a/src/main/resources/ehcache.xml b/src/main/resources/ehcache.xml
new file mode 100644
index 0000000..3265324
--- /dev/null
+++ b/src/main/resources/ehcache.xml
@@ -0,0 +1,32 @@
+<ehcache xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
+         xsi:noNamespaceSchemaLocation='http://ehcache.org/ehcache.xsd'>
+    <defaultCache eternal='true' overflowToDisk='false'/>
+    <!--maxBytesLocalHeap="200M"-->
+    <cache name='users'
+           timeToIdleSeconds="172800"
+           eternal='false'
+           memoryStoreEvictionPolicy="LRU"
+           maxEntriesLocalHeap="50"
+           overflowToDisk='false'/>
+    <cache name='id_tokens'
+           timeToIdleSeconds="172800"
+           eternal='false'
+           memoryStoreEvictionPolicy="LRU"
+           maxEntriesLocalHeap="50"
+           overflowToDisk='false'/>
+    <cache name='auth_codes'
+           timeToIdleSeconds="600"
+           eternal='false'
+           memoryStoreEvictionPolicy="LRU"
+           maxEntriesLocalHeap="100"
+           overflowToDisk='false'/>
+    <cache name='resources'
+           eternal='false'
+           overflowToDisk='false'
+           maxEntriesLocalHeap="60"
+           timeToIdleSeconds="172800"
+           diskSpoolBufferSizeMB="30"
+           diskExpiryThreadIntervalSeconds="120"
+           memoryStoreEvictionPolicy="LRU"
+           statistics="false"/>
+</ehcache>
diff --git a/src/main/resources/light-config.xml b/src/main/resources/light-config.xml
new file mode 100644
index 0000000..12dce87
--- /dev/null
+++ b/src/main/resources/light-config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xmlns="http://www.springframework.org/schema/beans"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+                            http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
+                            http://www.springframework.org/schema/util
+                            http://www.springframework.org/schema/util/spring-util-4.0.xsd">
+
+
+    <util:properties id="props" location="classpath:kustvakt.conf"/>
+
+    <bean id="kustvakt_auditing"
+          class="de.ids_mannheim.korap.interfaces.defaults.DefaultAuditing">
+    </bean>
+
+    <bean id="kustvakt_config"
+          class="de.ids_mannheim.korap.config.KustvaktConfiguration">
+        <property name="properties" ref="props"/>
+    </bean>
+
+    <bean name="kustvakt_encryption"
+          class="de.ids_mannheim.korap.interfaces.defaults.DefaultEncryption">
+    </bean>
+</beans>
\ No newline at end of file
diff --git a/src/main/resources/validation.properties b/src/main/resources/validation.properties
new file mode 100755
index 0000000..9a0e0e5
--- /dev/null
+++ b/src/main/resources/validation.properties
@@ -0,0 +1,30 @@
+# The ESAPI validator does many security checks on input, such as canonicalization
+# and whitelist validation. Note that all of these validation rules are applied *after*
+# canonicalization. Double-encoded characters (even with different encodings involved,
+# are never allowed.
+#
+# To use:
+#
+# First set up a pattern below. You can choose any name you want, prefixed by the word
+# "Validation." For example:
+#   Validation.Email=^[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,4}$
+# 
+# Then you can validate in your code against the pattern like this:
+#     ESAPI.validator().isValidInput("User Email", input, "Email", maxLength, allowNull);
+# Where maxLength and allowNull are set for you needs, respectively.
+#
+# But note, when you use boolean variants of validation functions, you lose critical 
+# canonicalization. It is preferable to use the "get" methods (which throw exceptions) and
+# and use the returned user input which is in canonical form. Consider the following:
+#  
+# try {
+#    someObject.setEmail(ESAPI.validator().getValidInput("User Email", input, "Email", maxLength, allowNull));
+#
+#Validator.SafeString=^[.;:\\-\\p{Alnum}\\p{Space}]{0,1024}$
+Validator.SafeString=^[.;:,=\\*\/\/_()\\-0-9\\p{L}\\p{Space}]{0,1024}$
+Validator.Email=^[A-Za-z0-9._%'-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,4}$
+Validator.IPAddress=^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$
+Validator.URL=^(ht|f)tp(s?)\\:\\/\\/[0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z])*(:(0-9)*)*(\\/?)([a-zA-Z0-9\\-\\.\\?\\,\\:\\'\\/\\\\\\+=&amp;%\\$#_]*)?$
+Validator.CreditCard=^(\\d{4}[- ]?){3}\\d{4}$
+Validator.SSN=^(?!000)([0-6]\\d{2}|7([0-6]\\d|7[012]))([ -]?)(?!00)\\d\\d\\3(?!0000)\\d{4}$
+Validator.Password=((?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{8,20})
diff --git a/src/main/resources/version.prop b/src/main/resources/version.prop
new file mode 100644
index 0000000..e5683df
--- /dev/null
+++ b/src/main/resources/version.prop
@@ -0,0 +1 @@
+version=${project.version}
\ No newline at end of file
diff --git a/src/test/java/ClassLoaderTest.java b/src/test/java/ClassLoaderTest.java
new file mode 100644
index 0000000..207ef81
--- /dev/null
+++ b/src/test/java/ClassLoaderTest.java
@@ -0,0 +1,42 @@
+import de.ids_mannheim.korap.config.BeanConfiguration;
+import de.ids_mannheim.korap.config.DefaultHandler;
+import de.ids_mannheim.korap.interfaces.AuditingIface;
+import de.ids_mannheim.korap.interfaces.defaults.DefaultAuditing;
+import org.junit.Test;
+
+/**
+ * @author hanl
+ * @date 27/07/2015
+ */
+public class ClassLoaderTest {
+
+    @Test
+    public void testBeanConfigurationLoaderThrowsNoException() {
+        BeanConfiguration.loadClasspathContext("classpath-config.xml");
+        assert BeanConfiguration.getBeans() != null;
+    }
+
+    @Test
+    public void testDefaultCreationThrowsNoException() {
+        DefaultHandler pl = new DefaultHandler();
+        Object o = pl.getDefault(BeanConfiguration.KUSTVAKT_AUDITING);
+        assert o != null;
+        assert o instanceof AuditingIface;
+    }
+
+    @Test(expected = RuntimeException.class)
+    public void testDefaultCreationThrowsException() {
+        BeanConfiguration.loadClasspathContext();
+        BeanConfiguration.getBeans().getAuthenticationManager();
+    }
+
+    @Test
+    public void testDefaultInterfaceMatchThrowsNoException() {
+        BeanConfiguration.loadClasspathContext();
+        AuditingIface iface = BeanConfiguration.getBeans()
+                .getAuditingProvider();
+        assert iface != null;
+        assert iface instanceof DefaultAuditing;
+    }
+
+}
diff --git a/src/test/java/CollectionRewriteTest.java b/src/test/java/CollectionRewriteTest.java
new file mode 100644
index 0000000..8e704b8
--- /dev/null
+++ b/src/test/java/CollectionRewriteTest.java
@@ -0,0 +1,6 @@
+/**
+ * @author hanl
+ * @date 03/09/2015
+ */
+public class CollectionRewriteTest {
+}
diff --git a/src/test/java/FileAuditingTest.java b/src/test/java/FileAuditingTest.java
new file mode 100644
index 0000000..5646590
--- /dev/null
+++ b/src/test/java/FileAuditingTest.java
@@ -0,0 +1,47 @@
+import de.ids_mannheim.korap.auditing.AuditRecord;
+import de.ids_mannheim.korap.config.BeanConfiguration;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import org.joda.time.LocalDate;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.Date;
+
+/**
+ * @author hanl
+ * @date 27/07/2015
+ */
+//todo: test audit commit in thread and that no concurrency issue arrises
+public class FileAuditingTest {
+
+    @BeforeClass
+    public static void init() {
+        BeanConfiguration.loadClasspathContext();
+    }
+
+    @AfterClass
+    public static void finish() {
+        BeanConfiguration.closeApplication();
+        File f = new File("logs/default_audit.log");
+        assert f.length() > 0;
+    }
+
+    @Test
+    public void testAdd() {
+        for (int i = 0; i < 20; i++) {
+            AuditRecord record = AuditRecord
+                    .serviceRecord("MichaelHanl", StatusCodes.ILLEGAL_ARGUMENT,
+                            String.valueOf(i), "string value");
+            BeanConfiguration.getBeans().getAuditingProvider().audit(record);
+        }
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRetrieval() {
+        BeanConfiguration.getBeans().getAuditingProvider()
+                .retrieveRecords(new LocalDate(new Date().getTime()), 10);
+    }
+
+}
diff --git a/src/test/java/FoundryRewriteTest.java b/src/test/java/FoundryRewriteTest.java
new file mode 100644
index 0000000..a437208
--- /dev/null
+++ b/src/test/java/FoundryRewriteTest.java
@@ -0,0 +1,212 @@
+import com.fasterxml.jackson.databind.JsonNode;
+import de.ids_mannheim.korap.config.BeanConfiguration;
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.query.serialize.QuerySerializer;
+import de.ids_mannheim.korap.resource.LayerMapper;
+import de.ids_mannheim.korap.resource.RewriteProcessor;
+import de.ids_mannheim.korap.resource.rewrite.CollectionCleanupFilter;
+import de.ids_mannheim.korap.resource.rewrite.CollectionConstraint;
+import de.ids_mannheim.korap.resource.rewrite.FoundryInject;
+import de.ids_mannheim.korap.resource.rewrite.RewriteHandler;
+import de.ids_mannheim.korap.utils.JsonUtils;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * @author hanl
+ * @date 18/06/2015
+ */
+//todo: refactor and structure tests
+public class FoundryRewriteTest {
+
+    private static String simple_add_query = "[pos=ADJA]";
+    private static String simple_rewrite_query = "[base=Haus]";
+    private static String complex_rewrite_query = "<c=INFC>";
+    private static String complex_rewrite_query2 = "[orth=laufe/i & base!=Lauf]";
+    private static String complex_rewrite_query3 = "[(base=laufen | base=gehen) & tt/pos=VVFIN]";
+
+    private static KustvaktConfiguration config;
+
+    @BeforeClass
+    public static void init() {
+        BeanConfiguration.loadClasspathContext();
+        config = BeanConfiguration.getBeans().getConfiguration();
+    }
+
+    @Test
+    public void testSimpleFoundryAddThrowsNoError() {
+        RewriteProcessor processor = new RewriteProcessor(config);
+        QuerySerializer s = new QuerySerializer();
+        s.setQuery(simple_add_query, "poliqarp");
+        String result = processor.process(s.toJSON());
+        JsonNode node = JsonUtils.readTree(result);
+
+        assert node != null;
+        assert !node.at("/query/wrap/foundry").isMissingNode();
+    }
+
+    @Test
+    public void testDefaultLayerMapperThrowsNoException() {
+        LayerMapper m = new LayerMapper(config);
+
+        assert m.findFoundry("lemma").equals(config.getDefault_lemma());
+        assert m.findFoundry("pos").equals(config.getDefault_pos());
+        assert m.findFoundry("surface").equals(config.getDefault_surface());
+        assert m.findFoundry("d").equals(config.getDefault_dep());
+        assert m.findFoundry("c").equals(config.getDefault_const());
+    }
+
+    @Test
+    public void testFoundryInjectPosNoErrors() {
+        QuerySerializer s = new QuerySerializer();
+        RewriteHandler handler = new RewriteHandler();
+        s.setQuery("[pos=ADJA]", "poliqarp");
+        handler.add(new FoundryInject(config));
+        String result = handler.apply(s.toJSON(), null);
+        JsonNode node = JsonUtils.readTree(result);
+
+        assert node != null;
+        assert !node.at("/query/wrap/foundry").isMissingNode();
+        assert !node.at("/query/wrap/rewrites").isMissingNode();
+        assert node.at("/query/wrap/rewrites/0/@type").asText()
+                .equals("koral:rewrite");
+    }
+
+    @Test
+    public void testFoundryInjectJoinedQueryNoErrors() {
+        QuerySerializer s = new QuerySerializer();
+        RewriteHandler handler = new RewriteHandler();
+        s.setQuery("[orth=laufe/i & base!=Lauf]", "poliqarp");
+        handler.add(new FoundryInject(config));
+        String result = handler.apply(s.toJSON(), null);
+        JsonNode node = JsonUtils.readTree(result);
+
+        assert node != null;
+        assert node.at("/query/wrap/@type").asText().equals("koral:termGroup");
+        assert !node.at("/query/wrap/operands/0/foundry").isMissingNode();
+        assert !node.at("/query/wrap/operands/0/rewrites").isMissingNode();
+        assert !node.at("/query/wrap/operands/1/foundry").isMissingNode();
+        assert !node.at("/query/wrap/operands/1/rewrites").isMissingNode();
+    }
+
+    @Test
+    public void testFoundryInjectGroupedQueryNoErrors() {
+        QuerySerializer s = new QuerySerializer();
+        RewriteHandler handler = new RewriteHandler();
+        s.setQuery("[(base=laufen | base=gehen) & tt/pos=VVFIN]", "poliqarp");
+        handler.add(new FoundryInject(config));
+        String result = handler.apply(s.toJSON(), null);
+        JsonNode node = JsonUtils.readTree(result);
+
+        assert node != null;
+        assert node.at("/query/wrap/@type").asText().equals("koral:termGroup");
+        assert !node.at("/query/wrap/operands/0/operands/0/foundry")
+                .isMissingNode();
+        assert !node.at("/query/wrap/operands/0/operands/0/rewrites")
+                .isMissingNode();
+        assert !node.at("/query/wrap/operands/0/operands/1/foundry")
+                .isMissingNode();
+        assert !node.at("/query/wrap/operands/0/operands/1/rewrites")
+                .isMissingNode();
+
+        assert !node.at("/query/wrap/operands/1/foundry").isMissingNode();
+        assert node.at("/query/wrap/operands/1/rewrites").isMissingNode();
+    }
+
+    @Test
+    public void testCollectionNodeRemoveCorpusIdNoErrors() {
+        RewriteHandler handler = new RewriteHandler();
+        handler.add(new CollectionConstraint());
+        QuerySerializer s = new QuerySerializer();
+        s.setQuery(simple_add_query, "poliqarp");
+        s.setCollection("textClass=politik & corpusID=WPD");
+        String result = s.toJSON();
+        JsonNode node = JsonUtils.readTree(handler.apply(result, null));
+        assert node != null;
+        assert node.at("/collection/operands").size() == 1;
+    }
+
+    @Test
+    public void testCollectionNodeRemoveAllCorpusIdNoErrors() {
+        RewriteHandler handler = new RewriteHandler();
+        handler.add(new CollectionConstraint());
+        QuerySerializer s = new QuerySerializer();
+        s.setQuery(simple_add_query, "poliqarp");
+        s.setCollection("corpusID=BRZ13 & corpusID=WPD");
+        String result = s.toJSON();
+        JsonNode node = JsonUtils.readTree(handler.apply(result, null));
+
+        assert node != null;
+        assert node.at("/collection/operands").size() == 0;
+    }
+
+    @Test
+    public void testCollectionNodeRemoveGroupedCorpusIdNoErrors() {
+        RewriteHandler handler = new RewriteHandler();
+        handler.add(new CollectionConstraint());
+        QuerySerializer s = new QuerySerializer();
+        s.setQuery(simple_add_query, "poliqarp");
+        s.setCollection(
+                "(corpusID=BRZ13 & textClass=Wissenschaft) & corpusID=WPD");
+        String result = s.toJSON();
+        JsonNode node = JsonUtils.readTree(handler.apply(result, null));
+
+        assert node != null;
+        assert node.at("/collection/operands/0/@type").asText()
+                .equals("koral:docGroup");
+        assert node.at("/collection/operands/0/operands/0/key").asText()
+                .equals("textClass");
+    }
+
+    //fixme: will probably fail when one doc groups are being refactored
+    @Test
+    public void testCollectionCleanEmptyDocGroupNoErrors() {
+        RewriteHandler handler = new RewriteHandler();
+        handler.add(new CollectionConstraint());
+        handler.add(new CollectionCleanupFilter());
+        QuerySerializer s = new QuerySerializer();
+        s.setQuery(simple_add_query, "poliqarp");
+        s.setCollection(
+                "(corpusID=BRZ13 & corpusID=WPD) & textClass=Wissenschaft & textClass=Sport");
+        String result = s.toJSON();
+        JsonNode node = JsonUtils.readTree(handler.apply(result, null));
+        assert node != null;
+        assert node.at("/collection/@type").asText().equals("koral:docGroup");
+        assert node.at("/collection/operands").size() == 2;
+        assert node.at("/collection/operands/0/key").asText()
+                .equals("textClass");
+        assert node.at("/collection/operands/1/key").asText()
+                .equals("textClass");
+    }
+
+    @Test
+    public void testCollectionCleanMoveOneDocFromGroupUpNoErrors() {
+        RewriteHandler handler = new RewriteHandler();
+        handler.add(new CollectionConstraint());
+        handler.add(new CollectionCleanupFilter());
+        QuerySerializer s = new QuerySerializer();
+        s.setQuery(simple_add_query, "poliqarp");
+        s.setCollection("(corpusID=BRZ13 & textClass=Wissenschaft)");
+        String result = s.toJSON();
+        JsonNode node = JsonUtils.readTree(handler.apply(result, null));
+        assert node != null;
+        assert node.at("/collection/@type").asText().equals("koral:doc");
+    }
+
+    @Test
+    public void testCollectionCleanEmptyGroupAndMoveOneDocFromGroupUpNoErrors() {
+        RewriteHandler handler = new RewriteHandler();
+        handler.add(new CollectionConstraint());
+        handler.add(new CollectionCleanupFilter());
+        QuerySerializer s = new QuerySerializer();
+        s.setQuery(simple_add_query, "poliqarp");
+        s.setCollection(
+                "(corpusID=BRZ13 & corpusID=WPD) & textClass=Wissenschaft");
+        String result = s.toJSON();
+        JsonNode node = JsonUtils.readTree(handler.apply(result, null));
+        assert node != null;
+        assert node.at("/collection/@type").asText().equals("koral:doc");
+        assert node.at("/collection/key").asText().equals("textClass");
+    }
+
+}
diff --git a/src/test/java/RewriteTest.java b/src/test/java/RewriteTest.java
deleted file mode 100644
index 92e61fd..0000000
--- a/src/test/java/RewriteTest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-import de.ids_mannheim.korap.config.BeanConfiguration;
-import de.ids_mannheim.korap.query.serialize.QuerySerializer;
-import de.ids_mannheim.korap.resource.LayerMapper;
-import de.ids_mannheim.korap.resource.RewriteProcessor;
-import de.ids_mannheim.korap.utils.CollectionQueryBuilder3;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-/**
- * @author hanl
- * @date 18/06/2015
- */
-public class RewriteTest {
-
-    private static String simple_add_query = "[pos=ADJA]";
-    private static String simple_rewrite_query = "[base=Haus]";
-    private static String complex_rewrite_query = "<c=INFC>";
-    private static String complex_rewrite_query2 = "[orth=laufe/i & base!=Lauf]";
-    private static String complex_rewrite_query3 = "[(base=laufen | base=gehen) & tt/pos=VVFIN]";
-
-    public RewriteTest() {
-
-    }
-
-    @BeforeClass
-    public static void init() {
-        BeanConfiguration.loadFileContext(
-                "/Users/hanl/Projects/KorAP-project/KorAP-modules/Kustvakt-core/src/main/resources/default-config.xml");
-    }
-
-    @Test
-    public void testQuery() {
-        QuerySerializer s = new QuerySerializer();
-        s.setQuery(simple_add_query, "poliqarp");
-        System.out.println("query " + s.toJSON());
-
-        CollectionQueryBuilder3 b = new CollectionQueryBuilder3();
-        b.add("textClass=politik & corpusID=WPD");
-        System.out.println("collection query " + b.toJSON());
-    }
-
-    @Test
-    public void testLayerMapper() {
-        LayerMapper m = new LayerMapper();
-        System.out.println("foundry " + m.findFoundry("lemma"));
-        System.out.println("foundry " + m.findFoundry("surface"));
-        System.out.println("foundry " + m.findFoundry("pos"));
-    }
-
-    @Test
-    public void testRewrite() {
-        RewriteProcessor processor = new RewriteProcessor();
-
-        QuerySerializer s = new QuerySerializer();
-        s.setQuery(complex_rewrite_query3, "poliqarp");
-        System.out.println("query " + s.toJSON());
-        System.out.println("finished node " + processor.process(s.toJSON()));
-    }
-}
diff --git a/src/test/java/de/ids_mannheim/korap/web/service/FastJerseyTest.java b/src/test/java/de/ids_mannheim/korap/web/service/FastJerseyTest.java
new file mode 100644
index 0000000..f3a0a04
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/web/service/FastJerseyTest.java
@@ -0,0 +1,118 @@
+package de.ids_mannheim.korap.web.service;
+
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.core.DefaultResourceConfig;
+import com.sun.jersey.spi.inject.SingletonTypeInjectableProvider;
+import com.sun.jersey.test.framework.AppDescriptor;
+import com.sun.jersey.test.framework.LowLevelAppDescriptor;
+import com.sun.jersey.test.framework.spi.container.TestContainer;
+import com.sun.jersey.test.framework.spi.container.TestContainerFactory;
+import com.sun.jersey.test.framework.spi.container.grizzly.GrizzlyTestContainerFactory;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriBuilder;
+import java.net.URI;
+
+/**
+ * @author hanl
+ * @date 29/07/2015
+ */
+public abstract class FastJerseyTest {
+
+    protected static String API_VERSION = "v0.1";
+
+    private static DefaultResourceConfig resourceConfig = new DefaultResourceConfig();
+
+    private static TestContainerFactory testContainerFactory;
+
+    private static TestContainer testContainer;
+
+    private static Client client;
+
+    public static void addClass(Class<?> resourceClass) {
+        resourceConfig.getClasses().add(resourceClass);
+    }
+
+    public static void addSingleton(Object resourceSingleton) {
+        resourceConfig.getSingletons().add(resourceSingleton);
+    }
+
+    public static <T> void addProviderForContext(Class<T> contextClass,
+            T contextObject) {
+        addSingleton(
+                new SingletonTypeInjectableProvider<Context, T>(contextClass,
+                        contextObject) {
+                });
+    }
+
+    public static void addRequestFilter(Object filter) {
+        resourceConfig.getContainerRequestFilters().add(filter);
+    }
+
+    public static void addResponseFilter(Object filter) {
+        resourceConfig.getContainerResponseFilters().add(filter);
+    }
+
+    public static void setTestContainerFactory(
+            TestContainerFactory newTestContainerFactory) {
+        testContainerFactory = newTestContainerFactory;
+    }
+
+    @BeforeClass
+    public static void cleanStaticVariables() {
+        resourceConfig = new DefaultResourceConfig();
+    }
+
+    public static void initServer() {
+        AppDescriptor ad = new LowLevelAppDescriptor.Builder(resourceConfig)
+                .build();
+        TestContainerFactory tcf = testContainerFactory;
+        if (tcf == null) {
+            tcf = new GrizzlyTestContainerFactory();
+        }
+        testContainer = tcf
+                .create(UriBuilder.fromUri("http://localhost/").port(9998)
+                        .build(), ad);
+        client = testContainer.getClient();
+        if (client == null) {
+            client = Client.create(ad.getClientConfig());
+        }
+    }
+
+    public static void startServer() {
+        if (testContainer != null) {
+            testContainer.start();
+        }
+    }
+
+    @AfterClass
+    public static void stopServer() {
+        testContainer.stop();
+        testContainer = null;
+        client = null;
+    }
+
+    public Client client() {
+        return client;
+    }
+
+    public URI getBaseUri() {
+        return testContainer.getBaseUri();
+    }
+
+    public WebResource resource() {
+        return client.resource(getBaseUri());
+    }
+
+    @Before
+    public void startServerBeforeFirstTestRun() {
+        if (testContainer == null) {
+            initServer();
+            startServer();
+        }
+    }
+}
diff --git a/src/test/java/de/ids_mannheim/korap/web/service/KustvaktCoreRestTest.java b/src/test/java/de/ids_mannheim/korap/web/service/KustvaktCoreRestTest.java
new file mode 100644
index 0000000..88ad2bf
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/web/service/KustvaktCoreRestTest.java
@@ -0,0 +1,80 @@
+package de.ids_mannheim.korap.web.service;
+
+import com.sun.jersey.api.client.ClientResponse;
+import de.ids_mannheim.korap.config.BeanConfiguration;
+import de.ids_mannheim.korap.query.serialize.CollectionQueryProcessor;
+import de.ids_mannheim.korap.utils.JsonUtils;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * @author hanl
+ * @date 26/06/2015
+ */
+public class KustvaktCoreRestTest extends FastJerseyTest {
+
+    @BeforeClass
+    public static void configure() {
+        BeanConfiguration.loadClasspathContext();
+        addClass(LightService.class);
+    }
+
+    @Test
+    public void testFieldsInSearch() {
+        ClientResponse response = resource().path(API_VERSION).path("search")
+                .queryParam("q", "[base=Wort]").queryParam("ql", "poliqarp")
+                .get(ClientResponse.class);
+        assert ClientResponse.Status.OK.getStatusCode() == response.getStatus();
+    }
+
+    @Test
+    public void testQuery() {
+        ClientResponse response = resource().path(API_VERSION).path("search")
+                .queryParam("q", "Sonne prox/unit=word/distance<=5 Erde")
+                .queryParam("ql", "CQL").get(ClientResponse.class);
+        assert ClientResponse.Status.OK.getStatusCode() == response.getStatus();
+    }
+
+    // in case no index is there, this will throw an error
+    @Ignore
+    @Test
+    public void testGetMatchInfoThrowsNoException() {
+        ClientResponse response = resource().path(API_VERSION)
+                .get(ClientResponse.class);
+    }
+
+    @Test
+    public void testGetStatsThrowsNoException() {
+        CollectionQueryProcessor pr = new CollectionQueryProcessor();
+        pr.process("corpusID=WPD & textClass=Sport");
+        Map map = new LinkedHashMap();
+        map.put("collection", pr.getRequestMap());
+        ClientResponse response = resource().path(API_VERSION).path("stats")
+                .post(ClientResponse.class, JsonUtils.toJSON(map));
+        assert ClientResponse.Status.OK.getStatusCode() == response.getStatus();
+    }
+
+    @Test
+    public void testBuildQueryThrowsNoException() {
+        ClientResponse response = resource().path(API_VERSION).path("search")
+                .queryParam("q", "[base=Haus & surface=Hauses]")
+                .queryParam("ql", "poliqarp").queryParam("cutOff", "true")
+                .queryParam("page", "1").method("TRACE", ClientResponse.class);
+        assert ClientResponse.Status.OK.getStatusCode() == response.getStatus();
+    }
+
+    @Test
+    public void testQueryByNameThrowsNoException() {
+        ClientResponse response = resource().path(API_VERSION).path("corpus")
+                .path("WPD").path("search")
+                .queryParam("q", "[base=Haus & surface=Hauses]")
+                .queryParam("ql", "poliqarp").queryParam("cutOff", "true")
+                .queryParam("page", "1").get(ClientResponse.class);
+        System.out.println("RESPONSE " + response);
+    }
+
+}