Merged core to full

Change-Id: I47cbb4c98e6a7d549c287c27419177bd08b72608
diff --git a/full/src/main/java/de/ids_mannheim/korap/annotation/AnnotationParser.java b/full/src/main/java/de/ids_mannheim/korap/annotation/AnnotationParser.java
new file mode 100644
index 0000000..ccb6d1a
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/annotation/AnnotationParser.java
@@ -0,0 +1,244 @@
+package de.ids_mannheim.korap.annotation;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.stereotype.Component;
+
+import de.ids_mannheim.korap.constant.AnnotationType;
+import de.ids_mannheim.korap.core.entity.Annotation;
+import de.ids_mannheim.korap.core.entity.AnnotationKey;
+import de.ids_mannheim.korap.core.entity.AnnotationLayer;
+import de.ids_mannheim.korap.dao.AnnotationDao;
+
+/** Parser for extracting annotation descriptions from Kalamar javascripts 
+ * 
+ * @author margaretha
+ *
+ */
+@Deprecated
+@Component
+public class AnnotationParser {
+
+    private Logger log = LogManager.getLogger(AnnotationDao.class);
+
+    public static final Pattern quotePattern = Pattern.compile("\"([^\"]*)\"");
+
+    @Autowired
+    private AnnotationDao annotationDao;
+
+    private Annotation foundry = null;
+    private AnnotationLayer layer = null;
+    private AnnotationKey key = null;
+
+    private Set<AnnotationKey> keys = new HashSet<>();
+    private Set<Annotation> values = new HashSet<>();
+
+    public void run () throws IOException {
+        PathMatchingResourcePatternResolver resolver =
+                new PathMatchingResourcePatternResolver(
+                        getClass().getClassLoader());
+        Resource[] resources = resolver
+                .getResources("classpath:annotation-scripts/foundries/*.js");
+
+        if (resources.length < 1) return;
+
+        for (Resource r : resources) {
+//            log.debug(r.getFilename());
+            readFile(r.getInputStream());
+        }
+    }
+
+    private void readFile (InputStream inputStream) throws IOException {
+        BufferedReader br =
+                new BufferedReader(new InputStreamReader(inputStream), 1024);
+
+        foundry = null;
+
+        String line, annotationCode = "", annotationType = "";
+        Matcher m;
+        ArrayList<String> array;
+        while ((line = br.readLine()) != null) {
+            line = line.trim();
+            if (line.startsWith("ah")) {
+                m = quotePattern.matcher(line);
+                if (m.find()) {
+                    annotationCode = m.group(1);
+                    annotationType = computeAnnotationType(annotationCode);
+                }
+                m.reset();
+            }
+            else if (line.startsWith("];")) {
+                if (!keys.isEmpty()) {
+                    layer.setKeys(keys);
+                    annotationDao.updateAnnotationLayer(layer);
+                }
+                if (!values.isEmpty()) {
+                    key.setValues(values);
+                    annotationDao.updateAnnotationKey(key);
+                }
+                keys.clear();
+                values.clear();
+                layer = null;
+                key = null;
+            }
+            else if (line.startsWith("[")) {
+                array = computeValues(line);
+                parseArray(annotationCode, annotationType, array);
+            }
+
+        }
+        br.close();
+    }
+
+    public static ArrayList<String> computeValues (String line) {
+        ArrayList<String> values;
+        Matcher m = quotePattern.matcher(line);
+        values = new ArrayList<String>();
+        while (m.find()) {
+            values.add(m.group(1));
+        }
+        return values;
+    }
+
+    private void parseArray (String annotationCode, String annotationType,
+            ArrayList<String> array) {
+        if (annotationType.equals(AnnotationType.FOUNDRY)) {
+            String code = array.get(1).substring(0, array.get(1).length() - 1);
+            foundry = retrieveOrCreateAnnotation(code, AnnotationType.FOUNDRY,
+                    null, array.get(0));
+        }
+        else if (annotationType.equals(AnnotationType.LAYER)) {
+            String code = array.get(1);
+            if (code.endsWith("=")) {
+                code = code.substring(0, code.length() - 1);
+            }
+            Annotation layer = retrieveOrCreateAnnotation(code, annotationType,
+                    null, array.get(0));
+            try {
+                AnnotationLayer annotationLayer =
+                        annotationDao.retrieveAnnotationLayer(foundry.getCode(),
+                                layer.getCode());
+                if (annotationLayer == null) {
+                    annotationDao.createAnnotationLayer(foundry, layer);
+                }
+            }
+            catch (Exception e) {
+                log.debug("Duplicate annotation layer: " + foundry.getCode()
+                        + "/" + layer.getCode());
+            }
+        }
+        else if (annotationType.equals(AnnotationType.KEY))
+
+        {
+            if (layer == null) {
+                computeLayer(annotationCode);
+            }
+
+            Annotation annotation = null;
+            if (array.size() == 2) {
+                String code = array.get(1);
+                if (code.endsWith("=") || code.endsWith(":")) {
+                    code = code.substring(0, code.length() - 1);
+                }
+                annotation = retrieveOrCreateAnnotation(code, annotationType,
+                        null, array.get(0));
+            }
+            else if (array.size() == 3) {
+                annotation = retrieveOrCreateAnnotation(array.get(0),
+                        annotationType, array.get(1), array.get(2));
+            }
+            if (annotation != null) {
+                AnnotationKey annotationKey =
+                        annotationDao.retrieveAnnotationKey(layer, annotation);
+                if (annotationKey == null) {
+                    annotationDao.createAnnotationKey(layer, annotation);
+                }
+                this.keys.add(annotationKey);
+            }
+        }
+        else if (annotationType.equals(AnnotationType.VALUE)) {
+            if (this.key == null) {
+                computeKey(annotationCode);
+            }
+            Annotation value = retrieveOrCreateAnnotation(array.get(0),
+                    AnnotationType.VALUE, array.get(1), array.get(2));
+            if (value != null) {
+                values.add(value);
+            }
+        }
+    }
+
+    private void computeKey (String code) {
+        String[] codes = code.split("=");
+        if (codes.length > 1) {
+            computeLayer(codes[0]);
+            String keyCode = codes[1];
+            if (keyCode.endsWith(":") || keyCode.endsWith("-")) {
+                keyCode = keyCode.substring(0, keyCode.length() - 1);
+            }
+            Annotation key = annotationDao.retrieveAnnotation(keyCode,
+                    AnnotationType.KEY);
+            this.key = annotationDao.retrieveAnnotationKey(layer, key);
+        }
+
+    }
+
+    private void computeLayer (String code) {
+        String[] codes = code.split("/");
+        if (codes.length > 1) {
+            String layerCode = codes[1];
+            if (layerCode.endsWith("=")) {
+                layerCode = layerCode.substring(0, layerCode.length() - 1);
+            }
+            this.layer =
+                    annotationDao.retrieveAnnotationLayer(codes[0], layerCode);
+            if (layer == null) {
+                log.warn("Layer is null for " + code);
+            }
+        }
+    }
+
+    private Annotation retrieveOrCreateAnnotation (String code, String type,
+            String text, String description) {
+        Annotation annotation = annotationDao.retrieveAnnotation(code, type);
+        if (annotation == null) {
+            annotation = annotationDao.createAnnotation(code, type, text,
+                    description);
+        }
+        return annotation;
+    }
+
+    private String computeAnnotationType (String code) {
+        String[] codes = code.split("/");
+        if (codes.length == 1) {
+            if (codes[0].equals("-")) {
+                return AnnotationType.FOUNDRY;
+            }
+            return AnnotationType.LAYER;
+        }
+        else if (codes.length == 2) {
+            if (codes[1].endsWith(":") || codes[1].endsWith("-")) {
+                return AnnotationType.VALUE;
+            }
+            else {
+                return AnnotationType.KEY;
+            }
+        }
+
+        return "unknown";
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/annotation/ArrayVariables.java b/full/src/main/java/de/ids_mannheim/korap/annotation/ArrayVariables.java
new file mode 100644
index 0000000..8740919
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/annotation/ArrayVariables.java
@@ -0,0 +1,95 @@
+package de.ids_mannheim.korap.annotation;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import de.ids_mannheim.korap.constant.AnnotationType;
+import de.ids_mannheim.korap.core.entity.Annotation;
+
+/**
+ * Helper class to parse annotation scripts variables. It prints out
+ * corenlp constituency layer for each negranodes.
+ * 
+ * @author margaretha
+ *
+ */
+@Deprecated
+public class ArrayVariables {
+
+    public static HashMap<String, List<Annotation>> annotationMap =
+            new HashMap<>();
+
+    public static void main (String[] args) throws IOException {
+        ArrayVariables variables = new ArrayVariables();
+        variables.extractVariables();
+
+        List<Annotation> negranodes = annotationMap.get("negranodes");
+        for (Annotation n : negranodes) {
+            System.out.println("ah[\"corenlp/c=" + n.getCode() + "-\"] = [");
+            int i = 1;
+            List<Annotation> negraedges = annotationMap.get("negraedges");
+            for (Annotation edge : negraedges) {
+                System.out.print(
+                        "  [\"" + edge.getCode() + "\", \"" + edge.getText()
+                                + "\", \"" + edge.getDescription() + "\"]");
+                if (i < negraedges.size()) {
+                    System.out.println(",");
+                }
+                else {
+                    System.out.println();
+                }
+                i++;
+            }
+            System.out.println("];");
+            System.out.println();
+        }
+    }
+
+    public void extractVariables () throws IOException {
+        String dir = "annotation-scripts/variables";
+        if (dir.isEmpty()) return;
+
+        File d = new File(dir);
+        if (!d.isDirectory()) {
+            throw new IOException("Directory " + dir + " is not valid");
+        }
+
+        for (File file : d.listFiles()) {
+            if (!file.exists()) {
+                throw new IOException("File " + file + " is not found.");
+            }
+            readFile(file);
+        }
+
+    }
+
+    private void readFile (File file) throws IOException {
+        BufferedReader br = new BufferedReader(
+                new InputStreamReader(new FileInputStream(file)));
+
+        String line;
+        ArrayList<String> values;
+        List<Annotation> annotations = new ArrayList<>();
+        while ((line = br.readLine()) != null) {
+            line = line.trim();
+            if (line.startsWith("[")) {
+                values = AnnotationParser.computeValues(line);
+
+                Annotation annotation = new Annotation(values.get(0),
+                        AnnotationType.VALUE, values.get(1), values.get(2));
+                annotations.add(annotation);
+            }
+        }
+        br.close();
+
+        String filename = file.getName();
+        filename = filename.substring(0, filename.length() - 3);
+        annotationMap.put(filename, annotations);
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/auditing/AuditRecord.java b/full/src/main/java/de/ids_mannheim/korap/auditing/AuditRecord.java
new file mode 100644
index 0000000..5370c9f
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/auditing/AuditRecord.java
@@ -0,0 +1,173 @@
+package de.ids_mannheim.korap.auditing;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.databind.JsonNode;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.utils.JsonUtils;
+import de.ids_mannheim.korap.utils.TimeUtils;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Arrays;
+import java.util.Date;
+
+/**
+ * @author hanl
+ *         <p/>
+ *         Record holder for auditing requests. Holds the data until
+ *         it can be persisted to a database
+ */
+@Getter
+@Setter
+public class AuditRecord {
+
+    // todo: handle via status codes
+    @Deprecated
+    public enum Operation {
+        GET, INSERT, UPDATE, DELETE, CREATE
+    }
+
+    public enum CATEGORY {
+        SECURITY, DATABASE, RESOURCE, QUERY, SERVICE
+    }
+
+    @JsonIgnore
+    private Integer id;
+    //security access describes changes in user authorities and access control permissions of resources
+    private String userid;
+    private String target;
+
+    //fixme: replace with more specific error codes
+    private CATEGORY category;
+    private String loc;
+    private Long timestamp;
+    private Integer status = -1;
+    private String args;
+    private String field_1 = "None";
+
+
+    private AuditRecord () {
+        this.timestamp = TimeUtils.getNow().getMillis();
+    }
+
+
+    public AuditRecord (CATEGORY category) {
+        this();
+        this.category = category;
+    }
+
+
+    public AuditRecord (CATEGORY cat, Object userID, Integer status) {
+        this(cat);
+        this.status = status;
+        if (userID != null) {
+            //todo: client info!
+            //            this.loc = clientInfoToString(user.getTokenContext().getHostAddress(),
+            //                    user.getTokenContext().getUserAgent());
+            this.loc = clientInfoToString("null", "null");
+            userid = String.valueOf(userID);
+        }
+        else {
+            this.loc = clientInfoToString("null", "null");
+            userid = "-1";
+        }
+    }
+
+
+    public static AuditRecord serviceRecord (Object user, Integer status,
+            String ... args) {
+        AuditRecord r = new AuditRecord(CATEGORY.SERVICE);
+        r.setArgs(Arrays.asList(args).toString());
+        r.setUserid(String.valueOf(user));
+        r.setStatus(status);
+        return r;
+    }
+
+
+    public static AuditRecord dbRecord (Object user, Integer status,
+            String ... args) {
+        AuditRecord r = new AuditRecord(CATEGORY.DATABASE);
+        r.setArgs(Arrays.asList(args).toString());
+        r.setUserid(String.valueOf(user));
+        r.setStatus(status);
+        return r;
+    }
+
+
+    public AuditRecord fromJson (String json) throws KustvaktException {
+        JsonNode n = JsonUtils.readTree(json);
+        AuditRecord r = new AuditRecord();
+        r.setCategory(CATEGORY.valueOf(n.path("category").asText()));
+        r.setTarget(n.path("target").asText());
+        r.setField_1(n.path("field_1").asText());
+        r.setUserid(n.path("account").asText());
+        r.setStatus(n.path("status").asInt());
+        r.setLoc(n.path("loc").asText());
+        return r;
+    }
+
+
+    private String clientInfoToString (String IP, String userAgent) {
+        return userAgent + "@" + IP;
+    }
+
+
+    // fixme: add id, useragent
+    @Override
+    public String toString () {
+        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
+    public boolean equals (Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        AuditRecord that = (AuditRecord) o;
+
+        if (userid != null ? !userid.equals(that.userid) : that.userid != null)
+            return false;
+        if (category != that.category)
+            return false;
+        if (status != null ? !status.equals(that.status) : that.status != null)
+            return false;
+        if (field_1 != null ? !field_1.equals(that.field_1)
+                : that.field_1 != null)
+            return false;
+        if (loc != null ? !loc.equals(that.loc) : that.loc != null)
+            return false;
+        if (target != null ? !target.equals(that.target) : that.target != null)
+            return false;
+        if (timestamp != null ? !timestamp.equals(that.timestamp)
+                : that.timestamp != null)
+            return false;
+
+        return true;
+    }
+
+
+    @Override
+    public int hashCode () {
+        int result = userid != null ? userid.hashCode() : 0;
+        result = 31 * result + (target != null ? target.hashCode() : 0);
+        result = 31 * result + category.hashCode();
+        result = 31 * result + (loc != null ? loc.hashCode() : 0);
+        result = 31 * result + (timestamp != null ? timestamp.hashCode() : 0);
+        result = 31 * result + (status != null ? status.hashCode() : 0);
+        result = 31 * result + (field_1 != null ? field_1.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/authentication/AuthenticationIface.java b/full/src/main/java/de/ids_mannheim/korap/authentication/AuthenticationIface.java
new file mode 100644
index 0000000..47d0738
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/authentication/AuthenticationIface.java
@@ -0,0 +1,27 @@
+package de.ids_mannheim.korap.authentication;
+
+import java.util.Map;
+
+import de.ids_mannheim.korap.constant.TokenType;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.security.context.TokenContext;
+import de.ids_mannheim.korap.user.User;
+
+public interface AuthenticationIface {
+
+    public TokenContext getTokenContext(String authToken) throws KustvaktException;
+
+
+    public TokenContext createTokenContext(User user, Map<String, Object> attr)
+            throws KustvaktException;
+
+
+    void removeUserSession (String token) throws KustvaktException;
+
+
+    public TokenContext refresh (TokenContext context) throws KustvaktException;
+
+
+    public TokenType getTokenType ();
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/authentication/AuthenticationManager.java b/full/src/main/java/de/ids_mannheim/korap/authentication/AuthenticationManager.java
new file mode 100644
index 0000000..2fcc4e9
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/authentication/AuthenticationManager.java
@@ -0,0 +1,96 @@
+package de.ids_mannheim.korap.authentication;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.ws.rs.core.HttpHeaders;
+
+import de.ids_mannheim.korap.config.KustvaktCacheable;
+import de.ids_mannheim.korap.constant.AuthenticationMethod;
+import de.ids_mannheim.korap.constant.TokenType;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.security.context.TokenContext;
+import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.user.Userdata;
+
+/**
+ * @author hanl
+ * @date 15/06/2015
+ */
+public abstract class AuthenticationManager extends KustvaktCacheable {
+
+    private Map<TokenType, AuthenticationIface> providers;
+
+    public AuthenticationManager () {
+        super();
+    }
+    
+    public AuthenticationManager (String cache) {
+        super(cache, "key:"+cache);
+        this.providers = new HashMap<>();
+    }
+
+    public void setProviders (Set<AuthenticationIface> providers) {
+        for (AuthenticationIface i : providers) {
+            this.providers.put(i.getTokenType(), i);
+        }
+    }
+
+    protected AuthenticationIface getProvider (TokenType scheme,
+            TokenType default_iface) {
+
+        // Debug FB: loop a Map
+
+        /*for (Map.Entry<String, AuthenticationIface> entry : this.providers.entrySet()) 
+        {
+        System.out.println("Debug: provider: Key : " + entry.getKey() + " Value : " + entry.getValue());
+        }
+        */
+        // todo: configurable authentication schema
+        if (scheme == null) {
+            return this.providers.get(default_iface);
+        }
+        else {
+            return this.providers.get(scheme);
+        }
+    }
+
+    public abstract TokenContext getTokenContext (TokenType type, String token,
+            String host, String useragent) throws KustvaktException;
+
+    public abstract User getUser (String username) throws KustvaktException;
+
+    public abstract boolean isRegistered (String id);
+
+    public abstract User authenticate (AuthenticationMethod method,
+            String username, String password, Map<String, Object> attributes)
+            throws KustvaktException;
+
+    public abstract TokenContext createTokenContext (User user,
+            Map<String, Object> attr, TokenType type) throws KustvaktException;
+
+    public abstract void setAccessAndLocation (User user, HttpHeaders headers);
+
+    public abstract void logout (TokenContext context) throws KustvaktException;
+
+    public abstract void lockAccount (User user) throws KustvaktException;
+
+    public abstract boolean deleteAccount (User user) throws KustvaktException;
+
+    @Deprecated
+    public abstract <T extends Userdata> T getUserData (User user,
+            Class<T> clazz) throws KustvaktException;
+
+    @Deprecated
+    public abstract void updateUserData (Userdata data)
+            throws KustvaktException;
+
+    public String providerList () {
+        return "provider list: " + this.providers.toString();
+    }
+
+    public abstract User getUser (String username, String method)
+            throws KustvaktException;
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/authentication/http/AuthorizationData.java b/full/src/main/java/de/ids_mannheim/korap/authentication/http/AuthorizationData.java
new file mode 100644
index 0000000..236f26a
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/authentication/http/AuthorizationData.java
@@ -0,0 +1,22 @@
+package de.ids_mannheim.korap.authentication.http;
+
+import de.ids_mannheim.korap.constant.AuthenticationScheme;
+import lombok.Getter;
+import lombok.Setter;
+
+/** Describes the values stored in Authorization header of HTTP requests. 
+ * 
+ * @author margaretha
+ *
+ */
+@Getter
+@Setter
+public class AuthorizationData {
+
+    private String token;
+    private AuthenticationScheme authenticationScheme;
+    private String username;
+    private String password;
+
+}
+
diff --git a/full/src/main/java/de/ids_mannheim/korap/authentication/http/HttpAuthorizationHandler.java b/full/src/main/java/de/ids_mannheim/korap/authentication/http/HttpAuthorizationHandler.java
new file mode 100644
index 0000000..241e9ce
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/authentication/http/HttpAuthorizationHandler.java
@@ -0,0 +1,66 @@
+package de.ids_mannheim.korap.authentication.http;
+
+import org.springframework.stereotype.Component;
+
+import de.ids_mannheim.korap.constant.AuthenticationScheme;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.utils.ParameterChecker;
+
+/**
+ * Implementation of Basic HTTP authentication scheme (see RFC 7253
+ * and 7617) for client asking for authorization and sending user
+ * data.
+ * 
+ * @author margaretha
+ * 
+ */
+@Component
+public class HttpAuthorizationHandler {
+
+    public static String createBasicAuthorizationHeaderValue (String username,
+            String password) throws KustvaktException {
+        ParameterChecker.checkStringValue(username, "username");
+        ParameterChecker.checkStringValue(password, "password");
+
+        String credentials = TransferEncoding.encodeBase64(username, password);
+        return AuthenticationScheme.BASIC.displayName() + " " + credentials;
+    }
+
+    public AuthorizationData parseAuthorizationHeaderValue (
+            String authorizationHeader) throws KustvaktException {
+        ParameterChecker.checkStringValue(authorizationHeader,
+                "authorization header");
+
+        String[] values = authorizationHeader.split(" ");
+        if (values.length != 2) {
+            throw new KustvaktException(StatusCodes.AUTHENTICATION_FAILED,
+                    "Cannot parse authorization header value "
+                            + authorizationHeader
+                            + ". Use this format: [authentication "
+                            + "scheme] [authentication token]",
+                    authorizationHeader);
+        }
+
+        AuthorizationData data = new AuthorizationData();
+        String scheme = values[0];
+        try {
+            data.setAuthenticationScheme(
+                    AuthenticationScheme.valueOf(scheme.toUpperCase()));
+        }
+        catch (IllegalArgumentException e) {
+            throw new KustvaktException(StatusCodes.AUTHENTICATION_FAILED,
+                    "Authentication scheme is not supported.", scheme);
+        }
+        data.setToken(values[1]);
+        return data;
+    }
+
+    public AuthorizationData parseBasicToken (AuthorizationData data)
+            throws KustvaktException {
+        String[] credentials = TransferEncoding.decodeBase64(data.getToken());
+        data.setUsername(credentials[0]);
+        data.setPassword(credentials[1]);
+        return data;
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/authentication/http/TransferEncoding.java b/full/src/main/java/de/ids_mannheim/korap/authentication/http/TransferEncoding.java
new file mode 100644
index 0000000..52d1a90
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/authentication/http/TransferEncoding.java
@@ -0,0 +1,53 @@
+package de.ids_mannheim.korap.authentication.http;
+
+import org.apache.commons.codec.binary.Base64;
+import org.springframework.stereotype.Component;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.utils.ParameterChecker;
+
+/** TransferEncoding contains encoding and decoding methods for data transfer, 
+ *  e.g. transfering credentials using basic Http authentication.  
+ *   
+ * @author margaretha
+ *
+ */
+@Component
+public class TransferEncoding {
+
+    /** Encodes username and password using Base64.
+     * 
+     * @param username username
+     * @param password password
+     * @return
+     */
+    public static String encodeBase64 (String username, String password) {
+        String s = username + ":" + password;
+        return new String(Base64.encodeBase64(s.getBytes()));
+    }
+
+    /** Decodes the given string using Base64.
+     * 
+     * @param encodedStr 
+     * @return username and password as an array of strings.
+     * @throws KustvaktException 
+     */
+    public static String[] decodeBase64 (String encodedStr)
+            throws KustvaktException {
+
+        ParameterChecker.checkStringValue(encodedStr, "encoded string");
+        String decodedStr = new String(Base64.decodeBase64(encodedStr));
+
+        if (decodedStr.contains(":") && decodedStr.split(":").length == 2) {
+            String[] strArr = decodedStr.split(":");
+            if ((strArr[0] != null && !strArr[0].isEmpty())
+                    && (strArr[1] != null && !strArr[1].isEmpty())) {
+                return decodedStr.split(":");
+            }
+
+        }
+
+        throw new IllegalArgumentException(
+                "Unknown Base64 encoding format: " + decodedStr);
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/Attributes.java b/full/src/main/java/de/ids_mannheim/korap/config/Attributes.java
new file mode 100644
index 0000000..a3e2433
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/Attributes.java
@@ -0,0 +1,181 @@
+package de.ids_mannheim.korap.config;
+
+public class Attributes {
+
+    // EM: openid auth_time
+    public static final String AUTHENTICATION_TIME = "auth_time";
+    public static final String DEFAULT_TIME_ZONE = "Europe/Berlin";
+    // -- EM
+    
+    public static final String AUTHORIZATION = "Authorization";
+    // moved to de.ids_mannheim.korap.config.AuthenticationScheme
+//    public static final String SESSION_AUTHENTICATION = "session_token";
+//    public static final String API_AUTHENTICATION = "api_token";
+//    public static final String OAUTH2_AUTHORIZATION = "bearer";
+//    public static final String OPENID_AUTHENTICATION = "id_token";
+//    public static final String BASIC_AUTHENTICATION = "basic";
+
+    public static final String LOCATION = "location"; // location of Client: User.INTERN/EXTERN
+    public static final String CORPUS_ACCESS = "corpusAccess"; // User.ALL/PUB/FREE.
+    
+    public static final String CLIENT_ID = "client_id";
+    public static final String CLIENT_SECRET = "client_secret";
+    public static final String SCOPE = "scope";
+
+    public static final String PUBLIC_GROUP = "public";
+
+    public static final String SERVICE_ACCESS = "service_access";
+    public static final String USER = "KorapUser";
+    public static final String SHIBUSER = "ShibUser";
+    public static final String DEMO_DISPLAY = "Anonymous";
+    public static final String DEMOUSER_PASSWORD = "demo";
+
+    public static final String SETTINGS = "LocalSettings";
+    //    public static final String STORAGE_SETTINGS = "StorageSettings";
+
+    public static final String QUERY_ABBREVIATION = "Q";
+    public static final String LAYER = "layer";
+
+    public static final String TYPE = "type";
+
+    public static final String ID = "ID";
+    @Deprecated
+    //refactor
+    public static final String UID = "accountID";
+    public static final String USERNAME = "username";
+    public static final String PASSWORD = "password";
+    public static final String GENDER = "gender";
+    public static final String FIRSTNAME = "firstName";
+    public static final String LASTNAME = "lastName";
+    public static final String PHONE = "phone";
+    public static final String INSTITUTION = "institution";
+    public static final String EMAIL = "email";
+    public static final String ADDRESS = "address";
+    public static final String COUNTRY = "country";
+    public static final String IPADDRESS = "ipaddress";
+    public static final String IS_ADMIN = "admin";
+    // deprecated, use created
+    public static final String ACCOUNT_CREATION = "account_creation";
+    public static final String ACCOUNTLOCK = "account_lock";
+    public static final String ACCOUNTLINK = "account_link";
+    public static final String URI = "uri";
+    public static final String URI_FRAGMENT = "uri_fragment";
+    public static final String URI_EXPIRATION = "uri_expiration";
+    public static final String PRIVATE_USAGE = "privateUsage";
+
+    /**
+     * token context
+     */
+    public static final String TOKEN = "token";
+    public static final String TOKEN_TYPE = "token_type";
+    public static final String TOKEN_EXPIRATION = "expires";
+    public static final String TOKEN_CREATION = "tokenCreated";
+    public static final String USER_AGENT = "User-Agent";
+    public static final String HOST = "userIP";
+
+    public static final String QUERY_PARAM_URI = "uri";
+    public static final String QUERY_PARAM_USER = "user";
+
+    /**
+     * shibboleth attribute names
+     */
+    public static final String EPPN = "eppn";
+    public static final String COMMON_NAME = "cn";
+    public static final String SURNAME = "sn";
+
+    public static final String EDUPERSON = "eduPersonPrincipalName";
+    public static final String CN = "cn";
+    public static final String MAIL = "mail";
+    public static final String EDU_AFFIL = "eduPersonScopedAffiliation";
+
+    /**
+     * resource mappings
+     */
+
+    public static final String RID = "id";
+    public static final String OWNER = "owner";
+    public static final String NAME = "name";
+    public static final String DESCRIPTION = "description";
+
+    public static final String CORPUS_SIGLE = "corpusSigle";
+    public static final String DOC_SIGLE = "docSigle";
+    public static final String TEXT_SIGLE = "textSigle";
+
+    public static final String AVAILABILITY = "availability";
+    
+    public static final String REF_CORPUS = "refCorpus";
+    public static final String QUERY = "query";
+    public static final String CACHE = "cache";
+    public static final String DOCIDS = "docIDs";
+    public static final String FOUNDRIES = "foundries";
+    public static final String DEFAULT_VALUE = "defaultColl";
+
+    public static final String FILE_FORMAT_FOR_EXPORT = "fileFormatForExport";
+    public static final String FILENAME_FOR_EXPORT = "fileNameForExport";
+    @Deprecated
+    public static final String ITEM_FOR_SIMPLE_ANNOTATION = "itemForSimpleAnnotation";
+    public static final String LEFT_CONTEXT_ITEM_FOR_EXPORT = "leftContextItemForExport";
+    public static final String LEFT_CONTEXT_SIZE_FOR_EXPORT = "leftContextSizeForExport";
+    public static final String LOCALE = "locale";
+    public static final String LEFT_CONTEXT_ITEM = "leftContextItem";
+    public static final String LEFT_CONTEXT_SIZE = "leftContextSize";
+    public static final String RIGHT_CONTEXT_ITEM = "rightContextItem";
+    public static final String RIGHT_CONTEXT_ITEM_FOR_EXPORT = "rightContextItemForExport";
+    public static final String RIGHT_CONTEXT_SIZE = "rightContextSize";
+    public static final String RIGHT_CONTEXT_SIZE_FOR_EXPORT = "rightContextSizeForExport";
+    public static final String SELECTED_COLLECTION = "selectedCollection";
+    public static final String QUERY_LANGUAGE = "queryLanguage";
+    public static final String PAGE_LENGTH = "pageLength";
+    public static final String METADATA_QUERY_EXPERT_MODUS = "metadataQueryExpertModus";
+    @Deprecated
+    public static final String SEARCH_SETTINGS_TAB = "searchSettingsTab";
+    @Deprecated
+    public static final String SELECTED_BROWSER_PROPERTY = "selectedBrowserProperty";
+    @Deprecated
+    public static final String SELECTED_CONTEXT_ITEM = "selectedContextItem";
+    @Deprecated
+    public static final String SELECTED_GRAPH_TYPE = "selectedGraphType";
+    @Deprecated
+    public static final String SELECTED_SORT_TYPE = "selectedSortType";
+    @Deprecated
+    public static final String SELECTED_VIEW_FOR_SEARCH_RESULTS = "selectedViewForSearchResults";
+    public static final String COLLECT_AUDITING_DATA = "collectData";
+
+    /**
+     * default layers
+     */
+    public static final String DEFAULT_FOUNDRY_POS = "pos-foundry";
+    public static final String DEFAULT_FOUNDRY_LEMMA = "lemma-foundry";
+    public static final String DEFAULT_FOUNDRY_CONSTITUENT = "constituent-foundry";
+    public static final String DEFAULT_FOUNDRY_RELATION = "relation-foundry";
+    public static final String DEFAULT_FOUNDRY_MORPHOLOGY = "morphology-foundry";
+    public static final String DEFAULT_FOUNDRY_STRUCTURE = "structure-foundry";
+    
+    /**
+     * db column keys
+     */
+
+    public static final String SELF_REF = "self";
+
+    public static final String SYM_USE = "sym_use";
+    public static final String COMMERCIAL = "commercial";
+    public static final String LICENCE = "licence";
+    public static final String QUERY_ONLY = "query_only";
+    public static final String EXPORT = "export";
+    public static final String TIME_SPANS = "spans";
+    public static final String RANGE = "range";
+
+    public static final String GROUP_ID = "group_id";
+    public static final String CREATED = "created";
+    public static final String CREATOR = "creator";
+    public static final String ENABLED = "enabled";
+    public static final String EXPIRE = "expired";
+    public static final String TARGET_ID = "target_id";
+    public static final String IP_RANG = "ip_range";
+    public static final String PERSISTENT_ID = "persistent_id";
+    public static final String DISABLED = "disabled";
+    public static final String USER_ID = "user_id";
+    public static final String PARENT_ID = "parent_id";
+    //    public static final String
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/BeanInjectable.java b/full/src/main/java/de/ids_mannheim/korap/config/BeanInjectable.java
new file mode 100644
index 0000000..54f432e
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/BeanInjectable.java
@@ -0,0 +1,10 @@
+package de.ids_mannheim.korap.config;
+
+/**
+ * @author hanl
+ * @date 26/02/2016
+ */
+public interface BeanInjectable {
+
+    <T extends ContextHolder> void insertBeans (T beans);
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/BeansFactory.java b/full/src/main/java/de/ids_mannheim/korap/config/BeansFactory.java
new file mode 100644
index 0000000..46d319e
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/BeansFactory.java
@@ -0,0 +1,136 @@
+package de.ids_mannheim.korap.config;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Collection;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.context.support.FileSystemXmlApplicationContext;
+
+import de.ids_mannheim.korap.interfaces.KustvaktTypeInterface;
+import de.ids_mannheim.korap.web.CoreResponseHandler;
+
+/**
+ * User: hanl
+ * Date: 10/9/13
+ * Time: 11:20 AM
+ */
+public class BeansFactory {
+
+    private static ContextHolder beanHolder;
+
+
+    //todo: allow this for external plugin systems that are not kustvakt specific
+    @Deprecated
+    public static void setCustomBeansHolder (ContextHolder holder) {
+        beanHolder = holder;
+    }
+
+
+    public static synchronized ContextHolder getKustvaktContext () {
+        return beanHolder;
+    }
+
+
+    public static synchronized ContextHolder getKustvaktContext (int i) {
+        return beanHolder;
+    }
+
+
+    public static synchronized TypeBeanFactory getTypeFactory () {
+        return new TypeBeanFactory();
+    }
+
+
+    public static int loadClasspathContext (String ... files) {
+        ApplicationContext context;
+        if (files.length == 0)
+            throw new IllegalArgumentException("Spring XML config file is not specified.");
+        else
+            context = new ClassPathXmlApplicationContext(files);
+        ContextHolder h = new ContextHolder(context) {};
+        BeansFactory.beanHolder = h;
+        //        return BeansFactory.beanHolder.indexOf(h);
+        return 0;
+    }
+
+
+    public static synchronized int addApplicationContext (
+            ApplicationContext context) {
+        ContextHolder h = new ContextHolder(context) {};
+        BeansFactory.beanHolder = h;
+        //        return BeansFactory.beanHolder.indexOf(h);
+        return 0;
+    }
+
+
+    public static synchronized void setKustvaktContext (ContextHolder holder) {
+        BeansFactory.beanHolder = holder;
+    }
+
+
+    public static synchronized int setApplicationContext (
+            ApplicationContext context) {
+        ContextHolder h = new ContextHolder(context) {};
+        BeansFactory.beanHolder = h;
+        return 0;
+    }
+
+
+    public static synchronized int loadFileContext (String filepath) {
+        ApplicationContext context = new FileSystemXmlApplicationContext(
+                "file:" + filepath);
+        ContextHolder h = new ContextHolder(context) {};
+        BeansFactory.beanHolder = h;
+        return 0;
+    }
+
+
+    public static void closeApplication () {
+        BeansFactory.beanHolder = null;
+    }
+
+
+    //todo: set response handler
+    @Deprecated
+    public static CoreResponseHandler getResponseHandler () {
+        return null;
+    }
+
+
+    public BeansFactory () {}
+
+    public static class TypeBeanFactory {
+
+        public <T> T getTypeInterfaceBean (Collection objs, Class type) {
+            for (Object o : objs) {
+                if (o instanceof KustvaktTypeInterface) {
+                    Class t = ((KustvaktTypeInterface) o).type();
+                    if (type.equals(t))
+                        return (T) o;
+                }
+            }
+            throw new RuntimeException(
+                    "Could not find typed bean in context for class '" + type
+                            + "'");
+        }
+
+
+        @Deprecated
+        public <T> T getTypedBean (Collection objs, Class type) {
+            for (Object o : objs) {
+                Type gtype = o.getClass().getGenericSuperclass();
+                if (gtype instanceof ParameterizedType) {
+                    ParameterizedType ptype = (ParameterizedType) gtype;
+                    Object ctype = ptype.getActualTypeArguments()[0];
+                    if (ctype.equals(type))
+                        return (T) o;
+                }
+            }
+            throw new RuntimeException(
+                    "Could not find typed bean in context for class '" + type
+                            + "'");
+        }
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/ConfigLoader.java b/full/src/main/java/de/ids_mannheim/korap/config/ConfigLoader.java
new file mode 100644
index 0000000..9fcc3bd
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/ConfigLoader.java
@@ -0,0 +1,57 @@
+package de.ids_mannheim.korap.config;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+/**
+ * Created by hanl on 08.06.16.
+ */
+public class ConfigLoader {
+
+    private static final Logger jlog = LogManager.getLogger(ConfigLoader.class);
+
+
+    private ConfigLoader () {}
+
+
+    public static InputStream loadConfigStream (String name) {
+        InputStream stream = null;
+        try {
+            File f = new File(System.getProperty("user.dir"), name);
+
+            if (f.exists()) {
+                jlog.info("Loading config '" + name + "' from file!");
+                stream = new FileInputStream(f);
+            }
+            else {
+                jlog.info("Loading config '" + name + "' from classpath!");
+                stream = ConfigLoader.class.getClassLoader().getResourceAsStream(
+                        name);
+            }
+        }
+        catch (IOException e) {
+            // do nothing
+        }
+        if (stream == null)
+            throw new RuntimeException("Config file '"+name+"' could not be loaded ...");
+        return stream;
+    }
+
+
+    public static Properties loadProperties (String name){
+        Properties p = new Properties();
+        try {
+            p.load(loadConfigStream(name));
+        } catch (IOException e) {
+            throw new RuntimeException("Properties from config file '"+name+"' could not be loaded ...");
+        }
+        return p;
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/Configurable.java b/full/src/main/java/de/ids_mannheim/korap/config/Configurable.java
new file mode 100644
index 0000000..af656a2
--- /dev/null
+++ b/full/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/full/src/main/java/de/ids_mannheim/korap/config/ContextHolder.java b/full/src/main/java/de/ids_mannheim/korap/config/ContextHolder.java
new file mode 100644
index 0000000..99c54ae
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/ContextHolder.java
@@ -0,0 +1,98 @@
+package de.ids_mannheim.korap.config;
+
+import java.util.Collection;
+
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.context.ApplicationContext;
+
+import de.ids_mannheim.korap.interfaces.db.AuditingIface;
+import de.ids_mannheim.korap.interfaces.db.PersistenceClient;
+import de.ids_mannheim.korap.interfaces.db.UserDataDbIface;
+import de.ids_mannheim.korap.web.CoreResponseHandler;
+
+/**
+ * @author hanl
+ * @date 26/02/2016
+ */
+public abstract class ContextHolder {
+
+    public static final String KUSTVAKT_DB = "kustvakt_db";
+    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 final String KUSTVAKT_USERDATA = "kustvakt_userdata";
+    public static final String KUSTVAKT_RESOURCES = "kustvakt_resources";
+
+    public static final String KUSTVAKT_AUTHENTICATION_MANAGER = "kustvakt_authenticationmanager";
+    public static final String KUSTVAKT_AUTHPROVIDERS = "kustvakt_authproviders";
+    public static final String KUSTVAKT_USERDB = "kustvakt_userdb";
+    public static final String KUSTVAKT_ADMINDB = "kustvakt_admindb";
+    public static final String KUSTVAKT_POLICIES = "kustvakt_policies";
+
+    private ApplicationContext context = null;
+    private DefaultHandler handler;
+
+
+    public ContextHolder (ApplicationContext context) {
+        this.handler = new DefaultHandler();
+        this.context = context;
+        // todo: better method?!
+        new CoreResponseHandler(getAuditingProvider());
+    }
+
+
+    protected <T> T getBean (Class<T> clazz) {
+        if (this.context != null) {
+            try {
+                return context.getBean(clazz);
+            }
+            catch (NoSuchBeanDefinitionException e) {
+                // do nothing
+            }
+        }
+        return this.handler.getDefault(clazz);
+    }
+
+
+    protected <T> T getBean (String name) {
+        T bean = null;
+        if (this.context != null) {
+            try {
+                bean = (T) context.getBean(name);
+            }
+            catch (NoSuchBeanDefinitionException e) {
+                // do nothing
+                bean = (T) this.handler.getDefault(name);
+            }
+        }
+
+        return bean;
+    }
+
+
+    public AuditingIface getAuditingProvider () {
+        return (AuditingIface) getBean(KUSTVAKT_AUDITING);
+    }
+
+
+    @Deprecated
+    public <T extends KustvaktConfiguration> T getConfiguration () {
+        return (T) getBean(KUSTVAKT_CONFIG);
+    }
+
+
+    @Deprecated
+    public PersistenceClient getPersistenceClient () {
+        return getBean(KUSTVAKT_DB);
+    }
+
+    @Deprecated
+    public Collection<UserDataDbIface> getUserDataProviders () {
+        return getBean(KUSTVAKT_USERDATA);
+    }
+
+    private void close () {
+        this.getAuditingProvider().finish();
+        this.context = null;
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/DefaultHandler.java b/full/src/main/java/de/ids_mannheim/korap/config/DefaultHandler.java
new file mode 100644
index 0000000..982985f
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/DefaultHandler.java
@@ -0,0 +1,61 @@
+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/full/src/main/java/de/ids_mannheim/korap/config/KustvaktCacheable.java b/full/src/main/java/de/ids_mannheim/korap/config/KustvaktCacheable.java
new file mode 100644
index 0000000..b22cb70
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/KustvaktCacheable.java
@@ -0,0 +1,118 @@
+package de.ids_mannheim.korap.config;
+
+import java.io.InputStream;
+import java.util.Map;
+
+import de.ids_mannheim.korap.utils.ServiceInfo;
+import de.ids_mannheim.korap.utils.StringUtils;
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.CacheManager;
+import net.sf.ehcache.Element;
+import net.sf.ehcache.config.CacheConfiguration;
+import net.sf.ehcache.config.PersistenceConfiguration;
+import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
+
+/**
+ * @author hanl
+ * @date 03/02/2016
+ */
+public abstract class KustvaktCacheable {
+
+    private static boolean loaded = false;
+    private String prefix;
+    private String name;
+
+    public KustvaktCacheable(String cache_name, String prefix) {
+        init();
+        if(!enabled())
+            createDefaultFileCache(cache_name);
+        this.prefix = prefix;
+        this.name = cache_name;
+    }
+    
+    public KustvaktCacheable () {
+        // TODO Auto-generated constructor stub
+    }
+
+    private static Cache getCache(String name) {
+        return CacheManager.getInstance().getCache(name);
+    }
+
+
+    private void createDefaultFileCache(String name) {
+        Cache default_cache = new Cache(
+                new CacheConfiguration(name, 20000)
+                        .memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.LFU)
+                        .eternal(false)
+                        .timeToLiveSeconds(15000)
+                        .timeToIdleSeconds(5000)
+                        .diskExpiryThreadIntervalSeconds(0)
+                        .persistence(new PersistenceConfiguration().strategy(PersistenceConfiguration.Strategy.LOCALTEMPSWAP)));
+        if (!CacheManager.getInstance().cacheExists(name))
+            CacheManager.getInstance().addCache(default_cache);
+    }
+
+
+    public void init () {
+        if (!loaded) {
+            if (ServiceInfo.getInfo().getCacheable()) {
+                String file = "ehcache.xml";
+                InputStream in = ConfigLoader.loadConfigStream(file);
+                CacheManager.newInstance(in);
+                loaded = true;
+            } else {
+                CacheManager.create();
+            }
+        }
+    }
+
+    public boolean hasCacheEntry(Object key) {
+        return getCache(this.name).isKeyInCache(createKey(key.toString()));
+    }
+
+
+    public boolean enabled() {
+        // check that caching is enabled
+        return ServiceInfo.getInfo().getCacheable();
+    }
+
+    public Object getCacheValue(Object key) {
+        Element e = getCache(this.name).get(createKey(key.toString()));
+        if (e!= null)
+            return e.getObjectValue();
+        return null;
+    }
+
+    public long getCacheCreationTime(Object key) {
+        Element e = getCache(this.name).get(createKey(key.toString()));
+        if (e!= null)
+            return e.getCreationTime();
+        return -1;
+    }
+
+    public void storeInCache(Object key, Object value) {
+        getCache(this.name).put(new Element(createKey(key.toString()), value));
+    }
+
+    public void removeCacheEntry(Object key) {
+        getCache(this.name).remove(createKey(key.toString()));
+    }
+
+    public void clearCache() {
+        Cache c = getCache(this.name);
+        if (enabled()) {
+            c.removeAll();
+//            c.clearStatistics();
+            
+        }
+    }
+
+    private String createKey(String input) {
+        return StringUtils.toSHAHash(this.prefix+ "@" + input);
+    }
+    
+    public Map<Object, Element> getAllCacheElements () {
+        Cache cache = getCache(name);
+        return cache.getAll(cache.getKeysWithExpiryCheck());
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/KustvaktClassLoader.java b/full/src/main/java/de/ids_mannheim/korap/config/KustvaktClassLoader.java
new file mode 100644
index 0000000..83100bc
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/KustvaktClassLoader.java
@@ -0,0 +1,55 @@
+package de.ids_mannheim.korap.config;
+
+import org.reflections.Reflections;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Set;
+
+/**
+ * @author hanl
+ * @date 10/06/2015
+ */
+public class KustvaktClassLoader {
+
+    private static final Reflections reflections = new Reflections(
+            "de.ids_mannheim.korap");
+
+
+    private KustvaktClassLoader () {}
+
+
+    /**
+     * loads interface implementations in current classpath
+     * 
+     * @param iface
+     * @param <T>
+     * @return
+     */
+    public static <T> Set<Class<? extends T>> loadSubTypes (Class<T> iface) {
+        return reflections.getSubTypesOf(iface);
+    }
+
+
+    public static Set<Class<?>> loadFromAnnotation (
+            Class<? extends Annotation> annotation) {
+        return reflections.getTypesAnnotatedWith(annotation);
+    }
+
+
+    public static <T> Class<? extends T> getTypeClass (Class type,
+            Class<T> iface) {
+        Set<Class<? extends T>> c = KustvaktClassLoader.loadSubTypes(iface);
+        for (Class<? extends T> o : c) {
+            Type ctype = o.getGenericInterfaces()[0];
+            if (ctype instanceof ParameterizedType) {
+                ParameterizedType ptype = (ParameterizedType) ctype;
+                Class tclass = (Class) ptype.getActualTypeArguments()[0];
+                if (tclass.equals(type))
+                    return o;
+            }
+        }
+        return null;
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/KustvaktConfiguration.java b/full/src/main/java/de/ids_mannheim/korap/config/KustvaktConfiguration.java
new file mode 100644
index 0000000..40073b8
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/KustvaktConfiguration.java
@@ -0,0 +1,277 @@
+package de.ids_mannheim.korap.config;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import de.ids_mannheim.korap.util.KrillProperties;
+import de.ids_mannheim.korap.utils.TimeUtils;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Describes configuration for Kustvakt by importing properties 
+ * from kustvakt.conf file and setting default values if they are 
+ * not configured.  
+ * 
+ * MH: if configuration class is extended, loadSubTypes method should be
+ * overriden
+ * 
+ * @author hanl
+ * @author margaretha
+ */
+
+@Setter
+@Getter
+public class KustvaktConfiguration {
+
+    public static final Map<String, Object> KUSTVAKT_USER = new HashMap<>();
+    public static final String DATA_FOLDER = "data";
+
+    private String vcInCaching;
+    
+    private String indexDir;
+    private int port;
+    // todo: make exclusive so that the containg languages can really
+    // only be used then
+    private List<String> queryLanguages;
+
+    private String serverHost;
+
+    private int maxhits;
+    private int returnhits;
+    private String keystoreLocation;
+    private String keystorePassword;
+    private Properties mailProperties;
+    private String host;
+    private String shibUserMapping;
+    private String userConfig;
+    private int inactiveTime;
+    private int loginAttemptTTL;
+    private long loginAttemptNum;
+    private boolean allowMultiLogIn;
+    private int loadFactor;
+    @Deprecated
+    private int validationStringLength;
+    @Deprecated
+    private int validationEmaillength;
+
+    private byte[] sharedSecret;
+    private int longTokenTTL;
+    private int tokenTTL;
+    private int shortTokenTTL;
+    private String[] rewrite_strategies;
+
+    private String default_pos;
+    private String default_morphology;
+    private String default_lemma;
+    private String default_orthography;
+    private String default_dep;
+    private String default_const;
+    private String apiWelcomeMessage;
+    private String defaultStructureFoundry;
+    private ArrayList<String> foundries;
+    private ArrayList<String> layers;
+
+    private String baseURL;
+    private Properties properties;
+
+    private Set<String> supportedVersions;
+    private String currentVersion;
+
+    // deprec?!
+    private final BACKENDS DEFAULT_ENGINE = BACKENDS.LUCENE;
+    private String networkEndpointURL;
+    
+    // license patterns
+    protected Pattern publicLicensePattern;
+    protected Pattern freeLicensePattern;
+    protected Pattern allLicensePattern;
+
+    // random code generator
+    private String secureRandomAlgorithm;
+    private String messageDigestAlgorithm;
+
+    // EM: metadata restriction
+    // another variable might be needed to define which metadata fields are restricted 
+    private boolean isMetadataRestricted = false;
+    
+    // EM: Maybe needed when we support pipe registration
+    @Deprecated
+    public static Map<String, String> pipes = new HashMap<>();
+    
+    public KustvaktConfiguration (Properties properties) throws Exception {
+        load(properties);
+//        readPipesFile("pipes");
+        KrillProperties.setProp(properties);
+    }
+
+    public KustvaktConfiguration () {}
+    
+    public void loadBasicProperties (Properties properties) {
+        port = Integer.valueOf(properties.getProperty("server.port", "8095"));
+        baseURL = properties.getProperty("kustvakt.base.url", "/api/*");
+        setSecureRandomAlgorithm(properties
+                .getProperty("security.secure.random.algorithm", ""));
+        setMessageDigestAlgorithm(
+                properties.getProperty("security.md.algorithm", "MD5"));
+    }
+    
+    /**
+     * loading of the properties and mapping to parameter variables
+     * 
+     * @param properties
+     * @return
+     * @throws Exception
+     */
+    protected void load (Properties properties) throws Exception {
+        loadBasicProperties(properties);
+
+        apiWelcomeMessage = properties.getProperty("api.welcome.message", "Welcome to KorAP API!");
+        currentVersion = properties.getProperty("current.api.version", "v1.0");
+
+        String supportedVersions =
+                properties.getProperty("supported.api.versions", "");
+        
+        this.supportedVersions = new HashSet<>();
+        if (!supportedVersions.isEmpty()){
+            List<String> versionArray = Arrays.asList(supportedVersions.split(" "));
+            this.supportedVersions.addAll(versionArray);
+        }
+        this.supportedVersions.add(currentVersion);
+
+        maxhits = Integer.valueOf(properties.getProperty("maxhits", "50000"));
+        returnhits = Integer.valueOf(properties.getProperty("returnhits", "50000"));
+        indexDir = properties.getProperty("krill.indexDir", "");
+        
+        // server options
+        serverHost = String
+                .valueOf(properties.getProperty("server.host", "localhost"));
+        String queries = properties.getProperty("korap.ql", "");
+        String[] qls = queries.split(",");
+        queryLanguages = new ArrayList<>();
+        for (String querylang : qls)
+            queryLanguages.add(querylang.trim().toUpperCase());
+
+        default_const =
+                properties.getProperty("default.foundry.constituent", "corenlp");
+        default_dep =
+                properties.getProperty("default.foundry.dependency", "malt");
+        default_lemma = properties.getProperty("default.foundry.lemma", "tt");
+        default_morphology = properties.getProperty("default.foundry.morphology", "marmot");
+        default_pos =
+                properties.getProperty("default.foundry.partOfSpeech", "tt");
+        default_orthography =
+                properties.getProperty("default.foundry.orthography", "opennlp");
+        defaultStructureFoundry =
+                properties.getProperty("default.foundry.structure", "base");
+
+        // security configuration
+        inactiveTime = TimeUtils.convertTimeToSeconds(
+                properties.getProperty("security.idleTimeoutDuration", "10M"));
+        allowMultiLogIn = Boolean
+                .valueOf(properties.getProperty("security.multipleLogIn"));
+
+        loginAttemptNum = Long.parseLong(
+                properties.getProperty("security.loginAttemptNum", "3"));
+        loginAttemptTTL = TimeUtils.convertTimeToSeconds(
+                properties.getProperty("security.authAttemptTTL", "30M"));
+
+        loadFactor = Integer.valueOf(
+                properties.getProperty("security.encryption.loadFactor", "15"));
+        validationStringLength = Integer.valueOf(properties
+                .getProperty("security.validation.stringLength", "150"));
+        validationEmaillength = Integer.valueOf(properties
+                .getProperty("security.validation.emailLength", "40"));
+
+        sharedSecret =
+                properties.getProperty("security.sharedSecret", "").getBytes();
+
+        longTokenTTL = TimeUtils.convertTimeToSeconds(
+                properties.getProperty("security.longTokenTTL", "100D"));
+        tokenTTL = TimeUtils.convertTimeToSeconds(
+                properties.getProperty("security.tokenTTL", "72H"));
+        shortTokenTTL = TimeUtils.convertTimeToSeconds(
+                properties.getProperty("security.shortTokenTTL", "3H"));
+        
+        // network endpoint
+        networkEndpointURL =
+                properties.getProperty("network.endpoint.url", "");
+    }
+    
+    @Deprecated
+    public void readPipesFile (String filename) throws IOException {
+        File file = new File(filename);
+        if (file.exists()) {
+            BufferedReader br = new BufferedReader(
+                    new InputStreamReader(new FileInputStream(file)));
+
+            String line = null;
+            while( (line=br.readLine())!=null ){
+                String[] parts = line.split("\t");
+                if (parts.length !=2){
+                    continue;
+                }
+                else{
+                    pipes.put(parts[0], parts[1]);
+                }
+            }
+            br.close();
+        }
+    }
+    
+
+    /**
+     * set properties
+     * 
+     * @param props
+     * @throws IOException
+     */
+    // public void setProperties (Properties props) throws IOException
+    // {
+    // this.load(props);
+    // }
+
+    /**
+     * properties can be overloaded after spring init
+     * 
+     * @param stream
+     * @throws Exception
+     */
+    public void setPropertiesAsStream (InputStream stream) throws Exception {
+        try {
+
+            Properties p = new Properties();
+            p.load(stream);
+            this.load(p);
+        }
+        catch (IOException e) {
+            e.printStackTrace();
+        }
+
+    }
+
+    public BACKENDS chooseBackend (String value) {
+        if (value == null || value.equals("null"))
+            return DEFAULT_ENGINE;
+        else
+            return Enum.valueOf(BACKENDS.class, value.toUpperCase());
+    }
+
+    public enum BACKENDS {
+        NEO4J, LUCENE, NETWORK
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/ParamFields.java b/full/src/main/java/de/ids_mannheim/korap/config/ParamFields.java
new file mode 100644
index 0000000..2d9a255
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/ParamFields.java
@@ -0,0 +1,44 @@
+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.getClass().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/full/src/main/java/de/ids_mannheim/korap/config/QueryBuilderUtil.java b/full/src/main/java/de/ids_mannheim/korap/config/QueryBuilderUtil.java
new file mode 100644
index 0000000..bc21668
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/QueryBuilderUtil.java
@@ -0,0 +1,21 @@
+package de.ids_mannheim.korap.config;
+
+import de.ids_mannheim.korap.query.serialize.MetaQueryBuilder;
+
+/**
+ * @author hanl
+ * @date 25/06/2015
+ */
+public class QueryBuilderUtil {
+
+    public static MetaQueryBuilder defaultMetaBuilder (Integer pageIndex,
+            Integer pageInteger, Integer pageLength, String ctx, Boolean cutoff) {
+        MetaQueryBuilder meta = new MetaQueryBuilder();
+        meta.addEntry("startIndex", pageIndex)
+                .addEntry("startPage", pageInteger)
+                .addEntry("count", pageLength).setSpanContext(ctx)
+                .addEntry("cutOff", cutoff);
+        return meta;
+
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/Scopes.java b/full/src/main/java/de/ids_mannheim/korap/config/Scopes.java
new file mode 100644
index 0000000..20e5627
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/Scopes.java
@@ -0,0 +1,95 @@
+package de.ids_mannheim.korap.config;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.user.Userdata;
+import de.ids_mannheim.korap.utils.JsonUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author hanl
+ * @date 09/12/2014
+ */
+public class Scopes {
+
+    public enum Scope {
+        openid, profile, email, queries, account, preferences, search
+
+    }
+
+    private static final String[] profile = { Attributes.EMAIL,
+            Attributes.FIRSTNAME, Attributes.LASTNAME, Attributes.INSTITUTION,
+            Attributes.ADDRESS, Attributes.PHONE, Attributes.GENDER,
+            Attributes.COUNTRY };
+
+    private static final String[] OPENID_CONNECT = { Scope.profile.toString() };
+
+    private static final Enum[] SERVICE_DEFAULTS = { Scope.account,
+            Scope.preferences, Scope.search, Scope.queries };
+
+
+    public static Scopes getProfileScopes (Userdata values) {
+        Scopes r = new Scopes();
+        for (String key : profile) {
+            Object v = values.get(key);
+            if (v != null)
+                r.values.put(key, v);
+        }
+        return r;
+    }
+
+
+    /**
+     * expects space separated values
+     * 
+     * @param scopes
+     * @return
+     */
+    //todo: test
+    public static Scope[] mapScopes (String scopes) {
+        List<Enum> s = new ArrayList<>();
+        for (String value : scopes.split(" "))
+            s.add(Scope.valueOf(value.toLowerCase()));
+        return s.toArray(new Scope[s.size()]);
+    }
+
+
+    public static Scopes mapScopes (String scopes, Userdata details) {
+        Scopes m = new Scopes();
+        if (scopes != null && !scopes.isEmpty()) {
+            Scope[] scopearr = mapScopes(scopes);
+            for (Scope s : scopearr) {
+                Object v = details.get(s.toString());
+                if (v != null)
+                    m.values.put(s.toString(), v);
+            }
+            if (scopes.contains(Scope.profile.toString()))
+                m.values.putAll(Scopes.getProfileScopes(details).values);
+            m.values.put(Attributes.SCOPE, scopes);
+        }
+        return m;
+    }
+
+    private Map<String, Object> values;
+
+
+    private Scopes () {
+        this.values = new HashMap<>();
+    }
+
+
+    public String toEntity () throws KustvaktException {
+        if (this.values.isEmpty())
+            return "";
+        return JsonUtils.toJSON(this.values);
+    }
+
+
+    public Map<String, Object> toMap () {
+        return new HashMap<>(this.values);
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/URIParam.java b/full/src/main/java/de/ids_mannheim/korap/config/URIParam.java
new file mode 100644
index 0000000..9fd7911
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/URIParam.java
@@ -0,0 +1,28 @@
+package de.ids_mannheim.korap.config;
+
+import lombok.Getter;
+
+/**
+ * @author hanl
+ * @date 15/07/15
+ */
+@Getter
+public class URIParam extends ParamFields.Param {
+
+    private final String uriFragment;
+    private final Long uriExpiration;
+
+
+    public URIParam (String uri, Long expire) {
+        this.uriFragment = uri;
+        this.uriExpiration = expire;
+    }
+
+
+    @Override
+    public boolean hasValues () {
+        return this.uriFragment != null && !this.uriFragment.isEmpty()
+                && this.uriExpiration != null;
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/constant/AnnotationType.java b/full/src/main/java/de/ids_mannheim/korap/constant/AnnotationType.java
new file mode 100644
index 0000000..ea91e16
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/constant/AnnotationType.java
@@ -0,0 +1,13 @@
+package de.ids_mannheim.korap.constant;
+
+/** Defines various annotation types as constants  
+ * 
+ * @author margaretha
+ *
+ */
+public class AnnotationType {
+    public static String FOUNDRY = "foundry";
+    public static String LAYER = "layer";
+    public static String KEY = "key";
+    public static String VALUE = "value";
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/constant/AuthenticationMethod.java b/full/src/main/java/de/ids_mannheim/korap/constant/AuthenticationMethod.java
new file mode 100644
index 0000000..cbdc07f
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/constant/AuthenticationMethod.java
@@ -0,0 +1,18 @@
+package de.ids_mannheim.korap.constant;
+
+/** Lists possible actual authentication methods. Multiple 
+ *  {@link AuthenticationScheme} may use an identical 
+ *  authentication method. 
+ * 
+ * @author margaretha
+ * 
+ * @see AuthenticationScheme 
+ *
+ */
+public enum AuthenticationMethod {
+    LDAP,
+    // not available
+    SHIBBOLETH, DATABASE,
+    // by pass authentication for testing
+    TEST; 
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/constant/AuthenticationScheme.java b/full/src/main/java/de/ids_mannheim/korap/constant/AuthenticationScheme.java
new file mode 100644
index 0000000..543b3fd
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/constant/AuthenticationScheme.java
@@ -0,0 +1,22 @@
+package de.ids_mannheim.korap.constant;
+
+import org.apache.commons.lang.WordUtils;
+
+/** Lists possible authentication schemes used in the Authorization header 
+ *  of HTTP requests.  
+ * 
+ * @author margaretha
+ *
+ */
+public enum AuthenticationScheme {
+    // standard http
+    BASIC, BEARER,
+    // custom
+    // SESSION, has not been supported yet 
+    @Deprecated
+    API;
+
+    public String displayName () {
+        return WordUtils.capitalizeFully(name());
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/constant/OAuth2Scope.java b/full/src/main/java/de/ids_mannheim/korap/constant/OAuth2Scope.java
new file mode 100644
index 0000000..5055972
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/constant/OAuth2Scope.java
@@ -0,0 +1,60 @@
+package de.ids_mannheim.korap.constant;
+
+/** Defines all possible authorization scopes
+ * 
+ * @author margaretha
+ *
+ */
+public enum OAuth2Scope {
+    
+    ALL, 
+    @Deprecated
+    ADMIN,
+    
+    OPENID, 
+    AUTHORIZE,
+    
+    LIST_USER_CLIENT,
+    INSTALL_USER_CLIENT,
+    UNINSTALL_USER_CLIENT,
+    
+    CLIENT_INFO,
+    REGISTER_CLIENT,
+    DEREGISTER_CLIENT,
+    RESET_CLIENT_SECRET,
+    
+    SEARCH, 
+    SERIALIZE_QUERY,
+    MATCH_INFO, 
+    
+    USER_INFO,
+    
+    USER_GROUP_INFO, 
+    CREATE_USER_GROUP, 
+    DELETE_USER_GROUP, 
+    
+    DELETE_USER_GROUP_MEMBER, 
+    ADD_USER_GROUP_MEMBER, 
+    
+    EDIT_USER_GROUP_MEMBER_ROLE,
+    ADD_USER_GROUP_MEMBER_ROLE,
+    DELETE_USER_GROUP_MEMBER_ROLE, 
+           
+    CREATE_VC, 
+    VC_INFO,
+    EDIT_VC, 
+    DELETE_VC, 
+    
+    SHARE_VC, 
+    DELETE_VC_ACCESS, 
+    VC_ACCESS_INFO, 
+    
+    CREATE_DEFAULT_SETTING, 
+    READ_DEFAULT_SETTING, 
+    DELETE_DEFAULT_SETTING;
+
+    @Override
+    public String toString () {
+        return super.toString().toLowerCase();
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/constant/TokenType.java b/full/src/main/java/de/ids_mannheim/korap/constant/TokenType.java
new file mode 100644
index 0000000..76bac5b
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/constant/TokenType.java
@@ -0,0 +1,26 @@
+package de.ids_mannheim.korap.constant;
+
+import org.apache.commons.lang.StringUtils;
+
+import de.ids_mannheim.korap.security.context.TokenContext;
+
+/** Defines the types of authentication tokens. Token types are used to
+ * create {@link TokenContext} and determine which authentication provider
+ * must be used to create a TokenContext and parse given tokens. 
+ * 
+ * @author margaretha
+ *
+ */
+public enum TokenType {
+    BASIC, API, SESSION, 
+    // openid token, e.g. within oauth2 response (json body)
+    ID_TOKEN,
+    // OAuth2 access_token
+    BEARER,
+    // OAuth2 client
+    CLIENT; 
+
+    public String displayName () {
+        return StringUtils.capitalize(name().toLowerCase());
+    }
+}
\ No newline at end of file
diff --git a/full/src/main/java/de/ids_mannheim/korap/core/entity/Annotation.java b/full/src/main/java/de/ids_mannheim/korap/core/entity/Annotation.java
new file mode 100644
index 0000000..fe723fc
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/core/entity/Annotation.java
@@ -0,0 +1,52 @@
+package de.ids_mannheim.korap.core.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Describes annotation tags available in the system / used in
+ * annotating corpus data.
+ * 
+ * @author margaretha
+ *
+ */
+@Setter
+@Getter
+@Entity
+@Table(name = "annotation")
+public class Annotation {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private int id;
+    private String code;
+    private String type;
+    private String text;
+    private String description;
+    @Column(name = "de_description")
+    private String germanDescription;
+
+    public Annotation () {}
+
+    public Annotation (String code, String type, String text,
+                       String description) {
+        this.code = code;
+        this.type = type;
+        this.text = text;
+        this.description = description;
+    }
+
+    @Override
+    public String toString () {
+        return "id=" + id + ", code= " + code + ", type= " + type
+                + ", description=" + description + ", germanDescription="
+                + germanDescription;
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/core/entity/AnnotationKey.java b/full/src/main/java/de/ids_mannheim/korap/core/entity/AnnotationKey.java
new file mode 100644
index 0000000..f94c715
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/core/entity/AnnotationKey.java
@@ -0,0 +1,69 @@
+package de.ids_mannheim.korap.core.entity;
+
+import java.util.Set;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Describes the annotation key mapping to annotation_key table in the
+ * database and annotation key relations to {@link AnnotationLayer}
+ * and {@link Annotation}.
+ * 
+ * @author margaretha
+ *
+ */
+@Setter
+@Getter
+@Entity
+@Table(name = "annotation_key", uniqueConstraints = @UniqueConstraint(
+        columnNames = { "layer_id", "key_id" }))
+public class AnnotationKey {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private int id;
+    @Column(name = "key_id")
+    private int keyId;
+    @Column(name = "layer_id")
+    private int layerId;
+
+    @ManyToOne(fetch = FetchType.EAGER)
+    @JoinColumn(name = "layer_id", insertable = false, updatable = false)
+    private AnnotationLayer layer;
+
+    @ManyToOne(fetch = FetchType.EAGER)
+    @JoinColumn(name = "key_id", insertable = false, updatable = false)
+    private Annotation key;
+
+    @ManyToMany(fetch = FetchType.EAGER)
+    @JoinTable(name = "annotation_value",
+            joinColumns = @JoinColumn(name = "key_id",
+                    referencedColumnName = "id"),
+            inverseJoinColumns = @JoinColumn(name = "value_id",
+                    referencedColumnName = "id"),
+            uniqueConstraints = @UniqueConstraint(
+                    columnNames = { "key_id", "value_id" }))
+    private Set<Annotation> values;
+
+    public AnnotationKey () {}
+
+    public AnnotationKey (int layerId, int keyId) {
+        this.layerId = layerId;
+        this.keyId = keyId;
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/core/entity/AnnotationLayer.java b/full/src/main/java/de/ids_mannheim/korap/core/entity/AnnotationLayer.java
new file mode 100644
index 0000000..3160cef
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/core/entity/AnnotationLayer.java
@@ -0,0 +1,68 @@
+package de.ids_mannheim.korap.core.entity;
+
+import java.util.Set;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+
+import org.hibernate.annotations.Fetch;
+import org.hibernate.annotations.FetchMode;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Describes annotation layers as a pair of foundry and layer where
+ * foundry denotes where the annotation comes from e.g. Tree tagger
+ * parser, and layer denotes the annotation layer e.g. part of speech.
+ * 
+ * @author margaretha
+ * @see Annotation
+ */
+@Setter
+@Getter
+@Entity
+@Table(name = "annotation_layer", uniqueConstraints = @UniqueConstraint(
+        columnNames = { "foundry_id", "layer_id" }))
+public class AnnotationLayer {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private int id;
+    @Column(name = "foundry_id")
+    private int foundryId;
+    @Column(name = "layer_id")
+    private int layerId;
+    @Column(name = "description")
+    private String description;
+
+    @Fetch(FetchMode.SELECT)
+    @ManyToOne // (fetch=FetchType.LAZY)
+    @JoinColumn(name = "foundry_id", insertable = false, updatable = false)
+    private Annotation foundry;
+
+    @Fetch(FetchMode.SELECT)
+    @ManyToOne // (fetch=FetchType.LAZY)
+    @JoinColumn(name = "layer_id", insertable = false, updatable = false)
+    private Annotation layer;
+
+    @OneToMany(mappedBy = "layer", fetch = FetchType.EAGER,
+            cascade = CascadeType.REMOVE)
+    private Set<AnnotationKey> keys;
+
+    @Override
+    public String toString () {
+        return "id=" + id + ", foundry=" + foundry + ", layer=" + layer
+                + ", description=" + description + ", keys= " + keys;
+
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/core/entity/Resource.java b/full/src/main/java/de/ids_mannheim/korap/core/entity/Resource.java
new file mode 100644
index 0000000..1f26d7f
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/core/entity/Resource.java
@@ -0,0 +1,69 @@
+package de.ids_mannheim.korap.core.entity;
+
+import java.util.Set;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
+import javax.persistence.Table;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Describes resources having free licenses. Primarily for
+ * accommodating clients in providing data without login such as
+ * KorapSRU.
+ * 
+ * @author margaretha
+ *
+ */
+@Getter
+@Setter
+@Entity
+@Table(name = "resource")
+public class Resource {
+
+    @Id
+    private String id;
+
+    @Column(name = "de_title")
+    private String germanTitle;
+
+    @Column(name = "en_title")
+    private String englishTitle;
+
+    @Column(name = "en_description")
+    private String englishDescription;
+
+    @ManyToMany(fetch = FetchType.EAGER)
+    @JoinTable(name = "resource_layer",
+            joinColumns = @JoinColumn(name = "resource_id",
+                    referencedColumnName = "id"),
+            inverseJoinColumns = @JoinColumn(name = "layer_id",
+                    referencedColumnName = "id"))
+    private Set<AnnotationLayer> layers;
+
+    public Resource () {}
+
+    public Resource (String id, String germanTitle, String englishTitle,
+                     String englishDescription, Set<AnnotationLayer> layers) {
+        this.id = id;
+        this.germanTitle = germanTitle;
+        this.englishTitle = englishTitle;
+        this.englishDescription = englishDescription;
+        this.layers = layers;
+    }
+
+    @Override
+    public String toString () {
+        return "id=" + id + ", germanTitle=" + germanTitle + ", englishTitle="
+                + englishTitle + ", description=" + englishDescription
+                + ", layers= " + layers;
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/core/service/AnnotationService.java b/full/src/main/java/de/ids_mannheim/korap/core/service/AnnotationService.java
new file mode 100644
index 0000000..4730a4f
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/core/service/AnnotationService.java
@@ -0,0 +1,96 @@
+package de.ids_mannheim.korap.core.service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import de.ids_mannheim.korap.core.entity.AnnotationLayer;
+import de.ids_mannheim.korap.core.web.controller.AnnotationController;
+import de.ids_mannheim.korap.dao.AnnotationDao;
+import de.ids_mannheim.korap.dto.FoundryDto;
+import de.ids_mannheim.korap.dto.LayerDto;
+import de.ids_mannheim.korap.dto.converter.AnnotationConverter;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+
+/** AnnotationService defines the logic behind {@link AnnotationController}.
+ * 
+ * @author margaretha
+ *
+ */
+@Service
+public class AnnotationService {
+
+    private static final boolean DEBUG = false;
+
+    private static Logger jlog =
+            LogManager.getLogger(AnnotationService.class);
+
+    @Autowired
+    private AnnotationDao annotationDao;
+
+    @Autowired
+    private AnnotationConverter annotationConverter;
+
+    public List<LayerDto> getLayerDtos () {
+        List<AnnotationLayer> layers = annotationDao.getAllFoundryLayerPairs();
+        if (DEBUG){
+            jlog.debug("/layers " + layers.toString());
+        }
+        List<LayerDto> layerDto = annotationConverter.convertToLayerDto(layers);
+        return layerDto;
+    }
+
+    public List<FoundryDto> getFoundryDtos (List<String> codes, String language)
+            throws KustvaktException {
+        List<AnnotationLayer> annotationPairs = null;
+        String foundry = "", layer = "";
+        if (codes.contains("*")) {
+            annotationPairs =
+                    annotationDao.getAnnotationDescriptions(foundry, layer);
+        }
+        else {
+            String[] annotationCode;
+            annotationPairs = new ArrayList<AnnotationLayer>();
+            for (String code : codes) {
+                if (DEBUG){
+                    jlog.debug("code " + code);
+                }
+                annotationCode = code.split("/");
+                if (annotationCode.length == 1) {
+                    foundry = annotationCode[0];
+                }
+                else if (annotationCode.length == 2) {
+                    foundry = annotationCode[0];
+                    layer = annotationCode[1];
+                }
+                else {
+                    jlog.error("Annotation code is wrong: " + annotationCode);
+                    throw new KustvaktException(StatusCodes.INVALID_ATTRIBUTE,
+                            "Bad attribute:", code);
+                }
+
+                annotationPairs.addAll(annotationDao
+                        .getAnnotationDescriptions(foundry, layer));
+            }
+        }
+
+        if (annotationPairs != null && !annotationPairs.isEmpty()) {
+            List<FoundryDto> foundryDtos = annotationConverter
+                    .convertToFoundryDto(annotationPairs, language);
+            if (DEBUG){
+                jlog.debug("/description " + annotationPairs.toString());
+            }
+            return foundryDtos;
+        }
+        else {
+            throw new KustvaktException(StatusCodes.NO_RESULT_FOUND,
+                    "No result found", "");
+        }
+
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/core/service/BasicService.java b/full/src/main/java/de/ids_mannheim/korap/core/service/BasicService.java
new file mode 100644
index 0000000..c1072dd
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/core/service/BasicService.java
@@ -0,0 +1,17 @@
+package de.ids_mannheim.korap.core.service;
+
+import java.util.List;
+
+public class BasicService {
+
+    protected String combineMultipleCorpusQuery (List<String> cqList) {
+        String combinedCorpusQuery = null;
+        if (cqList!=null && cqList.size() > 0) {
+            combinedCorpusQuery = cqList.get(0);
+            for (int i = 1; i < cqList.size(); i++) {
+                combinedCorpusQuery += "&" + cqList.get(i);
+            }
+        }
+        return combinedCorpusQuery;
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/core/service/SearchNetworkEndpoint.java b/full/src/main/java/de/ids_mannheim/korap/core/service/SearchNetworkEndpoint.java
new file mode 100644
index 0000000..de6038e
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/core/service/SearchNetworkEndpoint.java
@@ -0,0 +1,87 @@
+package de.ids_mannheim.korap.core.service;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import org.apache.http.HttpStatus;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+
+@Service
+public class SearchNetworkEndpoint {
+
+    private final static Logger jlog = LogManager
+            .getLogger(SearchNetworkEndpoint.class);
+
+    @Autowired
+    private KustvaktConfiguration config;
+
+    public String search (String query) throws KustvaktException {
+        String networkEndpointURL = config.getNetworkEndpointURL();
+        if (networkEndpointURL == null || networkEndpointURL.isEmpty()) {
+            throw new KustvaktException(
+                    StatusCodes.NETWORK_ENDPOINT_NOT_AVAILABLE,
+                    "Network endpoint is not available");
+        }
+        else {
+            try {
+                URL url = new URL(networkEndpointURL);
+                HttpURLConnection connection = (HttpURLConnection) url
+                        .openConnection();
+                connection.setRequestMethod("POST");
+                connection.setRequestProperty("Content-Type",
+                        "application/json; charset=UTF-8");
+                connection.setRequestProperty("Accept", "application/json");
+                connection.setDoOutput(true);
+                OutputStream os = connection.getOutputStream();
+                byte[] input = query.getBytes("utf-8");
+                os.write(input, 0, input.length);
+
+                String entity = null;
+                if (connection.getResponseCode() == HttpStatus.SC_OK) {
+                    BufferedReader br = new BufferedReader(
+                            new InputStreamReader(connection.getInputStream(),
+                                    "utf-8"));
+                    StringBuilder response = new StringBuilder();
+                    String responseLine = null;
+                    while ((responseLine = br.readLine()) != null) {
+                        response.append(responseLine.trim());
+                    }
+                    entity = response.toString();
+                }
+
+                if (entity != null && !entity.isEmpty()) {
+                    return entity;
+                }
+                else {
+                    String message = connection.getResponseCode() + " "
+                            + connection.getResponseMessage();
+                    jlog.warn("Search on network endpoint failed "
+                            + networkEndpointURL + ". Message: " + message);
+
+                    throw new KustvaktException(
+                            StatusCodes.SEARCH_NETWORK_ENDPOINT_FAILED,
+                            "Failed searching at network endpoint: "
+                                    + networkEndpointURL,
+                            message);
+                }
+            }
+            catch (Exception e) {
+                throw new KustvaktException(
+                        StatusCodes.SEARCH_NETWORK_ENDPOINT_FAILED,
+                        "Failed searching at network endpoint: "
+                                + networkEndpointURL,
+                        e.getCause());
+            }
+        }
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/core/service/SearchService.java b/full/src/main/java/de/ids_mannheim/korap/core/service/SearchService.java
new file mode 100644
index 0000000..96b5f62
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/core/service/SearchService.java
@@ -0,0 +1,493 @@
+package de.ids_mannheim.korap.core.service;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import javax.annotation.PostConstruct;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.UriBuilder;
+
+import org.apache.http.HttpStatus;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+//import de.ids_mannheim.de.init.VCLoader;
+import de.ids_mannheim.korap.authentication.AuthenticationManager;
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+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.response.Notifications;
+import de.ids_mannheim.korap.rewrite.RewriteHandler;
+import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.user.User.CorpusAccess;
+import de.ids_mannheim.korap.utils.JsonUtils;
+import de.ids_mannheim.korap.web.ClientsHandler;
+import de.ids_mannheim.korap.web.SearchKrill;
+
+@Service
+public class SearchService extends BasicService{
+
+    private static final boolean DEBUG = false;
+
+    private static Logger jlog = LogManager.getLogger(SearchService.class);
+
+    @Autowired
+    private KustvaktConfiguration config;
+    @Autowired
+    private AuthenticationManager authenticationManager;
+
+    @Autowired
+    private RewriteHandler rewriteHandler;
+
+    @Autowired
+    private SearchKrill searchKrill;
+    @Autowired
+    private SearchNetworkEndpoint searchNetwork;
+
+    private ClientsHandler graphDBhandler;
+
+    @PostConstruct
+    private void doPostConstruct () {
+        UriBuilder builder = UriBuilder.fromUri("http://10.0.10.13").port(9997);
+        this.graphDBhandler = new ClientsHandler(builder.build());
+    }
+
+    public String getKrillVersion () {
+        return searchKrill.getIndex().getVersion();
+
+    }
+    
+    @SuppressWarnings("unchecked")
+    public String serializeQuery (String q, String ql, String v, String cq,
+            Integer pageIndex, Integer startPage, Integer pageLength,
+            String context, Boolean cutoff, boolean accessRewriteDisabled)
+            throws KustvaktException {
+        QuerySerializer ss = new QuerySerializer().setQuery(q, ql, v);
+        if (cq != null) ss.setCollection(cq);
+
+        MetaQueryBuilder meta = new MetaQueryBuilder();
+        if (pageIndex != null) meta.addEntry("startIndex", pageIndex);
+        if (pageIndex == null && startPage != null)
+            meta.addEntry("startPage", startPage);
+        if (pageLength != null) meta.addEntry("count", pageLength);
+        if (context != null) meta.setSpanContext(context);
+        meta.addEntry("cutOff", cutoff);
+
+        ss.setMeta(meta.raw());
+        // return ss.toJSON();
+
+        String query = ss.toJSON();
+        query = rewriteHandler.processQuery(ss.toJSON(), null);
+        return query;
+    }
+
+    private User createUser (String username, HttpHeaders headers)
+            throws KustvaktException {
+        User user = authenticationManager.getUser(username);
+        authenticationManager.setAccessAndLocation(user, headers);
+        if (DEBUG) {
+            if (user != null) {
+                jlog.debug("Debug: user location=" + user.locationtoString()
+                        + ", access=" + user.accesstoString());
+            }
+        }
+        return user;
+    }
+
+    public String search (String jsonld, String username, HttpHeaders headers)
+            throws KustvaktException {
+
+        User user = createUser(username, headers);
+
+        JsonNode node  = JsonUtils.readTree(jsonld);
+        node = node.at("/meta/snippets");
+        if (node !=null && node.asBoolean()){
+            user.setCorpusAccess(CorpusAccess.ALL);
+        }
+        
+        String query = this.rewriteHandler.processQuery(jsonld, user);
+        // MH: todo: should be possible to add the meta part to
+        // the query serialization
+        // User user = controller.getUser(ctx.getUsername());
+        // jsonld = this.processor.processQuery(jsonld, user);
+        return searchKrill.search(query);
+    }
+
+    @SuppressWarnings("unchecked")
+    public String search (String engine, String username, HttpHeaders headers,
+            String q, String ql, String v, List<String> cqList, String fields,
+            String pipes, Integer pageIndex, Integer pageInteger, String ctx,
+            Integer pageLength, Boolean cutoff, boolean accessRewriteDisabled,
+            boolean showTokens, boolean showSnippet)
+            throws KustvaktException {
+
+        if (pageInteger != null && pageInteger < 1) {
+            throw new KustvaktException(StatusCodes.INVALID_ARGUMENT,
+                    "page must start from 1", "page");
+        }
+        
+        String[] pipeArray = null;
+        if (pipes!=null && !pipes.isEmpty()){
+            pipeArray = pipes.split(",");
+        }
+        
+        User user = createUser(username, headers);
+        CorpusAccess corpusAccess = user.getCorpusAccess();
+        
+        // EM: TODO: check if requested fields are public metadata. Currently 
+        // it is not needed because all metadata are public.        
+        if (accessRewriteDisabled){
+            corpusAccess = CorpusAccess.ALL;
+            user.setCorpusAccess(CorpusAccess.ALL);
+        }
+        
+        QuerySerializer serializer = new QuerySerializer();
+        serializer.setQuery(q, ql, v);
+        String cq = combineMultipleCorpusQuery(cqList);
+        if (cq != null) serializer.setCollection(cq);
+
+        List<String> fieldList = convertFieldsToList(fields);
+        handleNonPublicFields(fieldList, accessRewriteDisabled, serializer);
+        
+        MetaQueryBuilder meta = createMetaQuery(pageIndex, pageInteger, ctx,
+                pageLength, cutoff, corpusAccess, fieldList, accessRewriteDisabled,
+                showTokens, showSnippet);
+        serializer.setMeta(meta.raw());
+        
+        // There is an error in query processing
+        // - either query, corpus or meta
+        if (serializer.hasErrors()) {
+            throw new KustvaktException(serializer.toJSON());
+        }
+
+        String query = serializer.toJSON();
+        
+        if (accessRewriteDisabled && showTokens) {
+            Notifications n = new Notifications();
+            n.addWarning(StatusCodes.NOT_ALLOWED,
+                    "Tokens cannot be shown without access.");
+            JsonNode warning = n.toJsonNode();
+            query = addWarning(query, warning);
+        }
+        
+        query = runPipes(query,pipeArray);
+        
+        query = this.rewriteHandler.processQuery(query, user);
+        if (DEBUG){
+            jlog.debug("the serialized query " + query);
+        }
+
+        KustvaktConfiguration.BACKENDS searchEngine = this.config.chooseBackend(engine);
+        String result;
+        if (searchEngine.equals(KustvaktConfiguration.BACKENDS.NEO4J)) {
+            result = searchNeo4J(query, pageLength, meta, false);
+        }
+        else if (searchEngine.equals(KustvaktConfiguration.BACKENDS.NETWORK)) {
+            result = searchNetwork.search(query);
+        }
+        else {
+            result = searchKrill.search(query);
+        }
+        // jlog.debug("Query result: " + result);
+        return result;
+
+    }
+
+    /**
+     * Pipes are service URLs for modifying KoralQuery. A POST request
+     * with Content-Type application/json will be sent for each pipe.
+     * Kustvakt expects a KoralQuery in JSON format as the pipe response. 
+     * 
+     * @param query the original koral query
+     * @param pipeArray the pipe service URLs
+     * @param serializer the query serializer
+     * @return a modified koral query
+     * @throws KustvaktException 
+     */
+    private String runPipes (String query, String[] pipeArray) throws KustvaktException {
+        if (pipeArray !=null){
+            for (int i=0; i<pipeArray.length; i++){
+                String pipeURL = pipeArray[i];
+                try {
+                    URL url = new URL(pipeURL);
+                    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+                    connection.setRequestMethod("POST");
+                    connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
+                    connection.setRequestProperty("Accept", "application/json");
+                    connection.setDoOutput(true);
+                    OutputStream os = connection.getOutputStream();
+                    byte[] input = query.getBytes("utf-8");
+                    os.write(input, 0, input.length);     
+                    
+                    String entity = null;
+                    if (connection.getResponseCode() == HttpStatus.SC_OK) {
+                        BufferedReader br =
+                                new BufferedReader(new InputStreamReader(
+                                        connection.getInputStream(), "utf-8"));
+                        StringBuilder response = new StringBuilder();
+                        String responseLine = null;
+                        while ((responseLine = br.readLine()) != null) {
+                            response.append(responseLine.trim());
+                        }
+                        entity = response.toString();
+                    }
+
+                    if (entity != null && !entity.isEmpty()) {
+                        query = entity;
+                    }
+                    else {
+                        query = handlePipeError(query, pipeURL,
+                                connection.getResponseCode() + " "
+                                        + connection.getResponseMessage());
+                    }
+                }
+                catch (Exception e) {
+                    query = handlePipeError(query, pipeURL,
+                            e.getMessage());
+                }
+            }
+        }
+        return query;
+    }
+    
+    private String handlePipeError (String query, String url,
+            String message) throws KustvaktException {
+        jlog.warn("Failed running the pipe at " + url + ". Message: "+ message);
+       
+        Notifications n = new Notifications();
+        n.addWarning(StatusCodes.PIPE_FAILED,
+                "Pipe failed", url, message);
+        JsonNode warning = n.toJsonNode();
+        
+        query = addWarning(query, warning);
+        return query; 
+    }
+    
+
+    private String addWarning (String query, JsonNode warning)
+            throws KustvaktException {
+        
+        ObjectNode node = (ObjectNode) JsonUtils.readTree(query);
+        if (node.has("warnings")){
+            warning = warning.at("/warnings/0");
+            ArrayNode arrayNode = (ArrayNode) node.get("warnings");
+            arrayNode.add(warning);
+            node.set("warnings", arrayNode);
+        }
+        else{
+            node.setAll((ObjectNode) warning);
+        }
+        return node.toString();
+    }
+
+    private void handleNonPublicFields (List<String> fieldList,
+            boolean accessRewriteDisabled, QuerySerializer serializer) {
+        List<String> nonPublicFields = new ArrayList<>(); 
+        nonPublicFields.add("snippet");
+        
+        List<String> ignoredFields = new ArrayList<>();
+        if (accessRewriteDisabled && !fieldList.isEmpty()) {
+            for (String field : fieldList) {
+                if (nonPublicFields.contains(field)) {
+                    ignoredFields.add(field);
+                }
+            }
+            if (!ignoredFields.isEmpty()) {
+                serializer.addWarning(StatusCodes.NON_PUBLIC_FIELD_IGNORED,
+                        "The requested non public fields are ignored",
+                        ignoredFields);
+            }
+        }
+    }
+    
+    private MetaQueryBuilder createMetaQuery (Integer pageIndex,
+            Integer pageInteger, String ctx, Integer pageLength,
+            Boolean cutoff, CorpusAccess corpusAccess, List<String> fieldList,
+            boolean accessRewriteDisabled,
+            boolean showTokens, boolean showSnippet) {
+        MetaQueryBuilder meta = new MetaQueryBuilder();
+        meta.addEntry("startIndex", pageIndex);
+        meta.addEntry("startPage", pageInteger);
+        meta.setSpanContext(ctx);
+        meta.addEntry("count", pageLength);
+        // todo: what happened to cutoff?
+        meta.addEntry("cutOff", cutoff);
+        meta.addEntry("snippets", (showSnippet && !accessRewriteDisabled));
+        if (!accessRewriteDisabled) {
+            meta.addEntry("tokens", showTokens);
+        }
+        
+        // meta.addMeta(pageIndex, pageInteger, pageLength, ctx,
+        // cutoff);
+        // fixme: should only apply to CQL queries per default!
+        // meta.addEntry("itemsPerResource", 1);
+        
+        if (corpusAccess.equals(CorpusAccess.FREE)){
+            meta.addEntry("timeout", 10000);
+        }
+        else{
+            meta.addEntry("timeout", 90000);
+        }
+        
+        if (fieldList != null && !fieldList.isEmpty()){
+            meta.addEntry("fields", fieldList);
+        }
+        return meta;
+    }
+
+    private List<String> convertFieldsToList (String fields) {
+        if (fields != null && !fields.isEmpty()) {
+            String[] fieldArray = fields.split(",");
+            List<String> fieldList = new ArrayList<>(fieldArray.length);
+            for (String field : fieldArray) {
+                fieldList.add(field.trim());
+            }
+            return fieldList;
+        }
+        else {
+            return new ArrayList<>();
+        }
+    }
+    
+    private String searchNeo4J (String query, int pageLength,
+            MetaQueryBuilder meta, boolean raw) throws KustvaktException {
+
+        if (raw) {
+            throw new KustvaktException(StatusCodes.ILLEGAL_ARGUMENT,
+                    "raw not supported!");
+        }
+
+        MultivaluedMap<String, String> map = new MultivaluedHashMap<String, String>();
+        map.add("q", query);
+        map.add("count", String.valueOf(pageLength));
+        map.add("lctxs", String.valueOf(meta.getSpanContext().getLeftSize()));
+        map.add("rctxs", String.valueOf(meta.getSpanContext().getRightSize()));
+        return this.graphDBhandler.getResponse(map, "distKwic");
+
+    }
+
+    private Pattern determineAvailabilityPattern (User user) {
+        Pattern p = null;
+        if (user != null) {
+            CorpusAccess corpusAccess = user.getCorpusAccess();
+            switch (corpusAccess) {
+                case PUB:
+                    p = config.getPublicLicensePattern();
+                    break;
+                case ALL:
+                    p = config.getAllLicensePattern();
+                    break;
+                default: // FREE
+                    p = config.getFreeLicensePattern();
+                    break;
+            }
+        }
+        return p;
+    }
+    
+    public String retrieveMatchInfo (
+        String corpusId, String docId,
+        String textId, String matchId, boolean info, Set<String> foundries,
+        String username, HttpHeaders headers, Set<String> layers,
+        boolean spans,
+        boolean snippet, boolean tokens,
+        boolean sentenceExpansion,
+        boolean highlights
+        ) throws KustvaktException {
+        String matchid =
+            searchKrill.getMatchId(corpusId, docId, textId, matchId);
+
+        User user = createUser(username, headers);
+        Pattern p = determineAvailabilityPattern(user);
+
+//        boolean match_only = foundries == null || foundries.isEmpty();
+        String results;
+//        try {
+
+        ArrayList<String> foundryList = null;
+        ArrayList<String> layerList = null;
+
+        if (foundries != null && !foundries.isEmpty()) {
+            foundryList = new ArrayList<String>();
+            layerList = new ArrayList<String>();
+            // EM: now without user, just list all foundries and
+            // layers
+            if (foundries.contains("*")) {
+                foundryList = config.getFoundries();
+                layerList = config.getLayers();
+            }
+            else {
+                foundryList.addAll(foundries);
+                layerList.addAll(layers);
+            }
+        } else {
+            sentenceExpansion = false;
+            spans = false;
+            info = false;
+            highlights = true;
+        };
+        
+        results = searchKrill.getMatch(
+            matchid, info, foundryList, layerList,
+            spans, snippet, tokens, highlights,
+            sentenceExpansion, p);
+//        }
+//        catch (Exception e) {
+//            jlog.error("Exception in the MatchInfo service encountered!", e);
+//            throw new KustvaktException(StatusCodes.ILLEGAL_ARGUMENT,
+//                    e.getMessage());
+//        }
+        if (DEBUG){
+            jlog.debug("MatchInfo results: " + results);
+        }
+        return results;
+    }
+
+    public String retrieveDocMetadata (String corpusId, String docId,
+            String textId, String fields, String username, HttpHeaders headers)
+            throws KustvaktException {
+        List<String> fieldList = null;
+        if (fields != null && !fields.isEmpty()){
+            fieldList = convertFieldsToList(fields);
+        }
+        Pattern p = null;
+        if (config.isMetadataRestricted()){
+            User user = createUser(username, headers);
+            p = determineAvailabilityPattern(user);
+        }
+        String textSigle = searchKrill.getTextSigle(corpusId, docId, textId);
+        return searchKrill.getFields(textSigle, fieldList, p);
+    }
+    
+    public String getCollocationBase (String query) throws KustvaktException {
+        return graphDBhandler.getResponse("distCollo", "q", query);
+    }
+    
+    public void closeIndexReader () throws KustvaktException {
+        searchKrill.closeIndexReader();
+    }
+
+    /**
+     * Return the fingerprint of the latest index revision.
+     */
+    public String getIndexFingerprint () {
+        return searchKrill.getIndexFingerprint();
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/core/service/StatisticService.java b/full/src/main/java/de/ids_mannheim/korap/core/service/StatisticService.java
new file mode 100644
index 0000000..54f5e1a
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/core/service/StatisticService.java
@@ -0,0 +1,105 @@
+package de.ids_mannheim.korap.core.service;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.response.Notifications;
+import de.ids_mannheim.korap.utils.JsonUtils;
+import de.ids_mannheim.korap.utils.KoralCollectionQueryBuilder;
+import de.ids_mannheim.korap.web.SearchKrill;
+
+@Service
+public class StatisticService extends BasicService {
+
+    @Autowired
+    private SearchKrill searchKrill;
+    @Autowired
+    private KustvaktConfiguration config;
+
+    public String retrieveStatisticsForCorpusQuery (List<String> cqList,
+            boolean isDeprecated) throws KustvaktException {
+
+        KoralCollectionQueryBuilder builder = new KoralCollectionQueryBuilder();
+        String cq = combineMultipleCorpusQuery(cqList);
+        String json = null;
+        if (cq != null && !cq.isEmpty()) {
+            builder.with(cq);
+            json = builder.toJSON();
+        }
+
+        if (json != null) {
+            checkVC(json);
+        }
+        String stats = searchKrill.getStatistics(json);
+
+        if (isDeprecated) {
+            Notifications n = new Notifications();
+            n.addWarning(StatusCodes.DEPRECATED,
+                    "Parameter corpusQuery is deprecated in favor of cq.");
+            ObjectNode warning = (ObjectNode) n.toJsonNode();
+            ObjectNode node = (ObjectNode) JsonUtils.readTree(stats);
+            node.setAll(warning);
+            stats = node.toString();
+        }
+        
+        if (stats.contains("-1")) {
+            throw new KustvaktException(StatusCodes.NO_RESULT_FOUND);
+        }
+        return stats;
+    }
+
+    private void checkVC (String json) throws KustvaktException {
+        JsonNode node = JsonUtils.readTree(json);
+        node = node.at("/collection");
+        if (node.has("ref")) {
+            String vcName = node.path("ref").asText();
+            if (vcName.contains("/")) {
+                String[] names = vcName.split("/");
+                if (names.length == 2) {
+                    vcName = names[1];
+                }
+            }
+
+            String vcInCaching = config.getVcInCaching();
+            if (vcName.equals(vcInCaching)) {
+                throw new KustvaktException(
+                        de.ids_mannheim.korap.exceptions.StatusCodes.CACHING_VC,
+                        "VC is currently busy and unaccessible due to "
+                                + "caching process",
+                        node.get("ref").asText());
+            }
+        }
+    }
+
+    public String retrieveStatisticsForKoralQuery (String koralQuery)
+            throws KustvaktException {
+        String stats = null;
+        if (koralQuery != null && !koralQuery.isEmpty()) {
+            checkVC(koralQuery);
+            stats = searchKrill.getStatistics(koralQuery);
+        }
+        else {
+            stats = searchKrill.getStatistics(null);
+        }
+        
+        if (stats.contains("-1")) {
+            throw new KustvaktException(StatusCodes.NO_RESULT_FOUND);
+        }
+        return stats;
+    }
+
+    /**
+     * Return the fingerprint of the latest index revision.
+     */
+    public String getIndexFingerprint () {
+        return searchKrill.getIndexFingerprint();
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/core/web/controller/AnnotationController.java b/full/src/main/java/de/ids_mannheim/korap/core/web/controller/AnnotationController.java
new file mode 100644
index 0000000..26d51dc
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/core/web/controller/AnnotationController.java
@@ -0,0 +1,125 @@
+package de.ids_mannheim.korap.core.web.controller;
+
+import java.io.IOException;
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import de.ids_mannheim.korap.web.utils.ResourceFilters;
+import de.ids_mannheim.korap.core.service.AnnotationService;
+import de.ids_mannheim.korap.dto.FoundryDto;
+import de.ids_mannheim.korap.dto.LayerDto;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.utils.JsonUtils;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
+import de.ids_mannheim.korap.web.filter.APIVersionFilter;
+import de.ids_mannheim.korap.web.filter.DemoUserFilter;
+import de.ids_mannheim.korap.web.filter.PiwikFilter;
+
+/**
+ * Provides services regarding annotation related information.
+ * 
+ * @author margaretha
+ *
+ */
+@Controller
+@Path("/{version}/annotation/")
+@ResourceFilters({APIVersionFilter.class, DemoUserFilter.class, PiwikFilter.class })
+@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+public class AnnotationController {
+
+    @Autowired
+    private KustvaktResponseHandler kustvaktResponseHandler;
+
+    @Autowired
+    private AnnotationService annotationService;
+
+    /**
+     * Returns information about all supported layers
+     * 
+     * @return a json serialization of all supported layers
+     */
+    @GET
+    @Path("layers")
+    public List<LayerDto> getLayers () {
+        return annotationService.getLayerDtos();
+    }
+
+
+    /**
+     * Returns a list of foundry descriptions.
+     * 
+     * @param codes
+     *            foundry-layer code or a Kleene-star
+     * @param language
+     *            2-letter language code (description language)
+     * @return a list of foundry, layer, value information in json
+     */
+    @SuppressWarnings("unchecked")
+    @POST
+    @Path("description")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public List<FoundryDto> getFoundryDescriptions (String json) {
+        if (json == null || json.isEmpty()) {
+            throw kustvaktResponseHandler
+                    .throwit(new KustvaktException(StatusCodes.MISSING_PARAMETER,
+                            "Missing a json string.", ""));
+        }
+
+        JsonNode node;
+        try {
+            node = JsonUtils.readTree(json);
+        }
+        catch (KustvaktException e1) {
+            throw kustvaktResponseHandler.throwit(e1);
+        }
+
+        String language;
+        if (!node.has("language")) {
+            language = "en";
+        }
+        else {
+            language = node.get("language").asText();
+            if (language == null || language.isEmpty()) {
+                language = "en";
+            }
+            else if (!(language.equals("en") || language.equals("de"))) {
+                throw kustvaktResponseHandler.throwit(
+                        new KustvaktException(StatusCodes.UNSUPPORTED_VALUE,
+                                "Unsupported value:", language));
+            }
+        }
+
+        List<String> codes;
+        try {
+            codes = JsonUtils.convert(node.get("codes"), List.class);
+        }
+        catch (IOException | NullPointerException e) {
+            throw kustvaktResponseHandler.throwit(new KustvaktException(
+                    StatusCodes.INVALID_ARGUMENT, "Bad argument:", json));
+        }
+        if (codes == null || codes.isEmpty()) {
+            throw kustvaktResponseHandler.throwit(
+                    new KustvaktException(StatusCodes.MISSING_ATTRIBUTE,
+                            "codes is null or empty", "codes"));
+        }
+
+        try {
+            return annotationService.getFoundryDtos(codes, language);
+        }
+        catch (KustvaktException e) {
+            throw kustvaktResponseHandler.throwit(e);
+        }
+    }
+}
+
diff --git a/full/src/main/java/de/ids_mannheim/korap/core/web/controller/SearchController.java b/full/src/main/java/de/ids_mannheim/korap/core/web/controller/SearchController.java
new file mode 100644
index 0000000..acddf23
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/core/web/controller/SearchController.java
@@ -0,0 +1,490 @@
+package de.ids_mannheim.korap.core.web.controller;// package
+                                             // de.ids_mannheim.korap.ext.web;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+
+import de.ids_mannheim.korap.web.utils.ResourceFilters;
+import de.ids_mannheim.korap.web.utils.SearchResourceFilters;
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.constant.OAuth2Scope;
+import de.ids_mannheim.korap.core.service.SearchService;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.oauth2.service.OAuth2ScopeService;
+import de.ids_mannheim.korap.security.context.TokenContext;
+import de.ids_mannheim.korap.utils.JsonUtils;
+import de.ids_mannheim.korap.utils.ServiceInfo;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
+import de.ids_mannheim.korap.web.filter.APIVersionFilter;
+import de.ids_mannheim.korap.web.filter.AdminFilter;
+import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
+import de.ids_mannheim.korap.web.filter.DemoUserFilter;
+import de.ids_mannheim.korap.web.filter.PiwikFilter;
+
+/**
+ * 
+ * @author hanl, margaretha, diewald
+ * @date 29/01/2014
+ * @lastUpdate 05/07/2019
+ * 
+ */
+@Controller
+@Path("/")
+@ResourceFilters({ APIVersionFilter.class, AuthenticationFilter.class,
+        DemoUserFilter.class, PiwikFilter.class })
+public class SearchController {
+
+    private static final boolean DEBUG = false;
+
+    private static Logger jlog = LogManager.getLogger(SearchController.class);
+    private @Context ServletContext context;
+    
+    @Autowired
+    private KustvaktResponseHandler kustvaktResponseHandler;
+
+    @Autowired
+    private SearchService searchService;
+    @Autowired
+    private OAuth2ScopeService scopeService;
+    @Autowired
+    private KustvaktConfiguration config;
+    
+    @GET
+    @Path("{version}")
+    public Response index (){
+        return Response
+            .ok(config.getApiWelcomeMessage())
+            .header("X-Index-Revision", searchService.getIndexFingerprint())
+            .build();
+    }
+    
+    @GET
+    @Path("{version}/info")
+    @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+    public Response info (){
+        Map<String, Object> m = new HashMap<>();
+        m.put("latest_api_version", config.getCurrentVersion());
+        m.put("supported_api_versions", config.getSupportedVersions());
+        m.put("kustvakt_version", ServiceInfo.getInfo().getVersion());
+        m.put("krill_version", searchService.getKrillVersion());
+        m.put("koral_version", ServiceInfo.getInfo().getKoralVersion());
+        try {
+            return Response.ok(JsonUtils.toJSON(m)).build();
+        }
+        catch (KustvaktException e) {
+            throw kustvaktResponseHandler.throwit(e);
+        }
+    }
+    
+    @POST
+    @Path("{version}/index/close")
+    // overrides the whole filters
+    @ResourceFilters({APIVersionFilter.class,AdminFilter.class})
+    public Response closeIndexReader (){
+        try {
+            searchService.closeIndexReader();
+        }
+        catch (KustvaktException e) {
+            throw kustvaktResponseHandler.throwit(e);
+        }
+        return Response.ok().build();
+    }
+    
+    
+//     EM: This web service is DISABLED until there is a need for it.
+//     ND: In case rewrite is supported, it could be used to check the authorization 
+//         scope without searching etc. In case not, it helps to compare queries in 
+//         different query languages.
+//     MH: ref query parameter removed!
+//    @GET
+//    @Path("{version}/query")
+//    @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+    public Response serializeQuery (@Context Locale locale,
+            @Context SecurityContext securityContext, @QueryParam("q") String q,
+            @QueryParam("ql") String ql, @QueryParam("v") String v,
+            @QueryParam("context") String context,
+            @QueryParam("cutoff") Boolean cutoff,
+            @QueryParam("count") Integer pageLength,
+            @QueryParam("offset") Integer pageIndex,
+            @QueryParam("page") Integer startPage,
+            @QueryParam("access-rewrite-disabled") boolean accessRewriteDisabled,
+            @QueryParam("cq") String cq) {
+        TokenContext ctx = (TokenContext) securityContext.getUserPrincipal();
+        try {
+            scopeService.verifyScope(ctx, OAuth2Scope.SERIALIZE_QUERY);
+            String result = searchService.serializeQuery(q, ql, v, cq,
+                    pageIndex, startPage, pageLength, context, cutoff,
+                    accessRewriteDisabled);
+            if (DEBUG){
+                jlog.debug("Query: " + result);
+            }
+            return Response.ok(result).build();
+        }
+        catch (KustvaktException e) {
+            throw kustvaktResponseHandler.throwit(e);
+        }
+    }
+
+    
+//    This web service is DISABLED until there is a need for it. 
+    @POST
+    @Path("{version}/search")
+    @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+    @SearchResourceFilters
+    public Response searchPost (@Context SecurityContext context,
+            @Context Locale locale, 
+            @Context HttpHeaders headers,
+            String jsonld) {
+        
+        if (DEBUG){
+            jlog.debug("Serialized search: " + jsonld);
+        }
+        
+        TokenContext ctx = (TokenContext) context.getUserPrincipal();
+        try {
+            scopeService.verifyScope(ctx, OAuth2Scope.SEARCH);
+            String result = searchService.search(jsonld, ctx.getUsername(),
+                    headers);
+            return Response.ok(result).build();
+        }
+        catch (KustvaktException e) {
+            throw kustvaktResponseHandler.throwit(e);
+        }
+    }
+
+    /** Performs for the given query 
+     * 
+     * @param securityContext
+     * @param request
+     * @param headers
+     * @param locale
+     * @param q
+     *            query
+     * @param ql
+     *            query language
+     * @param v
+     *            query language version
+     * @param ctx
+     *            result context
+     * @param cutoff
+     *            determines to limit search results to one page only
+     *            or not (default false)
+     * @param pageLength
+     *            the number of results should be included in a page
+     * @param pageIndex 
+     * @param pageInteger page number
+     * @param fields
+     *            metadata fields to be included, separated by comma
+     * @param pipes
+     *            external plugins for additional processing,
+     *            separated by comma
+     * @param accessRewriteDisabled
+     *            determine if access rewrite should be disabled
+     *            (default false)
+     * @param cq
+     *            corpus query defining a virtual corpus
+     * @param engine
+     * @return search results in JSON
+     */
+    @GET
+    @Path("{version}/search")
+    @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+    @SearchResourceFilters
+    public Response searchGet (@Context SecurityContext securityContext,
+            @Context HttpServletRequest request,
+            @Context HttpHeaders headers, @Context Locale locale,
+            @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") String fields,
+            @QueryParam("pipes") String pipes,
+            @QueryParam("access-rewrite-disabled") boolean accessRewriteDisabled,
+            @QueryParam("show-tokens") boolean showTokens,
+            @DefaultValue("true") @QueryParam("show-snippet") boolean showSnippet,
+            @QueryParam("cq") List<String> cq, 
+            @QueryParam("engine") String engine) {
+
+        TokenContext context =
+                (TokenContext) securityContext.getUserPrincipal();
+
+        String result;
+        try {
+            scopeService.verifyScope(context, OAuth2Scope.SEARCH);
+            result = searchService.search(engine, context.getUsername(),
+                    headers, q, ql, v, cq, fields, pipes, pageIndex,
+                    pageInteger, ctx, pageLength, cutoff,
+                    accessRewriteDisabled, showTokens, showSnippet);
+        }
+        catch (KustvaktException e) {
+            throw kustvaktResponseHandler.throwit(e);
+        }
+
+        return Response.ok(result).build();
+    }
+
+    // EM: legacy support
+    @Deprecated
+    @GET
+    @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+    @Path("{version}/corpus/{corpusId}/{docId}/{textId}/{matchId}/matchInfo")
+    @SearchResourceFilters
+    public Response getMatchInfo (@Context SecurityContext ctx,
+            @Context HttpHeaders headers, @Context Locale locale,
+            @PathParam("corpusId") String corpusId,
+            @PathParam("docId") String docId,
+            @PathParam("textId") String textId,
+            @PathParam("matchId") String matchId,
+            @QueryParam("foundry") Set<String> foundries,
+            @QueryParam("layer") Set<String> layers,
+            @QueryParam("spans") Boolean spans, 
+            // Highlights may also be a list of valid highlight classes
+            @QueryParam("hls") Boolean highlights) throws KustvaktException {
+
+        return retrieveMatchInfo(ctx, headers, locale, corpusId, docId, textId,
+                                 matchId, foundries, layers, spans, "true", "false",
+                                 "sentence", highlights);
+    }
+    
+    @GET
+    @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+    @Path("{version}/corpus/{corpusId}/{docId}/{textId}/{matchId}")
+    @SearchResourceFilters
+    public Response retrieveMatchInfo (@Context SecurityContext ctx,
+            @Context HttpHeaders headers, @Context Locale locale,
+            @PathParam("corpusId") String corpusId,
+            @PathParam("docId") String docId,
+            @PathParam("textId") String textId,
+            @PathParam("matchId") String matchId,
+            @QueryParam("foundry") Set<String> foundries,
+            @QueryParam("layer") Set<String> layers,
+            @QueryParam("spans") Boolean spans, 
+            @DefaultValue("true") @QueryParam("show-snippet") String snippetStr, 
+            @DefaultValue("false") @QueryParam("show-tokens") String tokensStr, 
+            @QueryParam("expand") String expansion, 
+            // Highlights may also be a list of valid highlight classes
+            @QueryParam("hls") Boolean highlights) throws KustvaktException {
+
+        TokenContext tokenContext = (TokenContext) ctx.getUserPrincipal();
+        try {
+            scopeService.verifyScope(tokenContext, OAuth2Scope.MATCH_INFO);
+        }
+        catch (KustvaktException e) {
+            throw kustvaktResponseHandler.throwit(e);
+        }
+
+        Boolean expandToSentence = true;
+        if (expansion != null
+                && (expansion.equals("false") || expansion.equals("null"))) {
+            expandToSentence = false;
+        }
+        spans = spans != null ? spans : false;
+        Boolean snippet = true;
+        Boolean tokens = false;
+        if (snippetStr != null
+                && (snippetStr.equals("false") || snippetStr.equals("null")))
+            snippet = false;
+
+        if (tokensStr != null && (tokensStr.equals("true")
+                || tokensStr.equals("1") || tokensStr.equals("yes")))
+            tokens = true;
+
+        highlights = highlights != null ? highlights : false;
+        if (layers == null || layers.isEmpty())
+            layers = new HashSet<>();
+
+        try {
+            String results = searchService.retrieveMatchInfo(corpusId, docId,
+                    textId, matchId, true, foundries,
+                    tokenContext.getUsername(), headers, layers, spans, snippet,
+                    tokens, expandToSentence, highlights);
+            return Response.ok(results).build();
+        }
+        catch (KustvaktException e) {
+            throw kustvaktResponseHandler.throwit(e);
+        }
+
+    }
+
+    /*
+     * Returns the meta data fields of a certain document
+     */
+    // This is currently identical to LiteService#getMeta(),
+    // but may need auth code to work following policies
+    @GET
+    @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+    @Path("{version}/corpus/{corpusId}/{docId}/{textId}")
+    public Response getMetadata (@PathParam("corpusId") String corpusId,
+            @PathParam("docId") String docId,
+            @PathParam("textId") String textId,
+            @QueryParam("fields") String fields,
+            @Context SecurityContext ctx,
+            @Context HttpHeaders headers
+    ) throws KustvaktException {
+        TokenContext tokenContext = (TokenContext) ctx.getUserPrincipal();
+        try {
+            String results = searchService.retrieveDocMetadata(corpusId, docId,
+                    textId, fields, tokenContext.getUsername(), headers);
+            return Response.ok(results).build();
+        }
+        catch (KustvaktException e) {
+            throw kustvaktResponseHandler.throwit(e);
+        }
+    }
+
+//  EM: This web service requires Karang and is DISABLED.
+//    @POST
+//    @Path("{version}/colloc")
+//    @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+    public Response getCollocationBase (@QueryParam("q") String query) {
+        String result;
+        try {
+            result = searchService.getCollocationBase(query);
+        }
+        catch (KustvaktException e) {
+            throw kustvaktResponseHandler.throwit(e);
+        }
+        return Response.ok(result).build();
+    }
+
+    // @GET
+    // @Path("colloc")
+    // public Response getCollocationsAll(@Context SecurityContext
+    // ctx,
+    // @Context Locale locale, @QueryParam("props") String properties,
+    // @QueryParam("sfskip") Integer sfs,
+    // @QueryParam("sflimit") Integer limit, @QueryParam("q") String
+    // query,
+    // @QueryParam("ql") String ql, @QueryParam("context") Integer
+    // context,
+    // @QueryParam("foundry") String foundry,
+    // @QueryParam("paths") Boolean wPaths) {
+    // TokenContext tokenContext = (TokenContext)
+    // ctx.getUserPrincipal();
+    // ColloQuery.ColloQueryBuilder builder;
+    // KoralCollectionQueryBuilder cquery = new
+    // KoralCollectionQueryBuilder();
+    // String result;
+    // try {
+    // User user = controller.getUser(tokenContext.getUsername());
+    // Set<VirtualCollection> resources = ResourceFinder
+    // .search(user, VirtualCollection.class);
+    // for (KustvaktResource c : resources)
+    // cquery.addResource(((VirtualCollection) c).getQuery());
+    //
+    // builder = functions
+    // .buildCollocations(query, ql, properties, context, limit,
+    // sfs, foundry, new ArrayList<Dependency>(), wPaths,
+    // cquery);
+    //
+    // result = graphDBhandler
+    // .getResponse("distCollo", "q", builder.build().toJSON());
+    // }catch (KustvaktException e) {
+    // throw KustvaktResponseHandler.throwit(e);
+    // }catch (JsonProcessingException e) {
+    // throw
+    // KustvaktResponseHandler.throwit(StatusCodes.ILLEGAL_ARGUMENT);
+    // }
+    // return Response.ok(result).build();
+    // }
+
+    // /**
+    // * @param locale
+    // * @param properties a json object string containing field, op
+    // and value
+    // for the query
+    // * @param query
+    // * @param context
+    // * @return
+    // */
+    // @GET
+    // @Path("{type}/{id}/colloc")
+    // public Response getCollocations(@Context SecurityContext ctx,
+    // @Context Locale locale, @QueryParam("props") String properties,
+    // @QueryParam("sfskip") Integer sfs,
+    // @QueryParam("sflimit") Integer limit, @QueryParam("q") String
+    // query,
+    // @QueryParam("ql") String ql, @QueryParam("context") Integer
+    // context,
+    // @QueryParam("foundry") String foundry,
+    // @QueryParam("paths") Boolean wPaths, @PathParam("id") String
+    // id,
+    // @PathParam("type") String type) {
+    // ColloQuery.ColloQueryBuilder builder;
+    // type = StringUtils.normalize(type);
+    // id = StringUtils.decodeHTML(id);
+    // TokenContext tokenContext = (TokenContext)
+    // ctx.getUserPrincipal();
+    // String result;
+    // try {
+    // KoralCollectionQueryBuilder cquery = new
+    // KoralCollectionQueryBuilder();
+    // try {
+    // User user = controller.getUser(tokenContext.getUsername());
+    //
+    // KustvaktResource resource = this.resourceHandler
+    // .findbyStrId(id, user, type);
+    //
+    // if (resource instanceof VirtualCollection)
+    // cquery.addResource(
+    // ((VirtualCollection) resource).getQuery());
+    // else if (resource instanceof Corpus)
+    // cquery.addMetaFilter("corpusID",
+    // resource.getPersistentID());
+    // else
+    // throw KustvaktResponseHandler
+    // .throwit(StatusCodes.ILLEGAL_ARGUMENT,
+    // "Type parameter not supported", type);
+    //
+    // }catch (KustvaktException e) {
+    // throw KustvaktResponseHandler.throwit(e);
+    // }catch (NumberFormatException ex) {
+    // throw KustvaktResponseHandler
+    // .throwit(StatusCodes.ILLEGAL_ARGUMENT);
+    // }
+    //
+    // builder = functions
+    // .buildCollocations(query, ql, properties, context, limit,
+    // sfs, foundry, new ArrayList<Dependency>(), wPaths,
+    // cquery);
+    //
+    // result = graphDBhandler
+    // .getResponse("distCollo", "q", builder.build().toJSON());
+    //
+    // }catch (JsonProcessingException e) {
+    // throw
+    // KustvaktResponseHandler.throwit(StatusCodes.ILLEGAL_ARGUMENT);
+    // }catch (KustvaktException e) {
+    // throw KustvaktResponseHandler.throwit(e);
+    // }
+    //
+    // return Response.ok(result).build();
+    // }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/core/web/controller/StatisticController.java b/full/src/main/java/de/ids_mannheim/korap/core/web/controller/StatisticController.java
new file mode 100644
index 0000000..c053d45
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/core/web/controller/StatisticController.java
@@ -0,0 +1,111 @@
+package de.ids_mannheim.korap.core.web.controller;
+
+import java.util.List;
+import java.util.Locale;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+
+import de.ids_mannheim.korap.web.utils.ResourceFilters;
+import de.ids_mannheim.korap.core.service.StatisticService;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.web.CoreResponseHandler;
+import de.ids_mannheim.korap.web.filter.APIVersionFilter;
+import de.ids_mannheim.korap.web.filter.PiwikFilter;
+
+/**
+ * Web services related to statistics
+ * 
+ * @author hanl
+ * @author margaretha
+ *
+ * @date 08/11/2017
+ * 
+ */
+@Controller
+@Path("{version}/statistics/")
+@ResourceFilters({ APIVersionFilter.class, PiwikFilter.class })
+@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+public class StatisticController {
+
+    private static final boolean DEBUG = false;
+    private static Logger jlog =
+            LogManager.getLogger(StatisticController.class);
+    @Autowired
+    private CoreResponseHandler kustvaktResponseHandler;
+    @Autowired
+    private StatisticService service;
+
+    /**
+     * Returns statistics of the virtual corpus defined by the given
+     * corpusQuery parameter.
+     * 
+     * @param context
+     *            SecurityContext
+     * @param locale
+     *            Locale
+     * @param cq
+     *            a collection query specifying a virtual corpus
+     * @param corpusQuery
+     *            (DEPRECATED) a collection query specifying a virtual
+     *            corpus
+     * @return statistics of the virtual corpus defined by the given
+     *         corpusQuery parameter.
+     */
+    @GET
+    public Response getStatistics (@Context SecurityContext context,
+            @Context Locale locale, @QueryParam("cq") List<String> cq,
+            @QueryParam("corpusQuery") List<String> corpusQuery) {
+
+        String stats;
+        boolean isDeprecated = false;
+        try {
+            if (cq.isEmpty() && corpusQuery != null && !corpusQuery.isEmpty()) {
+                isDeprecated = true;
+                cq = corpusQuery;
+            }
+            stats = service.retrieveStatisticsForCorpusQuery(cq, isDeprecated);
+            if (DEBUG) {
+                jlog.debug("Stats: " + stats);
+            }
+
+            return Response
+                .ok(stats)
+                .header("X-Index-Revision", service.getIndexFingerprint())
+                .build();
+        }
+        catch (KustvaktException e) {
+            throw kustvaktResponseHandler.throwit(e);
+        }
+    }
+
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON + ";charset=utf-8")
+    public Response getStatisticsFromKoralQuery (
+            @Context SecurityContext context, @Context Locale locale,
+            String koralQuery) {
+        try {
+            String stats = service.retrieveStatisticsForKoralQuery(koralQuery);
+            return Response
+                .ok(stats)
+                .header("X-Index-Revision", service.getIndexFingerprint())
+                .build();
+        }
+        catch (KustvaktException e) {
+            throw kustvaktResponseHandler.throwit(e);
+        }
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/dao/AdminDao.java b/full/src/main/java/de/ids_mannheim/korap/dao/AdminDao.java
new file mode 100644
index 0000000..fcf32d0
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/dao/AdminDao.java
@@ -0,0 +1,11 @@
+package de.ids_mannheim.korap.dao;
+
+import de.ids_mannheim.korap.user.User;
+
+public interface AdminDao {
+
+    void addAccount (User user);
+
+    boolean isAdmin (String userId);
+
+}
\ No newline at end of file
diff --git a/full/src/main/java/de/ids_mannheim/korap/dao/AnnotationDao.java b/full/src/main/java/de/ids_mannheim/korap/dao/AnnotationDao.java
new file mode 100644
index 0000000..43b7fd0
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/dao/AnnotationDao.java
@@ -0,0 +1,219 @@
+package de.ids_mannheim.korap.dao;
+
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
+import javax.persistence.PersistenceContext;
+import javax.persistence.Query;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import de.ids_mannheim.korap.constant.AnnotationType;
+import de.ids_mannheim.korap.core.entity.Annotation;
+import de.ids_mannheim.korap.core.entity.AnnotationKey;
+import de.ids_mannheim.korap.core.entity.AnnotationKey_;
+import de.ids_mannheim.korap.core.entity.AnnotationLayer;
+import de.ids_mannheim.korap.core.entity.AnnotationLayer_;
+import de.ids_mannheim.korap.core.entity.Annotation_;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.utils.ParameterChecker;
+
+/**
+ * AnnotationDao manages queries to database regarding annotations including
+ * foundry and layer pairs.
+ * 
+ * @author margaretha
+ *
+ */
+@Repository
+public class AnnotationDao {
+
+    @PersistenceContext
+    private EntityManager entityManager;
+
+    /**
+     * Retrieves all foundry-layer pairs.
+     * 
+     * @return a list of foundry-layer pairs.
+     */
+    @SuppressWarnings("unchecked")
+    public List<AnnotationLayer> getAllFoundryLayerPairs () {
+        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
+        CriteriaQuery<AnnotationLayer> query =
+                criteriaBuilder.createQuery(AnnotationLayer.class);
+        Root<AnnotationLayer> layer = query.from(AnnotationLayer.class);
+        layer.fetch(AnnotationLayer_.foundry);
+        layer.fetch(AnnotationLayer_.layer);
+        query.select(layer);
+        Query q = entityManager.createQuery(query);
+        return q.getResultList();
+    }
+
+    /**
+     * Retrieves foundry-layer pairs and their values for the given
+     * foundry and layer. If layer is empty, retrieves data for all
+     * layer in the given foundry. If foundry is empty, retrieves data
+     * for all foundry and layer pairs.
+     * 
+     * @param foundry
+     *            a foundry code
+     * @param layer
+     *            a layer code
+     * @return a list of foundry-layer pairs.
+     */
+    @SuppressWarnings("unchecked")
+    public List<AnnotationLayer> getAnnotationDescriptions (String foundry,
+            String layer) {
+
+        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
+        CriteriaQuery<Object> query = criteriaBuilder.createQuery();
+        Root<AnnotationLayer> annotationPair =
+                query.from(AnnotationLayer.class);
+        annotationPair.fetch(AnnotationLayer_.foundry);
+        annotationPair.fetch(AnnotationLayer_.layer);
+        annotationPair.fetch(AnnotationLayer_.keys);
+
+        // EM: Hibernate bug in join n:m (see AnnotationPair.values).
+        // There should not be any redundant AnnotationPair.
+        // The redundancy can be also avoided with
+        // fetch=FetchType.EAGER
+        // because Hibernate does 2 selects.
+        query.distinct(true);
+        query = query.select(annotationPair);
+
+        if (!foundry.isEmpty()) {
+            Predicate foundryPredicate = criteriaBuilder.equal(annotationPair
+                    .get(AnnotationLayer_.foundry).get(Annotation_.code),
+                    foundry);
+            if (layer.isEmpty() || layer.equals("*")) {
+                query.where(foundryPredicate);
+            }
+            else {
+                Predicate layerPredicate = criteriaBuilder.equal(annotationPair
+                        .get(AnnotationLayer_.layer).get(Annotation_.code),
+                        layer);
+                Predicate andPredicate =
+                        criteriaBuilder.and(foundryPredicate, layerPredicate);
+                query.where(andPredicate);
+            }
+        }
+
+        Query q = entityManager.createQuery(query);
+        return q.getResultList();
+    }
+
+    public Annotation retrieveAnnotation (String code, String type) {
+        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
+        CriteriaQuery<Annotation> query =
+                criteriaBuilder.createQuery(Annotation.class);
+
+        Root<Annotation> annotation = query.from(Annotation.class);
+        Predicate predicates = criteriaBuilder.and(
+                criteriaBuilder.equal(annotation.get(Annotation_.code), code),
+                criteriaBuilder.equal(annotation.get(Annotation_.type), type));
+        query.select(annotation).where(predicates);
+        Query q = entityManager.createQuery(query);
+        try {
+            return (Annotation) q.getSingleResult();
+        }
+        catch (NoResultException e) {
+            return null;
+        }
+    }
+
+    public AnnotationLayer retrieveAnnotationLayer (String foundry,
+            String layer) {
+        Annotation ann1 = retrieveAnnotation(foundry, AnnotationType.FOUNDRY);
+        Annotation ann2 = retrieveAnnotation(layer, AnnotationType.LAYER);
+
+        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
+        CriteriaQuery<AnnotationLayer> query =
+                criteriaBuilder.createQuery(AnnotationLayer.class);
+
+        Root<AnnotationLayer> annotation = query.from(AnnotationLayer.class);
+        Predicate predicates = criteriaBuilder.and(
+                criteriaBuilder.equal(annotation.get(AnnotationLayer_.foundry),
+                        ann1),
+                criteriaBuilder.equal(annotation.get(AnnotationLayer_.layer),
+                        ann2));
+        query.select(annotation).where(predicates);
+        Query q = entityManager.createQuery(query);
+        try {
+            return (AnnotationLayer) q.getSingleResult();
+        }
+        catch (NoResultException e) {
+            return null;
+        }
+
+    }
+
+    @Transactional
+    public Annotation createAnnotation (String code, String type, String text,
+            String description) {
+        Annotation ann = new Annotation(code, type, text, description);
+        entityManager.persist(ann);
+        return ann;
+    }
+
+    @Transactional
+    public AnnotationLayer createAnnotationLayer (Annotation foundry,
+            Annotation layer) throws KustvaktException {
+        ParameterChecker.checkObjectValue(foundry, "foundry");
+        ParameterChecker.checkObjectValue(layer, "layer");
+
+        AnnotationLayer annotationLayer = new AnnotationLayer();
+        annotationLayer.setFoundryId(foundry.getId());
+        annotationLayer.setLayerId(layer.getId());
+        annotationLayer.setDescription(
+                foundry.getDescription() + " " + layer.getDescription());
+        entityManager.persist(annotationLayer);
+        return annotationLayer;
+    }
+
+    @Transactional
+    public void updateAnnotationLayer (AnnotationLayer layer) {
+        entityManager.merge(layer);
+    }
+
+    @Transactional
+    public void updateAnnotationKey (AnnotationKey key) {
+        entityManager.merge(key);
+    }
+
+    @Transactional
+    public AnnotationKey createAnnotationKey (AnnotationLayer layer,
+            Annotation key) {
+        AnnotationKey annotation =
+                new AnnotationKey(layer.getId(), key.getId());
+        entityManager.persist(annotation);
+        return annotation;
+    }
+
+    public AnnotationKey retrieveAnnotationKey (AnnotationLayer layer,
+            Annotation key) {
+
+        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
+        CriteriaQuery<AnnotationKey> query =
+                criteriaBuilder.createQuery(AnnotationKey.class);
+
+        Root<AnnotationKey> annotation = query.from(AnnotationKey.class);
+        Predicate predicates = criteriaBuilder.and(
+                criteriaBuilder.equal(annotation.get(AnnotationKey_.layer),
+                        layer),
+                criteriaBuilder.equal(annotation.get(AnnotationKey_.key), key));
+        query.select(annotation).where(predicates);
+        Query q = entityManager.createQuery(query);
+        try {
+            return (AnnotationKey) q.getSingleResult();
+        }
+        catch (NoResultException e) {
+            return null;
+        }
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/dao/ResourceDao.java b/full/src/main/java/de/ids_mannheim/korap/dao/ResourceDao.java
new file mode 100644
index 0000000..c26a39d
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/dao/ResourceDao.java
@@ -0,0 +1,82 @@
+package de.ids_mannheim.korap.dao;
+
+import java.util.List;
+import java.util.Set;
+
+import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
+import javax.persistence.PersistenceContext;
+import javax.persistence.Query;
+import javax.persistence.TypedQuery;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Root;
+
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import de.ids_mannheim.korap.core.entity.AnnotationLayer;
+import de.ids_mannheim.korap.core.entity.Resource;
+import de.ids_mannheim.korap.core.entity.Resource_;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.utils.ParameterChecker;
+
+/**
+ * ResourceDao manages SQL queries regarding resource info and layers.
+ * 
+ * @author margaretha
+ *
+ */
+@Repository
+public class ResourceDao {
+
+    @PersistenceContext
+    private EntityManager entityManager;
+
+    /**
+     * Select all from the resource table
+     * 
+     * @return a list of resources
+     */
+    public List<Resource> getAllResources () {
+        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
+        CriteriaQuery<Resource> query =
+                criteriaBuilder.createQuery(Resource.class);
+        Root<Resource> resource = query.from(Resource.class);
+        query.select(resource);
+
+        TypedQuery<Resource> q = entityManager.createQuery(query);
+        return q.getResultList();
+    }
+
+    public Resource retrieveResource (String id) {
+        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
+        CriteriaQuery<Resource> query =
+                criteriaBuilder.createQuery(Resource.class);
+        Root<Resource> resource = query.from(Resource.class);
+        query.select(resource);
+        query.where(criteriaBuilder.equal(resource.get(Resource_.id), id));
+
+        Query q = entityManager.createQuery(query);
+        try {
+            return (Resource) q.getSingleResult();
+        }
+        catch (NoResultException e) {
+            return null;
+        }
+    }
+
+    @Transactional
+    public void createResource (String id, String germanTitle,
+            String englishTitle, String englishDescription,
+            Set<AnnotationLayer> layers) throws KustvaktException {
+        ParameterChecker.checkStringValue(id, "id");
+        ParameterChecker.checkStringValue(englishTitle, "en_title");
+        ParameterChecker.checkStringValue(germanTitle, "de_title");
+
+        Resource r = new Resource(id, germanTitle, englishTitle,
+                englishDescription, layers);
+        entityManager.persist(r);
+
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/dto/FoundryDto.java b/full/src/main/java/de/ids_mannheim/korap/dto/FoundryDto.java
new file mode 100644
index 0000000..c8f68b5
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/dto/FoundryDto.java
@@ -0,0 +1,59 @@
+package de.ids_mannheim.korap.dto;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Data transfer object for annotation descriptions (e.g. for
+ * Kalamar).
+ * 
+ * @author margaretha
+ * 
+ */
+@Getter
+@Setter
+@JsonInclude(Include.NON_EMPTY) // new fasterxml annotation, not used by current jersey version
+//@JsonSerialize(include=Inclusion.NON_EMPTY) // old codehouse annotation, used by jersey
+public class FoundryDto {
+
+    private String code;
+    private String description;
+    private List<Layer> layers;
+
+    @Getter
+    @Setter
+    @JsonInclude(Include.NON_EMPTY)
+//    @JsonSerialize(include=Inclusion.NON_EMPTY) // old codehouse annotation used by jersey
+    public class Layer {
+        private String code;
+        private String description;
+        private Set<Key> keys;
+    }
+
+    @Getter
+    @Setter
+    @JsonInclude(Include.NON_EMPTY)
+//    @JsonSerialize(include=Inclusion.NON_EMPTY) // old codehouse annotation used by jersey
+    public class Key implements Comparable<Key>{
+
+        private String code;
+        private String description;
+        private Map<String, String> values;
+
+        public Key (String code) {
+            this.code = code;
+        }
+        
+        @Override
+        public int compareTo (Key k) {
+            return this.code.compareTo(k.code); 
+        }
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/dto/LayerDto.java b/full/src/main/java/de/ids_mannheim/korap/dto/LayerDto.java
new file mode 100644
index 0000000..b9884d8
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/dto/LayerDto.java
@@ -0,0 +1,21 @@
+package de.ids_mannheim.korap.dto;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Data transfer object for layer description (e.g. for KorapSRU).
+ * 
+ * @author margaretha
+ *
+ */
+@Getter
+@Setter
+public class LayerDto {
+
+    private int id;
+    private String code;
+    private String layer;
+    private String foundry;
+    private String description;
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/dto/converter/AnnotationConverter.java b/full/src/main/java/de/ids_mannheim/korap/dto/converter/AnnotationConverter.java
new file mode 100644
index 0000000..b192cfd
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/dto/converter/AnnotationConverter.java
@@ -0,0 +1,156 @@
+package de.ids_mannheim.korap.dto.converter;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import org.springframework.stereotype.Component;
+
+import de.ids_mannheim.korap.core.entity.Annotation;
+import de.ids_mannheim.korap.core.entity.AnnotationKey;
+import de.ids_mannheim.korap.core.entity.AnnotationLayer;
+import de.ids_mannheim.korap.dto.FoundryDto;
+import de.ids_mannheim.korap.dto.FoundryDto.Key;
+import de.ids_mannheim.korap.dto.FoundryDto.Layer;
+import de.ids_mannheim.korap.dto.LayerDto;
+
+/**
+ * AnnotationConverter prepares data transfer objects (DTOs) from
+ * entities. The DTOs, for instance, are serialized into JSON in the
+ * controller classes and then sent as the response entity.
+ * 
+ * @author margaretha
+ *
+ */
+@Component
+public class AnnotationConverter {
+
+    /**
+     * Returns layer descriptions in a list of {@link LayerDto}s.
+     * 
+     * @param pairs
+     *            a list of {@link AnnotationLayer}s
+     * @return a list of {@link LayerDto}s
+     */
+    public List<LayerDto> convertToLayerDto (List<AnnotationLayer> pairs) {
+        List<LayerDto> layerDtos = new ArrayList<LayerDto>(pairs.size());
+        LayerDto dto;
+        String foundry, layer;
+        for (AnnotationLayer p : pairs) {
+            dto = new LayerDto();
+            dto.setId(p.getId());
+            dto.setDescription(p.getDescription());
+
+            foundry = p.getFoundry().getCode();
+            dto.setFoundry(foundry);
+
+            layer = p.getLayer().getCode();
+            dto.setLayer(layer);
+            dto.setCode(foundry + "/" + layer);
+            layerDtos.add(dto);
+        }
+
+        return layerDtos;
+    }
+
+    /**
+     * Returns foundry description in {@link FoundryDto}s
+     * 
+     * @param pairs
+     *            a list of {@link AnnotationLayer}s
+     * @param language
+     * @return a list of {@link FoundryDto}s
+     */
+    public List<FoundryDto> convertToFoundryDto (List<AnnotationLayer> pairs,
+            String language) {
+        List<FoundryDto> foundryDtos = new ArrayList<FoundryDto>(pairs.size());
+        Map<String, List<AnnotationLayer>> foundryMap = createFoundryMap(pairs);
+
+        for (String foundryCode : foundryMap.keySet()) {
+            List<AnnotationLayer> foundries = foundryMap.get(foundryCode);
+            List<Layer> layers = new ArrayList<Layer>(foundries.size());
+            FoundryDto dto = null;
+
+            for (AnnotationLayer f : foundries) {
+                if (dto == null) {
+                    Annotation foundry = f.getFoundry();
+                    dto = new FoundryDto();
+                    if (language.equals("de")) {
+                        dto.setDescription(foundry.getGermanDescription());
+                    }
+                    else {
+                        dto.setDescription(foundry.getDescription());
+                    }
+                    dto.setCode(foundry.getCode());
+                }
+
+                Annotation layer = f.getLayer();
+                Set<Key> keys = new TreeSet<>();
+
+                for (AnnotationKey ak : f.getKeys()) {
+                    Annotation a = ak.getKey();
+                    Map<String, String> values = new TreeMap<>();
+                    
+                    Key key = dto.new Key(a.getCode());
+                    if (language.equals("de")) {
+                        key.setDescription(a.getGermanDescription());
+                        for (Annotation v : ak.getValues()) {
+                            values.put(v.getCode(), v.getGermanDescription());
+                        }
+
+                    }
+                    else {
+                        key.setDescription(a.getDescription());
+                        for (Annotation v : ak.getValues()) {
+                            values.put(v.getCode(), v.getDescription());
+                        }
+                    }
+                    key.setValues(values);
+                    keys.add(key);
+                }
+
+                Layer l = dto.new Layer();
+                l.setCode(layer.getCode());
+
+                if (language.equals("de")) {
+                    l.setDescription(layer.getGermanDescription());
+                }
+                else {
+                    l.setDescription(layer.getDescription());
+                }
+
+                l.setKeys(keys);
+                layers.add(l);
+            }
+
+            dto.setLayers(layers);
+            foundryDtos.add(dto);
+        }
+
+        return foundryDtos;
+    }
+
+    private Map<String, List<AnnotationLayer>> createFoundryMap (
+            List<AnnotationLayer> pairs) {
+        Map<String, List<AnnotationLayer>> foundries =
+                new HashMap<String, List<AnnotationLayer>>();
+        for (AnnotationLayer p : pairs) {
+            String foundryCode = p.getFoundry().getCode();
+            if (foundries.containsKey(foundryCode)) {
+                foundries.get(foundryCode).add(p);
+            }
+            else {
+                List<AnnotationLayer> foundryList =
+                        new ArrayList<AnnotationLayer>();
+                foundryList.add(p);
+                foundries.put(foundryCode, foundryList);
+            }
+        }
+
+        return foundries;
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/encryption/RandomCodeGenerator.java b/full/src/main/java/de/ids_mannheim/korap/encryption/RandomCodeGenerator.java
new file mode 100644
index 0000000..bf8cca1
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/encryption/RandomCodeGenerator.java
@@ -0,0 +1,107 @@
+package de.ids_mannheim.korap.encryption;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.stream.Collectors;
+
+import javax.annotation.PostConstruct;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+
+/**
+ * Generates a random string that can be used for tokens, client id,
+ * client secret, etc.
+ * 
+ * @author margaretha
+ *
+ */
+@Component
+public class RandomCodeGenerator {
+
+    public static String alphanumeric = "34679bdfhmnprtFGHJLMNPRT";
+    public static List<Character> charList = alphanumeric.chars()
+            .mapToObj(c -> (char) c).collect(Collectors.toList());
+
+    public final static List<String> LIMITED_CHARACTERS =
+            Arrays.asList(new String[] { "3", "4", "6", "7", "9", "b", "d", "f",
+                    "h", "m", "n", "p", "r", "t", "F", "G", "H", "J", "L", "M",
+                    "N", "P", "R", "T" });
+
+    private Logger log = LogManager.getLogger(RandomCodeGenerator.class);
+    
+    @Autowired
+    public KustvaktConfiguration config;
+
+    public static SecureRandom secureRandom;
+
+    @PostConstruct
+    public void init () throws NoSuchAlgorithmException {
+        String algorithm = config.getSecureRandomAlgorithm();
+        if (!algorithm.isEmpty()) {
+            secureRandom = SecureRandom.getInstance(algorithm);
+        }
+        else {
+            secureRandom = new SecureRandom();
+        }
+        log.info(
+                "Secure random algorithm: " + secureRandom.getAlgorithm());
+    }
+
+    public String createRandomCode (KustvaktConfiguration c)
+            throws KustvaktException, NoSuchAlgorithmException {
+        config = c;
+        init();
+        return createRandomCode();
+    }
+
+    public String createRandomCode () throws KustvaktException {
+        UUID randomUUID = UUID.randomUUID();
+        byte[] uuidBytes = randomUUID.toString().getBytes();
+        byte[] secureBytes = new byte[3];
+        secureRandom.nextBytes(secureBytes);
+
+        byte[] bytes = ArrayUtils.addAll(uuidBytes, secureBytes);
+
+        try {
+            MessageDigest md = MessageDigest
+                    .getInstance(config.getMessageDigestAlgorithm());
+            md.update(bytes);
+            byte[] digest = md.digest();
+            String code = Base64.encodeBase64URLSafeString(digest);
+            md.reset();
+            return code;
+        }
+        catch (NoSuchAlgorithmException e) {
+            throw new KustvaktException(StatusCodes.INVALID_ALGORITHM,
+                    config.getMessageDigestAlgorithm()
+                            + "is not a valid MessageDigest algorithm");
+        }
+    }
+
+    public String filterRandomCode (String code) {
+        StringBuffer s = new StringBuffer();
+        for (char c : code.toCharArray()) {
+            if (!charList.contains(c)) {
+                int n = ThreadLocalRandom.current().nextInt(0,
+                        charList.size());
+                c = charList.get(n);
+            }
+            s.append(c);
+        }
+        return s.toString();
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/exceptions/DatabaseException.java b/full/src/main/java/de/ids_mannheim/korap/exceptions/DatabaseException.java
new file mode 100644
index 0000000..26190cc
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/exceptions/DatabaseException.java
@@ -0,0 +1,50 @@
+package de.ids_mannheim.korap.exceptions;
+
+import de.ids_mannheim.korap.auditing.AuditRecord;
+
+import java.util.Arrays;
+
+/**
+ * @author hanl
+ * @date 08/04/2015
+ */
+public class DatabaseException extends KustvaktException {
+
+    private DatabaseException (Object userid, Integer status, String message,
+                         String args, Exception e) {
+        super(String.valueOf(userid), status, message, args, e);
+    }
+
+    public DatabaseException (Object userid, String target, Integer status, String message,
+            String ... args) {
+        this(null, userid, target, status, message);
+    }
+
+    public DatabaseException (Exception e, Object userid, String target, Integer status, String message,
+                        String ... args) {
+        this(userid, status, message, Arrays.asList(args).toString(), e);
+        AuditRecord record = new AuditRecord(AuditRecord.CATEGORY.DATABASE);
+        record.setUserid(String.valueOf(userid));
+        record.setStatus(status);
+        record.setTarget(target);
+        record.setArgs(this.getEntity());
+        this.records.add(record);
+    }
+
+
+    public DatabaseException (KustvaktException e, Integer status, String ... args) {
+        this(e.getUserid(), e.getStatusCode(), e.getMessage(), e.getEntity(), e);
+        AuditRecord record = AuditRecord.dbRecord(e.getUserid(), status, args);
+        record.setField_1(e.string());
+        this.records.addAll(e.getRecords());
+        this.records.add(record);
+    }
+
+
+    @Override
+    public String string () {
+        return "DBExcpt{" + "status=" + getStatusCode() + ", message="
+                + getMessage() + ", args=" + getEntity() + ", userid="
+                + this.getUserid() + '}';
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/exceptions/EmptyResultException.java b/full/src/main/java/de/ids_mannheim/korap/exceptions/EmptyResultException.java
new file mode 100644
index 0000000..d52238b
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/exceptions/EmptyResultException.java
@@ -0,0 +1,20 @@
+package de.ids_mannheim.korap.exceptions;
+
+/**
+ * @author hanl
+ * @date 25/03/2014
+ */
+@Deprecated
+// even useful anymore?
+public class EmptyResultException extends KustvaktException {
+
+    public EmptyResultException (String message, String entity) {
+        super(StatusCodes.NO_RESULT_FOUND, message, entity);
+    }
+
+
+    public EmptyResultException (String entity) {
+        super(StatusCodes.NO_RESULT_FOUND, "No entity found for id", entity);
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/exceptions/KustvaktException.java b/full/src/main/java/de/ids_mannheim/korap/exceptions/KustvaktException.java
new file mode 100644
index 0000000..6ccbbce
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/exceptions/KustvaktException.java
@@ -0,0 +1,123 @@
+package de.ids_mannheim.korap.exceptions;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import de.ids_mannheim.korap.auditing.AuditRecord;
+//import de.ids_mannheim.korap.constant.TokenType;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author hanl
+ * @date 11/12/2013
+ */
+@Setter
+@Getter
+public class KustvaktException extends Exception {
+
+    private static final long serialVersionUID = -1955783565699446322L;
+    protected List<AuditRecord> records = new ArrayList<>();
+    private String userid;
+    private Integer statusCode;
+    private int responseStatus;
+    
+    private String entity;
+    private String notification;
+    private boolean isNotification;
+//    private TokenType authType;
+    private URI redirectUri;
+
+    public KustvaktException (int status) {
+        this.statusCode = status;
+    }
+    
+    public KustvaktException (int status, String ... args) {
+        super(args[0]);
+        this.statusCode = status;
+        String[] subarray = Arrays.copyOfRange(args, 1, args.length);
+        this.entity = Arrays.asList(subarray).toString();
+    }
+
+    public KustvaktException (int status, String notification, boolean isNotification) {
+        this.statusCode = status;
+        this.notification = notification;
+        this.isNotification = isNotification;
+    }
+    
+    public boolean hasNotification () {
+        return isNotification;
+    }
+
+    public KustvaktException (Object userid, int status) {
+        this(status);
+        this.userid = String.valueOf(userid);
+    }
+
+    // add throwable to parameters
+    public KustvaktException (Object userid, int status, String message,
+                              String entity) {
+        this(userid, status, message, entity, null);
+    }
+
+
+    public KustvaktException (Object userid, int status, String message,
+                              String entity, Exception e) {
+        super(message, e);
+        this.statusCode = status;
+        this.entity = entity;
+        this.userid = String.valueOf(userid);
+    }
+
+
+    public KustvaktException (Object userid, int status, String entity) {
+        super(StatusCodes.getMessage(status));
+        this.statusCode = status;
+        this.entity = entity;
+        this.userid = String.valueOf(userid);
+    }
+
+    public KustvaktException (int status, String message, String entity) {
+        super(message);
+        this.statusCode = status;
+        this.entity = entity;
+    }
+    
+    public KustvaktException (int status, String message, String entity, Throwable e) {
+        super(message, e);
+        this.statusCode = status;
+        this.entity = entity;
+    }
+    
+    public KustvaktException (int status, String message, Throwable e) {
+        super(message, e);
+        this.statusCode = status;
+    }
+
+
+    public KustvaktException (Throwable cause, int status) {
+        super(cause);
+        this.statusCode = status;
+    }
+
+
+    public KustvaktException (String message, Throwable cause, int status) {
+        super(message, cause);
+        this.statusCode = status;
+    }
+
+
+
+    public KustvaktException (String notification) {
+        this.notification = notification;
+        isNotification = true;
+    }
+
+    public String string () {
+        return "Excpt{" + "status=" + getStatusCode() + ", message="
+                + getMessage() + ", args=" + getEntity() + ", userid=" + userid
+                + '}';
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/exceptions/ServiceException.java b/full/src/main/java/de/ids_mannheim/korap/exceptions/ServiceException.java
new file mode 100644
index 0000000..99ddebe
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/exceptions/ServiceException.java
@@ -0,0 +1,62 @@
+package de.ids_mannheim.korap.exceptions;
+
+import de.ids_mannheim.korap.auditing.AuditRecord;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+/**
+ * @author hanl
+ * @date 08/04/2015
+ */
+// should be a http exception that responds to a service point
+// is the extension of the notauthorized exception!
+@Setter(AccessLevel.PROTECTED)
+@Getter(AccessLevel.PROTECTED)
+public class ServiceException extends Exception {
+
+    protected List<AuditRecord> records = new ArrayList<>();
+    private static final Logger jlog = LogManager
+            .getLogger(ServiceException.class);
+
+    private int status;
+    private String entity;
+    private Object userid;
+
+
+    protected ServiceException (Object userid, Integer status, String message,
+                                String args) {
+        super(message);
+        this.userid = userid;
+        this.status = status;
+        this.entity = args;
+        AuditRecord record = AuditRecord.serviceRecord(userid, status, args);
+        this.records.add(record);
+    }
+
+
+    @Deprecated
+    public ServiceException (Object userid, Integer status, String ... args) {
+        this(userid, status, StatusCodes.getMessage(status), Arrays
+                .asList(args).toString());
+    }
+
+
+    public ServiceException (Integer status, KustvaktException ex) {
+        this(ex.getUserid(), ex.getStatusCode(), ex.getMessage(), ex
+                .getEntity());
+        AuditRecord record = AuditRecord.serviceRecord(ex.getUserid(), status,
+                ex.getEntity());
+        record.setField_1(ex.toString());
+        this.records.add(record);
+        jlog.error("Exception: " + ex.toString());
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java b/full/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
new file mode 100644
index 0000000..c1777a4
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
@@ -0,0 +1,220 @@
+package de.ids_mannheim.korap.exceptions;
+
+import java.util.Properties;
+
+import de.ids_mannheim.korap.config.ConfigLoader;
+
+/**
+ * @author hanl, margaretha
+ * @date 07/09/2014
+ */
+public class StatusCodes {
+
+    /**
+     * 100 status codes for standard system errors
+     */
+    public static final int GENERAL_ERROR = 100;
+    public static final int NO_RESULT_FOUND = 101;
+    public static final int UNSUPPORTED_AUTHENTICATION_SCHEME = 102;
+    public static final int UNSUPPORTED_OPERATION = 103;
+    public static final int ILLEGAL_ARGUMENT = 104;
+    public static final int MISSING_PARAMETER = 105;
+    public static final int CONNECTION_ERROR = 106;
+    public static final int INVALID_ARGUMENT = 107;
+    public static final int NOT_SUPPORTED = 108;
+    public static final int NOT_ALLOWED = 109;
+    public static final int HTTPS_REQUIRED = 110;
+    public static final int INVALID_ALGORITHM = 111;
+    public static final int UNSUPPORTED_API_VERSION = 112;
+    public static final int NON_UNIQUE_RESULT_FOUND = 113;
+    public static final int NO_RESOURCE_FOUND = 114;
+    public static final int DEPRECATED = 115;
+    public static final int CACHING_VC = 116;
+    public static final int NETWORK_ENDPOINT_NOT_AVAILABLE = 117;
+    public static final int SEARCH_NETWORK_ENDPOINT_FAILED = 118;
+    
+    /**
+     * 200 status codes general JSON serialization error
+     */
+
+    public static final int SERIALIZATION_FAILED = 200;
+    public static final int DESERIALIZATION_FAILED = 201;
+    public static final int MISSING_ATTRIBUTE = 202;
+    public static final int INVALID_ATTRIBUTE = 203;
+    public static final int UNSUPPORTED_VALUE = 204;
+
+    /**
+     * 300 status codes for query language and serialization
+     * see Koral (de.ids_mannheim.korap.query.serialize.util.StatusCodes)
+     */
+
+    /**
+     *  400 status codes for rewrite functions
+     */
+
+    public static final int REWRITE_ERROR_DEFAULT = 400;
+    public static final int NON_PUBLIC_FIELD_IGNORED = 401;
+    public static final int PIPE_FAILED = 402;
+    
+//    public static final int UNSUPPORTED_RESOURCE = 402;
+    //    public static final int REWRITE_FAILED = 403;
+    //public static final int UNSUPPORTED_FOUNDRY = 403;
+    //public static final int UNSUPPORTED_CORPUS = 404;
+    //public static final int UNSUPPORTED_LAYER = 405;
+    // make a distinction between no and invalid vc?
+    //public static final int UNSUPPORTED_COLLECTION = 406;
+    //public static final int CORPUS_REWRITE = 407;
+    //public static final int FOUNDRY_REWRITE = 408;
+    //public static final int FOUNDRY_INJECTION = 409;
+    //    public static final int MISSING_RESOURCE = 405;
+//    public static final int NO_POLICY_TARGET = 406;
+//    public static final int NO_POLICY_CONDITION = 407;
+//    public static final int NO_POLICY_PERMISSION = 408;
+//    public static final int NO_POLICIES = 409;
+
+
+
+    /**
+     * 500 status codes for access control related components (also
+     * policy rewrite)
+     */
+    // todo: extend according to policy rewrite possible!
+    // policy errors
+
+
+    // database codes
+    public static final int DB_GET_FAILED = 500;
+    public static final int DB_INSERT_FAILED = 501;
+    public static final int DB_DELETE_FAILED = 502;
+    public static final int DB_UPDATE_FAILED = 503;
+
+    public static final int DB_GET_SUCCESSFUL = 504;
+    public static final int DB_INSERT_SUCCESSFUL = 505;
+    public static final int DB_DELETE_SUCCESSFUL = 506;
+    public static final int DB_UPDATE_SUCCESSFUL = 507;
+    public static final int DB_ENTRY_EXISTS = 508;
+
+
+    //    public static final int ARGUMENT_VALIDATION_FAILURE = 700;
+    // public static final int ARGUMENT_VALIDATION_FAILURE = 701;
+
+    // service status codes
+    public static final int CREATE_ACCOUNT_SUCCESSFUL = 700;
+    public static final int CREATE_ACCOUNT_FAILED = 701;
+    public static final int DELETE_ACCOUNT_SUCCESSFUL = 702;
+    public static final int DELETE_ACCOUNT_FAILED = 703;
+    public static final int UPDATE_ACCOUNT_SUCCESSFUL = 704;
+    public static final int UPDATE_ACCOUNT_FAILED = 705;
+
+    public static final int GET_ACCOUNT_SUCCESSFUL = 706;
+    public static final int GET_ACCOUNT_FAILED = 707;
+
+
+    public static final int STATUS_OK = 1000;
+    public static final int NOTHING_CHANGED = 1001;
+    public static final int REQUEST_INVALID = 1002;
+    
+//    public static final int ACCESS_DENIED = 1003;
+
+    
+    // User group and member 
+    public static final int GROUP_MEMBER_EXISTS = 1601;
+    public static final int GROUP_MEMBER_INACTIVE = 1602;
+    public static final int GROUP_MEMBER_DELETED = 1603;
+    public static final int GROUP_MEMBER_NOT_FOUND = 1604;
+    public static final int INVITATION_EXPIRED = 1605;
+    public static final int GROUP_DELETED = 1606;
+    
+    /**
+     * 1800 Oauth2 and OpenID
+     */
+
+    public static final int OAUTH2_SYSTEM_ERROR = 1800;
+    
+    public static final int CLIENT_REGISTRATION_FAILED = 1801;
+    public static final int CLIENT_DEREGISTRATION_FAILED = 1802;
+    public static final int CLIENT_AUTHENTICATION_FAILED = 1803;
+    public static final int CLIENT_AUTHORIZATION_FAILED = 1804;
+    public static final int CLIENT_NOT_FOUND = 1805;
+    public static final int INVALID_REDIRECT_URI = 1806;
+    public static final int MISSING_REDIRECT_URI = 1807;
+    public static final int INVALID_SCOPE = 1808;
+    public static final int INVALID_AUTHORIZATION = 1809;
+    public static final int INVALID_REFRESH_TOKEN = 1810;
+    
+    public static final int UNSUPPORTED_GRANT_TYPE = 1811;
+    public static final int UNSUPPORTED_AUTHENTICATION_METHOD = 1812;
+    
+    public static final int ID_TOKEN_CLAIM_ERROR = 1813;
+    public static final int ID_TOKEN_SIGNING_FAILED = 1814;
+    public static final int USER_REAUTHENTICATION_REQUIRED = 1815;
+    
+    public static final int INVALID_REFRESH_TOKEN_EXPIRY = 1816;
+    
+    /**
+     * 1850 Plugins
+     */
+
+    public static final int PLUGIN_NOT_PERMITTED = 1850;
+    public static final int PLUGIN_HAS_BEEN_INSTALLED = 1851;
+    
+    
+    /**
+     * 1900 User account and logins
+     */
+
+    public static final int LOGIN_SUCCESSFUL = 1900;
+    public static final int ALREADY_LOGGED_IN = 1901;
+
+    public static final int LOGOUT_SUCCESSFUL = 1902;
+    public static final int LOGOUT_FAILED = 1903;
+
+    public static final int ACCOUNT_CONFIRMATION_FAILED = 1904;
+    public static final int PASSWORD_RESET_FAILED = 1905;
+
+    /**
+     * 2000 status and error codes concerning authentication
+     * 
+     * Response with WWW-Authenticate header will be created 
+     * for all KustvaktExceptions with status codes 2001 or greater  
+     *  
+     * MH: service level messages and callbacks
+     */
+
+    @Deprecated
+    public static final int INCORRECT_ADMIN_TOKEN = 2000;
+    
+    public static final int AUTHENTICATION_FAILED = 2001;
+    public static final int LOGIN_FAILED = 2002;
+    public static final int EXPIRED = 2003;
+    public static final int BAD_CREDENTIALS = 2004;
+    public static final int ACCOUNT_NOT_CONFIRMED = 2005;
+    public static final int ACCOUNT_DEACTIVATED = 2006;
+
+    //    public static final int CLIENT_AUTHORIZATION_FAILED = 2013;
+    public static final int AUTHORIZATION_FAILED = 2010;
+    public static final int INVALID_ACCESS_TOKEN = 2011;
+
+    // 2020 - 2029 reserviert für LDAP-Fehlercodes - 21.04.17/FB
+    public static final int LDAP_BASE_ERRCODE = 2020;
+
+    /**/
+    private static StatusCodes codes;
+
+    private final Properties props;
+
+    private StatusCodes () {
+        this.props = ConfigLoader.loadProperties("codes.info");
+    }
+
+
+    public static final String getMessage (int code) {
+        return getCodes().props.getProperty(String.valueOf(code));
+    }
+
+    public static StatusCodes getCodes () {
+        if (codes == null) codes = new StatusCodes();
+        return codes;
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/exceptions/WrappedException.java b/full/src/main/java/de/ids_mannheim/korap/exceptions/WrappedException.java
new file mode 100644
index 0000000..534419a
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/exceptions/WrappedException.java
@@ -0,0 +1,38 @@
+package de.ids_mannheim.korap.exceptions;
+
+import de.ids_mannheim.korap.auditing.AuditRecord;
+
+import java.util.Arrays;
+
+/**
+ * @author hanl
+ * @date 08/04/2015
+ */
+// should be a http exception that responds to a service point
+// is the extension of the notauthorized exception!
+public class WrappedException extends KustvaktException {
+
+    private WrappedException (Object userid, Integer status, String message,
+                              String args, Exception rootCause) {
+        super(String.valueOf(userid), status, message, args, rootCause);
+    }
+
+
+    public WrappedException (Object userid, Integer status, String ... args) {
+        this(userid, status, "", Arrays.asList(args).toString(), null);
+        AuditRecord record = AuditRecord.serviceRecord(userid, status, args);
+        this.records.add(record);
+    }
+
+
+    public WrappedException (KustvaktException e, Integer status,
+                             String ... args) {
+        this(e.getUserid(), e.getStatusCode(), e.getMessage(), e.getEntity(), e);
+        AuditRecord record = AuditRecord.serviceRecord(e.getUserid(), status,
+                args);
+        record.setField_1(e.string());
+        this.records.addAll(e.getRecords());
+        this.records.add(record);
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/interfaces/EncryptionIface.java b/full/src/main/java/de/ids_mannheim/korap/interfaces/EncryptionIface.java
new file mode 100644
index 0000000..46f519a
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/interfaces/EncryptionIface.java
@@ -0,0 +1,75 @@
+package de.ids_mannheim.korap.interfaces;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.user.User;
+
+import java.io.UnsupportedEncodingException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Map;
+
+public interface EncryptionIface {
+
+    public enum Encryption {
+                @Deprecated
+        SIMPLE, ESAPICYPHER, BCRYPT
+    }
+
+
+    /**
+     * One-way hashing of String input. Used to canonicalize
+     * 
+     * @param input
+     * @param salt
+     * @return
+     */
+    public String secureHash (String input, String salt)
+            throws KustvaktException;
+
+
+    public String secureHash (String input);
+
+
+    /**
+     * @param plain
+     * @param hash
+     * @param salt
+     * @return
+     */
+    public boolean checkHash (String plain, String hash, String salt);
+
+
+    public boolean checkHash (String plain, String hash);
+
+
+    /**
+     * create random String to be used as authentication token
+     * 
+     * @return
+     */
+    public String createToken (boolean hash, Object ... obj);
+
+
+    public String createToken ();
+
+
+    /**
+     * create a random Integer to be used as ID for databases
+     * 
+     * @return
+     */
+    public String createRandomNumber (Object ... obj);
+
+
+    public String encodeBase ();
+
+
+   // @Deprecated
+    //public Map<String, Object> validateMap (Map<String, Object> map)
+    //        throws KustvaktException;
+
+
+    //@Deprecated
+    //public String validateEntry (String input, String type)
+    //        throws KustvaktException;
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/interfaces/EntityHandlerIface.java b/full/src/main/java/de/ids_mannheim/korap/interfaces/EntityHandlerIface.java
new file mode 100644
index 0000000..9bcf614
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/interfaces/EntityHandlerIface.java
@@ -0,0 +1,36 @@
+package de.ids_mannheim.korap.interfaces;
+
+import de.ids_mannheim.korap.exceptions.EmptyResultException;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.user.User;
+
+/**
+ * User: hanl
+ * Date: 8/19/13
+ * Time: 11:04 AM
+ */
+public interface EntityHandlerIface {
+
+    User getAccount (String username) throws EmptyResultException,
+            KustvaktException;
+
+
+    int updateAccount (User user) throws KustvaktException;
+
+
+    int createAccount (User user) throws KustvaktException;
+
+
+    int deleteAccount (Integer userid) throws KustvaktException;
+
+
+    int truncate () throws KustvaktException;
+
+
+    int resetPassphrase (String username, String uriToken, String passphrase)
+            throws KustvaktException;
+
+
+    int activateAccount (String username, String uriToken)
+            throws KustvaktException;
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/interfaces/KustvaktBaseDaoInterface.java b/full/src/main/java/de/ids_mannheim/korap/interfaces/KustvaktBaseDaoInterface.java
new file mode 100644
index 0000000..42a2a74
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/interfaces/KustvaktBaseDaoInterface.java
@@ -0,0 +1,14 @@
+package de.ids_mannheim.korap.interfaces;
+
+/**
+ * @author hanl
+ * @date 11/02/2016
+ */
+public interface KustvaktBaseDaoInterface {
+
+    int size ();
+
+
+    int truncate ();
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/interfaces/KustvaktTypeInterface.java b/full/src/main/java/de/ids_mannheim/korap/interfaces/KustvaktTypeInterface.java
new file mode 100644
index 0000000..2f66b95
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/interfaces/KustvaktTypeInterface.java
@@ -0,0 +1,10 @@
+package de.ids_mannheim.korap.interfaces;
+
+/**
+ * @author hanl
+ * @date 11/02/2016
+ */
+public interface KustvaktTypeInterface<T> {
+
+    Class<T> type ();
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/interfaces/db/AuditingIface.java b/full/src/main/java/de/ids_mannheim/korap/interfaces/db/AuditingIface.java
new file mode 100644
index 0000000..5e6866b
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/interfaces/db/AuditingIface.java
@@ -0,0 +1,80 @@
+package de.ids_mannheim.korap.interfaces.db;
+
+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;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * User: hanl
+ * Date: 8/20/13
+ * Time: 10:45 AM
+ */
+//fixme: move table to different database!
+public abstract class AuditingIface {
+
+    protected static int BATCH_SIZE = 15;
+    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 (
+            AuditRecord.CATEGORY category, DateTime day, DateTime until,
+            boolean exact, int limit);
+
+
+    public abstract <T extends AuditRecord> List<T> retrieveRecords (
+            AuditRecord.CATEGORY category, User user, int limit);
+
+
+    public abstract <T extends AuditRecord> List<T> retrieveRecords (
+            LocalDate day, int hitMax);
+
+
+    public abstract <T extends AuditRecord> List<T> retrieveRecords (
+            String userID, LocalDate start, LocalDate end, int hitMax);
+
+
+    private void addAndRun (AuditRecord record) {
+        if (buffer.size() > BATCH_SIZE) {
+            records.clear();
+            records.addAll(buffer);
+            new Thread(new Runnable() {
+                @Override
+                public void run () {
+                    apply();
+                }
+            }).start();
+            buffer.clear();
+        }
+        if (buffer.size() <= BATCH_SIZE)
+            buffer.add(record);
+    }
+
+
+    public <T extends AuditRecord> void audit (T request) {
+        addAndRun(request);
+    }
+
+
+    public <T extends AuditRecord> void audit (List<T> requests) {
+        for (T rec : requests)
+            addAndRun(rec);
+    }
+
+
+    public abstract void apply ();
+
+
+    protected List<AuditRecord> getRecordsToSave () {
+        return this.records;
+    }
+
+
+    public abstract void finish ();
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/interfaces/db/PersistenceClient.java b/full/src/main/java/de/ids_mannheim/korap/interfaces/db/PersistenceClient.java
new file mode 100644
index 0000000..9fd0f79
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/interfaces/db/PersistenceClient.java
@@ -0,0 +1,61 @@
+package de.ids_mannheim.korap.interfaces.db;
+
+import lombok.Getter;
+
+import java.io.*;
+
+@Getter
+public abstract class PersistenceClient<SOURCE> {
+
+    private SOURCE source;
+    private TYPE type;
+
+    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 ();
+
+
+    public abstract void createDatabase () throws IOException;
+
+    public enum TYPE {
+        SQL, CASSANDRA
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/interfaces/db/UserDataDbIface.java b/full/src/main/java/de/ids_mannheim/korap/interfaces/db/UserDataDbIface.java
new file mode 100644
index 0000000..23dfbc4
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/interfaces/db/UserDataDbIface.java
@@ -0,0 +1,32 @@
+package de.ids_mannheim.korap.interfaces.db;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.interfaces.KustvaktTypeInterface;
+import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.user.Userdata;
+
+/**
+ * @author hanl
+ * @date 27/01/2016
+ */
+public interface UserDataDbIface<T extends Userdata> extends
+        KustvaktTypeInterface<T> {
+
+    public int store (T data) throws KustvaktException;
+
+
+    public int update (T data) throws KustvaktException;
+
+
+    public T get (Integer id) throws KustvaktException;
+
+
+    public T get (User user) throws KustvaktException;
+
+
+    public int delete (T data) throws KustvaktException;
+
+
+    public int deleteAll () throws KustvaktException;
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/interfaces/defaults/DefaultAuditing.java b/full/src/main/java/de/ids_mannheim/korap/interfaces/defaults/DefaultAuditing.java
new file mode 100644
index 0000000..86e533e
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/interfaces/defaults/DefaultAuditing.java
@@ -0,0 +1,91 @@
+package de.ids_mannheim.korap.interfaces.defaults;
+
+import de.ids_mannheim.korap.auditing.AuditRecord;
+import de.ids_mannheim.korap.config.ContextHolder;
+import de.ids_mannheim.korap.config.Configurable;
+import de.ids_mannheim.korap.interfaces.db.AuditingIface;
+import de.ids_mannheim.korap.user.User;
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author hanl
+ * @date 05/06/2015
+ */
+@Configurable(ContextHolder.KUSTVAKT_AUDITING)
+public class DefaultAuditing extends AuditingIface {
+
+    private FileOutputStream stream;
+
+
+    public DefaultAuditing () {
+        try {
+            File f = new File("logs");
+            f.mkdirs();
+            stream = new FileOutputStream(new File(f, "default_audit.log"));
+        }
+        catch (FileNotFoundException e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    @Override
+    public <T extends AuditRecord> List<T> retrieveRecords (
+            AuditRecord.CATEGORY category, DateTime day, DateTime until,
+            boolean exact, int limit) {
+        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!");
+    }
+
+
+    @Override
+    public <T extends AuditRecord> List<T> retrieveRecords (LocalDate day,
+            int hitMax) {
+        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!");
+    }
+
+
+    @Override
+    public void apply () {
+        List<AuditRecord> rcs = getRecordsToSave();
+        try {
+            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/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ScopeService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ScopeService.java
new file mode 100644
index 0000000..53bfaf0
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ScopeService.java
@@ -0,0 +1,24 @@
+package de.ids_mannheim.korap.oauth2.service;
+
+import de.ids_mannheim.korap.constant.OAuth2Scope;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.security.context.TokenContext;
+
+/**
+ * @author margaretha
+ *
+ */
+public interface OAuth2ScopeService {
+
+    /**
+     * Verifies whether the given token context contains the required
+     * scope
+     * 
+     * @param context a token context containing authorized scopes
+     * @param requiredScope the required scope
+     * @throws KustvaktException
+     */
+    void verifyScope (TokenContext context, OAuth2Scope requiredScope)
+            throws KustvaktException;
+
+}
\ No newline at end of file
diff --git a/full/src/main/java/de/ids_mannheim/korap/rewrite/CollectionCleanRewrite.java b/full/src/main/java/de/ids_mannheim/korap/rewrite/CollectionCleanRewrite.java
new file mode 100644
index 0000000..530eb86
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/rewrite/CollectionCleanRewrite.java
@@ -0,0 +1,73 @@
+package de.ids_mannheim.korap.rewrite;
+
+import java.util.Iterator;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.user.User;
+import edu.emory.mathcs.backport.java.util.Arrays;
+
+/** EM: not used anymore. This rewrite was to remove an empty koral:doc group in operands.
+ * 
+ * @author hanl
+ * @date 28/07/2015
+ */
+public class CollectionCleanRewrite implements RewriteTask.RewriteNodeAt {
+
+    @Override
+    public KoralNode rewriteQuery (KoralNode node, KustvaktConfiguration config,
+            User user) {
+        JsonNode jsonNode = process(node.rawNode());
+        return node.wrapNode(jsonNode);
+    }
+
+
+    private JsonNode process (JsonNode root) {
+        JsonNode sub = 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();
+                }
+
+                int count = node.size();
+                // remove group element and replace with single doc
+                if (count == 1)
+                    sub = node.path(0);
+                // indicate empty group
+                else if (count == 0) // can't do anything here -- fixme: edge case?!
+                    return null;
+            }
+
+            // what happens to array nodes?
+            if (!root.equals(sub)) {
+                if (sub.isObject()) {
+                    ObjectNode ob = (ObjectNode) root;
+                    ob.remove(Arrays.asList(new String[] { "@type",
+                            "operation", "operands" }));
+                    ob.putAll((ObjectNode) sub);
+                }
+            }
+        }
+        return root;
+    }
+
+
+    @Override
+    public JsonNode rewriteResult (KoralNode node) {
+        return null;
+    }
+
+
+    @Override
+    public String at () {
+        return "/collection";
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/rewrite/FoundryInject.java b/full/src/main/java/de/ids_mannheim/korap/rewrite/FoundryInject.java
new file mode 100644
index 0000000..9146e5f
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/rewrite/FoundryInject.java
@@ -0,0 +1,62 @@
+package de.ids_mannheim.korap.rewrite;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.rewrite.KoralNode.RewriteIdentifier;
+import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.user.UserSettingProcessor;
+
+/**
+ * @author hanl, margaretha
+ * @date 30/06/2015
+ */
+public class FoundryInject implements RewriteTask.IterableRewritePath {
+
+    @Autowired
+    protected LayerMapper mapper;
+    
+    @Override
+    public KoralNode rewriteQuery (KoralNode node, KustvaktConfiguration config,
+            User user) throws KustvaktException {
+        
+        if (node.get("@type").equals("koral:span")) {
+            if (!node.isMissingNode("/wrap")){
+                node = node.at("/wrap");
+                JsonNode term = rewriteQuery(node, config, user).rawNode();
+                node.replaceAt("/wrap", term, new RewriteIdentifier("koral:term", "replace"));
+            }
+        }
+        else if (node.get("@type").equals("koral:term") && !node.has("foundry")) {
+            String layer;
+            if (node.has("layer")){
+                layer = node.get("layer");
+            }
+            else{
+                layer = node.get("key");
+            }
+            UserSettingProcessor settingProcessor = null;
+            if (user!=null){
+                settingProcessor = user.getUserSettingProcessor();
+            }
+            String foundry = mapper.findFoundry(layer, settingProcessor);
+            if (foundry != null)
+                node.put("foundry", foundry);
+        }
+        return node;
+    }
+
+    @Override
+    public String path () {
+        return "query";
+    }
+
+
+    @Override
+    public JsonNode rewriteResult (KoralNode node) {
+        return null;
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/rewrite/IdWriter.java b/full/src/main/java/de/ids_mannheim/korap/rewrite/IdWriter.java
new file mode 100644
index 0000000..1f0dad6
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/rewrite/IdWriter.java
@@ -0,0 +1,41 @@
+package de.ids_mannheim.korap.rewrite;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import de.ids_mannheim.korap.config.BeanInjectable;
+import de.ids_mannheim.korap.config.ContextHolder;
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.user.User;
+
+/**
+ * @author hanl
+ * @date 25/09/2015
+ */
+public class IdWriter implements RewriteTask.RewriteKoralToken {
+
+    private int counter;
+
+
+    public IdWriter () {
+        this.counter = 0;
+    }
+
+
+    @Override
+    public KoralNode rewriteQuery (KoralNode node, KustvaktConfiguration config,
+            User user) {
+        if (node.get("@type").equals("koral:token")) {
+            String s = extractToken(node.rawNode());
+            if (s != null && !s.isEmpty())
+                node.put("idn", s + "_" + counter++);
+        }
+        return node;
+    }
+
+
+    private String extractToken (JsonNode token) {
+        JsonNode wrap = token.path("wrap");
+        if (!wrap.isMissingNode())
+            return wrap.path("key").asText();
+        return null;
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/rewrite/KoralNode.java b/full/src/main/java/de/ids_mannheim/korap/rewrite/KoralNode.java
new file mode 100644
index 0000000..a8870b7
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/rewrite/KoralNode.java
@@ -0,0 +1,301 @@
+package de.ids_mannheim.korap.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.utils.JsonUtils;
+
+import java.util.*;
+
+/**
+ * @author hanl
+ * @date 04/07/2015
+ */
+public class KoralNode {
+    private JsonNode node;
+    private KoralRewriteBuilder rewrites;
+    private boolean remove;
+
+
+    public KoralNode (JsonNode node) {
+        this.node = node;
+        this.rewrites = new KoralRewriteBuilder();
+        this.remove = false;
+    }
+    
+    public KoralNode (JsonNode node, KoralRewriteBuilder rewrites) {
+        this.node = node;
+        this.rewrites = rewrites;
+        this.remove = false;
+    }
+
+
+    public static KoralNode wrapNode (JsonNode node) {
+        return new KoralNode(node);
+    }
+    
+    public void buildRewrites (JsonNode node) {
+        this.rewrites.build(node);
+    }
+
+
+    public void buildRewrites () {
+        this.rewrites.build(this.node);
+    }
+
+
+    @Override
+    public String toString () {
+        return this.node.toString();
+    }
+
+
+    public void put (String name, Object value) {
+        if (this.node.isObject() && this.node.path(name).isMissingNode()) {
+            ObjectNode node = (ObjectNode) this.node;
+            if (value instanceof String)
+                node.put(name, (String) value);
+            else if (value instanceof Integer)
+                node.put(name, (Integer) value);
+            else if (value instanceof JsonNode)
+                node.put(name, (JsonNode) value);
+            this.rewrites.add("injection", name);
+        }
+        else
+            throw new UnsupportedOperationException(
+                    "node doesn't support this operation");
+    }
+
+
+    public void remove (Object identifier, RewriteIdentifier ident) {
+        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 (ident != null)
+            identifier = ident.toString();
+
+        if (set) {
+            this.rewrites.add("deletion", identifier);
+        }
+    }
+
+
+    public void replace (String name, Object value, RewriteIdentifier ident) {
+        if (this.node.isObject() && this.node.has(name)) {
+            ObjectNode n = (ObjectNode) this.node;
+            if (value instanceof String)
+                n.put(name, (String) value);
+            else if (value instanceof Integer)
+                n.put(name, (Integer) value);
+            else if (value instanceof JsonNode)
+                n.put(name, (JsonNode) value);
+
+            if (ident != null)
+                name = ident.toString();
+
+            this.rewrites.add("override", name);
+        }
+    }
+    
+    public void replaceAt (String path, Object value, RewriteIdentifier ident) {
+        if (this.node.isObject() && !this.node.at(path).isMissingNode()) {
+            ObjectNode n = (ObjectNode) this.node.at(path);
+            n.removeAll();
+            n.putAll((ObjectNode)value);
+
+            String name = path;
+            if (ident != null)
+                name = ident.toString();
+
+            this.rewrites.add("override", name);
+        }
+    }
+
+    public void set (String name, Object value, RewriteIdentifier ident) {
+        if (this.node.isObject()) {
+            ObjectNode n = (ObjectNode) this.node;
+            if (value instanceof String)
+                n.put(name, (String) value);
+            else if (value instanceof Integer)
+                n.put(name, (Integer) value);
+            else if (value instanceof JsonNode)
+                n.put(name, (JsonNode) value);
+
+
+            if (ident != null)
+                name = ident.toString();
+
+            this.rewrites.add("insertion", name);
+        }
+    }
+
+    public void setAll (ObjectNode other) {
+        if (this.node.isObject()) {
+            ObjectNode n = (ObjectNode) this.node;
+            n.setAll(other);
+        }
+        this.rewrites.add("insertion",null);
+    }
+
+    public String get (String name) {
+        if (this.node.isObject())
+            return this.node.path(name).asText();
+        return null;
+    }
+
+
+    public KoralNode at (String name) {
+//        this.node = this.node.at(name);
+//        return this;
+        return new KoralNode(this.node.at(name), this.rewrites);
+    }
+
+
+    public boolean has (Object ident) {
+        if (ident instanceof String)
+            return this.node.has((String) ident);
+        else if (ident instanceof Integer)
+            return this.node.has((int) ident);
+        return false;
+    }
+
+
+    public JsonNode rawNode () {
+        return this.node;
+    }
+
+
+    public void removeNode (RewriteIdentifier ident) {
+        this.rewrites.add("deletion", ident.toString());
+        this.remove = true;
+    }
+
+    public static class RewriteIdentifier {
+
+        private String key, value;
+
+
+        public RewriteIdentifier (String key, Object value) {
+            this.key = key;
+            this.value = value.toString();
+        }
+
+
+        @Override
+        public String toString () {
+            return key + "(" + value + ")";
+        }
+
+
+    }
+
+
+    public boolean isRemove () {
+        return this.remove;
+    }
+
+
+    public static class KoralRewriteBuilder {
+
+        private List<KoralRewrite> rewrites;
+
+
+        public KoralRewriteBuilder () {
+            this.rewrites = new ArrayList<>();
+        }
+
+
+        public KoralRewriteBuilder add (String op, Object scope) {
+            KoralRewrite rewrite = new KoralRewrite();
+            rewrite.setOperation(op);
+            if (scope !=null){
+                rewrite.setScope(scope.toString());
+            }
+            this.rewrites.add(rewrite);
+            return this;
+        }
+
+
+        public JsonNode build (JsonNode node) {
+            for (KoralRewrite rewrite : this.rewrites) {
+                if (rewrite.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(rewrite.map));
+                }
+                else if (node.isObject()) {
+                    ObjectNode n = (ObjectNode) node;
+                    List l = new LinkedList<>();
+                    l.add(JsonUtils.valueToTree(rewrite.map));
+                    n.put("rewrites", JsonUtils.valueToTree(l));
+                }
+                else {
+                    //fixme: matches in result will land here. rewrites need to be placed under root node - though then there might be unclear where they belong to
+                }
+
+            }
+            this.rewrites.clear();
+            return node;
+        }
+
+    }
+
+
+
+    private static class KoralRewrite {
+
+        private Map<String, String> map;
+
+
+        private KoralRewrite () {
+            this.map = new LinkedHashMap<>();
+            this.map.put("@type", "koral:rewrite");
+            this.map.put("src", "Kustvakt");
+        }
+
+
+        public KoralRewrite setOperation (String op) {
+            if (!op.startsWith("operation:"))
+                op = "operation:" + op;
+            this.map.put("operation", op);
+            return this;
+        }
+
+
+        public KoralRewrite setScope (String scope) {
+            this.map.put("scope", scope);
+            return this;
+        }
+
+    }
+
+
+    public boolean isMissingNode (String string) {
+        return this.node.at(string).isMissingNode();
+    }
+
+
+    public int size () {
+        return this.node.size();
+    }
+
+
+    public KoralNode get (int i) {
+//        this.node = this.node.get(i);
+        return this.wrapNode(this.node.get(i));
+    }
+    
+    
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/rewrite/LayerMapper.java b/full/src/main/java/de/ids_mannheim/korap/rewrite/LayerMapper.java
new file mode 100644
index 0000000..29360d9
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/rewrite/LayerMapper.java
@@ -0,0 +1,123 @@
+package de.ids_mannheim.korap.rewrite;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import de.ids_mannheim.korap.config.Attributes;
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.user.UserSettingProcessor;
+
+/** EM:
+ *  <ul>
+ *  <li> Added default morphology foundry </li>
+ *  <li> Made this class as a spring component</li>
+ *  </ul>
+ * @author hanl, margaretha
+ * @date 14/10/2014
+ */
+@Component
+public class LayerMapper {
+
+    @Autowired
+    private KustvaktConfiguration config;
+
+    public String findFoundry (String layer) {
+        return findFoundry(layer, null);
+    }
+
+    /**
+     * find foundry entry in settings specific settings. Includes a
+     * call to #translateLayer to get the
+     * correct mapping for the layer denomination!
+     * 
+     * @param layer
+     * @return
+     */
+
+    //todo: make mapping configurable!
+    public String findFoundry (String layer, UserSettingProcessor settings) {
+        if (settings != null) {
+            switch (translateLayer(layer.toLowerCase().trim())) {
+                case "d":
+                    return (String) settings
+                            .get(Attributes.DEFAULT_FOUNDRY_RELATION);
+                case "c":
+                    return (String) settings
+                            .get(Attributes.DEFAULT_FOUNDRY_CONSTITUENT);
+                case "pos":
+                    return (String) settings
+                            .get(Attributes.DEFAULT_FOUNDRY_POS);
+                case "lemma":
+                    return (String) settings
+                            .get(Attributes.DEFAULT_FOUNDRY_LEMMA);
+                case "surface":
+                    return "opennlp";
+                // EM: added
+                case "s":
+                    return (String) settings
+                            .get(Attributes.DEFAULT_FOUNDRY_STRUCTURE);    
+                case "morphology":
+                    return (String) settings
+                            .get(Attributes.DEFAULT_FOUNDRY_MORPHOLOGY);    
+                default:
+                    // if the layer is not in this specific listing, assume a default layer
+                    // like orth or other tokenization layers
+                    return null;
+            }
+        }
+        else {
+            switch (translateLayer(layer.toLowerCase().trim())) {
+                case "d":
+                    return config.getDefault_dep();
+                case "c":
+                    return config.getDefault_const();
+                case "pos":
+                    return config.getDefault_pos();
+                case "lemma":
+                    return config.getDefault_lemma();
+                case "surface":
+                    return config.getDefault_orthography();
+                    // refers to "structure" and is used for paragraphs or sentence boundaries
+                case "s":
+                    return config.getDefaultStructureFoundry();
+                //EM: added
+                case "morphology":
+                    return config.getDefault_morphology();
+                default:
+                    // if the layer is not in this specific listing, assume a default layer
+                    // like orth or other tokenization layers
+                    return null;
+            }
+        }
+    }
+
+
+    // relevance: map to access control id references. p is usually mapped to pos, l to lemma, etc.
+    public String translateLayer (String layer) {
+        switch (layer.toLowerCase().trim()) {
+        //            case "pos":
+        //                return "p";
+        //            case "lemma":
+        //                return "l";
+            case "m":
+                return "morphology"; // EM: changed msd to morphology
+//                return "msd";
+                //todo the orth layer does not need a foundry entry
+            case "orth":
+                return "surface";
+            // EM: layer t does not exist at all   
+//            case "t":
+//                return "surface";
+            // EM: this islegacy support    
+            case "const":
+                return "c";
+            case "p":
+                return "pos";
+            case "l":
+                return "lemma";
+            default:
+                return layer;
+        }
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/rewrite/MetaConstraint.java b/full/src/main/java/de/ids_mannheim/korap/rewrite/MetaConstraint.java
new file mode 100644
index 0000000..a9380b9
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/rewrite/MetaConstraint.java
@@ -0,0 +1,39 @@
+package de.ids_mannheim.korap.rewrite;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import de.ids_mannheim.korap.config.BeanInjectable;
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.user.User;
+
+/**
+ * @author hanl
+ * @date 04/07/2015
+ */
+public class MetaConstraint implements RewriteTask.RewriteNodeAt {
+
+
+    @Override
+    public KoralNode rewriteQuery (KoralNode node, KustvaktConfiguration config,
+            User user) {
+        // redundant
+        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;
+    }
+
+
+    @Override
+    public JsonNode rewriteResult (KoralNode node) {
+        return null;
+    }
+
+
+    @Override
+    public String at () {
+        return "/meta";
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/rewrite/RewriteHandler.java b/full/src/main/java/de/ids_mannheim/korap/rewrite/RewriteHandler.java
new file mode 100644
index 0000000..d674ba6
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/rewrite/RewriteHandler.java
@@ -0,0 +1,309 @@
+package de.ids_mannheim.korap.rewrite;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.utils.JsonUtils;
+
+/**
+ * @author hanl
+ * @date 30/06/2015
+ */
+// todo: do post processing!
+//todo: load rewritenode and rewritequery automatically from classpath by default, but namespaced from package
+public class RewriteHandler{
+    //implements BeanInjectable {
+
+    private static Logger jlog = LogManager.getLogger(RewriteHandler.class);
+    private Collection<RewriteTask.IterableRewritePath> node_processors;
+    private Collection<RewriteTask.RewriteKoralToken> token_node_processors;
+    private Collection<RewriteTask> query_processors;
+
+    private Set<Class> failed_task_registration;
+    @Autowired
+    private KustvaktConfiguration config;
+
+    public RewriteHandler (List<RewriteTask> rewriteTasks) {
+        this();
+        for (RewriteTask t : rewriteTasks){
+            addProcessor(t);
+        }
+    }
+    
+    // EM: for testing
+    public RewriteHandler (KustvaktConfiguration config) {
+        this();
+        this.config=config;
+    }
+
+    public RewriteHandler () {
+        this.node_processors = new HashSet<>();
+        this.token_node_processors = new HashSet<>();
+        this.query_processors = new LinkedHashSet<>();
+        this.failed_task_registration = new HashSet<>();
+    }
+
+    public Set getFailedProcessors () {
+        return this.failed_task_registration;
+    }
+
+
+    public boolean addProcessor (RewriteTask rewriter) {
+        if (rewriter instanceof RewriteTask.RewriteKoralToken)
+            return this.token_node_processors
+                    .add((RewriteTask.RewriteKoralToken) rewriter);
+        else if (rewriter instanceof RewriteTask.IterableRewritePath)
+            return this.node_processors
+                    .add((RewriteTask.IterableRewritePath) rewriter);
+        else if (rewriter instanceof RewriteTask.RewriteQuery
+                | rewriter instanceof RewriteTask.RewriteResult)
+            return this.query_processors.add(rewriter);
+
+        this.failed_task_registration.add(rewriter.getClass());
+        return false;
+    }
+
+
+    @Override
+    public String toString () {
+        StringBuilder b = new StringBuilder();
+        b.append("--------------------------");
+        b.append("pre/post: " + this.node_processors.toString()).append("\n")
+                .append("\n")
+                .append("query: " + this.query_processors.toString())
+                .append("\n")
+                .append("koraltoken: " + this.token_node_processors.toString());
+        b.append("---------------------------");
+        return b.toString();
+    }
+
+
+    /**
+     * expects extended RewriteNode/Query class with empty default
+     * constructor
+     * 
+     * @param rewriter
+     * @return boolean if rewriter class was successfully added to
+     *         rewrite handler!
+     */
+    @Deprecated
+    public boolean add (Class<? extends RewriteTask> rewriter) {
+        RewriteTask task;
+        try {
+            Constructor c = rewriter.getConstructor();
+            task = (RewriteTask) c.newInstance();
+        }
+        catch (NoSuchMethodException | InvocationTargetException
+                | IllegalAccessException | InstantiationException e) {
+            this.failed_task_registration.add(rewriter);
+            return false;
+        }
+        return addProcessor(task);
+    }
+
+
+
+    public String processQuery (JsonNode root, User user)
+            throws KustvaktException {
+        RewriteProcess process = new RewriteProcess(root, user);
+        JsonNode pre = process.start(false);
+        return JsonUtils.toJSON(pre);
+    }
+
+
+    public String processQuery (String json, User user)
+            throws KustvaktException {
+        return processQuery(JsonUtils.readTree(json), user);
+    }
+
+
+    public String processResult (String json, User user)
+            throws KustvaktException {
+        return processResult(JsonUtils.readTree(json), user);
+    }
+
+
+    public String processResult (JsonNode node, User user)
+            throws KustvaktException {
+        RewriteProcess process = new RewriteProcess(node, user);
+        JsonNode pre = process.start(true);
+        return JsonUtils.toJSON(pre);
+    }
+
+
+    public void clear () {
+        this.node_processors.clear();
+        this.query_processors.clear();
+        this.token_node_processors.clear();
+    }
+
+
+//    public <T extends ContextHolder> void insertBeans (T beans) {
+//        this.beans = beans;
+//        this.config = beans.getConfiguration();
+//    }
+
+
+
+    public class RewriteProcess {
+
+        private static final boolean DEBUG = false;
+        private JsonNode root;
+        private User user;
+
+
+        private RewriteProcess (JsonNode root, User user) {
+            this.root = root;
+            this.user = user;
+        }
+
+
+        private KoralNode processNode (String key, JsonNode value,
+                boolean result) throws KustvaktException {
+            KoralNode kroot = KoralNode.wrapNode(value);
+            if (value.isObject()) {
+                if (value.has("operands")) {
+                    JsonNode ops = value.at("/operands");
+                    Iterator<JsonNode> it = ops.elements();
+                    while (it.hasNext()) {
+                        JsonNode next = it.next();
+                        KoralNode kn = processNode(key, next, result);
+                        if (kn.isRemove())
+                            it.remove();
+                    }
+                }
+                else if (value.path("@type").asText().equals("koral:token")) {
+                    // todo: koral:token nodes cannot be flagged for deletion --> creates the possibility for empty koral:token nodes
+                    rewrite(key, kroot,
+                            RewriteHandler.this.token_node_processors, result);
+                    return processNode(key, value.path("wrap"), result);
+                }
+                else {
+                    return rewrite(key, kroot,
+                            RewriteHandler.this.node_processors, result);
+                }
+            }
+            else if (value.isArray()) {
+                Iterator<JsonNode> it = value.elements();
+                while (it.hasNext()) {
+                    JsonNode next = it.next();
+                    KoralNode kn = processNode(key, next, result);
+                    if (kn.isRemove())
+                        it.remove();
+                }
+            }
+            return kroot;
+        }
+
+
+        private JsonNode start (boolean result) throws KustvaktException {
+            if (DEBUG){
+                jlog.debug("Running rewrite process on query "+ root);
+            }
+            if (root != null) {
+                Iterator<Map.Entry<String, JsonNode>> it = root.fields();
+                while (it.hasNext()) {
+                    Map.Entry<String, JsonNode> next = it.next();
+                    processNode(next.getKey(), next.getValue(), result);
+                }
+                processFixedNode(root, RewriteHandler.this.query_processors,
+                        result);
+            }
+            return root;
+        }
+
+
+        /**
+         * @param node
+         * @param tasks
+         * @return boolean true if node is to be removed from parent!
+         *         Only
+         *         applies if parent is an array node
+         */
+        private KoralNode rewrite (String rootNode, KoralNode node,
+                Collection<? extends RewriteTask> tasks, boolean result)
+                throws KustvaktException {
+            if (RewriteHandler.this.config == null)
+                throw new RuntimeException("KustvaktConfiguration must be set!");
+
+            for (RewriteTask task : tasks) {
+                if (DEBUG) {
+                    jlog.debug("running processor on node: " + node);
+                    jlog.debug("on processor: " + task.getClass().toString());
+                }
+
+//                if (RewriteHandler.this.beans != null
+//                        && task instanceof BeanInjectable)
+//                    ((BeanInjectable) task)
+//                            .insertBeans(RewriteHandler.this.beans);
+
+                if (task instanceof RewriteTask.IterableRewritePath) {
+                    RewriteTask.IterableRewritePath rw = (RewriteTask.IterableRewritePath) task;
+                    if (rw.path() != null && !rw.path().equals(rootNode)) {
+                        if (DEBUG){
+                            jlog.debug("skipping node: " + node);
+                        }
+                        continue;
+                    }
+                }
+                if (!result && task instanceof RewriteTask.RewriteQuery) {
+                    ((RewriteTask.RewriteQuery) task).rewriteQuery(node,
+                            RewriteHandler.this.config, this.user);
+                }
+                else if (task instanceof RewriteTask.RewriteResult) {
+                    ((RewriteTask.RewriteResult) task).rewriteResult(node);
+                }
+
+                if (node.isRemove()) {
+                    node.buildRewrites(this.root.at("/" + rootNode));
+                    break;
+                }
+                else
+                    node.buildRewrites();
+            }
+            return node;
+        }
+
+
+        // fixme: merge with processNode!
+        private void processFixedNode (JsonNode node,
+                Collection<RewriteTask> tasks, boolean post)
+                throws KustvaktException {
+            for (RewriteTask task : tasks) {
+                KoralNode next = KoralNode.wrapNode(node);
+                if (task instanceof RewriteTask.RewriteNodeAt) {
+                    RewriteTask.RewriteNodeAt rwa = (RewriteTask.RewriteNodeAt) task;
+                    if ((rwa.at() != null && !node.at(rwa.at()).isMissingNode()))
+                        next = next.at(rwa.at());
+                }
+
+                if (!post & task instanceof RewriteTask.RewriteQuery)
+                    next = ((RewriteTask.RewriteQuery) task).rewriteQuery(next,
+                            RewriteHandler.this.config, user);
+                else if (task instanceof RewriteTask.RewriteResult)
+                    ((RewriteTask.RewriteResult) task).rewriteResult(next);
+                next.buildRewrites();
+
+            }
+        }
+
+
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/rewrite/RewriteTask.java b/full/src/main/java/de/ids_mannheim/korap/rewrite/RewriteTask.java
new file mode 100644
index 0000000..79065a5
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/rewrite/RewriteTask.java
@@ -0,0 +1,75 @@
+package de.ids_mannheim.korap.rewrite;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.user.User;
+
+/**
+ * @author hanl
+ * @date 30/06/2015
+ */
+public interface RewriteTask {
+
+
+    /**
+     * unspecified query rewrite that gets injected the entire root
+     * node during preprocessing
+     */
+    interface RewriteQuery extends RewriteTask {
+        /**
+         * @param node
+         *            Json node in KoralNode wrapper
+         * @param config
+         *            {@link KustvaktConfiguration} singleton instance
+         *            to use default configuration parameters
+         * @param user
+         *            injected by rewrite handler if available. Might
+         *            cause {@link NullPointerException} if not
+         *            checked properly
+         * @return
+         */
+        KoralNode rewriteQuery (KoralNode node, KustvaktConfiguration config,
+                User user) throws KustvaktException;
+
+    }
+
+    /**
+     * Post processor targeted at result sets for queries
+     * {@link RewriteResult} queries will run
+     * after {@link IterableRewritePath} have been processed
+     */
+    interface RewriteResult extends RewriteTask {
+        JsonNode rewriteResult (KoralNode node) throws KustvaktException;
+    }
+
+    /**
+     * nodes subject to rewrites at fixed json pointer location.
+     * Json-pointer based rewrites are processed after iterable
+     * rewrites
+     * Deletion via KoralNode not allowed. Supports pre- and
+     * post-processing
+     */
+    interface RewriteNodeAt extends RewriteQuery, RewriteResult {
+        String at ();
+    }
+
+    /**
+     * terminal object nodes that are subject to rewrites through node
+     * iteration
+     * (both object and array node iteration supported)
+     */
+    interface IterableRewritePath extends RewriteQuery, RewriteResult {
+        String path ();
+    }
+
+    /**
+     * koral token nodes that are subject to rewrites
+     * Be aware that node rewrites are processed before query
+     * rewrites. Thus query rewrite may override previous node
+     * rewrites {@link RewriteKoralToken} rewrite DOES NOT support the
+     * deletion of the respective node
+     */
+    interface RewriteKoralToken extends RewriteQuery {}
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/rewrite/TreeConstraint.java b/full/src/main/java/de/ids_mannheim/korap/rewrite/TreeConstraint.java
new file mode 100644
index 0000000..1315c7a
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/rewrite/TreeConstraint.java
@@ -0,0 +1,81 @@
+package de.ids_mannheim.korap.rewrite;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import de.ids_mannheim.korap.config.BeanInjectable;
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.user.User;
+
+/**
+ * #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 implements RewriteTask.RewriteNodeAt {
+
+    private String pointer;
+
+
+    public TreeConstraint () {
+        super();
+    }
+
+
+    @Override
+    public KoralNode rewriteQuery (KoralNode node, KustvaktConfiguration config,
+            User user) {
+        System.out.println("FIND PATH " + node.rawNode().findParent(pointer));
+
+        return node;
+    }
+
+
+    @Override
+    public JsonNode rewriteResult (KoralNode node) {
+        return null;
+    }
+
+
+    @Override
+    public String at () {
+        return null;
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/security/context/KustvaktContext.java b/full/src/main/java/de/ids_mannheim/korap/security/context/KustvaktContext.java
new file mode 100644
index 0000000..6d36199
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/security/context/KustvaktContext.java
@@ -0,0 +1,46 @@
+package de.ids_mannheim.korap.security.context;
+
+import javax.ws.rs.core.SecurityContext;
+
+import java.security.Principal;
+
+/**
+ * @author hanl
+ * @date 13/05/2014
+ * 
+ *       wrapper for REST security context
+ * 
+ */
+public class KustvaktContext implements SecurityContext {
+
+    private TokenContext user;
+
+
+    public KustvaktContext (final TokenContext user) {
+        this.user = user;
+    }
+
+
+    @Override
+    public Principal getUserPrincipal () {
+        return this.user;
+    }
+
+
+    @Override
+    public boolean isUserInRole (String role) {
+        throw new UnsupportedOperationException();
+    }
+
+
+    @Override
+    public boolean isSecure () {
+        return false;
+    }
+
+
+    @Override
+    public String getAuthenticationScheme () {
+        return SecurityContext.BASIC_AUTH;
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/security/context/TokenContext.java b/full/src/main/java/de/ids_mannheim/korap/security/context/TokenContext.java
new file mode 100644
index 0000000..b31c643
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/security/context/TokenContext.java
@@ -0,0 +1,173 @@
+package de.ids_mannheim.korap.security.context;
+
+import java.io.Serializable;
+import java.time.ZonedDateTime;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import de.ids_mannheim.korap.config.Attributes;
+import de.ids_mannheim.korap.constant.TokenType;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.utils.JsonUtils;
+import de.ids_mannheim.korap.utils.TimeUtils;
+import lombok.AccessLevel;
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * EM: 
+ * - change datatype of tokenType from string to enum
+ * - added authenticationTime
+ * 
+ * @author hanl
+ * @date 27/01/2014
+ */
+@Data
+public class TokenContext implements java.security.Principal, Serializable {
+
+    private ZonedDateTime authenticationTime;
+    /**
+     * session relevant data. Are never persisted into a database
+     */
+    private String username;
+    private long expirationTime;
+    // either "session_token " / "api_token
+    private TokenType tokenType;
+    private String token;
+    private boolean secureRequired;
+
+//    @Getter(AccessLevel.PRIVATE)
+    @Setter(AccessLevel.PRIVATE)
+    private Map<String, Object> parameters;
+    private String hostAddress;
+    private String userAgent;
+
+
+    public TokenContext () {
+        this.parameters = new HashMap<>();
+        this.setUsername("");
+        this.setToken("");
+        this.setSecureRequired(false);
+        this.setExpirationTime(-1);
+    }
+
+
+    private Map statusMap () {
+        Map m = new HashMap();
+        if (username != null && !username.isEmpty())
+            m.put(Attributes.USERNAME, username);
+        m.put(Attributes.TOKEN_EXPIRATION,
+                TimeUtils.format(this.expirationTime));
+        m.put(Attributes.TOKEN, this.token);
+        m.put(Attributes.TOKEN_TYPE, this.tokenType);
+        return m;
+    }
+
+
+    public Map<String, Object> params () {
+        return new HashMap<>(parameters);
+    }
+    
+    public boolean match (TokenContext other) {
+        if (other.getToken().equals(this.token))
+            if (this.getHostAddress().equals(this.hostAddress))
+                // user agent should be irrelvant -- what about os
+                // system version?
+                // if (other.getUserAgent().equals(this.userAgent))
+                return true;
+        return false;
+    }
+
+
+    public void addContextParameter (String key, String value) {
+        this.parameters.put(key, value);
+    }
+
+
+    public void addParams (Map<String, Object> map) {
+        for (Map.Entry<String, Object> e : map.entrySet())
+            this.parameters.put(e.getKey(), String.valueOf(e.getValue()));
+    }
+
+
+    public void removeContextParameter (String key) {
+        this.parameters.remove(key);
+    }
+
+
+    public void setExpirationTime (long date) {
+        this.expirationTime = date;
+    }
+
+
+    // todo: complete
+    public static TokenContext fromJSON (String s) throws KustvaktException {
+        JsonNode node = JsonUtils.readTree(s);
+        TokenContext c = new TokenContext();
+        if (node != null) {
+            c.setUsername(node.path(Attributes.USERNAME).asText());
+            c.setToken(node.path(Attributes.TOKEN).asText());
+        }
+        return c;
+    }
+
+
+    public static TokenContext fromOAuth2 (String s) throws KustvaktException {
+        JsonNode node = JsonUtils.readTree(s);
+        TokenContext c = new TokenContext();
+        if (node != null) {
+            c.setToken(node.path("token").asText());
+            c.setTokenType(TokenType.valueOf(node.path("token_type").asText()));
+            c.setExpirationTime(node.path("expires_in").asLong());
+            c.addContextParameter("refresh_token",
+                    node.path("refresh_token").asText());
+
+        }
+        return c;
+    }
+
+
+    public boolean isValid () {
+        return (this.username != null && !this.username.isEmpty())
+                && (this.token != null && !this.token.isEmpty())
+                && (this.tokenType != null);
+    }
+
+
+    public String getToken () {
+        return token;
+    }
+
+
+    public String toJson () throws KustvaktException {
+        return JsonUtils.toJSON(this.statusMap());
+    }
+
+
+    public boolean isDemo () {
+        return User.UserFactory.isDemo(this.username);
+    }
+
+
+
+    @Override
+    public String getName () {
+        return this.getUsername();
+    }
+
+
+    public ZonedDateTime getAuthenticationTime () {
+        return authenticationTime;
+    }
+
+
+    public void setAuthenticationTime (ZonedDateTime authTime) {
+        this.authenticationTime = authTime;
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/server/KustvaktBaseServer.java b/full/src/main/java/de/ids_mannheim/korap/server/KustvaktBaseServer.java
new file mode 100644
index 0000000..e7fa88e
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/server/KustvaktBaseServer.java
@@ -0,0 +1,169 @@
+package de.ids_mannheim.korap.server;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.nio.charset.StandardCharsets;
+import java.security.NoSuchAlgorithmException;
+import java.util.Scanner;
+
+import javax.servlet.ServletContextListener;
+
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.HandlerList;
+import org.eclipse.jetty.server.handler.ShutdownHandler;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.servlet.ServletContainer;
+import org.springframework.web.context.ContextLoaderListener;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.encryption.RandomCodeGenerator;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author hanl
+ * @date 01/06/2015
+ */
+public abstract class KustvaktBaseServer {
+
+    protected static KustvaktConfiguration config;
+    protected static String springConfig = "default-config.xml";
+
+    protected static String rootPackages;
+    protected static KustvaktArgs kargs;
+
+    public KustvaktBaseServer () {
+        rootPackages = "de.ids_mannheim.korap.core.web; "
+                + "de.ids_mannheim.korap.web; "
+                + "com.fasterxml.jackson.jaxrs.json;";
+        
+        File d = new File(KustvaktConfiguration.DATA_FOLDER);
+        if (!d.exists()) {
+            d.mkdir();
+        }
+    }
+
+    protected KustvaktArgs readAttributes (String[] args) {
+        KustvaktArgs kargs = new KustvaktArgs();
+        for (int i = 0; i < args.length; i++) {
+            switch ((args[i])) {
+                case "--spring-config":
+                    kargs.setSpringConfig(args[i + 1]);
+                    break;
+                case "--port":
+                    kargs.setPort(Integer.valueOf(args[i + 1]));
+                    break;
+                case "--help":
+                    StringBuffer b = new StringBuffer();
+
+                    b.append("Parameter description: \n")
+                            .append("--spring-config  <Spring XML configuration>\n")
+                            .append("--port  <Server port number>\n")
+                            .append("--help : This help menu\n");
+                    System.out.println(b.toString());
+                    System.out.println();
+                    return (KustvaktArgs) null;
+            }
+        }
+        return kargs;
+    }
+
+    protected void start ()
+            throws KustvaktException, IOException, NoSuchAlgorithmException {
+
+        if (kargs.port == -1) {
+            kargs.setPort(config.getPort());
+        }
+
+        String adminToken="";
+        File f = new File("adminToken");
+        if (!f.exists()) {
+            RandomCodeGenerator random = new RandomCodeGenerator();
+            adminToken = random.createRandomCode(config);
+            FileOutputStream fos = new FileOutputStream(new File("adminToken"));
+            OutputStreamWriter writer =
+                    new OutputStreamWriter(fos, StandardCharsets.UTF_8.name());
+            writer.append("token=");
+            writer.append(adminToken);
+            writer.flush();
+            writer.close();
+        }
+        else {
+            Scanner scanner = new Scanner(f);
+            adminToken = scanner.nextLine().substring(6);
+            scanner.close();
+        }
+
+        Server server = new Server();
+
+        ServletContextHandler contextHandler =
+                new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
+        contextHandler.setContextPath("/");
+        
+        if (kargs.getSpringConfig()!=null) {
+            contextHandler.setInitParameter("contextConfigLocation",
+                    "file:" + kargs.getSpringConfig());
+        }
+        else {
+            contextHandler.setInitParameter("contextConfigLocation",
+                    "classpath:" + this.springConfig);
+        }
+        
+        ServletContextListener listener = new ContextLoaderListener();
+        contextHandler.addEventListener(listener);
+        contextHandler.setInitParameter("adminToken", adminToken);
+
+        ServletHolder servletHolder = new ServletHolder(new ServletContainer());
+        servletHolder.setInitParameter(
+                ServerProperties.PROVIDER_PACKAGES, rootPackages);
+        servletHolder.setInitOrder(1);
+        contextHandler.addServlet(servletHolder, config.getBaseURL());
+
+        ServerConnector connector = new ServerConnector(server);
+        connector.setPort(kargs.port);
+        connector.setIdleTimeout(60000);
+        connector.getConnectionFactory(HttpConnectionFactory.class)
+        .getHttpConfiguration().setRequestHeaderSize(64000);
+
+        ShutdownHandler shutdownHandler = new ShutdownHandler(adminToken,true,true);
+
+        HandlerList handlers = new HandlerList();
+        handlers.addHandler(shutdownHandler);
+        handlers.addHandler(contextHandler);
+
+        server.setHandler(handlers);
+
+        server.setConnectors(new Connector[] { connector });
+        try {
+            server.start();
+            server.join();
+        }
+        catch (Exception e) {
+            System.out.println("Server could not be started!");
+            System.out.println(e.getMessage());
+            e.printStackTrace();
+            System.exit(-1);
+        }
+    }
+    
+    @Setter
+    public static class KustvaktArgs {
+
+        @Getter
+        private String springConfig;
+        private int port;
+
+        public KustvaktArgs () {
+            this.port = -1;
+            this.springConfig = null;
+        }
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/user/DataFactory.java b/full/src/main/java/de/ids_mannheim/korap/user/DataFactory.java
new file mode 100644
index 0000000..21aea9a
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/user/DataFactory.java
@@ -0,0 +1,265 @@
+package de.ids_mannheim.korap.user;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+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.exceptions.KustvaktException;
+import de.ids_mannheim.korap.utils.JsonUtils;
+import de.ids_mannheim.korap.validator.Validator;
+
+/**
+ * EM: util class
+ * 
+ * @author hanl, margaretha
+ * @date 27/01/2016
+ */
+public abstract class DataFactory {
+
+    private static DataFactory factory;
+
+    private DataFactory () {}
+
+    public static DataFactory getFactory () {
+        if (factory == null)
+            factory = new DefaultFactory();
+        return factory;
+    }
+
+
+    /**
+     * if data string null, returns an empty data holding object
+     * 
+     * @param data
+     * @return
+     */
+    public abstract Object convertData (String data);
+
+
+    public abstract int size (Object data);
+
+
+    public abstract Set<String> keys (Object data);
+
+
+    public abstract Collection<Object> values (Object data);
+
+    public abstract Object validate(Object data, Validator validator) throws KustvaktException;
+
+    @Deprecated
+    public abstract Map<String, Object> fields (Object data);
+
+
+    public abstract Object getValue (Object data, String pointer);
+
+
+    public abstract boolean addValue (Object data, String field, Object value);
+
+
+    public abstract boolean removeValue (Object data, String field);
+
+
+    public abstract String toStringValue (Object data) throws KustvaktException;
+
+    public abstract Object filter(Object data, String ... keys);
+
+    public boolean checkDataType (Object data) {
+        throw new RuntimeException("Wrong data type for factory setting!");
+    }
+
+
+    /**
+     * updates data1 with values from data2
+     * 
+     * @param data1
+     *            data object that needs update
+     * @param data2
+     *            values that update data1
+     * @return
+     */
+    public abstract Object merge (Object data1, Object data2);
+
+
+
+    private static class DefaultFactory extends DataFactory {
+
+        @Override
+        public Object convertData (String data) {
+            if (data == null)
+                return JsonUtils.createObjectNode();
+            try {
+                return JsonUtils.readTree(data);
+            }
+            catch (KustvaktException e) {
+                return null;
+            }
+        }
+
+
+        @Override
+        public int size (Object data) {
+            if (checkDataType(data))
+                return ((JsonNode) data).size();
+            return -1;
+        }
+
+
+        @Override
+        public Set<String> keys (Object data) {
+            Set<String> keys = new HashSet<>();
+            if (checkDataType(data) && ((JsonNode) data).isObject()) {
+                Iterator<String> it = ((JsonNode) data).fieldNames();
+                while (it.hasNext())
+                    keys.add((String) it.next());
+            }
+            return keys;
+        }
+
+
+        @Override
+        public Collection<Object> values (Object data) {
+            return new HashSet<>();
+        }
+
+        @Override
+        public Object validate(Object data, Validator validator) throws KustvaktException {
+            if (checkDataType(data) && ((JsonNode) data).isObject()) {
+                try {
+                    @SuppressWarnings("unchecked")
+                    Map<String, Object> mdata = JsonUtils.read(toStringValue(data), HashMap.class);
+                    return validator.validateMap(mdata);
+                } catch (IOException e) {
+                    // do nothing
+                }
+            }
+            return JsonUtils.createObjectNode();
+        }
+
+
+        @Override
+        public Map<String, Object> fields (Object data) {
+            return new HashMap<>();
+        }
+
+
+        @Override
+        public Object getValue (Object data, String key) {
+            if (checkDataType(data)) {
+                JsonNode value;
+                if (key.startsWith("/"))
+                    value = ((JsonNode) data).at(key);
+                else
+                    value = ((JsonNode) data).path(key);
+
+                if (value.canConvertToInt())
+                    return value.asInt();
+                else if (value.isBoolean())
+                    return value.asBoolean();
+                else if (value.isTextual())
+                    return value.asText();
+            }
+            return null;
+        }
+
+
+        //fixme: test that this works with different types
+        @Override
+        public boolean addValue (Object data, String field, Object value) {
+            if (checkDataType(data)) {
+                if (((JsonNode) data).isObject()) {
+                    ObjectNode node = (ObjectNode) data;
+                    if (value instanceof String)
+                        node.put(field, (String) value);
+                    if (value instanceof Boolean)
+                        node.put(field, (Boolean) value);
+                    if (value instanceof Integer)
+                        node.put(field, (Integer) value);
+                    if (value instanceof JsonNode)
+                        node.set(field, (JsonNode) value);
+                    // EM: added
+                    if (value instanceof Collection<?>){
+                        Collection<?> list = (Collection<?>) value;
+                        ArrayNode arrayNode = JsonUtils.createArrayNode();
+                        for (Object o : list){
+                            addValue(arrayNode, null, o);
+                        }
+                        node.set(field,arrayNode);
+                    }
+                    return true;
+                }
+                else if (((JsonNode) data).isArray()) {
+                    ArrayNode node = (ArrayNode) data;
+                    if (value instanceof String)
+                        node.add((String) value);
+                    if (value instanceof Boolean)
+                        node.add((Boolean) value);
+                    if (value instanceof Integer)
+                        node.add((Integer) value);
+                    return true;
+                }
+            }
+            return false;
+        }
+
+
+        @Override
+        public boolean removeValue (Object data, String field) {
+            if (checkDataType(data) && ((JsonNode) data).isObject()) {
+                ObjectNode node = (ObjectNode) data;
+                node.remove(field);
+                return true;
+            }
+            return false;
+        }
+
+
+        @Override
+        public String toStringValue (Object data) throws KustvaktException {
+            if (data instanceof JsonNode)
+                return JsonUtils.toJSON(data);
+            return data.toString();
+        }
+
+        @Override
+        public Object filter(Object data, String... keys) {
+            if (checkDataType(data) && ((JsonNode) data).isObject()) {
+                ObjectNode node = ((JsonNode) data).deepCopy();
+                return node.retain(keys);
+            }
+            return JsonUtils.createObjectNode();
+        }
+
+
+        @Override
+        public boolean checkDataType (Object data) {
+            if (!(data instanceof JsonNode))
+                super.checkDataType(data);
+            return true;
+        }
+
+
+        @Override
+        public Object merge (Object data1, Object data2) {
+            if (checkDataType(data1) && checkDataType(data2)) {
+                if (((JsonNode) data1).isObject()
+                        && ((JsonNode) data2).isObject()) {
+                    ((ObjectNode) data1).setAll((ObjectNode) data2);
+                }
+                else if (((JsonNode) data1).isArray()
+                        && ((JsonNode) data2).isArray()) {
+                    ((ArrayNode) data1).addAll((ArrayNode) data2);
+                }
+            }
+            return data1;
+        }
+
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/user/DemoUser.java b/full/src/main/java/de/ids_mannheim/korap/user/DemoUser.java
new file mode 100644
index 0000000..5a48bcb
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/user/DemoUser.java
@@ -0,0 +1,29 @@
+package de.ids_mannheim.korap.user;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+
+@Getter
+@Setter
+public class DemoUser extends User implements Serializable {
+    private static final long serialVersionUID = -5015206272520970500L;
+    public static final String DEMOUSER_NAME = "guest";
+    public static final Integer DEMOUSER_ID = 1654234534;
+    private static final long ACCOUNT_CREATED = 1377102171202L;
+    public static final String PASSPHRASE = "demo";
+
+
+    protected DemoUser () {
+        super(DEMOUSER_NAME, 2);
+        this.setId(-1);
+        this.setAccountCreation(ACCOUNT_CREATED);
+    }
+
+
+    protected User clone () {
+        return new DemoUser();
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/user/GenericUserData.java b/full/src/main/java/de/ids_mannheim/korap/user/GenericUserData.java
new file mode 100644
index 0000000..cf254dd
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/user/GenericUserData.java
@@ -0,0 +1,24 @@
+package de.ids_mannheim.korap.user;
+
+/**
+ * Created by hanl on 07.06.16.
+ */
+public class GenericUserData extends Userdata {
+
+
+    public GenericUserData () {
+        super(-1);
+    }
+
+
+    @Override
+    public String[] requiredFields () {
+        return new String[0];
+    }
+
+
+    @Override
+    public String[] defaultFields () {
+        return new String[0];
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/user/KorAPUser.java b/full/src/main/java/de/ids_mannheim/korap/user/KorAPUser.java
new file mode 100644
index 0000000..8f51378
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/user/KorAPUser.java
@@ -0,0 +1,80 @@
+package de.ids_mannheim.korap.user;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class KorAPUser extends User {
+    private static Logger jlog = LogManager.getLogger(KorAPUser.class);
+    private static final long serialVersionUID = -7108308497625884584L;
+
+    //fixme: accountlink to shibboleth account
+    private String accountLink;
+
+    private String password;
+    private String URIFragment;
+    private Long URIExpiration;
+
+
+    public KorAPUser (String username) {
+        super(username, 0);
+        this.URIFragment = "";
+        this.URIExpiration = 0L;
+    }
+
+
+    public KorAPUser (Integer id, String username) {
+        this(username);
+        this.setId(id);
+    }
+
+
+    public KorAPUser () {
+        super();
+    }
+
+
+    @Override
+    protected User clone () {
+        KorAPUser user = new KorAPUser(this.getUsername());
+        user.setUsername(this.getUsername());
+        user.setAccountCreation(this.getAccountCreation());
+        return user;
+    }
+
+
+    @Override
+    public int hashCode () {
+        int result = super.hashCode();
+        result = 31 * result + (jlog != null ? jlog.hashCode() : 0);
+        result = 31 * result + (password != null ? password.hashCode() : 0);
+        result = 31 * result
+                + (URIFragment != null ? URIFragment.hashCode() : 0);
+        result = 31 * result
+                + (URIExpiration != null ? URIExpiration.hashCode() : 0);
+        return result;
+    }
+
+
+    @Override
+    public boolean equals (Object o) {
+        if (this == o)
+            return true;
+        if (!(o instanceof KorAPUser))
+            return false;
+        if (!super.equals(o))
+            return false;
+
+        KorAPUser korAPUser = (KorAPUser) o;
+        if (URIExpiration != korAPUser.URIExpiration)
+            return false;
+        if (URIFragment != null ? !URIFragment.equals(korAPUser.URIFragment)
+                : korAPUser.URIFragment != null)
+            return false;
+        return true;
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/user/User.java b/full/src/main/java/de/ids_mannheim/korap/user/User.java
new file mode 100644
index 0000000..eb6cf53
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/user/User.java
@@ -0,0 +1,298 @@
+package de.ids_mannheim.korap.user;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.joda.time.DateTime;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import de.ids_mannheim.korap.config.Attributes;
+import de.ids_mannheim.korap.config.ParamFields;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.utils.JsonUtils;
+import de.ids_mannheim.korap.utils.TimeUtils;
+import de.ids_mannheim.korap.web.utils.KustvaktMap;
+import lombok.AccessLevel;
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+
+@Data
+public abstract class User implements Serializable {
+
+    //EM: add
+    private String email;
+    //EM: finish
+    
+    private Integer id;
+    // in local its username, in shib it's edupersonPrincipalName
+    private String username;
+    private Long accountCreation;
+    private boolean isAccountLocked;
+    private int type;
+    private ParamFields fields;
+    @Getter(AccessLevel.PRIVATE)
+    @Setter(AccessLevel.PRIVATE)
+    private UserSettingProcessor settings;
+    //todo: remove!
+    @Getter(AccessLevel.PRIVATE)
+    @Setter(AccessLevel.PRIVATE)
+    private UserDetails details;
+    @Getter(AccessLevel.PRIVATE)
+    @Setter(AccessLevel.PRIVATE)
+    private List<UserQuery> queries;
+
+    private UserSettingProcessor userSettingProcessor;
+
+//    private boolean isSystemAdmin;
+
+    // Values for corpusAccess:
+    public enum CorpusAccess	 {
+    	FREE, 	// Access to licence free corpora only, without login   
+        PUB,	// Access to public (= öffentliche Korpora) only, externes Login.
+        ALL 	// Access to all corpora, internes Login.
+    	};
+    	
+    @Getter
+    @Setter
+    private CorpusAccess corpusAccess = CorpusAccess.FREE;
+        
+    // values for location (set using the X-forwarded-for Header):
+    public enum Location  {
+        INTERN, 	// KorAP accessed by internal Client (inside intranet).
+        EXTERN		// KorAP accessed by external Client (outside intranet).
+    };
+        
+    @Getter
+    @Setter
+    private Location location = Location.EXTERN;
+
+    
+    protected User () {
+        this.fields = new ParamFields();
+        this.accountCreation = TimeUtils.getNow().getMillis();
+        this.isAccountLocked = false;
+        this.username = "";
+        this.id = -1;
+        this.location 		= Location.EXTERN;
+        this.corpusAccess 	= CorpusAccess.FREE;
+    }
+
+
+    protected User (int type) {
+        this();
+        this.type = type;
+    }
+
+
+    protected User (String username, int type) {
+        this(type);
+        this.username = username;
+    }
+
+
+    public void addField (ParamFields.Param param) {
+        this.fields.add(param);
+    }
+
+
+    public <T extends ParamFields.Param> T getField (Class<T> cl) {
+        return this.fields.get(cl);
+    }
+
+    public void setId (Integer id) {
+        this.id = id;
+        //        if (this.settings != null)
+        //            this.settings.setUserID(this.id);
+        //        if (this.details != null)
+        //            this.details.setUserID(this.id);
+    }
+
+
+    public Map<String, Object> toMap () {
+        Map map = new HashMap();
+        map.put(Attributes.USERNAME, this.username);
+        //TimeUtils.format(new DateTime(this.accountCreation))
+        map.put(Attributes.ACCOUNT_CREATION, this.accountCreation);
+
+        //        if (this.getDetails() != null)
+        //            map.putAll(this.getDetails().toMap());
+        return map;
+    }
+
+
+    public Map toCache () {
+        Map map = new HashMap();
+        map.put(Attributes.ID, this.id);
+        map.put(Attributes.USERNAME, this.username);
+        map.put(Attributes.ACCOUNT_CREATION,
+                TimeUtils.format(new DateTime(this.accountCreation)));
+        map.put(Attributes.ACCOUNTLOCK, this.isAccountLocked);
+        map.put(Attributes.TYPE, this.type);
+        return map;
+    }
+
+
+    @Override
+    public boolean equals (Object o) {
+        if (this == o)
+            return true;
+        if (!(o instanceof User))
+            return false;
+        User user = (User) o;
+        if (!username.equals(user.username))
+            return false;
+        return true;
+    }
+
+//    public boolean isAdmin () {
+//        return this.getUsername().equals(ADMINISTRATOR_ID);
+//    }
+
+
+    protected abstract User clone ();
+
+
+    @Override
+    public String toString () {
+        final StringBuffer sb = new StringBuffer();
+        sb.append("id='").append(id).append('\'');
+        sb.append(", username='").append(username).append('\'');
+        return sb.toString();
+    }
+
+    public String locationtoString()
+    
+    {
+    	if( this.location == Location.INTERN)
+    		return "INTERN";
+    	else if( this.location == Location.EXTERN )
+    		return "EXTERN";
+    	else
+    		return "???";
+    }
+    
+    public String accesstoString()
+    
+    {
+    	if( this.corpusAccess == CorpusAccess.ALL )
+    		return "ALL";
+    	else if( this.corpusAccess == CorpusAccess.PUB )
+    		return "PUB";
+    	else if( this.corpusAccess == CorpusAccess.FREE )
+    		return "FREE";
+    	else
+    		return "???";
+    }
+    
+    public static class UserFactory {
+
+        public static KorAPUser getUser (String username) {
+            return new KorAPUser(username);
+        }
+
+
+        public static KorAPUser getUser (String username, String password) {
+            KorAPUser user = new KorAPUser(username);
+            user.setPassword(password);
+            return user;
+        }
+
+//        public static KorAPUser getAdmin () {
+//            return new KorAPUser(ADMINISTRATOR_ID, ADMINISTRATOR_NAME);
+//        }
+
+
+        public static DemoUser getDemoUser () {
+            return new DemoUser();
+        }
+
+
+        public static DemoUser getDemoUser (Integer id) {
+            DemoUser demo = new DemoUser();
+            demo.setId(id);
+            return demo;
+        }
+
+
+        public static boolean isDemo (String username) {
+            return new DemoUser().getUsername().equalsIgnoreCase(username);
+        }
+
+
+//        public static ShibUser getShibInstance (String eduPersonID,
+//                String mail, String cn) {
+//            ShibUser u = new ShibUser(eduPersonID);
+//            u.setAffiliation("");
+//            u.setMail(mail);
+//            u.setUsername(eduPersonID);
+//            u.setCn(cn);
+//            return u;
+//        }
+
+
+        public static KorAPUser toKorAPUser (Map<String, Object> map) {
+            KorAPUser user = UserFactory.getUser((String) map
+                    .get(Attributes.USERNAME));
+            user.setPassword((String) map.get(Attributes.PASSWORD));
+            int id = map.get(Attributes.ID) == null ? -1 : (int) map
+                    .get(Attributes.ID);
+            if (id != -1)
+                user.setId(id);
+            long cr = map.get(Attributes.ACCOUNT_CREATION) == null ? -1
+                    : (long) map.get(Attributes.ACCOUNT_CREATION);
+            if (cr != -1)
+                user.setAccountCreation((Long) map
+                        .get(Attributes.ACCOUNT_CREATION));
+            return user;
+        }
+
+
+        public static User toUser (Map<String, Object> map) {
+            KustvaktMap kmap = new KustvaktMap(map);
+            int type = map.get(Attributes.TYPE) == null ? 0 : (Integer) kmap
+                    .get(Attributes.TYPE, Integer.class);
+            User user;
+            long created = -1;
+            int id = kmap.get(Attributes.ID, Integer.class) == null ? -1
+                    : (Integer) kmap.get(Attributes.ID, Integer.class);
+
+            if (map.get(Attributes.ACCOUNT_CREATION) != null)
+                created = TimeUtils.getTime(kmap.get(Attributes.ACCOUNT_CREATION))
+                        .getMillis();
+            switch (type) {
+                case 0:
+                    user = UserFactory.getUser(kmap.get(Attributes.USERNAME));
+                    if (id != -1)
+                        user.setId((Integer) kmap.get(Attributes.ID,
+                                Integer.class));
+                    user.setAccountLocked(map.get(Attributes.ACCOUNTLOCK) == null ? false
+                            : (Boolean) kmap.get(Attributes.ACCOUNTLOCK,
+                                    Boolean.class));
+                    user.setAccountCreation(created);
+                    break;
+                default:
+                    user = UserFactory.getDemoUser();
+                    user.setAccountCreation(created);
+            }
+            return user;
+        }
+
+
+        public static KorAPUser toUser (String value) throws KustvaktException {
+            JsonNode node = JsonUtils.readTree(value);
+            KorAPUser user = UserFactory.getUser(node.path(Attributes.USERNAME)
+                    .asText());
+            user.setAccountLocked(node.path(Attributes.ACCOUNTLOCK).asBoolean());
+            user.setAccountLink(node.path(Attributes.ACCOUNTLINK).asText());
+            user.setAccountCreation(node.path(Attributes.ACCOUNT_CREATION)
+                    .asLong());
+            user.setPassword(node.path(Attributes.PASSWORD).asText());
+            return user;
+        }
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/user/UserDetails.java b/full/src/main/java/de/ids_mannheim/korap/user/UserDetails.java
new file mode 100644
index 0000000..9901592
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/user/UserDetails.java
@@ -0,0 +1,36 @@
+package de.ids_mannheim.korap.user;
+
+import de.ids_mannheim.korap.config.Attributes;
+
+/**
+ * @author hanl
+ * @date 22/01/2016
+ *       persistence issue with query request
+ */
+public class UserDetails extends Userdata {
+
+
+    public UserDetails() {
+
+    }
+
+    public UserDetails(Integer userid) {
+        super(userid);
+    }
+
+    //todo: make configurable!
+    @Override
+    public String[] requiredFields () {
+        return new String[] { Attributes.EMAIL, Attributes.ADDRESS,
+                Attributes.LASTNAME, Attributes.FIRSTNAME };
+    }
+
+
+    @Override
+    public String[] defaultFields () {
+        return new String[] { Attributes.EMAIL, Attributes.ADDRESS,
+                Attributes.LASTNAME, Attributes.FIRSTNAME, Attributes.PHONE,
+                Attributes.COUNTRY, Attributes.INSTITUTION, Attributes.GENDER };
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/user/UserQuery.java b/full/src/main/java/de/ids_mannheim/korap/user/UserQuery.java
new file mode 100644
index 0000000..ad48a2d
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/user/UserQuery.java
@@ -0,0 +1,143 @@
+package de.ids_mannheim.korap.user;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * User: hanl
+ * Date: 9/16/13
+ * Time: 4:38 PM
+ */
+@Data
+public class UserQuery {
+
+    private Integer id;
+    private String queryLanguage;
+    private String query;
+    private String name;
+    private String description;
+    private Integer creator;
+
+
+    public UserQuery (Integer id, int creator) {
+        setId(id);
+        setCreator(creator);
+        setName("");
+        setDescription("");
+        setQuery("");
+        setQueryLanguage("");
+    }
+
+
+    public UserQuery (String ql, String query, String description) {
+        setDescription(description);
+        setQuery(query);
+        setQueryLanguage(ql);
+    }
+
+
+    public UserQuery () {
+        setDescription("");
+        setQuery("");
+        setQueryLanguage("");
+        setName("");
+    }
+
+
+    public void setQuery (String query) {
+        this.query = query;
+        setName("Query: "
+                + query.substring(0, query.length() > 20 ? 20 : query.length()));
+    }
+
+
+    // todo: use example queries or store in database
+    public static List<UserQuery> demoUserQueries () {
+
+        List<UserQuery> queries = new ArrayList<>();
+        UserQuery q1 = new UserQuery();
+        q1.setQueryLanguage("COSMAS2");
+        q1.setQuery("$wegen #IN(L) <s>");
+        q1.setDescription("Findet 'wegen' an Satzanfängen. Berücksichtigt auch Groß- und Kleinschreibung");
+
+        //todo: change query
+        UserQuery q2 = new UserQuery();
+        q2.setQueryLanguage("COSMAS2");
+        q2.setQuery("base/cons:Buchstabe base/aggr:Buchstabe");
+
+        UserQuery q3 = new UserQuery();
+        q3.setQueryLanguage("COSMAS2");
+        q3.setDescription("Regular Expression Search");
+        q3.setQuery("id:/WPD_AAA.*/ AND textClass:sport");
+
+        UserQuery q4 = new UserQuery();
+        q4.setQueryLanguage("COSMAS2");
+        q4.setQuery("mpt/syntax_pos:@CC\\|und");
+
+        UserQuery q5 = new UserQuery();
+        q5.setQueryLanguage("COSMAS2");
+        q5.setQuery("VVINF\\|.*en");
+
+        queries.add(q1);
+        //        queries.add(q2);
+        //        queries.add(q3);
+        queries.add(q4);
+        queries.add(q5);
+        return queries;
+    }
+
+
+    //id is irrevelant, since data was coming
+    // from frontend and thus this object does not contain a id that could be compared!
+    // same with the userAccount. Not set yet!
+    @Override
+    public boolean equals (Object o) {
+        if (this == o)
+            return true;
+        if (!(o instanceof UserQuery))
+            return false;
+        UserQuery userQuery = (UserQuery) o;
+        if (!query.equals(userQuery.query))
+            return false;
+        if (!queryLanguage.equals(userQuery.queryLanguage))
+            return false;
+        return true;
+    }
+
+
+    @Override
+    public int hashCode () {
+        int result = getId() != null ? getId().hashCode() : 0;
+        result = 31 * result
+                + (queryLanguage != null ? queryLanguage.hashCode() : 0);
+        result = 31 * result + (query != null ? query.hashCode() : 0);
+        return result;
+    }
+
+
+    @Override
+    public String toString () {
+        final StringBuffer sb = new StringBuffer("UserQuery{");
+        sb.append("id=").append(getId());
+        //        sb.append(", owner=").append(getOwner());
+        sb.append(", queryLanguage='").append(queryLanguage).append('\'');
+        sb.append(", query='").append(query).append('\'');
+        sb.append(", description='").append(getDescription()).append('\'');
+        sb.append('}');
+        return sb.toString();
+    }
+
+
+    public Map toMap () {
+        Map map = new HashMap();
+        map.put("name", this.name);
+        map.put("description", this.description);
+        map.put("query", this.query);
+        map.put("queryLanguage", this.queryLanguage);
+        return map;
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/user/UserSettingProcessor.java b/full/src/main/java/de/ids_mannheim/korap/user/UserSettingProcessor.java
new file mode 100644
index 0000000..57b5e4b
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/user/UserSettingProcessor.java
@@ -0,0 +1,39 @@
+package de.ids_mannheim.korap.user;
+
+import de.ids_mannheim.korap.config.Attributes;
+
+/**
+ * @author hanl, margaretha
+ * @date 28/01/2016
+ */
+public class UserSettingProcessor extends Userdata {
+
+    public UserSettingProcessor() {
+
+    }
+
+    @Deprecated
+    public UserSettingProcessor(Integer userid) {
+        super(userid);
+    }
+
+    // EM: added
+    public UserSettingProcessor(String data) {
+        super(data);
+    }
+
+    @Override
+    public String[] requiredFields () {
+        return new String[] {};
+    }
+
+
+    @Override
+    public String[] defaultFields () {
+        return new String[] { Attributes.DEFAULT_FOUNDRY_RELATION,
+                Attributes.DEFAULT_FOUNDRY_POS,
+                Attributes.DEFAULT_FOUNDRY_CONSTITUENT,
+                Attributes.DEFAULT_FOUNDRY_LEMMA, Attributes.QUERY_LANGUAGE,
+                Attributes.PAGE_LENGTH };
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/user/Userdata.java b/full/src/main/java/de/ids_mannheim/korap/user/Userdata.java
new file mode 100644
index 0000000..e735776
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/user/Userdata.java
@@ -0,0 +1,167 @@
+package de.ids_mannheim.korap.user;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.validator.Validator;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.*;
+
+/**
+ * @author hanl, margaretha
+ * @date 22/01/2016
+ * 
+ */
+public abstract class Userdata {
+
+    public DataFactory dataFactory = DataFactory.getFactory();
+    
+    @Deprecated
+    @Getter
+    @Setter
+    private Integer id;
+    @Getter(AccessLevel.PRIVATE)
+    private Object data;
+    @Deprecated
+    @Getter
+    @Setter
+    private Integer userId;
+    
+    public Userdata () {
+        this(-1);
+    }
+
+    // EM: replace with username
+    @Deprecated
+    public Userdata(Integer userid) {
+        this.userId = userid;
+        this.id = -1;
+        this.data = dataFactory.convertData(null);
+    }
+
+    public Userdata (String data) {
+        this.data = dataFactory.convertData(data);
+    }
+
+    public int size () {
+        return dataFactory.size(this.data);
+    }
+
+
+    public Object get (String key) {
+        return dataFactory.getValue(this.data, key);
+    }
+
+    public Object filter(String ... keys) {
+        return dataFactory.filter(this.data, keys);
+    }
+
+
+    /**
+     * 
+     * @return
+     */
+    public boolean isValid () {
+        return findMissingFields().length == 0;
+    }
+
+
+    public String[] findMissingFields () {
+        Set<String> missing = new HashSet<>();
+        Set<String> keys = dataFactory.keys(this.data);
+        for (String key : requiredFields()) {
+            if (!keys.contains(key))
+                missing.add(key);
+        }
+        return missing.toArray(new String[0]);
+    }
+
+
+    public void checkRequired () throws KustvaktException {
+        String[] fields = findMissingFields();
+        if (findMissingFields().length != 0) {
+            throw new KustvaktException(userId, StatusCodes.MISSING_PARAMETER,
+                    "User data object not valid. Object has missing fields!",
+                    Arrays.asList(fields).toString());
+        }
+    }
+
+
+    //fixme: if data array, return empty?!
+    public Set<String> keys () {
+        return dataFactory.keys(this.data);
+    }
+
+
+    public Collection<Object> values () {
+        return dataFactory.values(this.data);
+    }
+
+
+    public void setData (String data) {
+        this.data = dataFactory.convertData(data);
+    }
+
+
+    public void update (Userdata other) {
+        if (other != null && this.getClass().equals(other.getClass()))
+            this.data = dataFactory.merge(this.data, other.data);
+    }
+
+
+    public String serialize () throws KustvaktException {
+        // to have consistency with required fields --> updates/deletion may cause required fields to be missing.
+        this.checkRequired();
+        return dataFactory.toStringValue(this.data);
+    }
+
+
+    public void setField (String key, Object value) {
+        dataFactory.addValue(this.data, key, value);
+    }
+
+    // EM: de.ids_mannheim.korap.interfaces.defaults.ApacheValidator.validateMap(Map<String, Object>)
+    // is not reliable
+    // todo: test
+    public void validate (Validator validator) throws KustvaktException {
+        dataFactory.validate(this.data, validator);
+    }
+
+
+    public void read (Map<String, Object> map, boolean defaults_only)
+            throws KustvaktException {
+        this.readQuietly(map, defaults_only);
+        this.checkRequired();
+    }
+
+
+    public void readQuietly (Map<String, Object> map, boolean defaults_only) {
+        if (map != null){
+            if (defaults_only) {
+                for (String k : defaultFields()) {
+                    Object o = map.get(k);
+                    if (o != null) {
+                        dataFactory.addValue(this.data, k, o);
+                    }
+                }
+            }
+            else {
+                for (String key : map.keySet())
+                    dataFactory.addValue(this.data, key, map.get(key));
+            }
+        }
+    }
+
+    // EM: added
+    public boolean removeField (String field) {
+        return dataFactory.removeValue(this.data, field);
+    }
+    
+    public abstract String[] requiredFields ();
+
+
+    public abstract String[] defaultFields ();
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/BooleanUtils.java b/full/src/main/java/de/ids_mannheim/korap/utils/BooleanUtils.java
new file mode 100644
index 0000000..8dbeb4f
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/BooleanUtils.java
@@ -0,0 +1,25 @@
+package de.ids_mannheim.korap.utils;
+
+/**
+ * @author hanl
+ * @date 19/02/2014
+ */
+public class BooleanUtils {
+
+    public static String dbname;
+
+
+    public static Object getBoolean (Object val) {
+        if (val == null)
+            val = false;
+        if (dbname != null && dbname.equalsIgnoreCase("sqlite")) {
+            if (val instanceof Boolean) {
+                return ((boolean) val) ? 1 : 0;
+            }
+            else if (val instanceof Integer) {
+                return ((Integer) val == 1);
+            }
+        }
+        return val;
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/CollectionTypes.java b/full/src/main/java/de/ids_mannheim/korap/utils/CollectionTypes.java
new file mode 100644
index 0000000..6cb0dcd
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/CollectionTypes.java
@@ -0,0 +1,107 @@
+package de.ids_mannheim.korap.utils;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author hanl
+ * @date 04/12/2013
+ */
+public class CollectionTypes {
+
+    private ObjectMapper mapper;
+
+
+    public CollectionTypes () {
+        this.mapper = new ObjectMapper();
+    }
+
+
+    public Map createGroup (String relation, String field, List terms) {
+        if (relation == null)
+            return null;
+
+        Map kgroup = new LinkedHashMap<>();
+        kgroup.put("@type", "korap:group");
+        if (field != null)
+            kgroup.put("@field", "korap:field#" + field);
+        kgroup.put("relation", relation);
+        kgroup.put("operands", terms);
+        return kgroup;
+    }
+
+
+    public Map createTerm (String field, String subtype, String value,
+            String type) {
+        Map term = new LinkedHashMap<>();
+        if (type == null)
+            type = "korap:term";
+        term.put("@type", type);
+        if (field != null)
+            term.put("@field", "korap:field#" + field);
+        if (subtype != null)
+            term.put("@subtype", "korap:value#" + subtype);
+        term.put("@value", value);
+        return term;
+    }
+
+
+    public Map createTerm (String field, String value, String type) {
+        return createTerm(field, null, value, type);
+    }
+
+
+    public Map createTerm (String field, String value) {
+        return createTerm(field, value, null);
+    }
+
+
+    public Map createResourceFilter (String resource, Map value) {
+        Map meta = new LinkedHashMap();
+        meta.put("@type", "korap:meta-filter");
+        meta.put("@id", "korap-filter#" + resource);
+        meta.put("@value", value);
+        return meta;
+    }
+
+
+    public Map createResourceFilter (String resource, String value)
+            throws IOException {
+        return createResourceFilter(resource, mapify(value));
+    }
+
+
+    public Map createResourceExtend (String resource, Map value) {
+        Map meta = new LinkedHashMap();
+        meta.put("@type", "korap:meta-extend");
+        meta.put("@id", "korap-filter#" + resource);
+        meta.put("@value", value);
+        return meta;
+    }
+
+
+    public Map createMetaFilter (Map value) {
+        Map meta = new LinkedHashMap();
+        meta.put("@type", "korap:meta-filter");
+        meta.put("@value", value);
+        return meta;
+    }
+
+
+    public Map createMetaExtend (Map value) {
+        Map meta = new LinkedHashMap();
+        meta.put("@type", "korap:meta-extend");
+        meta.put("@value", value);
+        return meta;
+    }
+
+
+    public Map mapify (String s) throws IOException {
+        return mapper.readValue(s, Map.class);
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/ConcurrentMultiMap.java b/full/src/main/java/de/ids_mannheim/korap/utils/ConcurrentMultiMap.java
new file mode 100644
index 0000000..ea02e44
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/ConcurrentMultiMap.java
@@ -0,0 +1,148 @@
+package de.ids_mannheim.korap.utils;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.MapMaker;
+
+import java.util.List;
+import java.util.concurrent.ConcurrentMap;
+
+import com.google.common.collect.Sets;
+
+import java.util.Collection;
+import java.util.Set;
+
+
+/**
+ * A general purpose Multimap implementation for delayed processing and concurrent insertion/deletes.
+ * This code is based on an implementation by Guido Medina!
+ *
+ * @param <K> A comparable Key
+ * @param <V> A comparable Value
+ */
+
+/**
+ * User: hanl
+ * Date: 8/27/13
+ * Time: 11:18 AM
+ */
+
+public class ConcurrentMultiMap<K extends Comparable, V extends Comparable> {
+
+    private final int initialCapacity;
+    private final LockMap<K> locks;
+    private final ConcurrentMap<K, List<V>> cache;
+
+
+    public ConcurrentMultiMap () {
+        this(16, 64);
+    }
+
+
+    public ConcurrentMultiMap (final int concurrencyLevel) {
+        this(concurrencyLevel, 64);
+    }
+
+
+    public ConcurrentMultiMap (final int concurrencyLevel,
+                               final int initialCapacity) {
+        this.initialCapacity = initialCapacity;
+        cache = new MapMaker().concurrencyLevel(concurrencyLevel)
+                .initialCapacity(initialCapacity).makeMap();
+        locks = new LockMap<K>(concurrencyLevel, initialCapacity);
+    }
+
+
+    public void put (final K key, final V value) {
+        synchronized (locks.getLock(key)) {
+            List<V> set = cache.get(key);
+            if (set == null) {
+                set = Lists.newArrayListWithExpectedSize(initialCapacity);
+                cache.put(key, set);
+            }
+            set.add(value);
+        }
+    }
+
+
+    public void putAll (final K key, final Collection<V> values) {
+        synchronized (locks.getLock(key)) {
+            List<V> set = cache.get(key);
+            if (set == null) {
+                set = Lists.newArrayListWithExpectedSize(initialCapacity);
+                cache.put(key, set);
+            }
+            set.addAll(values);
+        }
+    }
+
+
+    public List<V> remove (final K key) {
+        synchronized (locks.getLock(key)) {
+            return cache.remove(key);
+        }
+    }
+
+
+    public void remove (final K key, final V value) {
+        List<V> values = cache.get(key);
+        synchronized (locks.getLock(key)) {
+            values.remove(value);
+        }
+    }
+
+
+    public Set<K> getKeySet () {
+        return cache.keySet();
+    }
+
+
+    public int size () {
+        return cache.size();
+    }
+
+
+    public boolean containsKey (K key) {
+        return cache.containsKey(key);
+    }
+
+
+    public List<V> get (K key) {
+        return cache.get(key);
+    }
+
+
+    public class LockMap<K extends Comparable> {
+        private final ConcurrentMap<K, Object> locks;
+
+
+        public LockMap () {
+            this(16, 64);
+        }
+
+
+        public LockMap (final int concurrencyLevel) {
+            this(concurrencyLevel, 64);
+        }
+
+
+        public LockMap (final int concurrencyLevel, final int initialCapacity) {
+            locks = new MapMaker().concurrencyLevel(concurrencyLevel)
+                    .initialCapacity(initialCapacity).weakValues().makeMap();
+        }
+
+
+        public Object getLock (final K key) {
+            final Object object = new Object();
+            Object lock = locks.putIfAbsent(key, object);
+            return lock == null ? object : lock;
+        }
+
+    }
+
+
+    public String toString () {
+        return cache.toString();
+    }
+
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/IPNetMask.java b/full/src/main/java/de/ids_mannheim/korap/utils/IPNetMask.java
new file mode 100644
index 0000000..c2511a7
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/IPNetMask.java
@@ -0,0 +1,127 @@
+package de.ids_mannheim.korap.utils;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * User: hanl
+ * Date: 9/13/13
+ * Time: 2:25 PM
+ * 
+ * currently only supports IPv4!
+ * 
+ */
+// todo: integrate to gerrit
+public class IPNetMask {
+
+    private final Inet4Address i4addr;
+    private final byte maskCtr;
+
+    private final int addrInt;
+    private final int maskInt;
+
+    private static final int default_mask = 16;
+
+
+    private IPNetMask (Inet4Address i4addr, byte mask) {
+        this.i4addr = i4addr;
+        this.maskCtr = mask;
+
+        this.addrInt = addrToInt(i4addr);
+        this.maskInt = ~((1 << (32 - maskCtr)) - 1);
+    }
+
+
+    /**
+     * IPNetMask factory method.
+     * 
+     * @param addrSlashMask
+     *            IP/Mask String in format "nnn.nnn.nnn.nnn/mask". If
+     *            the "/mask" is omitted, "/32" (just the single
+     *            address) is assumed.
+     * @return a new IPNetMask
+     * @throws UnknownHostException
+     *             if address part cannot be parsed by
+     *             InetAddress
+     */
+    public static IPNetMask getIPMask (String addrSlashMask)
+            throws UnknownHostException {
+        int pos = addrSlashMask.indexOf('/');
+        String addr;
+        byte maskCtr;
+        if (pos == -1) {
+            addr = addrSlashMask;
+            maskCtr = default_mask;
+        }
+        else {
+            addr = addrSlashMask.substring(0, pos);
+            maskCtr = Byte.parseByte(addrSlashMask.substring(pos + 1));
+        }
+
+        return new IPNetMask((Inet4Address) InetAddress.getByName(addr),
+                maskCtr);
+    }
+
+
+    /**
+     * Test given IPv4 address against this IPNetMask object.
+     * 
+     * @param testAddr
+     *            address to isSystem.
+     * @return true if address is in the IP Mask range, false if not.
+     */
+    public boolean matches (Inet4Address testAddr) {
+        int testAddrInt = addrToInt(testAddr);
+        return ((addrInt & maskInt) == (testAddrInt & maskInt));
+    }
+
+
+    /**
+     * Convenience method that converts String host to IPv4 address.
+     * 
+     * @param addr
+     *            IP address to match in nnn.nnn.nnn.nnn format or
+     *            hostname.
+     * @return true if address is in the IP Mask range, false if not.
+     * @throws UnknownHostException
+     *             if the string cannot be decoded.
+     */
+    public boolean matches (String addr) throws UnknownHostException {
+        return matches((Inet4Address) InetAddress.getByName(addr));
+    }
+
+
+    /**
+     * Converts IPv4 address to integer representation.
+     */
+    private int addrToInt (Inet4Address i4addr) {
+        byte[] ba = i4addr.getAddress();
+        return (ba[0] << 24) | ((ba[1] & 0xFF) << 16) | ((ba[2] & 0xFF) << 8)
+                | (ba[3] & 0xFF);
+    }
+
+
+    @Override
+    public String toString () {
+        return i4addr.getHostAddress() + "/" + maskCtr;
+    }
+
+
+    @Override
+    public boolean equals (Object obj) {
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        final IPNetMask that = (IPNetMask) obj;
+        return (this.addrInt == that.addrInt && this.maskInt == that.maskInt);
+    }
+
+
+    @Override
+    public int hashCode () {
+        return this.maskInt + this.addrInt;
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/JerseyUtils.java b/full/src/main/java/de/ids_mannheim/korap/utils/JerseyUtils.java
new file mode 100644
index 0000000..0a8623f
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/JerseyUtils.java
@@ -0,0 +1,38 @@
+package de.ids_mannheim.korap.utils;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.message.internal.MediaTypes;
+import org.glassfish.jersey.server.ContainerRequest;
+
+public class JerseyUtils {
+
+    /**
+     * Get the form parameters of the request entity.
+     * <p>
+     * This method will ensure that the request entity is buffered
+     * such that it may be consumed by the application.
+     *
+     * @return the form parameters, if there is a request entity and the
+     * content type is "application/x-www-form-urlencoded", otherwise an
+     * instance containing no parameters will be returned.
+     */
+    public static Form getFormParameters (ContainerRequestContext requestContext) {
+        if (requestContext instanceof ContainerRequest) {
+            return getFormParameters((ContainerRequest) requestContext);
+        }
+        return new Form();
+    }
+
+    private static Form getFormParameters (ContainerRequest request) {
+        if (MediaTypes.typeEqual(MediaType.APPLICATION_FORM_URLENCODED_TYPE, request.getMediaType())) {
+            request.bufferEntity();
+            Form form = request.readEntity(Form.class);
+            return (form == null ? new Form() : form);
+        } else {
+            return new Form();
+        }
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/JsonUtils.java b/full/src/main/java/de/ids_mannheim/korap/utils/JsonUtils.java
new file mode 100644
index 0000000..792a6bb
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/JsonUtils.java
@@ -0,0 +1,123 @@
+package de.ids_mannheim.korap.utils;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+
+/**
+ * @author hanl
+ * @date 28/01/2014
+ */
+public class JsonUtils {
+    private static ObjectMapper mapper = new ObjectMapper();
+
+    private JsonUtils () {}
+
+
+    public static String toJSON (Object values) throws KustvaktException {
+        try {
+            return mapper.writeValueAsString(values);
+        }
+        catch (JsonProcessingException e) {
+            throw new KustvaktException(StatusCodes.SERIALIZATION_FAILED,
+                    "Failed serializing object in json", e);
+        }
+    }
+
+
+    public static JsonNode readTree (String json) throws KustvaktException {
+        try {
+            return mapper.readTree(json);
+        }
+        catch (IOException e) {
+            throw new KustvaktException(StatusCodes.DESERIALIZATION_FAILED,
+                    "Failed deserializing json object: " + json, json, e);
+        }
+    }
+    
+    public static ObjectNode createObjectNode () {
+        return mapper.createObjectNode();
+    }
+
+
+    public static ArrayNode createArrayNode () {
+        return mapper.createArrayNode();
+    }
+
+
+    public static JsonNode valueToTree (Object value) {
+        return mapper.valueToTree(value);
+    }
+
+    public static <T> T convert (JsonNode json, Class<T> cl)
+            throws IOException {
+        return mapper.convertValue(json, cl);
+    }
+
+    public static <T> T read (String json, Class<T> cl) throws IOException {
+        return mapper.readValue(json, cl);
+    }
+
+    public static <T> T read (InputStream is, Class<T> cl) throws IOException {
+        return mapper.readValue(is, cl);
+    }
+
+
+    public static <T> T readFile (String path, Class<T> clazz)
+            throws IOException {
+        return mapper.readValue(new File(path), clazz);
+    }
+
+
+    public static void writeFile (String path, Object content)
+            throws IOException {
+        mapper.writeValue(new File(path), content);
+    }
+
+
+    public static <T> T convertToClass (String json, Class<T> cl) throws KustvaktException {
+        T t = null;
+        try {
+            t = mapper.readValue(json, cl);
+        }
+        catch (IOException e) {
+            throw new KustvaktException(StatusCodes.DESERIALIZATION_FAILED,
+                    e.getMessage(), json, e);
+        }
+        return t;
+    }
+
+
+    public static List<Map<String, Object>> convertToList (String json)
+            throws JsonProcessingException, KustvaktException {
+        List d = new ArrayList();
+        JsonNode node = JsonUtils.readTree(json);
+        if (node.isArray()) {
+            Iterator<JsonNode> nodes = node.iterator();
+            while (nodes.hasNext()) {
+                Map<String, Object> map =
+                        mapper.treeToValue(nodes.next(), Map.class);
+                d.add(map);
+            }
+        }
+        else if (node.isObject()) {
+            Map<String, Object> map = mapper.treeToValue(node, Map.class);
+            d.add(map);
+        }
+        return d;
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/KoralCollectionQueryBuilder.java b/full/src/main/java/de/ids_mannheim/korap/utils/KoralCollectionQueryBuilder.java
new file mode 100644
index 0000000..a6260cb
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/KoralCollectionQueryBuilder.java
@@ -0,0 +1,232 @@
+package de.ids_mannheim.korap.utils;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+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.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.query.serialize.CollectionQueryProcessor;
+import de.ids_mannheim.korap.response.Notifications;
+import edu.emory.mathcs.backport.java.util.Arrays;
+
+/**
+ * convenience builder class for collection query
+ * 
+ * @author hanl, margaretha
+ * @date 29/06/2017
+ */
+public class KoralCollectionQueryBuilder {
+
+    public enum EQ {
+        EQUAL, UNEQUAL
+    }
+
+    private boolean verbose;
+    private JsonNode base;
+    private StringBuilder builder;
+    private String mergeOperator;
+
+    public KoralCollectionQueryBuilder () {
+        this(false);
+    }
+
+
+    public KoralCollectionQueryBuilder (boolean verbose) {
+        this.verbose = verbose;
+        this.builder = new StringBuilder();
+        this.base = null;
+        this.mergeOperator = null;
+    }
+
+
+    /**
+     * raw method for field - value pair adding. Supports all
+     * operators (leq, geq, contains, etc.)
+     * 
+     * @param field
+     * @param op
+     * @param value
+     * @return
+     */
+    public KoralCollectionQueryBuilder with (String field, String op,
+            String value) {
+        //String end = this.builder.substring(this.builder.length() - 4,
+        //        this.builder.length() - 1);
+        //if (this.builder.length() != 0
+        //        && (!end.contains("&") | !end.contains("|")))
+        //    throw new RuntimeException("no join operator given!");
+        this.with(field + op + value);
+        return this;
+    }
+
+
+    /**
+     * element can be a more complex sub query like
+     * (textClass=freizeit & Attributes.CORPUS_SIGLE=WPD)
+     * 
+     * @param query
+     *            will be enclosed by parenthesis in order to make sub
+     *            query
+     *            element
+     * @return
+     */
+    public KoralCollectionQueryBuilder with (String query) {
+        if (!query.startsWith("(") && !query.endsWith(")"))
+            query = "(" + query + ")";
+        this.builder.append(query);
+        return this;
+    }
+
+
+
+    public KoralCollectionQueryBuilder and () {
+        if (this.builder.length() != 0) this.builder.append(" & ");
+        if (this.base != null && this.mergeOperator == null)
+            this.mergeOperator = "AND";
+        return this;
+    }
+
+
+    public KoralCollectionQueryBuilder or () {
+        if (this.builder.length() != 0) this.builder.append(" | ");
+        if (this.base != null && this.mergeOperator == null)
+            this.mergeOperator = "OR";
+        return this;
+    }
+
+
+    public JsonNode rebaseCollection () throws KustvaktException {
+        if (this.builder.length() == 0 && this.base == null) return null;
+
+        JsonNode request = null;
+        if (this.builder.length() != 0) {
+            CollectionQueryProcessor tree =
+                    new CollectionQueryProcessor(this.verbose);
+            tree.process(this.builder.toString());
+            if (tree.getErrors().size() > 0) {
+                // legacy support
+                for (List<Object> e : tree.getErrors()) {
+                    if (e.get(1) instanceof String[]) {
+                        Notifications notif = new Notifications();
+                        int code = (int) e.get(0);
+                        notif.addError(code, (String[]) e.get(1));
+                        String notificationStr = notif.toJsonString();
+                        throw new KustvaktException(StatusCodes.SERIALIZATION_FAILED,
+                                notificationStr, true);
+                    }
+                    else{
+                        break;
+                    }
+                }
+                // -- end of legacy support
+                
+                Map<String, Object> map = new HashMap<>();
+                map.put("errors", tree.getErrors());
+                String errors = JsonUtils.toJSON(map);
+                throw new KustvaktException(StatusCodes.SERIALIZATION_FAILED,
+                        errors, true);
+            }
+            request = JsonUtils.valueToTree(tree.getRequestMap());
+        }
+
+        if (this.base != null) {
+            // check that collection non empty
+            JsonNode tmp = this.base.deepCopy();
+            if (request != null)
+                request = mergeWith(request);
+            else
+                request = tmp;
+        }
+        return request;
+    }
+
+
+    public JsonNode mergeWith (JsonNode node) {
+        if (this.base != null) {
+            if (node != null) {
+                JsonNode tobase = node.at("/collection");
+                JsonNode base = this.base.deepCopy();
+                JsonNode result = base.at("/collection");
+
+                if (result.isMissingNode() && !tobase.isMissingNode())
+                    result = tobase;
+                else if (result.isMissingNode() && tobase.isMissingNode())
+                    return base;
+                else {
+                    result = JsonBuilder.buildDocGroup(
+                            this.mergeOperator != null
+                                    ? this.mergeOperator.toLowerCase() : "and",
+                            result, tobase);
+                }
+                ((ObjectNode) base).put("collection", result);
+                return base;
+            }
+            return this.base;
+        }
+        throw new RuntimeException("no query found to merge with!");
+    }
+
+
+    /**
+     * sets base query. All consequent queries are added to the first
+     * koral:docGroup within the collection base query
+     * If no group in base query, consequent queries are skipped.
+     * 
+     * @param query
+     * @throws KustvaktException 
+     */
+    public KoralCollectionQueryBuilder setBaseQuery (String query) throws KustvaktException {
+        this.base = JsonUtils.readTree(query);
+        return this;
+    }
+
+
+    public KoralCollectionQueryBuilder setBaseQuery (JsonNode query) {
+        this.base = query;
+        return this;
+    }
+
+
+    public String toJSON () throws KustvaktException {
+        return JsonUtils.toJSON(rebaseCollection());
+    }
+
+
+    @Override
+    public String toString () {
+        return this.builder.toString();
+    }
+
+
+    private static class JsonBuilder {
+
+        public static ObjectNode buildDoc (String key, String value) {
+            ObjectNode node = JsonUtils.createObjectNode();
+            node.put("@type", "koral:doc");
+            // eq.equals(EQ.EQUAL) ? "match:eq" : "match:ne"
+            node.put("match", "match:eq");
+            node.put("key", key);
+            node.put("value", value);
+            return node;
+        }
+
+
+        public static ObjectNode buildDocGroup (String op,
+                JsonNode ... groups) {
+            ObjectNode node = JsonUtils.createObjectNode();
+            node.put("@type", "koral:docGroup");
+            node.put("operation", "operation:" + op);
+            ArrayNode ops = JsonUtils.createArrayNode();
+            ops.addAll(Arrays.asList(groups));
+            node.put("operands", ops);
+            return node;
+        }
+
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/NamingUtils.java b/full/src/main/java/de/ids_mannheim/korap/utils/NamingUtils.java
new file mode 100644
index 0000000..beafb32
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/NamingUtils.java
@@ -0,0 +1,19 @@
+package de.ids_mannheim.korap.utils;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Created by hanl on 14.05.16.
+ */
+public class NamingUtils {
+
+
+
+    private NamingUtils () {}
+
+
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/ParameterChecker.java b/full/src/main/java/de/ids_mannheim/korap/utils/ParameterChecker.java
new file mode 100644
index 0000000..2673c80
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/ParameterChecker.java
@@ -0,0 +1,61 @@
+package de.ids_mannheim.korap.utils;
+
+import java.util.Collection;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+
+public class ParameterChecker {
+
+    public static void checkObjectValue (Object obj, String name)
+            throws KustvaktException {
+        if (obj == null) {
+            throw new KustvaktException(StatusCodes.INVALID_ARGUMENT,
+                    name + " is null", name);
+        }
+    }
+
+    public static void checkCollection (Collection<?> collection, String name)
+            throws KustvaktException {
+        if (collection == null) {
+            throw new KustvaktException(StatusCodes.INVALID_ARGUMENT,
+                    name + " is null", name);
+        }
+        else if (collection.isEmpty()) {
+            throw new KustvaktException(StatusCodes.INVALID_ARGUMENT, 
+                    name + " is empty", name);
+        }
+    }
+
+    public static void checkStringValue (String string, String name)
+            throws KustvaktException {
+        if (string == null) {
+            throw new KustvaktException(StatusCodes.INVALID_ARGUMENT, 
+                    name + " is null", name);
+        }
+        else if (string.isEmpty()) {
+            throw new KustvaktException(StatusCodes.INVALID_ARGUMENT, 
+                    name + " is empty", name);
+        }
+    }
+
+    public static void checkIntegerValue (int integer, String name)
+            throws KustvaktException {
+        if (integer == 0) {
+            throw new KustvaktException(StatusCodes.MISSING_PARAMETER, 
+                    name + " is missing", name);
+        }
+    }
+    
+    public static void checkNameValue (String value, String name)
+            throws KustvaktException {
+        if (value == null) {
+            throw new KustvaktException(StatusCodes.INVALID_ARGUMENT,
+                    name + " is null", name);
+        }
+        else if (value.length() < 3) {
+            throw new KustvaktException(StatusCodes.INVALID_ARGUMENT,
+                    name+" must contain at least 3 characters", name);
+        }
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/PrefixTreeMap.java b/full/src/main/java/de/ids_mannheim/korap/utils/PrefixTreeMap.java
new file mode 100644
index 0000000..20e94d5
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/PrefixTreeMap.java
@@ -0,0 +1,48 @@
+package de.ids_mannheim.korap.utils;
+
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * @author hanl
+ * @date 01/07/2014
+ */
+public class PrefixTreeMap<V> extends TreeMap<String, V> {
+
+
+    public SortedMap<String, V> getPrefixSubMap (String prefix) {
+        if (prefix != null && prefix.length() > 0) {
+            SortedMap d = this.subMap(prefix, getEnd(prefix));
+            if (d.isEmpty())
+                return null;
+            return d;
+        }
+        return null;
+    }
+
+
+    private String getEnd (String prefix) {
+        char nextLetter = (char) (prefix.charAt(prefix.length() - 1) + 1);
+        return prefix.substring(0, prefix.length() - 1) + nextLetter;
+
+    }
+
+
+    public V getFirstValue (String prefix) {
+        if (prefix.length() > 0) {
+            String first = this.subMap(prefix, getEnd(prefix)).firstKey();
+            return this.get(first);
+        }
+        return null;
+    }
+
+
+    public V getLastValue (String prefix) {
+        if (prefix.length() > 0) {
+            String last = this.subMap(prefix, getEnd(prefix)).lastKey();
+            return this.get(last);
+        }
+        return null;
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/PropertyReader.java b/full/src/main/java/de/ids_mannheim/korap/utils/PropertyReader.java
new file mode 100644
index 0000000..0b16cc0
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/PropertyReader.java
@@ -0,0 +1,37 @@
+package de.ids_mannheim.korap.utils;
+
+import de.ids_mannheim.korap.config.BeanInjectable;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * @author hanl
+ * @date 27/09/2014
+ */
+public abstract class PropertyReader {
+
+    protected Map<String, Properties> read (String path) throws IOException {
+        Map<String, Properties> res = new HashMap<>();
+        Properties s = new Properties();
+        s.load(new FileInputStream(new File(path)));
+        for (Map.Entry<Object, Object> e : s.entrySet()) {
+            String key = e.getKey().toString().split("\\.")[0];
+            Properties in = res.get(key);
+            if (in == null) {
+                in = new Properties();
+                res.put(key, in);
+            }
+            in.setProperty(e.getKey().toString(), e.getValue().toString());
+        }
+        return res;
+    }
+
+
+    public abstract void load ();
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/ServiceInfo.java b/full/src/main/java/de/ids_mannheim/korap/utils/ServiceInfo.java
new file mode 100644
index 0000000..fa83ebc
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/ServiceInfo.java
@@ -0,0 +1,91 @@
+package de.ids_mannheim.korap.utils;
+
+import de.ids_mannheim.korap.config.ConfigLoader;
+import de.ids_mannheim.korap.query.serialize.QuerySerializer;
+import lombok.Getter;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+/**
+ * @author hanl
+ * @date 23/01/2014
+ */
+public class ServiceInfo {
+
+    private static final ServiceInfo info = new ServiceInfo();
+
+    public static final String UNKNOWN = "UNKNOWN";
+
+    @Getter
+    private String name;
+    @Getter
+    private String version;
+    @Getter
+    private String config;
+    @Getter
+    private String logger;
+    @Getter
+    private Boolean cacheable;
+    @Getter
+    private String cache_store;
+
+
+    @Getter
+    private String krillVersion;
+    @Getter
+    private String koralVersion;
+    
+    private ServiceInfo () {
+        load();
+    }
+
+
+    private void load () {
+        Properties props = new Properties();
+        try {
+            
+            InputStream stream = getStream();
+            props.load(stream);
+            stream.close();
+            this.version = (String) props.get("kustvakt.version");
+            this.name = (String) props.get("kustvakt.name");
+            this.config = (String) props.get("kustvakt.properties");
+            this.logger = (String) props.get("kustvakt.logging");
+            this.cacheable = Boolean.valueOf((String) props.get("kustvakt.cache"));
+            this.cache_store = (String) props.get("kustvakt.cache_store");
+            
+            this.krillVersion=(String) props.get("krill.version");
+            
+            QuerySerializer s = new QuerySerializer();
+            this.koralVersion = s.getVersion() ;
+        }
+        catch (IOException e) {
+            this.version = UNKNOWN;
+            this.name = UNKNOWN;
+            this.logger = UNKNOWN;
+            this.config = UNKNOWN;
+            this.cacheable = false;
+            this.cache_store = UNKNOWN;
+            
+            this.koralVersion = UNKNOWN;
+            this.krillVersion = UNKNOWN;
+        }
+    }
+
+
+    private static InputStream getStream () throws IOException {
+        String path = "service.properties";
+        InputStream stream = ConfigLoader.loadConfigStream(path);
+        if (stream == null)
+            throw new IOException("stream for resource " + path
+                    + " could not be found...");
+        return stream;
+    }
+
+
+    public static ServiceInfo getInfo () {
+        return info;
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/SqlBuilder.java b/full/src/main/java/de/ids_mannheim/korap/utils/SqlBuilder.java
new file mode 100644
index 0000000..fa1314b
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/SqlBuilder.java
@@ -0,0 +1,103 @@
+package de.ids_mannheim.korap.utils;
+
+/**
+ * @author hanl
+ * @date 26/11/2015
+ */
+public class SqlBuilder {
+
+    private StringBuffer buffer;
+    private String table;
+    private String[] fields;
+    private String where;
+
+
+    public SqlBuilder (String table) {
+        this.buffer = new StringBuffer();
+        this.table = table;
+    }
+
+
+    public SqlBuilder select (String ... fields) {
+        this.buffer.append("SELECT ");
+        if (fields.length > 0) {
+            for (int i = 0; i < fields.length; i++) {
+                if (i > 0)
+                    this.buffer.append(", ");
+                this.buffer.append(fields[i]);
+            }
+        }
+        else
+            this.buffer.append("*");
+        this.buffer.append(" FROM ").append(table);
+        return this;
+    }
+
+
+    public SqlBuilder update (String ... fields) {
+        this.buffer.append("UPDATE ").append(table);
+        this.fields = fields;
+        return this;
+    }
+
+
+    public SqlBuilder insert (String ... fields) {
+        this.buffer.append("INSERT INTO ").append(table);
+        this.fields = fields;
+        return this;
+    }
+
+
+    public SqlBuilder delete () {
+        this.buffer.append("DELETE FROM ").append(table);
+        return this;
+    }
+
+
+    public SqlBuilder params (String ... values) {
+        if (values.length != fields.length)
+            return this;
+        if (this.buffer.lastIndexOf("INSERT INTO") != -1) {
+            this.buffer.append(" (");
+            for (int i = 0; i < this.fields.length; i++) {
+                if (i > 0)
+                    this.buffer.append(", ");
+                this.buffer.append(fields[i]);
+            }
+
+            StringBuffer b = new StringBuffer();
+            for (int i = 0; i < values.length; i++) {
+                if (i > 0)
+                    b.append(", ");
+                b.append(values[i]);
+            }
+            this.buffer.append(") VALUES (").append(b.toString()).append(")");
+        }
+        if (this.buffer.lastIndexOf("UPDATE") != -1) {
+            this.buffer.append(" SET ");
+            for (int i = 0; i < this.fields.length; i++) {
+                if (i > 0)
+                    this.buffer.append(", ");
+                this.buffer.append(fields[i]).append("=").append(values[i]);
+            }
+        }
+        return this;
+    }
+
+
+    public SqlBuilder where (String where) {
+        this.where = where;
+        return this;
+    }
+
+
+    @Override
+    public String toString () {
+        StringBuffer b = new StringBuffer(this.buffer);
+        //exclude where clauses from insert statements
+        if (this.where != null && this.buffer.lastIndexOf("INSERT INTO") == -1)
+            b.append(" WHERE ").append(where);
+        return b.append(";").toString();
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/StringUtils.java b/full/src/main/java/de/ids_mannheim/korap/utils/StringUtils.java
new file mode 100644
index 0000000..99c9578
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/StringUtils.java
@@ -0,0 +1,226 @@
+package de.ids_mannheim.korap.utils;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.*;
+
+public class StringUtils {
+    private final static Logger jlog = LogManager
+            .getLogger(StringUtils.class);
+
+    private static final String SEP = ";";
+    private static final String SLASH = "/";
+
+
+    public static Collection<UUID> stringToUUIDList (String s) {
+        String[] array = s.split(SEP);
+        List<UUID> list = new LinkedList<>();
+        for (String att : array) {
+            list.add(UUID.fromString(att));
+        }
+        return list;
+    }
+
+
+    public static List<String> toList (String values) {
+        List<String> list = new ArrayList<>();
+        StringTokenizer tokenizer = new StringTokenizer(values, SEP);
+        while (tokenizer.hasMoreTokens())
+            list.add(tokenizer.nextToken());
+        return list;
+    }
+
+
+    public static Set<String> toSet (String values, String sep) {
+        Set<String> set = new HashSet<>();
+        if (values != null && !values.isEmpty()) {
+            StringTokenizer tokenizer = new StringTokenizer(values, sep);
+            while (tokenizer.hasMoreTokens())
+                set.add(tokenizer.nextToken());
+        }
+        return set;
+    }
+
+
+    public static Set<String> toSet (String values) {
+        return toSet(values, SEP);
+    }
+
+
+    public static String toString (Collection<String> values) {
+        return StringUtils.toString(values, SEP);
+    }
+
+
+    public static String toString (Collection<String> values, String sep) {
+        StringBuffer b = new StringBuffer();
+        for (String s : values)
+            b.append(s).append(sep);
+
+        if (b.length() > 0)
+            b.deleteCharAt(b.length() - 1);
+        return b.toString();
+    }
+
+
+    public static String orderedToString (Collection<String> hash) {
+        Set<String> orderedSet = new TreeSet<>();
+        orderedSet.addAll(hash);
+        if (orderedSet.isEmpty()) {
+            return "";
+        }
+        else {
+            StringBuilder builder = new StringBuilder();
+            for (String s : orderedSet) {
+                builder.append(s);
+                builder.append(SEP);
+            }
+            builder.deleteCharAt(builder.length() - 1);
+            return builder.toString();
+        }
+    }
+
+
+    public static String UUIDsetToString (Collection<UUID> hash) {
+        Set<UUID> orderedSet = new TreeSet<>();
+        orderedSet.addAll(hash);
+        if (orderedSet.isEmpty()) {
+            return "";
+        }
+        else {
+            StringBuilder builder = new StringBuilder();
+            for (UUID s : orderedSet) {
+                builder.append(s);
+                builder.append(SEP);
+            }
+            builder.deleteCharAt(builder.length() - 1);
+            return builder.toString();
+        }
+    }
+
+
+    public static String buildSQLRegex (String path) {
+        StringBuilder b = new StringBuilder();
+        String[] match = path.split("/");
+        b.append(match[0]);
+        b.append("(" + "/" + match[1] + ")");
+        b.append("*$");
+        return b.toString();
+    }
+
+
+    // todo: move to parameter utils
+    public static boolean isInteger (String value) {
+        try {
+            Integer.valueOf(value);
+            return true;
+        }
+        catch (IllegalArgumentException e) {
+            // do nothing!
+            return false;
+        }
+    }
+
+
+    public static String normalize (String value) {
+        return value.trim().toLowerCase();
+    }
+
+
+    public static String normalizeHTML (String value) {
+        return StringEscapeUtils.escapeHtml(value);
+    }
+
+
+    public static String decodeHTML (String value) {
+        return StringEscapeUtils.unescapeHtml(value);
+    }
+
+
+    public static String getDocSigle (String textSigle) {
+        if (textSigle != null)
+            return textSigle.split("\\.")[0];
+        return null;
+    }
+
+
+    public static String getCorpusSigle (String textSigle) {
+        //WPD_SSS.07367
+        if (textSigle != null)
+            return textSigle.split("_")[0];
+        return null;
+    }
+
+
+    public static Collection<String> joinStringSet (Collection<String> source,
+            String other) {
+        Set<String> set = new HashSet<>(source);
+        set.add(other);
+        return set;
+    }
+
+
+    public static Collection<UUID> joinUUIDSet (Collection<UUID> source,
+            UUID other) {
+        Set<UUID> set = new HashSet<>(source);
+        set.add(other);
+        return set;
+    }
+
+
+    public static String joinResources (String first, String second) {
+        String res;
+        if (first != null && !first.isEmpty())
+            res = first + SLASH + second;
+        else
+            res = second;
+        return res.replaceAll("\\s", "");
+    }
+
+
+    public static String[] splitAnnotations (String joined) {
+        String[] spl = joined.split(SLASH);
+        if (spl.length == 2)
+            return spl;
+        else
+            return null;
+    }
+
+
+    public static String stripTokenType (String token) {
+        int idx = token.lastIndexOf(" ");
+        if (idx == -1)
+            return token;
+        return token.substring(idx).replaceAll("\\s", "");
+    }
+
+
+    public static String getTokenType (String token) {
+        if (token.contains(" "))
+            return token.substring(0, token.lastIndexOf(" "))
+                    .replaceAll("\\s", "").toLowerCase();
+        else
+            return null;
+    }
+
+    public static String toSHAHash(String input) {
+        try {
+            MessageDigest md = MessageDigest.getInstance("SHA-256");
+            md.update(input.getBytes());
+            byte[] mdbytes = md.digest();
+
+            StringBuffer sb = new StringBuffer();
+            for (int i = 0; i < mdbytes.length; i++)
+                sb.append(Integer.toString((mdbytes[i] & 0xff) + 0x100, 16).substring(1));
+            return sb.toString();
+        } catch (NoSuchAlgorithmException e) {
+            return null;
+        }
+    }
+
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/TimeUtils.java b/full/src/main/java/de/ids_mannheim/korap/utils/TimeUtils.java
new file mode 100644
index 0000000..d6f2daf
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/TimeUtils.java
@@ -0,0 +1,227 @@
+package de.ids_mannheim.korap.utils;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+import org.joda.time.format.ISODateTimeFormat;
+
+import de.ids_mannheim.korap.config.Attributes;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * @author hanl
+ *         <p/>
+ *         calculates current, expiration and inactive time for
+ *         security
+ *         purposes.
+ * @return
+ */
+public class TimeUtils {
+
+    private static DecimalFormat df = new DecimalFormat("#.#############");
+    private static DateTimeFormatter dtf = DateTimeFormat
+            .forPattern("dd/MM/yyyy");
+    private static final DateTimeZone dtz = DateTimeZone.forID(Attributes.DEFAULT_TIME_ZONE);
+    private static final boolean DEBUG = false;
+    private static Logger jlog = LogManager.getLogger(TimeUtils.class);
+
+
+    public static int convertTimeToSeconds (String expirationVal) {
+        expirationVal = expirationVal.trim();
+        int finIndex = expirationVal.length() - 1;
+        char entity = expirationVal.charAt(finIndex);
+        int returnSec = Integer.valueOf(expirationVal.substring(0, finIndex));
+        if (DEBUG) {
+            jlog.debug("setting time value to " + returnSec + " with time in "
+                    + entity);
+        }
+        switch (entity) {
+            case 'D':
+                return returnSec * 60 * 60 * 24;
+            case 'H':
+                return returnSec * 60 * 60;
+            case 'M':
+                return returnSec * 60;
+            case 'S':
+                return returnSec;
+            default:
+                if (DEBUG){
+                    jlog.debug("no time unit specified. Trying to read from default (minutes)");
+                }
+                return Integer.valueOf(expirationVal) * 60;
+        }
+
+    }
+
+    public static DateTime getNow () {
+        return DateTime.now(dtz);
+    }
+
+    public static DateTime getTime(String time) {
+        return DateTime.parse(time).withZone(dtz);
+    }
+
+    public static DateTime getTime(long time) {
+        return new DateTime(time).withZone(dtz);
+    }
+
+    //returns difference in milliseconds
+    public static long calcDiff (DateTime now, DateTime future) {
+        long diff = (future.withZone(dtz).getMillis() - now.withZone(dtz)
+                .getMillis());
+        return diff;
+    }
+
+
+    public static boolean isExpired(long time) {
+        return getNow().isAfter(time);
+
+    }
+
+
+    // returns difference in seconds in floating number
+    public static float floating (DateTime past, DateTime now) {
+        long diff = (now.withZone(dtz).getMillis() - past.withZone(dtz)
+                .getMillis());
+        double fin = diff / 1000.0;
+        BigDecimal bd = new BigDecimal(fin).setScale(8, RoundingMode.HALF_EVEN);
+        return bd.floatValue();
+    }
+
+
+    public static DateTime fromCosmas (String date) {
+        int idx = date.length();
+        try {
+            Integer sec = Integer.valueOf(date.substring((idx = idx - 2),
+                    date.length()).trim());
+            Integer min = Integer.valueOf(date.substring((idx = idx - 2),
+                    idx + 2).trim());
+            Integer hours = Integer.valueOf(date.substring((idx = idx - 2),
+                    idx + 2).trim());
+            Integer day = Integer.valueOf(date.substring((idx = idx - 2),
+                    idx + 2).trim());
+            Integer month = Integer.valueOf(date.substring((idx = idx - 2),
+                    idx + 2).trim());
+            Integer year = Integer.valueOf(date.substring((idx = idx - 4),
+                    idx + 4).trim());
+            return new DateTime(year, month, day, hours, min, sec);
+        }
+        catch (NumberFormatException e) {
+            return getNow().toDateTime();
+        }
+    }
+
+
+    public static String formatDiff (DateTime now, DateTime after) {
+        return df.format(calcDiff(now, after));
+    }
+
+
+    /**
+     * converts time to the ISO8601 standard.
+     * 
+     * @param time
+     * @return
+     */
+    public static String format (DateTime time) {
+        DateTimeFormatter fmt = ISODateTimeFormat.dateTime();
+        return fmt.withZone(dtz).print(time);
+    }
+
+
+    public static String format (long time) {
+        DateTimeFormatter fmt = ISODateTimeFormat.dateTime();
+        return fmt.withZone(dtz).print(time);
+    }
+
+
+    /**
+     * calculate expiration time
+     * 
+     * @param creation
+     * @param plus
+     *            time in seconds
+     * @return
+     */
+    public static DateTime plusSeconds (long creation, int plus) {
+        return new DateTime(creation).withZone(dtz).plusSeconds(plus);
+    }
+
+
+    public static DateTime getExpiration (long now, int exp) {
+        return new DateTime(now).withZone(dtz).plusSeconds(exp);
+    }
+
+
+    /**
+     * @param plus
+     * @return
+     */
+    public static DateTime plusSeconds (int plus) {
+        return getNow().withZone(dtz).plusSeconds(plus);
+    }
+
+
+    public static DateTime plusHours (int hours) {
+        return getNow().withZone(dtz).plusHours(hours);
+    }
+
+
+    public static DateTime plusMinutes (int minutes) {
+        return getNow().withZone(dtz).plusMinutes(minutes);
+    }
+
+
+    /**
+     * create time stamp from long value
+     * 
+     * @param t
+     *            time
+     * @return Timestamp
+     */
+    public static LocalDate getTimeStamp (long t) {
+        return new DateTime(t).withZone(dtz).toLocalDate();
+    }
+
+
+    public static DateTime getDate (int day, int month, int year) {
+        DateTime date = new DateTime().withZone(dtz);
+        return date.withDate(year, month, day);
+    }
+
+
+    public static String toString (long val, Locale locale) {
+        if (locale == Locale.GERMAN)
+            return new DateTime(val).toString("dd. MMMM yyyy, HH:mm",
+                    Locale.GERMAN);
+        else
+            return new DateTime(val).toString("MM-dd-yyyy, hh:mm",
+                    Locale.ENGLISH);
+
+    }
+
+
+    public static String dateToString (long val, int i) {
+        switch (i) {
+            case 1:
+                return new DateTime(val).toString("yyyy-MM");
+            case 2:
+                return new DateTime(val).toString("yyyy-MM-dd");
+            default:
+                return new DateTime(val).toString("yyyy");
+        }
+    }
+
+    private static final List<DateTime> times = new ArrayList<>();
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/validator/ApacheValidator.java b/full/src/main/java/de/ids_mannheim/korap/validator/ApacheValidator.java
new file mode 100644
index 0000000..c88a0da
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/validator/ApacheValidator.java
@@ -0,0 +1,134 @@
+package de.ids_mannheim.korap.validator;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.commons.validator.routines.DateValidator;
+import org.apache.commons.validator.routines.EmailValidator;
+import org.apache.commons.validator.routines.RegexValidator;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import de.ids_mannheim.korap.config.Attributes;
+import de.ids_mannheim.korap.config.ConfigLoader;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.web.utils.KustvaktMap;
+
+/**
+ * Created by hanl on 09.06.16.
+ *  
+ */
+public class ApacheValidator implements Validator {
+
+    private static Logger jlog = LogManager.getLogger(ApacheValidator.class);
+
+    private static final String STRING_PATTERN = "^[\\.;:,&\\|@\\[\\]\\=\\*\\/\\/_()\\-0-9\\p{L}\\p{Space}]{0,1024}$";
+
+    private static final boolean DEBUG = false;
+
+    private Map<String, RegexValidator> validators;
+
+
+    public ApacheValidator () throws IOException {
+        this.validators = load();
+    }
+
+
+    private static Map<String, RegexValidator> load () throws IOException {
+        Map<String, RegexValidator> validatorMap = new HashMap<>();
+        Properties p = ConfigLoader.loadProperties("validation.properties");
+
+        for (String property : p.stringPropertyNames()) {
+            if (property.startsWith("Validator")) {
+                String name = property.replace("Validator.", "");
+                RegexValidator v = new RegexValidator(
+                        p.get(property).toString());
+                validatorMap.put(name, v);
+            }
+        }
+        return validatorMap;
+    }
+
+
+
+    @Override
+    public Map<String, Object> validateMap (Map<String, Object> map)
+            throws KustvaktException {
+        Map<String, Object> safeMap = new HashMap<>();
+        KustvaktMap kmap = new KustvaktMap(map);
+
+        if (map != null) {
+            loop: for (String key : kmap.keySet()) {
+                Object value = kmap.getRaw(key);
+                if (value instanceof List) {
+                    List list = (List) value;
+                    for (int i = 0; i < list.size(); i++) {
+                        if (!isValid(String.valueOf(list.get(i)), key)) {
+                            //                                list.remove(i);
+                            throw new KustvaktException(
+                                    StatusCodes.ILLEGAL_ARGUMENT,
+                                    "The value for the parameter " + key
+                                            + " is not valid or acceptable.");
+                        }
+                    }
+
+                    if (list.size() == 1)
+                        value = list.get(0);
+                    else
+                        value = list;
+                }
+                else {
+                    if (!isValid(kmap.get(key), key))
+                        continue loop;
+                }
+                safeMap.put(key, value);
+            }
+        }
+        return safeMap;
+    }
+
+
+    @Override
+    public String validateEntry (String input, String type)
+            throws KustvaktException {
+        if (!isValid(input, type))
+            throw new KustvaktException(StatusCodes.INVALID_ARGUMENT,
+                    "Entry did not validate for type '" + type + "'", input);
+        return input;
+    }
+
+
+    @Override
+    public boolean isValid (String input, String type) {
+        boolean valid = false;
+        RegexValidator validator = this.validators.get(type);
+        if (validator != null) {
+            valid = validator.isValid(input);
+        }
+        else {
+            if (Attributes.EMAIL.equals(type)) {
+                valid = EmailValidator.getInstance().isValid(input);
+            }
+            else if ("date".equals(type)) {
+                valid = DateValidator.getInstance().isValid(input);
+            }
+            else if ("string".equals(type)
+                    && !this.validators.containsKey("string")) {
+                RegexValidator regex = new RegexValidator(STRING_PATTERN);
+                valid = regex.isValid(input);
+            }
+            else
+                return this.isValid(input, "string");
+        }
+        if (DEBUG){
+            jlog.debug("validating entry "+input+" of type "+type+": "+ (
+                    valid ? "Is valid!" : "Is not valid!"));
+        }
+            
+        return valid;
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/validator/Validator.java b/full/src/main/java/de/ids_mannheim/korap/validator/Validator.java
new file mode 100644
index 0000000..b20cb3b
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/validator/Validator.java
@@ -0,0 +1,26 @@
+package de.ids_mannheim.korap.validator;
+
+import java.util.Map;
+
+import org.springframework.stereotype.Component;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+
+/**
+ * EM: made this as a spring component
+ * 
+ * Created by hanl on 08.06.16.
+ */
+@Component
+public interface Validator {
+
+
+    Map<String, Object> validateMap (Map<String, Object> map) throws KustvaktException;
+
+
+    String validateEntry (String input, String type)
+            throws KustvaktException;
+
+
+    boolean isValid (String input, String type);
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/ClientsHandler.java b/full/src/main/java/de/ids_mannheim/korap/web/ClientsHandler.java
new file mode 100644
index 0000000..4ae0a53
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/ClientsHandler.java
@@ -0,0 +1,59 @@
+package de.ids_mannheim.korap.web;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MultivaluedMap;
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author hanl
+ * @date 10/12/2013
+ */
+// use for Piotr Ps. rest api connection
+public class ClientsHandler {
+
+    private WebTarget service;
+
+
+    public ClientsHandler (URI address) {
+        Client client = ClientBuilder.newClient();
+        this.service = client.target(address);
+    }
+
+
+    public String getResponse (String path, String key, Object value)
+            throws KustvaktException {
+        try {
+            return service.path(path).queryParam(key, value).request().get(String.class);
+        }
+        catch (WebApplicationException e) {
+            throw new KustvaktException(StatusCodes.REQUEST_INVALID);
+        }
+    }
+
+
+    public String getResponse (MultivaluedMap<String, String> map, String ... paths)
+            throws KustvaktException {
+        try {
+            WebTarget resource = service;
+            for (String p : paths)
+                resource = resource.path(p);
+            for (Map.Entry<String, List<String>> e : map.entrySet()) {
+                for (String value : e.getValue())
+                    resource = resource.queryParam(e.getKey(), value);
+            }
+            return resource.request().get(String.class);
+        }
+        catch (WebApplicationException e) {
+            throw new KustvaktException(StatusCodes.REQUEST_INVALID);
+        }
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/CoreResponseHandler.java b/full/src/main/java/de/ids_mannheim/korap/web/CoreResponseHandler.java
new file mode 100644
index 0000000..a94d0b3
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/CoreResponseHandler.java
@@ -0,0 +1,107 @@
+package de.ids_mannheim.korap.web;
+
+import java.util.List;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Response;
+
+import de.ids_mannheim.korap.auditing.AuditRecord;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.interfaces.db.AuditingIface;
+import de.ids_mannheim.korap.response.Notifications;
+
+/**
+ * @author hanl, margaretha
+ * @date 29/01/2014
+ * @last 04/12/2017
+ */
+public class CoreResponseHandler {
+
+    private AuditingIface auditing;
+
+    public CoreResponseHandler (AuditingIface iface) {
+        this.auditing = iface;
+    }
+
+    private 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 WebApplicationException throwit (KustvaktException e) {
+        Response s;
+        if (e.hasNotification()) {
+            if (e.getStatusCode() != null) {
+                s = Response.status(getStatus(e.getStatusCode()))
+                        .entity(e.getNotification()).build();
+            }
+            // KustvaktException just wraps another exception 
+            else {
+                s=Response.status(Response.Status.BAD_REQUEST)
+                        .entity(e.getNotification()).build();
+            }
+        }
+        else {
+            s = Response.status(getStatus(e.getStatusCode()))
+                    .entity(buildNotification(e)).build();
+        }
+        return new WebApplicationException(s);
+    }
+
+    public WebApplicationException throwit (int code) {
+        return new WebApplicationException(Response.status(getStatus(code))
+                .entity(buildNotification(code, "", "")).build());
+    }
+
+
+    public WebApplicationException throwit (int code, String message,
+            String entity) {
+        return new WebApplicationException(Response.status(getStatus(code))
+                .entity(buildNotification(code, message, entity)).build());
+    }
+
+    public WebApplicationException throwit (int code, String notification) {
+        return new WebApplicationException(
+                Response.status(getStatus(code)).entity(notification).build());
+    }
+
+    protected String buildNotification (KustvaktException e) {
+        register(e.getRecords());
+        return buildNotification(e.getStatusCode(), e.getMessage(),
+                e.getEntity());
+    }
+
+    public static String buildNotification (int code, String message,
+            String entity) {
+        Notifications notif = new Notifications();
+        notif.addError(code, message, entity);
+        return notif.toJsonString() + "\n";
+    }
+
+    protected Response.Status getStatus (int code) {
+        Response.Status status = Response.Status.BAD_REQUEST;
+        switch (code) {
+            // case StatusCodes.NO_VALUE_FOUND:
+            // status = Response.Status.NO_CONTENT;
+            // break;
+            case StatusCodes.ILLEGAL_ARGUMENT:
+                status = Response.Status.NOT_ACCEPTABLE;
+                break;
+            case StatusCodes.STATUS_OK:
+                status = Response.Status.OK;
+                break;
+            // EM: Added 
+            case StatusCodes.NO_RESOURCE_FOUND:
+                status = Response.Status.NOT_FOUND;
+                break;
+            case StatusCodes.CACHING_VC:
+                status = Response.Status.SERVICE_UNAVAILABLE;
+                break;    
+        }
+        return status;
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/KustvaktResponseHandler.java b/full/src/main/java/de/ids_mannheim/korap/web/KustvaktResponseHandler.java
new file mode 100644
index 0000000..8a7e2f3
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/KustvaktResponseHandler.java
@@ -0,0 +1,71 @@
+package de.ids_mannheim.korap.web;
+
+import java.util.EnumSet;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+
+import de.ids_mannheim.korap.constant.AuthenticationScheme;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.interfaces.db.AuditingIface;
+
+/**
+ * KustvaktResponseHandler includes exceptions regarding
+ * authorization.
+ * 
+ * @author margaretha
+ *
+ */
+public class KustvaktResponseHandler extends CoreResponseHandler {
+
+    public KustvaktResponseHandler (AuditingIface iface) {
+        super(iface);
+    }
+
+    @Override
+    public WebApplicationException throwit (KustvaktException e) {
+        Response r;
+
+        // KustvaktException just wraps another exception
+        if (e.getStatusCode() == null && e.hasNotification()) {
+            r = Response.status(Response.Status.BAD_REQUEST)
+                    .entity(e.getNotification()).build();
+        }
+        else if (e.getStatusCode() == StatusCodes.USER_REAUTHENTICATION_REQUIRED
+                || e.getStatusCode() == StatusCodes.AUTHORIZATION_FAILED
+                || e.getStatusCode() >= StatusCodes.AUTHENTICATION_FAILED) {
+            String notification = buildNotification(e.getStatusCode(),
+                    e.getMessage(), e.getEntity());
+            r = createUnauthenticatedResponse(notification);
+        }
+        else if (e.hasNotification()) {
+            r = Response.status(getStatus(e.getStatusCode()))
+                    .entity(e.getNotification()).build();
+        }
+        else {
+            String notification = buildNotification(e.getStatusCode(),
+                    e.getMessage(), e.getEntity());
+            r = Response.status(getStatus(e.getStatusCode()))
+                    .entity(notification).build();
+        }
+        return new WebApplicationException(r);
+    }
+
+    public Response createUnauthenticatedResponse (String notification) {
+        ResponseBuilder builder = Response.status(Response.Status.UNAUTHORIZED);
+
+        EnumSet<AuthenticationScheme> schemes = EnumSet
+                .allOf(AuthenticationScheme.class);
+        schemes.remove(AuthenticationScheme.API);
+        
+        for (AuthenticationScheme s : schemes) {
+            builder = builder.header(HttpHeaders.WWW_AUTHENTICATE,
+                    s.displayName() + " realm=\"Kustvakt\"");
+        }
+
+        return builder.entity(notification).build();
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/SearchKrill.java b/full/src/main/java/de/ids_mannheim/korap/web/SearchKrill.java
new file mode 100644
index 0000000..6d6f52b
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/SearchKrill.java
@@ -0,0 +1,392 @@
+// Connector to the Lucene Backend
+package de.ids_mannheim.korap.web;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.lucene.store.MMapDirectory;
+
+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.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.response.Match;
+import de.ids_mannheim.korap.response.MetaFields;
+import de.ids_mannheim.korap.response.Result;
+import de.ids_mannheim.korap.util.QueryException;
+
+/**
+ * The SearchKrill class allows for searching in the
+ * Lucene based Krill backend by applying KoralQuery.
+ * 
+ * @author Nils Diewald
+ */
+public class SearchKrill {
+    private final static Logger jlog = LogManager.getLogger(SearchKrill.class);
+
+    private static final boolean DEBUG = false;
+
+    public static KrillIndex index;
+
+    /**
+     * Constructor
+     */
+    // todo: use korap.config to get index location
+    public SearchKrill (String path) {
+
+        try {
+            if (path.equals(":temp:")) {
+                index = new KrillIndex();
+            }
+            else {
+                File f = new File(path);
+                jlog.info("Loading index from " + path);
+                if (!f.exists()) {
+                    jlog.error("Index not found: " + path + "!");
+                    System.exit(-1);
+                }
+                index = new KrillIndex(new MMapDirectory(Paths.get(path)));
+            };
+        }
+        catch (IOException e) {
+            jlog.error("Unable to loadSubTypes index:" + e.getMessage());
+        };
+    };
+
+
+    public KrillIndex getIndex () {
+        return index;
+    };
+
+
+    public void closeIndexReader () throws KustvaktException {
+        try {
+            index.closeReader();
+        }
+        catch (IOException e) {
+            throw new KustvaktException(500, "Failed closing index reader");
+        }
+    }
+
+
+    /**
+     * Search in the Lucene index.
+     * 
+     * @param json
+     *            JSON-LD string with search and potential meta
+     *            filters.
+     */
+    public String search (String json) {
+        if (DEBUG) {
+            jlog.debug(json);
+        }
+        if (index != null) {
+            String result = new Krill(json).apply(index).toJsonString();
+            if (DEBUG) {
+                jlog.debug(result);
+            }
+            return result;
+        }
+        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.
+     * 
+     * @param json
+     *            JSON-LD string with search and potential meta
+     *            filters.
+     */
+    @Deprecated
+    public String searchTokenList (String json) {
+        if (DEBUG) {
+            jlog.debug(json);
+        }
+        if (index != null)
+            return new Krill(json).apply(index).toTokenListJsonString();
+        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.
+     * 
+     * @param id
+     *            match id
+     * @param availabilityList
+     * @throws KustvaktException
+     */
+    public String getMatch (String id, Pattern licensePattern)
+            throws KustvaktException {
+        Match km;
+        if (index != null) {
+            try {
+                km = index.getMatch(id);
+                String availability = km.getAvailability();
+                checkAvailability(licensePattern, availability, id);
+            }
+            catch (QueryException qe) {
+                km = new Match();
+                km.addError(qe.getErrorCode(), qe.getMessage());
+            }
+        }
+        else {
+            km = new Match();
+            km.addError(601, "Unable to find index");
+        }
+        return km.toJsonString();
+    };
+
+
+    private void checkAvailability (Pattern licensePattern, String availability,
+            String id) throws KustvaktException {
+        if (DEBUG) {
+            jlog.debug("pattern: " + licensePattern.toString()
+                    + ", availability: " + availability);
+        }
+        if (licensePattern != null && availability != null) {
+            Matcher m = licensePattern.matcher(availability);
+            if (!m.matches()) {
+                if (availability.isEmpty()) {
+                    throw new KustvaktException(StatusCodes.MISSING_ATTRIBUTE,
+                            "Availability for " + id + "is empty.", id);
+                }
+                throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
+                        "Retrieving resource with ID " + id
+                                + " is not allowed.",
+                        id);
+            }
+        }
+
+    }
+
+
+    /*
+     * Retrieve the meta fields for a certain document
+     */
+    public String getFields (String id, List<String> fields,
+            Pattern licensePattern) throws KustvaktException {
+        MetaFields meta;
+
+        // No index found
+        if (index == null) {
+            meta = new MetaFields(id);
+            meta.addError(601, "Unable to find index");
+        }
+
+        // Index available
+        else if (fields != null) {
+            // Get fields
+            meta = index.getFields(id, fields);
+        }
+        else {
+            // Get fields
+            meta = index.getFields(id);
+        }
+
+        // EM: this approach forbids the whole metadata
+        // this should be refined by filtering out only the restricted
+        // metadata fields
+        // String availability = meta.getFieldValue("availability");
+        // checkAvailability(licensePattern, availability, id);
+
+        return meta.toJsonString();
+    };
+
+
+    public String getMatch (
+        String id, boolean info, List<String> foundries,
+        List<String> layers, boolean includeSpans,
+        boolean includeSnippet, boolean includeTokens,
+        boolean includeHighlights, boolean sentenceExpansion,
+        Pattern licensePattern) throws KustvaktException {
+        Match km;
+        if (index != null) {
+            try {
+                km = index.getMatchInfo(id, "tokens", info, foundries, layers,
+                                        includeSpans, includeSnippet, includeTokens,
+                                        includeHighlights, sentenceExpansion);
+                String availability = km.getAvailability();
+                checkAvailability(licensePattern, availability, id);
+            }
+            catch (QueryException qe) {
+                km = new Match();
+                km.addError(qe.getErrorCode(), qe.getMessage());
+            }
+        }
+        else {
+            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.
+     * 
+     * @param id
+     *            match id
+     * @param foundry
+     *            the foundry of interest - may be null
+     * @param layer
+     *            the layer of interest - may be null
+     * @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,
+            boolean includeSpans, boolean includeHighlights,
+            boolean sentenceExpansion) {
+
+        if (index != null) {
+            try {
+                /*
+                  For multiple foundries/layers use
+                  String idString,
+                  "tokens",
+                  true,
+                  ArrayList<String> foundry,
+                  ArrayList<String> layer,
+                  boolean includeSpans,
+                  boolean includeHighlights,
+                  boolean extendToSentence
+                */
+                return index.getMatchInfo(id, "tokens", foundry, layer,
+                        includeSpans, includeHighlights, sentenceExpansion)
+                        .toJsonString();
+            }
+            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.
+     * 
+     * EM: might be changed later
+     * 
+     * @param json
+     *            JSON-LD string with potential meta filters.
+     * @throws KustvaktException
+     */
+    public String getStatistics (String json) throws KustvaktException {
+        if (index == null) {
+            return "{\"documents\" : -1, error\" : \"No index given\" }";
+        };
+
+        // Define a virtual corpus
+        KrillCollection kc;
+        if (json != null && !json.equals("")) {
+            if (DEBUG) {
+                jlog.debug(json);
+            }
+
+            // Create Virtual collection from json search
+            kc = new KrillCollection(json);
+        }
+
+        // There is no json string defined
+        else {
+
+            // Create Virtual collection of everything
+            kc = new KrillCollection();
+        };
+
+        // Set index
+        kc.setIndex(index);
+        long docs = 0, tokens = 0, sentences = 0, paragraphs = 0;
+        // Get numbers from index (currently slow)
+        try {
+            docs = kc.numberOf("documents");
+            if (docs > 0) {
+                tokens = kc.numberOf("tokens");
+                sentences = kc.numberOf("base/sentences");
+                paragraphs = kc.numberOf("base/paragraphs");
+            };
+        }
+        catch (IOException e) {
+            e.printStackTrace();
+        };
+
+        if (kc.hasErrors()) {
+            throw new KustvaktException(
+                    "{\"errors\":" + kc.getErrors().toJsonString() + "}");
+        }
+        // Build json response
+        StringBuilder sb = new StringBuilder("{");
+        sb.append("\"documents\":").append(docs).append(",\"tokens\":")
+                .append(tokens).append(",\"sentences\":").append(sentences)
+                .append(",\"paragraphs\":").append(paragraphs).append("}");
+        return sb.toString();
+    };
+
+
+    /**
+     * Return the match identifier as a string.
+     * This is a convenient method to deal with legacy instantiation
+     * of the
+     * code.
+     */
+    public String getMatchId (String corpusID, String docID, String textID,
+            String matchID) {
+        // Create a string representation of the match
+        StringBuilder sb = new StringBuilder();
+        sb.append("match-").append(corpusID).append('/').append(docID)
+                .append('/').append(textID).append('-').append(matchID);
+        return sb.toString();
+    };
+
+
+    /**
+     * Return the text sigle as a string.
+     */
+    public String getTextSigle (String corpusID, String docID, String textID) {
+        // Create a string representation of the match
+        StringBuilder sb = new StringBuilder();
+        sb.append(corpusID).append('/').append(docID).append('/')
+                .append(textID);
+        return sb.toString();
+    };
+
+
+    /**
+     * Return the fingerprint of the latest index revision.
+     */
+    public String getIndexFingerprint () {
+        if (index != null) {
+            return index.getFingerprint();
+        };
+        return "null";
+    }
+
+
+    public JsonNode getFieldValuesForVC (String koralQuery, String fieldName) {
+        return new Krill().retrieveFieldValues(koralQuery, index, fieldName);
+    }
+
+};
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/TRACE.java b/full/src/main/java/de/ids_mannheim/korap/web/TRACE.java
new file mode 100644
index 0000000..05d511b
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/TRACE.java
@@ -0,0 +1,16 @@
+package de.ids_mannheim.korap.web;
+
+import javax.ws.rs.HttpMethod;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author hanl
+ * @date 03/07/2014
+ */
+@Target({ ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+@HttpMethod("TRACE")
+public @interface TRACE {}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/filter/APIVersionFilter.java b/full/src/main/java/de/ids_mannheim/korap/web/filter/APIVersionFilter.java
new file mode 100644
index 0000000..853557a
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/filter/APIVersionFilter.java
@@ -0,0 +1,44 @@
+package de.ids_mannheim.korap.web.filter;
+
+import java.util.List;
+
+import javax.annotation.Priority;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.PathSegment;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+
+/**
+ * Checks API version in URL path.
+ * 
+ * @author margaretha
+ *
+ */
+@Component
+@Priority(Integer.MIN_VALUE)
+public class APIVersionFilter
+        implements ContainerRequestFilter {
+
+    @Autowired
+    private KustvaktConfiguration config;
+
+    public void filter (ContainerRequestContext request) {
+        List<PathSegment> pathSegments = request.getUriInfo().getPathSegments();
+        String version = pathSegments.get(0).getPath();
+
+        if (!config.getSupportedVersions().contains(version)) {
+            throw new NotFoundException();
+            // throw kustvaktResponseHandler.throwit(
+            // new
+            // KustvaktException(StatusCodes.UNSUPPORTED_API_VERSION,
+            // "API " + version + " is unsupported.", version));
+        }
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/filter/AdminFilter.java b/full/src/main/java/de/ids_mannheim/korap/web/filter/AdminFilter.java
new file mode 100644
index 0000000..885ce41
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/filter/AdminFilter.java
@@ -0,0 +1,78 @@
+package de.ids_mannheim.korap.web.filter;
+
+import javax.annotation.Priority;
+import javax.servlet.ServletContext;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.SecurityContext;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import de.ids_mannheim.korap.dao.AdminDao;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.security.context.TokenContext;
+import de.ids_mannheim.korap.utils.JerseyUtils;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
+
+/**
+ * Verifies admin credentials or token before allowing access to
+ * administrative services
+ * 
+ * @author hanl, margaretha
+ * 
+ * @see {@link AuthenticationFilter}
+ */
+@Component
+@Priority(Priorities.AUTHENTICATION)
+public class AdminFilter extends AuthenticationFilter {
+
+    private @Context ServletContext servletContext;
+    @Autowired
+    private AdminDao adminDao;
+
+    @Autowired
+    private KustvaktResponseHandler kustvaktResponseHandler;
+
+    @Override
+    public void filter (ContainerRequestContext context) {
+        super.filter(context);
+        String username = "guest";
+        String adminToken = JerseyUtils.getFormParameters(context).asMap()
+                .getFirst("token");
+        if (!checkAdminToken(adminToken)) {
+            SecurityContext securityContext = context.getSecurityContext();
+            TokenContext tokenContext = (TokenContext) securityContext
+                    .getUserPrincipal();
+            checkAdminCredentials(tokenContext, username);
+        }
+    }
+
+
+    private boolean checkAdminToken (String adminToken) {
+        if (adminToken != null && !adminToken.isEmpty()) {
+            if (adminToken
+                    .equals(servletContext.getInitParameter("adminToken"))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    private void checkAdminCredentials (TokenContext tokenContext,
+            String username) {
+        if (tokenContext != null) {
+            username = tokenContext.getUsername();
+            if (adminDao.isAdmin(username)) {
+                return;
+            }
+        }
+
+        throw kustvaktResponseHandler.throwit(new KustvaktException(
+                StatusCodes.AUTHORIZATION_FAILED,
+                "Unauthorized operation for user: " + username, username));
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/filter/AuthenticationFilter.java b/full/src/main/java/de/ids_mannheim/korap/web/filter/AuthenticationFilter.java
new file mode 100644
index 0000000..bb5d6c1
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/filter/AuthenticationFilter.java
@@ -0,0 +1,127 @@
+package de.ids_mannheim.korap.web.filter;
+
+import javax.annotation.Priority;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.glassfish.jersey.server.ContainerRequest;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import de.ids_mannheim.korap.authentication.AuthenticationManager;
+import de.ids_mannheim.korap.authentication.http.AuthorizationData;
+import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
+import de.ids_mannheim.korap.constant.TokenType;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.security.context.KustvaktContext;
+import de.ids_mannheim.korap.security.context.TokenContext;
+import de.ids_mannheim.korap.utils.TimeUtils;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
+
+/** Authentication filter extracts an authentication token from 
+ * authorization header and uses an authentication provider 
+ * with respect to the token type to create a token context as
+ * a security context.
+ * 
+ * @author hanl, margaretha
+ * @date 28/01/2014
+ * @last update 12/2017
+ */
+@Component
+@Priority(Priorities.AUTHENTICATION)
+public class AuthenticationFilter
+        implements ContainerRequestFilter {
+
+    private static Logger jlog = LogManager.getLogger(AuthenticationFilter.class);
+    
+    @Autowired
+    private HttpAuthorizationHandler authorizationHandler;
+
+    @Autowired
+    private AuthenticationManager authenticationManager;
+
+    @Autowired
+    private KustvaktResponseHandler kustvaktResponseHandler;
+
+    @Override
+    public void filter (ContainerRequestContext request) {
+        String host = request.getHeaderString(ContainerRequest.HOST);
+        String ua = request.getHeaderString(ContainerRequest.USER_AGENT);
+
+        String authorization =
+                request.getHeaderString(ContainerRequest.AUTHORIZATION);
+
+        if (authorization != null && !authorization.isEmpty()) {
+            TokenContext context = null;
+            AuthorizationData authData;
+            try {
+                authData = authorizationHandler
+                        .parseAuthorizationHeaderValue(authorization);
+
+                switch (authData.getAuthenticationScheme()) {
+                    // EM: For testing only, must be disabled for
+                    // production
+                    case BASIC:
+                        context = authenticationManager.getTokenContext(
+                                TokenType.BASIC, authData.getToken(), host, ua);
+                        break;
+                    // EM: has not been tested yet
+                    // case SESSION:
+                    // context =
+                    // authenticationManager.getTokenContext(
+                    // TokenType.SESSION, authData.getToken(), host,
+                    // ua);
+                    // break;
+
+                    // OAuth2 authentication scheme
+                    case BEARER:
+                        context = authenticationManager.getTokenContext(
+                                TokenType.BEARER, authData.getToken(), host,
+                                ua);
+                        break;
+                    // EM: JWT token-based authentication scheme
+                    case API:
+                        jlog.warn("Authentication filter using token API");
+                        throw new KustvaktException(
+                                StatusCodes.AUTHENTICATION_FAILED,
+                                "Authentication API is no longer supported.");
+                    default:
+                        throw new KustvaktException(
+                                StatusCodes.AUTHENTICATION_FAILED,
+                                "Authentication scheme is not supported.");
+                }
+                checkContext(context, request);
+                request.setSecurityContext(new KustvaktContext(context));
+            }
+            catch (KustvaktException e) {
+                throw kustvaktResponseHandler.throwit(e);
+            }
+        }
+    }
+
+
+    private void checkContext (TokenContext context, ContainerRequestContext request)
+            throws KustvaktException {
+        if (context == null) {
+            throw new KustvaktException(StatusCodes.AUTHENTICATION_FAILED,
+                    "Context is null.");
+        }
+        else if (!context.isValid()) {
+            throw new KustvaktException(StatusCodes.AUTHENTICATION_FAILED,
+                    "Context is not valid: "
+                            + "missing username, password or authentication scheme.");
+        }
+        else if (context.isSecureRequired() && !request.getSecurityContext().isSecure()) {
+            throw new KustvaktException(StatusCodes.AUTHENTICATION_FAILED,
+                    "Request is not secure.");
+        }
+        else if (TimeUtils.isExpired(context.getExpirationTime())) {
+            throw new KustvaktException(StatusCodes.EXPIRED,
+                    "Access token is expired");
+        }
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/filter/BlockingFilter.java b/full/src/main/java/de/ids_mannheim/korap/web/filter/BlockingFilter.java
new file mode 100644
index 0000000..bb14e10
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/filter/BlockingFilter.java
@@ -0,0 +1,50 @@
+package de.ids_mannheim.korap.web.filter;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Priority;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.SecurityContext;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.security.context.TokenContext;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
+
+/**
+ * @author hanl
+ * @date 11/12/2014
+ *       <p/>
+ *       endpoint filter to block access to an endpoint, in case no
+ *       anonymous access should be allowed!
+ */
+@Component
+@Priority(Priorities.AUTHORIZATION)
+public class BlockingFilter implements ContainerRequestFilter {
+
+    @Autowired
+    private KustvaktResponseHandler kustvaktResponseHandler;
+
+    @Override
+    public void filter (ContainerRequestContext request) {
+        TokenContext context;
+
+        SecurityContext securityContext = request.getSecurityContext();
+        if (securityContext != null) {
+            context = (TokenContext) securityContext.getUserPrincipal();
+        }
+        else {
+            throw kustvaktResponseHandler.throwit(new KustvaktException(
+                    StatusCodes.UNSUPPORTED_OPERATION));
+        }
+
+        if (context == null || context.isDemo()) {
+            throw kustvaktResponseHandler.throwit(new KustvaktException(
+                    StatusCodes.AUTHORIZATION_FAILED,
+                    "Unauthorized operation for user: guest", "guest"));
+        }
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/filter/DemoFilter.java b/full/src/main/java/de/ids_mannheim/korap/web/filter/DemoFilter.java
new file mode 100644
index 0000000..e020a8d
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/filter/DemoFilter.java
@@ -0,0 +1,51 @@
+package de.ids_mannheim.korap.web.filter;
+
+import javax.annotation.Priority;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.SecurityContext;
+
+import org.glassfish.jersey.server.ContainerRequest;
+
+import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
+import de.ids_mannheim.korap.constant.TokenType;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.security.context.KustvaktContext;
+import de.ids_mannheim.korap.security.context.TokenContext;
+
+/**
+ * @author hanl
+ * @date 08/02/2016
+ */
+@Priority(Priorities.AUTHENTICATION)
+public class DemoFilter implements ContainerRequestFilter {
+
+    @Override
+    public void filter (ContainerRequestContext request) {
+        String authentication =
+                request.getHeaderString(ContainerRequest.AUTHORIZATION);
+        if (authentication == null || authentication.isEmpty()) {
+            if (request.getSecurityContext() == null) {
+                request.setSecurityContext(createContext());
+            }
+        }
+    }
+
+
+    private SecurityContext createContext () {
+        TokenContext context = new TokenContext();
+        String token = null;
+        try {
+            token = HttpAuthorizationHandler
+                    .createBasicAuthorizationHeaderValue("demo", "demo2015");
+        }
+        catch (KustvaktException e) {
+            e.printStackTrace();
+        }
+        context.setToken(token);
+        context.setTokenType(TokenType.BASIC);
+        context.setUsername("demo");
+        return new KustvaktContext(context);
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/filter/DemoUserFilter.java b/full/src/main/java/de/ids_mannheim/korap/web/filter/DemoUserFilter.java
new file mode 100644
index 0000000..22b43f0
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/filter/DemoUserFilter.java
@@ -0,0 +1,69 @@
+package de.ids_mannheim.korap.web.filter;
+
+import java.security.Principal;
+
+import javax.annotation.Priority;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.core.UriInfo;
+
+import org.glassfish.jersey.server.ContainerRequest;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.constant.TokenType;
+import de.ids_mannheim.korap.security.context.KustvaktContext;
+import de.ids_mannheim.korap.security.context.TokenContext;
+import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.utils.TimeUtils;
+
+/**
+ * Created by hanl on 7/15/14.
+ */
+@Component
+@Priority(Priorities.AUTHENTICATION)
+public class DemoUserFilter implements ContainerRequestFilter {
+
+    @Context
+    UriInfo info;
+    @Autowired
+    private KustvaktConfiguration config;
+
+
+    @Override
+    public void filter (ContainerRequestContext request) {
+        String host = request.getHeaderString(ContainerRequest.HOST);
+        String ua = request.getHeaderString(ContainerRequest.USER_AGENT);
+        String authentication = request
+                .getHeaderString(ContainerRequest.AUTHORIZATION);
+
+        // means that this is the public service
+        if (authentication == null || authentication.isEmpty()) {
+            Principal pr = null;
+            SecurityContext securityContext = request.getSecurityContext();
+            if (securityContext != null) {
+                pr = securityContext.getUserPrincipal();
+            }
+            if (pr == null)
+                request.setSecurityContext(new KustvaktContext(
+                        createShorterToken(host, ua)));
+        }
+    }
+
+
+    private TokenContext createShorterToken (String host, String agent) {
+        User demo = User.UserFactory.getDemoUser();
+        TokenContext c = new TokenContext();
+        c.setUsername(demo.getUsername());
+        c.setHostAddress(host);
+        c.setUserAgent(agent);
+        c.setExpirationTime(
+                TimeUtils.plusSeconds(config.getShortTokenTTL()).getMillis());
+        c.setTokenType(TokenType.BASIC);
+        return c;
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/filter/NonDemoBlockingFilter.java b/full/src/main/java/de/ids_mannheim/korap/web/filter/NonDemoBlockingFilter.java
new file mode 100644
index 0000000..c4b539a
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/filter/NonDemoBlockingFilter.java
@@ -0,0 +1,52 @@
+package de.ids_mannheim.korap.web.filter;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Priority;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.SecurityContext;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.security.context.TokenContext;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
+
+/**
+ * EM: pretty much identical to {@link BlockingFilter}, should be deleted? 
+ * 
+ * @author hanl
+ * @date 11/12/2014
+ *       <p/>
+ *       endpoint filter to block access to an endpoint, in case no
+ *       anonymous access should be allowed!
+ */
+@Component
+@Priority(Priorities.AUTHORIZATION)
+public class NonDemoBlockingFilter
+        implements ContainerRequestFilter {
+
+    @Autowired
+    private KustvaktResponseHandler kustvaktResponseHandler;
+
+    @Override
+    public void filter (ContainerRequestContext request) {
+        TokenContext context;
+        SecurityContext securityContext = request.getSecurityContext();
+        if (securityContext != null) {
+            context = (TokenContext) securityContext.getUserPrincipal();
+        }
+        else {
+            throw kustvaktResponseHandler.throwit(new KustvaktException(
+                    StatusCodes.UNSUPPORTED_OPERATION));
+        }
+
+        if (context == null || context.isDemo()) {
+            throw kustvaktResponseHandler.throwit(
+                    new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
+                            "Operation is not permitted for guest users"));
+        }
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/filter/PiwikFilter.java b/full/src/main/java/de/ids_mannheim/korap/web/filter/PiwikFilter.java
new file mode 100644
index 0000000..5a9f19a
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/filter/PiwikFilter.java
@@ -0,0 +1,146 @@
+package de.ids_mannheim.korap.web.filter;
+
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Random;
+
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.core.UriBuilder;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Priority;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.WebTarget;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.logging.LoggingFeature;
+
+import de.ids_mannheim.korap.authentication.AuthenticationManager;
+import de.ids_mannheim.korap.config.Attributes;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.security.context.TokenContext;
+import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.user.UserSettingProcessor;
+import de.ids_mannheim.korap.user.Userdata;
+import net.minidev.json.JSONArray;
+
+/**
+ * @author hanl
+ * @date 13/05/2014
+ */
+@Component
+@Priority(Priorities.AUTHORIZATION)
+public class PiwikFilter implements ContainerRequestFilter {
+
+    private WebTarget service;
+    //    private static final String SERVICE = "http://localhost:8888";
+    private static final String SERVICE = "http://10.0.10.13";
+    private static Logger jlog = LogManager.getLogger(PiwikFilter.class);
+    public static boolean ENABLED = false;
+    private Map<String, String> customVars;
+    @Autowired
+    private AuthenticationManager authenticationManager;
+
+
+    public PiwikFilter () {
+//        controller = BeansFactory.getKustvaktContext()
+//                .getAuthenticationManager();
+        ClientConfig clientConfig = new ClientConfig();
+        if (jlog.isDebugEnabled())
+            clientConfig.register(LoggingFeature.class);
+        Client client = ClientBuilder.newClient(clientConfig);
+        UriBuilder b = UriBuilder.fromUri(SERVICE);
+        service = client.target(b.build());
+        this.customVars = new HashMap<>();
+    }
+
+
+    private void send (ContainerRequestContext request) {
+        Random random = new SecureRandom();
+        Locale l = null;
+        if (request.getAcceptableLanguages() != null)
+            l = request.getAcceptableLanguages().get(0);
+        try {
+            service.path("piwik/piwik.php")
+                    .queryParam("idsite", "2")
+                    .queryParam("rec", "1")
+                    //todo check for empty container
+                    .queryParam("_cvar", translateCustomData())
+                    .queryParam("cip", request.getHeaderString("Host"))
+                    .queryParam("cookie", "false")
+                    .queryParam("r", String.valueOf(random.nextDouble()))
+                    .queryParam("action_name",
+                            request.getUriInfo().getRequestUri().toASCIIString())
+                    .request()
+                    .accept("text/html")
+                    .header("Host", request.getHeaderString("Host"))
+                    .header("User-Agent", request.getHeaderString("User-Agent"))
+                    .acceptLanguage(l).method("GET");
+        }
+        catch (Exception e) {
+            // do nothing if piwik not available!
+        }
+    }
+
+
+    private String translateCustomData () {
+        final Map<String, List<String>> customVariables = new HashMap<String, List<String>>();
+        int i = 0;
+        for (final Map.Entry<String, String> entry : this.customVars.entrySet()) {
+            i++;
+            final List<String> list = new ArrayList<String>();
+            list.add(entry.getKey());
+            list.add(entry.getValue());
+            customVariables.put(Integer.toString(i), list);
+        }
+
+        final JSONArray json = new JSONArray();
+        json.add(customVariables);
+
+        // remove unnecessary parent square brackets from JSON-string
+        String jsonString = json.toString().substring(1,
+                json.toString().length() - 1);
+        customVars.clear();
+        return jsonString;
+    }
+
+
+    @Override
+    public void filter (ContainerRequestContext request) {
+        if (ENABLED) {
+            try {
+                TokenContext context;
+                SecurityContext securityContext = request.getSecurityContext();
+                if (securityContext != null) {
+                    context = (TokenContext) securityContext.getUserPrincipal();
+
+                    if (context.getUsername() != null){
+                        // since this is cached, not very expensive!
+                        User user = authenticationManager.getUser(context.getUsername());
+                        Userdata data = authenticationManager
+                                .getUserData(user, UserSettingProcessor.class);
+                        if ((Boolean) data.get(Attributes.COLLECT_AUDITING_DATA))
+                            customVars.put("username", context.getUsername());
+                    }
+                }
+            }
+            catch (KustvaktException e) {
+                //do nothing
+            }
+            send(request);
+        }
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/utils/FormRequestWrapper.java b/full/src/main/java/de/ids_mannheim/korap/web/utils/FormRequestWrapper.java
new file mode 100644
index 0000000..12c7825
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/utils/FormRequestWrapper.java
@@ -0,0 +1,94 @@
+package de.ids_mannheim.korap.web.utils;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.ws.rs.core.MultivaluedMap;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Helper class to wrapp multivaluedmap into a hashmap. Depending on
+ * the strict parameter,
+ * list values are retained in the resulting wrapper map.
+ * 
+ * @author hanl
+ * @date 25/04/2015
+ */
+public class FormRequestWrapper extends HttpServletRequestWrapper {
+
+    private MultivaluedMap<String, String> form;
+
+
+    /**
+     * Constructs a request object wrapping the given request.
+     * 
+     * @param request
+     * @throws IllegalArgumentException
+     *             if the request is null
+     */
+    public FormRequestWrapper (HttpServletRequest request,
+                               MultivaluedMap<String, String> form) {
+        super(request);
+        this.form = form;
+    }
+
+
+    @Override
+    public String getParameter (String name) {
+        String value = super.getParameter(name);
+        if (value == null){
+            value = form.getFirst(name);
+        }
+        return value;
+    }
+
+
+    @Override
+    public String[] getParameterValues (String name) {
+        String[] values = super.getParameterValues(name);
+        if (values == null && form.get(name) != null) {
+            values = new String[form.get(name).size()];
+            values = form.get(name).toArray(values);
+        }
+        return values;
+    }
+
+
+    public HashMap<String, Object> singleValueMap () {
+        return toMap(this.form, false);
+    }
+
+
+    /**
+     * @param strict
+     *            returns only values with size equal to one. If false
+     *            pairs key to first value
+     *            in value list and returns the result
+     * @return key/value map
+     */
+    public static HashMap<String, Object> toMap (
+            MultivaluedMap<String, String> form, boolean strict) {
+        if (form == null)
+            return null;
+        HashMap<String, Object> map = new HashMap<>();
+        for (String key : form.keySet()) {
+            if (strict && form.get(key).size() > 1)
+                continue;
+            map.put(key, form.getFirst(key));
+
+        }
+        return map;
+    }
+
+
+    public void put (String key, String value) {
+        this.form.putSingle(key, value);
+    }
+
+
+    public void put (String key, String ... values) {
+        this.form.put(key, Arrays.<String> asList(values));
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/utils/HTMLBuilder.java b/full/src/main/java/de/ids_mannheim/korap/web/utils/HTMLBuilder.java
new file mode 100644
index 0000000..543fbae
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/utils/HTMLBuilder.java
@@ -0,0 +1,60 @@
+package de.ids_mannheim.korap.web.utils;
+
+/**
+ * @author hanl
+ * @date 12/04/2014
+ */
+public class HTMLBuilder {
+
+    private StringBuilder html;
+    private StringBuilder body;
+    private String bodyAttr;
+
+
+    public HTMLBuilder () {
+        html = new StringBuilder();
+        body = new StringBuilder();
+        bodyAttr = "";
+        html.append("<html>");
+    }
+
+
+    public void addHeader (String header, int h) {
+        html.append("<h" + h + ">");
+        html.append(header);
+        html.append("</h" + h + ">");
+    }
+
+
+    public void addToBody (String body) {
+        this.body.append(body);
+    }
+
+
+    public void addToBody (String body, String attributes) {
+        this.body.append(body);
+        bodyAttr = attributes;
+    }
+
+
+    public String build () {
+        if (bodyAttr.isEmpty())
+            html.append("<body>");
+        else {
+            html.append("<body ");
+            html.append(bodyAttr);
+            html.append(">");
+        }
+
+        html.append(body);
+        html.append("</body>");
+        html.append("</html>");
+        return html.toString();
+    }
+
+
+    @Override
+    public String toString () {
+        return build();
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/utils/JsonExceptionMapper.java b/full/src/main/java/de/ids_mannheim/korap/web/utils/JsonExceptionMapper.java
new file mode 100644
index 0000000..126f34d
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/utils/JsonExceptionMapper.java
@@ -0,0 +1,36 @@
+package de.ids_mannheim.korap.web.utils;
+
+import javax.annotation.Priority;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import com.fasterxml.jackson.databind.JsonMappingException;
+
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.web.CoreResponseHandler;
+
+/**
+ * Creates appropriate responses in case of incorrect JSON
+ * deserialization or JsonMappingException, for instance when a
+ * request parameter should be deserialized as an ENUM but the
+ * parameter value does not match any of the available ENUM values.
+ * 
+ * @author margaretha
+ *
+ */
+@Priority(value = 1)
+@Provider
+public class JsonExceptionMapper
+        implements ExceptionMapper<JsonMappingException> {
+
+    @Override
+    public Response toResponse (JsonMappingException exception) {
+        String entity = CoreResponseHandler.buildNotification(
+                StatusCodes.DESERIALIZATION_FAILED, exception.getMessage(),
+                null);
+        return Response.status(Response.Status.BAD_REQUEST).entity(entity)
+                .build();
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/utils/KustvaktMap.java b/full/src/main/java/de/ids_mannheim/korap/web/utils/KustvaktMap.java
new file mode 100644
index 0000000..8e9de8a
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/utils/KustvaktMap.java
@@ -0,0 +1,79 @@
+package de.ids_mannheim.korap.web.utils;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author hanl
+ * @date 21/01/2016
+ */
+public class KustvaktMap {
+
+    private boolean monoTyped;
+    private Map<String, Object> values;
+
+
+    public KustvaktMap () {
+        this.values = new HashMap<>();
+        this.monoTyped = false;
+    }
+
+
+    public KustvaktMap (Map<String, Object> m) {
+        this();
+        setMap(m);
+    }
+
+
+    public void setMap (Map<String, Object> m) {
+        if (!isGeneric(m) | !this.monoTyped)
+            this.values.putAll(m);
+    }
+
+
+    public boolean isGeneric () {
+        return !this.monoTyped && isGeneric(this.values);
+    }
+
+
+    private static boolean isGeneric (Map<String, Object> map) {
+        int i = 0;
+        for (Object o : map.values()) {
+            if (o instanceof String)
+                i++;
+        }
+        return !(i == map.size());
+    }
+
+
+    public void setMonoValue (boolean monovalue) {
+        this.monoTyped = monovalue;
+    }
+
+
+    public String get (String key) {
+        Object o = this.values.get(key);
+        if (!isGeneric())
+            return (String) o;
+        return String.valueOf(o);
+    }
+
+
+    public Object getRaw (String key) {
+        return this.values.get(key);
+    }
+
+
+    public <T extends Object> Object get (String key, Class<T> cl) {
+        if (isGeneric())
+            return (T) this.values.get(key);
+        return get(key);
+    }
+
+
+    public Set<String> keySet () {
+        return this.values.keySet();
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/utils/KustvaktResponseBuilder.java b/full/src/main/java/de/ids_mannheim/korap/web/utils/KustvaktResponseBuilder.java
new file mode 100644
index 0000000..b43adf8
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/utils/KustvaktResponseBuilder.java
@@ -0,0 +1,33 @@
+package de.ids_mannheim.korap.web.utils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author hanl
+ * @date 20/01/2016
+ */
+public class KustvaktResponseBuilder {
+    Map<String, Object> _values;
+
+
+    public KustvaktResponseBuilder () {
+        this._values = new HashMap<>();
+    }
+
+
+    public KustvaktResponseBuilder addEntity (Object o) {
+        if (o instanceof Map && !((Map) o).isEmpty())
+            this._values.putAll((Map<? extends String, ?>) o);
+
+
+        return this;
+    }
+
+
+    @Override
+    public String toString () {
+        return "";
+    }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/utils/LocaleFactory.java b/full/src/main/java/de/ids_mannheim/korap/web/utils/LocaleFactory.java
new file mode 100644
index 0000000..cb72ba5
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/utils/LocaleFactory.java
@@ -0,0 +1,25 @@
+package de.ids_mannheim.korap.web.utils;
+
+import org.glassfish.hk2.api.Factory;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.ext.Provider;
+import java.util.List;
+import java.util.Locale;
+
+@Provider
+public class LocaleFactory implements Factory<Locale> {
+    @Context
+    private ContainerRequestContext context;
+
+    @Override
+    public Locale provide() {
+        final List<Locale> locales = context.getAcceptableLanguages();
+        if (locales.isEmpty())
+            return Locale.US;
+        return locales.get(0);
+    }
+
+    @Override
+    public void dispose(Locale instance) {}
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/utils/NotFoundMapper.java b/full/src/main/java/de/ids_mannheim/korap/web/utils/NotFoundMapper.java
new file mode 100644
index 0000000..a05983b
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/utils/NotFoundMapper.java
@@ -0,0 +1,84 @@
+package de.ids_mannheim.korap.web.utils;
+
+import java.net.URI;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ResourceContext;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.eclipse.jetty.http.HttpStatus;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+
+/**
+ * Handles not found API version by redirecting the request URI to a
+ * similar URI with current API version.
+ * 
+ * @author margaretha
+ *
+ */
+@Component
+@Provider
+public class NotFoundMapper implements ExceptionMapper<NotFoundException> {
+
+    private static Logger jlog = LogManager.getLogger(NotFoundMapper.class);
+    public static final Pattern VERSION_PATTERN =
+            Pattern.compile("/(v[0-9][^/]*)(/.*)");
+    private static final boolean DEBUG = false;
+
+    @Autowired
+    private KustvaktConfiguration config;
+
+    @Context
+    private ResourceContext resourceContext;
+
+    @Override
+    public Response toResponse (NotFoundException exception) {
+        ContainerRequestContext requestContext =
+                resourceContext.getResource(ContainerRequestContext.class);
+
+    	URI notFoundUri = requestContext.getUriInfo().getRequestUri();
+
+        String path = notFoundUri.getPath();
+        String baseUrl = config.getBaseURL();
+        baseUrl = baseUrl.substring(0, baseUrl.length() - 2);
+
+        if (path.startsWith(baseUrl)) {
+            path = path.substring(baseUrl.length(), path.length());
+            Matcher matcher = VERSION_PATTERN.matcher(path);
+            if (!matcher.matches()) {
+                path = baseUrl + "/" + config.getCurrentVersion() + path;
+                URI redirectUri = UriBuilder.fromUri(notFoundUri)
+                        .replacePath(path).build();
+                if (DEBUG) {
+                    jlog.debug("REDIRECT: " + redirectUri.toString());
+                }
+                return Response.status(HttpStatus.PERMANENT_REDIRECT_308)
+                        .location(redirectUri).build();
+            }
+            else if (!matcher.group(1).equals(config.getCurrentVersion())) {
+                path = baseUrl + "/" + config.getCurrentVersion()
+                        + matcher.group(2);
+                URI redirectUri = UriBuilder.fromUri(notFoundUri)
+                        .replacePath(path).build();
+                if (DEBUG) {
+                    jlog.debug("REDIRECT replace: " + redirectUri.toString());
+                }
+                return Response.status(HttpStatus.PERMANENT_REDIRECT_308)
+                        .location(redirectUri).build();
+            }
+        }
+        return Response.status(HttpStatus.NOT_FOUND_404).build();
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/utils/ResourceFilters.java b/full/src/main/java/de/ids_mannheim/korap/web/utils/ResourceFilters.java
new file mode 100644
index 0000000..ab0d4ea
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/utils/ResourceFilters.java
@@ -0,0 +1,23 @@
+package de.ids_mannheim.korap.web.utils;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Defines the list of {@link javax.ws.rs.container.ContainerRequestFilter}
+ * and {@link javax.ws.rs.container.ContainerResponseFilter}
+ * classes associated with a resource method.
+ * <p>
+ * This annotation can be specified on a class or on method(s). Specifying it
+ * at a class level means that it applies to all the methods in the class.
+ * Specifying it on a method means that it is applicable to that method only.
+ * If applied at both the class and methods level , the method value overrides
+ * the class value.
+ */
+@Target({ ElementType.TYPE, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ResourceFilters {
+    Class<?>[] value();
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/utils/ResourceFiltersFeature.java b/full/src/main/java/de/ids_mannheim/korap/web/utils/ResourceFiltersFeature.java
new file mode 100644
index 0000000..62b7745
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/utils/ResourceFiltersFeature.java
@@ -0,0 +1,27 @@
+package de.ids_mannheim.korap.web.utils;
+
+import javax.ws.rs.container.DynamicFeature;
+import javax.ws.rs.container.ResourceInfo;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * Registers {@link javax.ws.rs.container.ContainerRequestFilter}
+ * and {@link javax.ws.rs.container.ContainerResponseFilter}
+ * classes for a resource method annotated with {@link ResourceFilters}.
+ */
+@Provider
+public class ResourceFiltersFeature implements DynamicFeature {
+
+    @Override
+    public void configure (ResourceInfo resourceInfo, FeatureContext context) {
+        ResourceFilters filtersAnnotation = resourceInfo.getResourceMethod().getAnnotation(ResourceFilters.class);
+        if (filtersAnnotation == null)
+            filtersAnnotation = resourceInfo.getResourceClass().getAnnotation(ResourceFilters.class);
+
+        if (filtersAnnotation != null) {
+            for (Class<?> filter : filtersAnnotation.value())
+                context.register(filter);
+        }
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/utils/SearchResourceFilters.java b/full/src/main/java/de/ids_mannheim/korap/web/utils/SearchResourceFilters.java
new file mode 100644
index 0000000..12dbf57
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/utils/SearchResourceFilters.java
@@ -0,0 +1,22 @@
+package de.ids_mannheim.korap.web.utils;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Defines the list of {@link javax.ws.rs.container.ContainerRequestFilter}
+ * and {@link javax.ws.rs.container.ContainerResponseFilter}
+ * classes associated with a resource method.
+ * <p>
+ * This annotation can be specified on a class or on method(s). Specifying it
+ * at a class level means that it applies to all the methods in the class.
+ * Specifying it on a method means that it is applicable to that method only.
+ * If applied at both the class and methods level , the method value overrides
+ * the class value.
+ */
+@Target({ ElementType.TYPE, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SearchResourceFilters {
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/utils/SearchResourceFiltersFeature.java b/full/src/main/java/de/ids_mannheim/korap/web/utils/SearchResourceFiltersFeature.java
new file mode 100644
index 0000000..45a7c4e
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/utils/SearchResourceFiltersFeature.java
@@ -0,0 +1,58 @@
+package de.ids_mannheim.korap.web.utils;
+
+import java.util.List;
+
+import javax.ws.rs.container.DynamicFeature;
+import javax.ws.rs.container.ResourceInfo;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.model.internal.CommonConfig;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import de.ids_mannheim.korap.web.filter.APIVersionFilter;
+import edu.emory.mathcs.backport.java.util.Arrays;
+
+/**
+ * Registers {@link javax.ws.rs.container.ContainerRequestFilter}
+ * and {@link javax.ws.rs.container.ContainerResponseFilter}
+ * classes for a resource method annotated with {@link ResourceFilters}.
+ */
+@Provider
+@Component
+public class SearchResourceFiltersFeature implements DynamicFeature {
+
+    @Value("${search.resource.filters:AuthenticationFilter,DemoUserFilter}")
+    private String[] resourceFilters;
+    
+    @Override
+    public void configure (ResourceInfo resourceInfo, FeatureContext context) {
+        SearchResourceFilters filters = resourceInfo.getResourceMethod()
+                .getAnnotation(SearchResourceFilters.class);
+        if (filters != null) {
+            CommonConfig con = (CommonConfig) context.getConfiguration();
+            con.getComponentBag().clear();
+        }        
+        else {
+            filters = resourceInfo.getResourceClass()
+            .getAnnotation(SearchResourceFilters.class);
+        }
+        
+        if (filters != null) {
+            List<?> list = Arrays.asList(resourceFilters);
+            if (!list.contains("APIVersionFilter")) {
+                context.register(APIVersionFilter.class);
+            }
+            
+             for(String c : resourceFilters) {
+                    try {
+                        context.register(Class.forName("de.ids_mannheim.korap.web.filter." + c));
+                    }
+                    catch (ClassNotFoundException e) {
+                        e.printStackTrace();
+                    }
+            }
+        }
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/utils/ShutdownHook.java b/full/src/main/java/de/ids_mannheim/korap/web/utils/ShutdownHook.java
new file mode 100644
index 0000000..de34e38
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/utils/ShutdownHook.java
@@ -0,0 +1,29 @@
+package de.ids_mannheim.korap.web.utils;
+
+/**
+ * @author hanl
+ * @date 18/02/2014
+ */
+public class ShutdownHook extends Thread {
+
+    @Override
+    public void run () {
+        //        Properties config = ExtensionBeans.getInstance().getConfiguration()
+        //                .getMailProperties();
+
+        //                Email e = new SimpleEmail();
+        //                try {
+        //                    e.setHostName(config.getProperty("mail.host"));
+        //                    e.setSmtpPort(587);
+        //                    e.setSubject("KorAP Rest service shutdown Notification");
+        //                    e.setFrom(config.getProperty("mail.from"));
+        //                    e.addTo("hanl@ids-mannheim.de");
+        //                    e.setMsg("The KorAP - REST application shut down unexpectedly!!");
+        //                    e.send();
+        //                } catch (EmailException e1) {
+        //                    e1.printStackTrace();
+        //                }
+
+    }
+
+}
diff --git a/full/src/main/resources/annotation-scripts/foundries/base.js b/full/src/main/resources/annotation-scripts/foundries/base.js
new file mode 100644
index 0000000..a21cce6
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/foundries/base.js
@@ -0,0 +1,15 @@
+define(["hint/foundries"], function (ah) {
+  ah["-"].push(
+    ["Base Annotation", "base/", "Structure"]
+  );
+  
+  ah["base/"] = [
+	["Structure", "s="]
+  ];
+  
+  ah["base/s="] = [
+    ["s", "s", "Sentence"],
+    ["p", "p", "Paragraph"],
+    ["t", "t", "Text"]
+  ];
+});
diff --git a/full/src/main/resources/annotation-scripts/foundries/cnx.js b/full/src/main/resources/annotation-scripts/foundries/cnx.js
new file mode 100644
index 0000000..9316cbd
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/foundries/cnx.js
@@ -0,0 +1,63 @@
+define(["hint/foundries"], function (ah) {
+  ah["-"].push(
+    ["Connexor", "cnx/", "Constituency, Lemma, Morphology, Part-of-Speech, Syntax"]
+  );
+
+  ah["cnx/"] = [
+    ["Constituency", "c="],
+    ["Lemma", "l="],
+    ["Morphology", "m="],
+    ["Part-of-Speech", "p="],
+    ["Syntax", "syn="]
+  ];
+
+  ah["cnx/c="] = [
+    ["np", "np ", "Nominal Phrase"]
+  ];
+
+  // http://www.ids-mannheim.de/cosmas2/projekt/referenz/connexor/morph.html
+  ah["cnx/m="] = [
+    ["Abbr","Abbr ", "Nouns: Abbreviation"],
+    ["CMP","CMP ", "Adjective: Comparative"],
+    ["IMP", "IMP ", "Mood: Imperative"],
+    ["IND", "IND ", "Mood: Indicative"],
+    ["INF", "INF ", "Infinitive"],
+    ["ORD","ORD ", "Numeral: Ordinal"],
+    ["PAST", "PAST ", "Tense: past"],
+    ["PCP", "PCP ", "Participle"],
+    ["PERF", "PERF ", "Perfective Participle"],
+    ["PL","PL ", "Nouns: Plural"],
+    ["PRES", "PRES ", "Tense: present"],
+    ["PROG", "PROG ", "Progressive Participle"],
+    ["Prop","Prop ", "Nouns: Proper Noun"],
+    ["SUB", "SUB ", "Mood: Subjunctive"],
+    ["SUP","SUP ", "Adjective: Superlative"]
+  ];
+
+  // http://www.ids-mannheim.de/cosmas2/projekt/referenz/connexor/morph.html
+  ah["cnx/p="] = [
+    ["A", "A ", "Adjective"],
+    ["ADV", "ADV ", "Adverb"],
+    ["CC", "CC ", "Coordination Marker"],
+    ["CS", "CS ", "Clause Marker"],
+    ["DET", "DET ", "Determiner"],
+    ["INTERJ", "INTERJ ", "Interjection"],
+    ["N", "N ", "Noun"],
+    ["NUM", "NUM ", "Numeral"],
+    ["PREP", "PREP ", "Preposition"],
+    ["PRON", "PRON ", "Pro-Nominal"],
+    ["V", "V ", "Verb"]
+  ];
+
+  // http://www.ids-mannheim.de/cosmas2/projekt/referenz/connexor/syntax.html
+  ah["cnx/syn="] = [
+    ["@ADVL", "@ADVL ", "Adverbial Head"],
+    ["@AUX", "@AUX ", "Auxiliary Verb"],
+    ["@CC", "@CC ", "Coordination"]
+    ["@MAIN", "@MAIN ", "Main Verb"],
+    ["@NH", "@NH ", "Nominal Head"],
+    ["@POSTMOD", "@POSTMOD ", "Postmodifier"],
+    ["@PREMARK", "@PREMARK ", "Preposed Marker"],
+    ["@PREMOD", "@PREMOD ", "Premodifier"]
+  ];
+});
diff --git a/full/src/main/resources/annotation-scripts/foundries/corenlp.js b/full/src/main/resources/annotation-scripts/foundries/corenlp.js
new file mode 100644
index 0000000..ff3b44a
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/foundries/corenlp.js
@@ -0,0 +1,1374 @@
+define(["hint/foundries","hint/foundries/stts","hint/foundries/negranodes","hint/foundries/negraedges"],
+       function (ah, sttsArray, negraNodesArray, negraEdgesArray) {
+
+  ah["-"].push(
+    ["CoreNLP", "corenlp/", "Constituency, Named Entities, Part-of-Speech"]
+  );
+
+  ah["corenlp/"] = [
+    ["Constituency", "c="],
+    ["Named Entity", "ne=" , "Combined"],
+    ["Named Entity", "ne_dewac_175m_600=" , "ne_dewac_175m_600"],
+    ["Named Entity", "ne_hgc_175m_600=",    "ne_hgc_175m_600"],
+    ["Part-of-Speech", "p="]
+  ];
+
+  ah["corenlp/ne="] = [
+	["I-LOC",  "I-LOC ",  "Location"],
+	["I-MISC", "I-MISC ", "Miscellaneous"],
+	["I-ORG",  "I-ORG ",  "Organization"],
+	["I-PER",  "I-PER ",  "Person"]
+  ];
+  
+  ah["corenlp/ne_dewac_175m_600="] = [
+	["I-LOC",  "I-LOC ",  "Location"],
+	["I-MISC", "I-MISC ", "Miscellaneous"],
+	["I-ORG",  "I-ORG ",  "Organization"],
+	["I-PER",  "I-PER ",  "Person"]
+  ];
+	  
+  ah["corenlp/ne_hgc_175m_600="] = [
+	["I-LOC",  "I-LOC ",  "Location"],
+	["I-MISC", "I-MISC ", "Miscellaneous"],
+	["I-ORG",  "I-ORG ",  "Organization"],
+	["I-PER",  "I-PER ",  "Person"]
+  ];
+	  
+  ah["corenlp/p="] = [
+    ["ADJA","ADJA ", "Attributive Adjective"],
+    ["ADJD","ADJD ", "Predicative Adjective"],
+    ["ADV","ADV ", "Adverb"],
+    ["APPO","APPO ", "Postposition"],
+    ["APPR","APPR ", "Preposition"],
+    ["APPRART","APPRART ", "Preposition with Determiner"],
+    ["APZR","APZR ","Right Circumposition"],
+    ["ART","ART ", "Determiner"],
+    ["CARD","CARD ", "Cardinal Number"],
+    ["FM","FM ", "Foreign Material"],
+    ["ITJ","ITJ ", "Interjection"],
+    ["KOKOM","KOKOM ", "Comparison Particle"],
+    ["KON","KON ", "Coordinating Conjuncion"],
+    ["KOUI","KOUI ", "Subordinating Conjunction with 'zu'"],
+    ["KOUS","KOUS ", "Subordinating Conjunction with Sentence"],
+    ["NE","NE ", "Named Entity"],
+    ["NN","NN ", "Normal Nomina"],
+    ["PAV", "PAV ", "Pronominal Adverb"],
+    ["PDAT","PDAT ","Attributive Demonstrative Pronoun"],
+    ["PDS","PDS ", "Substitutive Demonstrative Pronoun"],
+    ["PIAT","PIAT ", "Attributive Indefinite Pronoun without Determiner"],
+    ["PIDAT","PIDAT ", "Attributive Indefinite Pronoun with Determiner"],
+    ["PIS","PIS ", "Substitutive Indefinite Pronoun"],
+    ["PPER","PPER ", "Personal Pronoun"],
+    ["PPOSAT","PPOSAT ", "Attributive Possessive Pronoun"],
+    ["PPOSS","PPOSS ", "Substitutive Possessive Pronoun"],
+    ["PRELAT","PRELAT ", "Attributive Relative Pronoun"],
+    ["PRELS","PRELS ", "Substitutive Relative Pronoun"],
+    ["PRF","PRF ", "Reflexive Pronoun"],
+    ["PROAV","PROAV ", "Pronominal Adverb"],
+    ["PTKA","PTKA ","Particle with Adjective"],
+    ["PTKANT","PTKANT ", "Answering Particle"],
+    ["PTKNEG","PTKNEG ", "Negation Particle"],
+    ["PTKVZ","PTKVZ ", "Separated Verbal Particle"],
+    ["PTKZU","PTKZU ", "'zu' Particle"],
+    ["PWAT","PWAT ", "Attributive Interrogative Pronoun"],
+    ["PWAV","PWAV ", "Adverbial Interrogative Pronoun"],
+    ["PWS","PWS ", "Substitutive Interrogative Pronoun"],
+    ["TRUNC","TRUNC ","Truncated"],
+    ["VAFIN","VAFIN ", "Auxiliary Finite Verb"],
+    ["VAIMP","VAIMP ", "Auxiliary Finite Imperative Verb"],
+    ["VAINF","VAINF ", "Auxiliary Infinite Verb"],
+    ["VAPP","VAPP ", "Auxiliary Perfect Participle"],
+    ["VMFIN","VMFIN ", "Modal Finite Verb"],
+    ["VMINF","VMINF ", "Modal Infinite Verb"],
+    ["VMPP","VMPP ", "Modal Perfect Participle"],
+    ["VVFIN","VVFIN ","Finite Verb"],
+    ["VVIMP","VVIMP ", "Finite Imperative Verb"],
+    ["VVINF","VVINF ", "Infinite Verb"],
+    ["VVIZU","VVIZU ", "Infinite Verb with 'zu'"],
+    ["VVPP","VVPP ", "Perfect Participle"],
+    ["XY", "XY ", "Non-Word"]
+  ];
+  
+  ah["corenlp/c="] = [
+    ["AA", "AA", "superlative phrase with 'am'"],
+    ["AP","AP", "adjektive phrase"],
+    ["AVP","AVP", "adverbial phrase"],
+    ["CAP","CAP", "coordinated adjektive phrase"],
+    ["CAVP","CAVP", "coordinated adverbial phrase"],
+    ["CAC","CAC", "coordinated adposition"],
+    ["CCP","CCP", "coordinated complementiser"],
+    ["CH","CH", "chunk"],
+    ["CNP","CNP", "coordinated noun phrase"],
+    ["CO","CO", "coordination"],
+    ["CPP","CPP", "coordinated adpositional phrase"],
+    ["CS","CS", "coordinated sentence"],
+    ["CVP","CVP", "coordinated verb phrase (non-finite)"],
+    ["CVZ","CVZ", "coordinated zu-marked infinitive"],
+    ["DL","DL", "discourse level constituent"],
+    ["ISU","ISU", "idiosyncratis unit"],
+    ["MPN","MPN", "multi-word proper noun"],
+    ["MTA","MTA", "multi-token adjective"],
+    ["NM","NM", "multi-token number"],
+    ["NP","NP", "noun phrase"],
+    ["PP","PP", "adpositional phrase"],
+    ["QL","QL", "quasi-languag"],
+    ["ROOT","ROOT", "root node"],
+    ["S","S", "sentence"],
+    ["VP","VP", "verb phrase (non-finite)"],
+    ["VZ","VZ", "zu-marked infinitive"]
+  ];
+  
+  ah["corenlp/c=AA-"] = [
+	  ["AC", "AC ", "adpositional case marker"],
+	  ["ADC", "ADC ", "adjective component"],
+	  ["AMS", "AMS ", "measure argument of adj"],
+	  ["APP", "APP ", "apposition"],
+	  ["AVC", "AVC ", "adverbial phrase component"],
+	  ["CC", "CC ", "comparative complement"],
+	  ["CD", "CD ", "coordinating conjunction"],
+	  ["CJ", "CJ ", "conjunct"],
+	  ["CM", "CM ", "comparative concjunction"],
+	  ["CP", "CP ", "complementizer"],
+	  ["DA", "DA ", "dative"],
+	  ["DH", "DH ", "discourse-level head"],
+	  ["DM", "DM ", "discourse marker"],
+	  ["GL", "GL ", "prenominal genitive"],
+	  ["GR", "GR ", "postnominal genitive"],
+	  ["HD", "HD ", "head"],
+	  ["JU", "JU ", "junctor"],
+	  ["MC", "MC ", "comitative"],
+	  ["MI", "MI ", "instrumental"],
+	  ["ML", "ML ", "locative"],
+	  ["MNR", "MNR ", "postnominal modifier"],
+	  ["MO", "MO ", "modifier"],
+	  ["MR", "MR ", "rhetorical modifier"],
+	  ["MW", "MW ", "way (directional modifier)"],
+	  ["NG", "NG ", "negation"],
+	  ["NK", "NK ", "noun kernel modifier"],
+	  ["NMC", "NMC ", "numerical component"],
+	  ["OA", "OA ", "accusative object"],
+	  ["OA2", "OA2 ", "second accusative object"],
+	  ["OC", "OC ", "clausal object"],
+	  ["OG", "OG ", "genitive object"],
+	  ["PD", "PD ", "predicate"],
+	  ["PG", "PG ", "pseudo-genitive"],
+	  ["PH", "PH ", "placeholder"],
+	  ["PM", "PM ", "morphological particle"],
+	  ["PNC", "PNC ", "proper noun component"],
+	  ["RC", "RC ", "relative clause"],
+	  ["RE", "RE ", "repeated element"],
+	  ["RS", "RS ", "reported speech"],
+	  ["SB", "SB ", "subject"],
+	  ["SBP", "SBP ", "passivised subject (PP)"],
+	  ["SP", "SP ", "subject or predicate"],
+	  ["SVP", "SVP ", "separable verb prefix"],
+	  ["UC", "UC ", "(idiosyncratic) unit component"],
+	  ["VO", "VO ", "vocative"]
+	];
+
+	ah["corenlp/c=AP-"] = [
+	  ["AC", "AC ", "adpositional case marker"],
+	  ["ADC", "ADC ", "adjective component"],
+	  ["AMS", "AMS ", "measure argument of adj"],
+	  ["APP", "APP ", "apposition"],
+	  ["AVC", "AVC ", "adverbial phrase component"],
+	  ["CC", "CC ", "comparative complement"],
+	  ["CD", "CD ", "coordinating conjunction"],
+	  ["CJ", "CJ ", "conjunct"],
+	  ["CM", "CM ", "comparative concjunction"],
+	  ["CP", "CP ", "complementizer"],
+	  ["DA", "DA ", "dative"],
+	  ["DH", "DH ", "discourse-level head"],
+	  ["DM", "DM ", "discourse marker"],
+	  ["GL", "GL ", "prenominal genitive"],
+	  ["GR", "GR ", "postnominal genitive"],
+	  ["HD", "HD ", "head"],
+	  ["JU", "JU ", "junctor"],
+	  ["MC", "MC ", "comitative"],
+	  ["MI", "MI ", "instrumental"],
+	  ["ML", "ML ", "locative"],
+	  ["MNR", "MNR ", "postnominal modifier"],
+	  ["MO", "MO ", "modifier"],
+	  ["MR", "MR ", "rhetorical modifier"],
+	  ["MW", "MW ", "way (directional modifier)"],
+	  ["NG", "NG ", "negation"],
+	  ["NK", "NK ", "noun kernel modifier"],
+	  ["NMC", "NMC ", "numerical component"],
+	  ["OA", "OA ", "accusative object"],
+	  ["OA2", "OA2 ", "second accusative object"],
+	  ["OC", "OC ", "clausal object"],
+	  ["OG", "OG ", "genitive object"],
+	  ["PD", "PD ", "predicate"],
+	  ["PG", "PG ", "pseudo-genitive"],
+	  ["PH", "PH ", "placeholder"],
+	  ["PM", "PM ", "morphological particle"],
+	  ["PNC", "PNC ", "proper noun component"],
+	  ["RC", "RC ", "relative clause"],
+	  ["RE", "RE ", "repeated element"],
+	  ["RS", "RS ", "reported speech"],
+	  ["SB", "SB ", "subject"],
+	  ["SBP", "SBP ", "passivised subject (PP)"],
+	  ["SP", "SP ", "subject or predicate"],
+	  ["SVP", "SVP ", "separable verb prefix"],
+	  ["UC", "UC ", "(idiosyncratic) unit component"],
+	  ["VO", "VO ", "vocative"]
+	];
+
+	ah["corenlp/c=AVP-"] = [
+	  ["AC", "AC ", "adpositional case marker"],
+	  ["ADC", "ADC ", "adjective component"],
+	  ["AMS", "AMS ", "measure argument of adj"],
+	  ["APP", "APP ", "apposition"],
+	  ["AVC", "AVC ", "adverbial phrase component"],
+	  ["CC", "CC ", "comparative complement"],
+	  ["CD", "CD ", "coordinating conjunction"],
+	  ["CJ", "CJ ", "conjunct"],
+	  ["CM", "CM ", "comparative concjunction"],
+	  ["CP", "CP ", "complementizer"],
+	  ["DA", "DA ", "dative"],
+	  ["DH", "DH ", "discourse-level head"],
+	  ["DM", "DM ", "discourse marker"],
+	  ["GL", "GL ", "prenominal genitive"],
+	  ["GR", "GR ", "postnominal genitive"],
+	  ["HD", "HD ", "head"],
+	  ["JU", "JU ", "junctor"],
+	  ["MC", "MC ", "comitative"],
+	  ["MI", "MI ", "instrumental"],
+	  ["ML", "ML ", "locative"],
+	  ["MNR", "MNR ", "postnominal modifier"],
+	  ["MO", "MO ", "modifier"],
+	  ["MR", "MR ", "rhetorical modifier"],
+	  ["MW", "MW ", "way (directional modifier)"],
+	  ["NG", "NG ", "negation"],
+	  ["NK", "NK ", "noun kernel modifier"],
+	  ["NMC", "NMC ", "numerical component"],
+	  ["OA", "OA ", "accusative object"],
+	  ["OA2", "OA2 ", "second accusative object"],
+	  ["OC", "OC ", "clausal object"],
+	  ["OG", "OG ", "genitive object"],
+	  ["PD", "PD ", "predicate"],
+	  ["PG", "PG ", "pseudo-genitive"],
+	  ["PH", "PH ", "placeholder"],
+	  ["PM", "PM ", "morphological particle"],
+	  ["PNC", "PNC ", "proper noun component"],
+	  ["RC", "RC ", "relative clause"],
+	  ["RE", "RE ", "repeated element"],
+	  ["RS", "RS ", "reported speech"],
+	  ["SB", "SB ", "subject"],
+	  ["SBP", "SBP ", "passivised subject (PP)"],
+	  ["SP", "SP ", "subject or predicate"],
+	  ["SVP", "SVP ", "separable verb prefix"],
+	  ["UC", "UC ", "(idiosyncratic) unit component"],
+	  ["VO", "VO ", "vocative"]
+	];
+
+	ah["corenlp/c=CAP-"] = [
+	  ["AC", "AC ", "adpositional case marker"],
+	  ["ADC", "ADC ", "adjective component"],
+	  ["AMS", "AMS ", "measure argument of adj"],
+	  ["APP", "APP ", "apposition"],
+	  ["AVC", "AVC ", "adverbial phrase component"],
+	  ["CC", "CC ", "comparative complement"],
+	  ["CD", "CD ", "coordinating conjunction"],
+	  ["CJ", "CJ ", "conjunct"],
+	  ["CM", "CM ", "comparative concjunction"],
+	  ["CP", "CP ", "complementizer"],
+	  ["DA", "DA ", "dative"],
+	  ["DH", "DH ", "discourse-level head"],
+	  ["DM", "DM ", "discourse marker"],
+	  ["GL", "GL ", "prenominal genitive"],
+	  ["GR", "GR ", "postnominal genitive"],
+	  ["HD", "HD ", "head"],
+	  ["JU", "JU ", "junctor"],
+	  ["MC", "MC ", "comitative"],
+	  ["MI", "MI ", "instrumental"],
+	  ["ML", "ML ", "locative"],
+	  ["MNR", "MNR ", "postnominal modifier"],
+	  ["MO", "MO ", "modifier"],
+	  ["MR", "MR ", "rhetorical modifier"],
+	  ["MW", "MW ", "way (directional modifier)"],
+	  ["NG", "NG ", "negation"],
+	  ["NK", "NK ", "noun kernel modifier"],
+	  ["NMC", "NMC ", "numerical component"],
+	  ["OA", "OA ", "accusative object"],
+	  ["OA2", "OA2 ", "second accusative object"],
+	  ["OC", "OC ", "clausal object"],
+	  ["OG", "OG ", "genitive object"],
+	  ["PD", "PD ", "predicate"],
+	  ["PG", "PG ", "pseudo-genitive"],
+	  ["PH", "PH ", "placeholder"],
+	  ["PM", "PM ", "morphological particle"],
+	  ["PNC", "PNC ", "proper noun component"],
+	  ["RC", "RC ", "relative clause"],
+	  ["RE", "RE ", "repeated element"],
+	  ["RS", "RS ", "reported speech"],
+	  ["SB", "SB ", "subject"],
+	  ["SBP", "SBP ", "passivised subject (PP)"],
+	  ["SP", "SP ", "subject or predicate"],
+	  ["SVP", "SVP ", "separable verb prefix"],
+	  ["UC", "UC ", "(idiosyncratic) unit component"],
+	  ["VO", "VO ", "vocative"]
+	];
+
+	ah["corenlp/c=CAVP-"] = [
+	  ["AC", "AC ", "adpositional case marker"],
+	  ["ADC", "ADC ", "adjective component"],
+	  ["AMS", "AMS ", "measure argument of adj"],
+	  ["APP", "APP ", "apposition"],
+	  ["AVC", "AVC ", "adverbial phrase component"],
+	  ["CC", "CC ", "comparative complement"],
+	  ["CD", "CD ", "coordinating conjunction"],
+	  ["CJ", "CJ ", "conjunct"],
+	  ["CM", "CM ", "comparative concjunction"],
+	  ["CP", "CP ", "complementizer"],
+	  ["DA", "DA ", "dative"],
+	  ["DH", "DH ", "discourse-level head"],
+	  ["DM", "DM ", "discourse marker"],
+	  ["GL", "GL ", "prenominal genitive"],
+	  ["GR", "GR ", "postnominal genitive"],
+	  ["HD", "HD ", "head"],
+	  ["JU", "JU ", "junctor"],
+	  ["MC", "MC ", "comitative"],
+	  ["MI", "MI ", "instrumental"],
+	  ["ML", "ML ", "locative"],
+	  ["MNR", "MNR ", "postnominal modifier"],
+	  ["MO", "MO ", "modifier"],
+	  ["MR", "MR ", "rhetorical modifier"],
+	  ["MW", "MW ", "way (directional modifier)"],
+	  ["NG", "NG ", "negation"],
+	  ["NK", "NK ", "noun kernel modifier"],
+	  ["NMC", "NMC ", "numerical component"],
+	  ["OA", "OA ", "accusative object"],
+	  ["OA2", "OA2 ", "second accusative object"],
+	  ["OC", "OC ", "clausal object"],
+	  ["OG", "OG ", "genitive object"],
+	  ["PD", "PD ", "predicate"],
+	  ["PG", "PG ", "pseudo-genitive"],
+	  ["PH", "PH ", "placeholder"],
+	  ["PM", "PM ", "morphological particle"],
+	  ["PNC", "PNC ", "proper noun component"],
+	  ["RC", "RC ", "relative clause"],
+	  ["RE", "RE ", "repeated element"],
+	  ["RS", "RS ", "reported speech"],
+	  ["SB", "SB ", "subject"],
+	  ["SBP", "SBP ", "passivised subject (PP)"],
+	  ["SP", "SP ", "subject or predicate"],
+	  ["SVP", "SVP ", "separable verb prefix"],
+	  ["UC", "UC ", "(idiosyncratic) unit component"],
+	  ["VO", "VO ", "vocative"]
+	];
+
+	ah["corenlp/c=CAC-"] = [
+	  ["AC", "AC ", "adpositional case marker"],
+	  ["ADC", "ADC ", "adjective component"],
+	  ["AMS", "AMS ", "measure argument of adj"],
+	  ["APP", "APP ", "apposition"],
+	  ["AVC", "AVC ", "adverbial phrase component"],
+	  ["CC", "CC ", "comparative complement"],
+	  ["CD", "CD ", "coordinating conjunction"],
+	  ["CJ", "CJ ", "conjunct"],
+	  ["CM", "CM ", "comparative concjunction"],
+	  ["CP", "CP ", "complementizer"],
+	  ["DA", "DA ", "dative"],
+	  ["DH", "DH ", "discourse-level head"],
+	  ["DM", "DM ", "discourse marker"],
+	  ["GL", "GL ", "prenominal genitive"],
+	  ["GR", "GR ", "postnominal genitive"],
+	  ["HD", "HD ", "head"],
+	  ["JU", "JU ", "junctor"],
+	  ["MC", "MC ", "comitative"],
+	  ["MI", "MI ", "instrumental"],
+	  ["ML", "ML ", "locative"],
+	  ["MNR", "MNR ", "postnominal modifier"],
+	  ["MO", "MO ", "modifier"],
+	  ["MR", "MR ", "rhetorical modifier"],
+	  ["MW", "MW ", "way (directional modifier)"],
+	  ["NG", "NG ", "negation"],
+	  ["NK", "NK ", "noun kernel modifier"],
+	  ["NMC", "NMC ", "numerical component"],
+	  ["OA", "OA ", "accusative object"],
+	  ["OA2", "OA2 ", "second accusative object"],
+	  ["OC", "OC ", "clausal object"],
+	  ["OG", "OG ", "genitive object"],
+	  ["PD", "PD ", "predicate"],
+	  ["PG", "PG ", "pseudo-genitive"],
+	  ["PH", "PH ", "placeholder"],
+	  ["PM", "PM ", "morphological particle"],
+	  ["PNC", "PNC ", "proper noun component"],
+	  ["RC", "RC ", "relative clause"],
+	  ["RE", "RE ", "repeated element"],
+	  ["RS", "RS ", "reported speech"],
+	  ["SB", "SB ", "subject"],
+	  ["SBP", "SBP ", "passivised subject (PP)"],
+	  ["SP", "SP ", "subject or predicate"],
+	  ["SVP", "SVP ", "separable verb prefix"],
+	  ["UC", "UC ", "(idiosyncratic) unit component"],
+	  ["VO", "VO ", "vocative"]
+	];
+
+	ah["corenlp/c=CCP-"] = [
+	  ["AC", "AC ", "adpositional case marker"],
+	  ["ADC", "ADC ", "adjective component"],
+	  ["AMS", "AMS ", "measure argument of adj"],
+	  ["APP", "APP ", "apposition"],
+	  ["AVC", "AVC ", "adverbial phrase component"],
+	  ["CC", "CC ", "comparative complement"],
+	  ["CD", "CD ", "coordinating conjunction"],
+	  ["CJ", "CJ ", "conjunct"],
+	  ["CM", "CM ", "comparative concjunction"],
+	  ["CP", "CP ", "complementizer"],
+	  ["DA", "DA ", "dative"],
+	  ["DH", "DH ", "discourse-level head"],
+	  ["DM", "DM ", "discourse marker"],
+	  ["GL", "GL ", "prenominal genitive"],
+	  ["GR", "GR ", "postnominal genitive"],
+	  ["HD", "HD ", "head"],
+	  ["JU", "JU ", "junctor"],
+	  ["MC", "MC ", "comitative"],
+	  ["MI", "MI ", "instrumental"],
+	  ["ML", "ML ", "locative"],
+	  ["MNR", "MNR ", "postnominal modifier"],
+	  ["MO", "MO ", "modifier"],
+	  ["MR", "MR ", "rhetorical modifier"],
+	  ["MW", "MW ", "way (directional modifier)"],
+	  ["NG", "NG ", "negation"],
+	  ["NK", "NK ", "noun kernel modifier"],
+	  ["NMC", "NMC ", "numerical component"],
+	  ["OA", "OA ", "accusative object"],
+	  ["OA2", "OA2 ", "second accusative object"],
+	  ["OC", "OC ", "clausal object"],
+	  ["OG", "OG ", "genitive object"],
+	  ["PD", "PD ", "predicate"],
+	  ["PG", "PG ", "pseudo-genitive"],
+	  ["PH", "PH ", "placeholder"],
+	  ["PM", "PM ", "morphological particle"],
+	  ["PNC", "PNC ", "proper noun component"],
+	  ["RC", "RC ", "relative clause"],
+	  ["RE", "RE ", "repeated element"],
+	  ["RS", "RS ", "reported speech"],
+	  ["SB", "SB ", "subject"],
+	  ["SBP", "SBP ", "passivised subject (PP)"],
+	  ["SP", "SP ", "subject or predicate"],
+	  ["SVP", "SVP ", "separable verb prefix"],
+	  ["UC", "UC ", "(idiosyncratic) unit component"],
+	  ["VO", "VO ", "vocative"]
+	];
+
+	ah["corenlp/c=CH-"] = [
+	  ["AC", "AC ", "adpositional case marker"],
+	  ["ADC", "ADC ", "adjective component"],
+	  ["AMS", "AMS ", "measure argument of adj"],
+	  ["APP", "APP ", "apposition"],
+	  ["AVC", "AVC ", "adverbial phrase component"],
+	  ["CC", "CC ", "comparative complement"],
+	  ["CD", "CD ", "coordinating conjunction"],
+	  ["CJ", "CJ ", "conjunct"],
+	  ["CM", "CM ", "comparative concjunction"],
+	  ["CP", "CP ", "complementizer"],
+	  ["DA", "DA ", "dative"],
+	  ["DH", "DH ", "discourse-level head"],
+	  ["DM", "DM ", "discourse marker"],
+	  ["GL", "GL ", "prenominal genitive"],
+	  ["GR", "GR ", "postnominal genitive"],
+	  ["HD", "HD ", "head"],
+	  ["JU", "JU ", "junctor"],
+	  ["MC", "MC ", "comitative"],
+	  ["MI", "MI ", "instrumental"],
+	  ["ML", "ML ", "locative"],
+	  ["MNR", "MNR ", "postnominal modifier"],
+	  ["MO", "MO ", "modifier"],
+	  ["MR", "MR ", "rhetorical modifier"],
+	  ["MW", "MW ", "way (directional modifier)"],
+	  ["NG", "NG ", "negation"],
+	  ["NK", "NK ", "noun kernel modifier"],
+	  ["NMC", "NMC ", "numerical component"],
+	  ["OA", "OA ", "accusative object"],
+	  ["OA2", "OA2 ", "second accusative object"],
+	  ["OC", "OC ", "clausal object"],
+	  ["OG", "OG ", "genitive object"],
+	  ["PD", "PD ", "predicate"],
+	  ["PG", "PG ", "pseudo-genitive"],
+	  ["PH", "PH ", "placeholder"],
+	  ["PM", "PM ", "morphological particle"],
+	  ["PNC", "PNC ", "proper noun component"],
+	  ["RC", "RC ", "relative clause"],
+	  ["RE", "RE ", "repeated element"],
+	  ["RS", "RS ", "reported speech"],
+	  ["SB", "SB ", "subject"],
+	  ["SBP", "SBP ", "passivised subject (PP)"],
+	  ["SP", "SP ", "subject or predicate"],
+	  ["SVP", "SVP ", "separable verb prefix"],
+	  ["UC", "UC ", "(idiosyncratic) unit component"],
+	  ["VO", "VO ", "vocative"]
+	];
+
+	ah["corenlp/c=CNP-"] = [
+	  ["AC", "AC ", "adpositional case marker"],
+	  ["ADC", "ADC ", "adjective component"],
+	  ["AMS", "AMS ", "measure argument of adj"],
+	  ["APP", "APP ", "apposition"],
+	  ["AVC", "AVC ", "adverbial phrase component"],
+	  ["CC", "CC ", "comparative complement"],
+	  ["CD", "CD ", "coordinating conjunction"],
+	  ["CJ", "CJ ", "conjunct"],
+	  ["CM", "CM ", "comparative concjunction"],
+	  ["CP", "CP ", "complementizer"],
+	  ["DA", "DA ", "dative"],
+	  ["DH", "DH ", "discourse-level head"],
+	  ["DM", "DM ", "discourse marker"],
+	  ["GL", "GL ", "prenominal genitive"],
+	  ["GR", "GR ", "postnominal genitive"],
+	  ["HD", "HD ", "head"],
+	  ["JU", "JU ", "junctor"],
+	  ["MC", "MC ", "comitative"],
+	  ["MI", "MI ", "instrumental"],
+	  ["ML", "ML ", "locative"],
+	  ["MNR", "MNR ", "postnominal modifier"],
+	  ["MO", "MO ", "modifier"],
+	  ["MR", "MR ", "rhetorical modifier"],
+	  ["MW", "MW ", "way (directional modifier)"],
+	  ["NG", "NG ", "negation"],
+	  ["NK", "NK ", "noun kernel modifier"],
+	  ["NMC", "NMC ", "numerical component"],
+	  ["OA", "OA ", "accusative object"],
+	  ["OA2", "OA2 ", "second accusative object"],
+	  ["OC", "OC ", "clausal object"],
+	  ["OG", "OG ", "genitive object"],
+	  ["PD", "PD ", "predicate"],
+	  ["PG", "PG ", "pseudo-genitive"],
+	  ["PH", "PH ", "placeholder"],
+	  ["PM", "PM ", "morphological particle"],
+	  ["PNC", "PNC ", "proper noun component"],
+	  ["RC", "RC ", "relative clause"],
+	  ["RE", "RE ", "repeated element"],
+	  ["RS", "RS ", "reported speech"],
+	  ["SB", "SB ", "subject"],
+	  ["SBP", "SBP ", "passivised subject (PP)"],
+	  ["SP", "SP ", "subject or predicate"],
+	  ["SVP", "SVP ", "separable verb prefix"],
+	  ["UC", "UC ", "(idiosyncratic) unit component"],
+	  ["VO", "VO ", "vocative"]
+	];
+
+	ah["corenlp/c=CO-"] = [
+	  ["AC", "AC ", "adpositional case marker"],
+	  ["ADC", "ADC ", "adjective component"],
+	  ["AMS", "AMS ", "measure argument of adj"],
+	  ["APP", "APP ", "apposition"],
+	  ["AVC", "AVC ", "adverbial phrase component"],
+	  ["CC", "CC ", "comparative complement"],
+	  ["CD", "CD ", "coordinating conjunction"],
+	  ["CJ", "CJ ", "conjunct"],
+	  ["CM", "CM ", "comparative concjunction"],
+	  ["CP", "CP ", "complementizer"],
+	  ["DA", "DA ", "dative"],
+	  ["DH", "DH ", "discourse-level head"],
+	  ["DM", "DM ", "discourse marker"],
+	  ["GL", "GL ", "prenominal genitive"],
+	  ["GR", "GR ", "postnominal genitive"],
+	  ["HD", "HD ", "head"],
+	  ["JU", "JU ", "junctor"],
+	  ["MC", "MC ", "comitative"],
+	  ["MI", "MI ", "instrumental"],
+	  ["ML", "ML ", "locative"],
+	  ["MNR", "MNR ", "postnominal modifier"],
+	  ["MO", "MO ", "modifier"],
+	  ["MR", "MR ", "rhetorical modifier"],
+	  ["MW", "MW ", "way (directional modifier)"],
+	  ["NG", "NG ", "negation"],
+	  ["NK", "NK ", "noun kernel modifier"],
+	  ["NMC", "NMC ", "numerical component"],
+	  ["OA", "OA ", "accusative object"],
+	  ["OA2", "OA2 ", "second accusative object"],
+	  ["OC", "OC ", "clausal object"],
+	  ["OG", "OG ", "genitive object"],
+	  ["PD", "PD ", "predicate"],
+	  ["PG", "PG ", "pseudo-genitive"],
+	  ["PH", "PH ", "placeholder"],
+	  ["PM", "PM ", "morphological particle"],
+	  ["PNC", "PNC ", "proper noun component"],
+	  ["RC", "RC ", "relative clause"],
+	  ["RE", "RE ", "repeated element"],
+	  ["RS", "RS ", "reported speech"],
+	  ["SB", "SB ", "subject"],
+	  ["SBP", "SBP ", "passivised subject (PP)"],
+	  ["SP", "SP ", "subject or predicate"],
+	  ["SVP", "SVP ", "separable verb prefix"],
+	  ["UC", "UC ", "(idiosyncratic) unit component"],
+	  ["VO", "VO ", "vocative"]
+	];
+
+	ah["corenlp/c=CPP-"] = [
+	  ["AC", "AC ", "adpositional case marker"],
+	  ["ADC", "ADC ", "adjective component"],
+	  ["AMS", "AMS ", "measure argument of adj"],
+	  ["APP", "APP ", "apposition"],
+	  ["AVC", "AVC ", "adverbial phrase component"],
+	  ["CC", "CC ", "comparative complement"],
+	  ["CD", "CD ", "coordinating conjunction"],
+	  ["CJ", "CJ ", "conjunct"],
+	  ["CM", "CM ", "comparative concjunction"],
+	  ["CP", "CP ", "complementizer"],
+	  ["DA", "DA ", "dative"],
+	  ["DH", "DH ", "discourse-level head"],
+	  ["DM", "DM ", "discourse marker"],
+	  ["GL", "GL ", "prenominal genitive"],
+	  ["GR", "GR ", "postnominal genitive"],
+	  ["HD", "HD ", "head"],
+	  ["JU", "JU ", "junctor"],
+	  ["MC", "MC ", "comitative"],
+	  ["MI", "MI ", "instrumental"],
+	  ["ML", "ML ", "locative"],
+	  ["MNR", "MNR ", "postnominal modifier"],
+	  ["MO", "MO ", "modifier"],
+	  ["MR", "MR ", "rhetorical modifier"],
+	  ["MW", "MW ", "way (directional modifier)"],
+	  ["NG", "NG ", "negation"],
+	  ["NK", "NK ", "noun kernel modifier"],
+	  ["NMC", "NMC ", "numerical component"],
+	  ["OA", "OA ", "accusative object"],
+	  ["OA2", "OA2 ", "second accusative object"],
+	  ["OC", "OC ", "clausal object"],
+	  ["OG", "OG ", "genitive object"],
+	  ["PD", "PD ", "predicate"],
+	  ["PG", "PG ", "pseudo-genitive"],
+	  ["PH", "PH ", "placeholder"],
+	  ["PM", "PM ", "morphological particle"],
+	  ["PNC", "PNC ", "proper noun component"],
+	  ["RC", "RC ", "relative clause"],
+	  ["RE", "RE ", "repeated element"],
+	  ["RS", "RS ", "reported speech"],
+	  ["SB", "SB ", "subject"],
+	  ["SBP", "SBP ", "passivised subject (PP)"],
+	  ["SP", "SP ", "subject or predicate"],
+	  ["SVP", "SVP ", "separable verb prefix"],
+	  ["UC", "UC ", "(idiosyncratic) unit component"],
+	  ["VO", "VO ", "vocative"]
+	];
+
+	ah["corenlp/c=CS-"] = [
+	  ["AC", "AC ", "adpositional case marker"],
+	  ["ADC", "ADC ", "adjective component"],
+	  ["AMS", "AMS ", "measure argument of adj"],
+	  ["APP", "APP ", "apposition"],
+	  ["AVC", "AVC ", "adverbial phrase component"],
+	  ["CC", "CC ", "comparative complement"],
+	  ["CD", "CD ", "coordinating conjunction"],
+	  ["CJ", "CJ ", "conjunct"],
+	  ["CM", "CM ", "comparative concjunction"],
+	  ["CP", "CP ", "complementizer"],
+	  ["DA", "DA ", "dative"],
+	  ["DH", "DH ", "discourse-level head"],
+	  ["DM", "DM ", "discourse marker"],
+	  ["GL", "GL ", "prenominal genitive"],
+	  ["GR", "GR ", "postnominal genitive"],
+	  ["HD", "HD ", "head"],
+	  ["JU", "JU ", "junctor"],
+	  ["MC", "MC ", "comitative"],
+	  ["MI", "MI ", "instrumental"],
+	  ["ML", "ML ", "locative"],
+	  ["MNR", "MNR ", "postnominal modifier"],
+	  ["MO", "MO ", "modifier"],
+	  ["MR", "MR ", "rhetorical modifier"],
+	  ["MW", "MW ", "way (directional modifier)"],
+	  ["NG", "NG ", "negation"],
+	  ["NK", "NK ", "noun kernel modifier"],
+	  ["NMC", "NMC ", "numerical component"],
+	  ["OA", "OA ", "accusative object"],
+	  ["OA2", "OA2 ", "second accusative object"],
+	  ["OC", "OC ", "clausal object"],
+	  ["OG", "OG ", "genitive object"],
+	  ["PD", "PD ", "predicate"],
+	  ["PG", "PG ", "pseudo-genitive"],
+	  ["PH", "PH ", "placeholder"],
+	  ["PM", "PM ", "morphological particle"],
+	  ["PNC", "PNC ", "proper noun component"],
+	  ["RC", "RC ", "relative clause"],
+	  ["RE", "RE ", "repeated element"],
+	  ["RS", "RS ", "reported speech"],
+	  ["SB", "SB ", "subject"],
+	  ["SBP", "SBP ", "passivised subject (PP)"],
+	  ["SP", "SP ", "subject or predicate"],
+	  ["SVP", "SVP ", "separable verb prefix"],
+	  ["UC", "UC ", "(idiosyncratic) unit component"],
+	  ["VO", "VO ", "vocative"]
+	];
+
+	ah["corenlp/c=CVP-"] = [
+	  ["AC", "AC ", "adpositional case marker"],
+	  ["ADC", "ADC ", "adjective component"],
+	  ["AMS", "AMS ", "measure argument of adj"],
+	  ["APP", "APP ", "apposition"],
+	  ["AVC", "AVC ", "adverbial phrase component"],
+	  ["CC", "CC ", "comparative complement"],
+	  ["CD", "CD ", "coordinating conjunction"],
+	  ["CJ", "CJ ", "conjunct"],
+	  ["CM", "CM ", "comparative concjunction"],
+	  ["CP", "CP ", "complementizer"],
+	  ["DA", "DA ", "dative"],
+	  ["DH", "DH ", "discourse-level head"],
+	  ["DM", "DM ", "discourse marker"],
+	  ["GL", "GL ", "prenominal genitive"],
+	  ["GR", "GR ", "postnominal genitive"],
+	  ["HD", "HD ", "head"],
+	  ["JU", "JU ", "junctor"],
+	  ["MC", "MC ", "comitative"],
+	  ["MI", "MI ", "instrumental"],
+	  ["ML", "ML ", "locative"],
+	  ["MNR", "MNR ", "postnominal modifier"],
+	  ["MO", "MO ", "modifier"],
+	  ["MR", "MR ", "rhetorical modifier"],
+	  ["MW", "MW ", "way (directional modifier)"],
+	  ["NG", "NG ", "negation"],
+	  ["NK", "NK ", "noun kernel modifier"],
+	  ["NMC", "NMC ", "numerical component"],
+	  ["OA", "OA ", "accusative object"],
+	  ["OA2", "OA2 ", "second accusative object"],
+	  ["OC", "OC ", "clausal object"],
+	  ["OG", "OG ", "genitive object"],
+	  ["PD", "PD ", "predicate"],
+	  ["PG", "PG ", "pseudo-genitive"],
+	  ["PH", "PH ", "placeholder"],
+	  ["PM", "PM ", "morphological particle"],
+	  ["PNC", "PNC ", "proper noun component"],
+	  ["RC", "RC ", "relative clause"],
+	  ["RE", "RE ", "repeated element"],
+	  ["RS", "RS ", "reported speech"],
+	  ["SB", "SB ", "subject"],
+	  ["SBP", "SBP ", "passivised subject (PP)"],
+	  ["SP", "SP ", "subject or predicate"],
+	  ["SVP", "SVP ", "separable verb prefix"],
+	  ["UC", "UC ", "(idiosyncratic) unit component"],
+	  ["VO", "VO ", "vocative"]
+	];
+
+	ah["corenlp/c=CVZ-"] = [
+	  ["AC", "AC ", "adpositional case marker"],
+	  ["ADC", "ADC ", "adjective component"],
+	  ["AMS", "AMS ", "measure argument of adj"],
+	  ["APP", "APP ", "apposition"],
+	  ["AVC", "AVC ", "adverbial phrase component"],
+	  ["CC", "CC ", "comparative complement"],
+	  ["CD", "CD ", "coordinating conjunction"],
+	  ["CJ", "CJ ", "conjunct"],
+	  ["CM", "CM ", "comparative concjunction"],
+	  ["CP", "CP ", "complementizer"],
+	  ["DA", "DA ", "dative"],
+	  ["DH", "DH ", "discourse-level head"],
+	  ["DM", "DM ", "discourse marker"],
+	  ["GL", "GL ", "prenominal genitive"],
+	  ["GR", "GR ", "postnominal genitive"],
+	  ["HD", "HD ", "head"],
+	  ["JU", "JU ", "junctor"],
+	  ["MC", "MC ", "comitative"],
+	  ["MI", "MI ", "instrumental"],
+	  ["ML", "ML ", "locative"],
+	  ["MNR", "MNR ", "postnominal modifier"],
+	  ["MO", "MO ", "modifier"],
+	  ["MR", "MR ", "rhetorical modifier"],
+	  ["MW", "MW ", "way (directional modifier)"],
+	  ["NG", "NG ", "negation"],
+	  ["NK", "NK ", "noun kernel modifier"],
+	  ["NMC", "NMC ", "numerical component"],
+	  ["OA", "OA ", "accusative object"],
+	  ["OA2", "OA2 ", "second accusative object"],
+	  ["OC", "OC ", "clausal object"],
+	  ["OG", "OG ", "genitive object"],
+	  ["PD", "PD ", "predicate"],
+	  ["PG", "PG ", "pseudo-genitive"],
+	  ["PH", "PH ", "placeholder"],
+	  ["PM", "PM ", "morphological particle"],
+	  ["PNC", "PNC ", "proper noun component"],
+	  ["RC", "RC ", "relative clause"],
+	  ["RE", "RE ", "repeated element"],
+	  ["RS", "RS ", "reported speech"],
+	  ["SB", "SB ", "subject"],
+	  ["SBP", "SBP ", "passivised subject (PP)"],
+	  ["SP", "SP ", "subject or predicate"],
+	  ["SVP", "SVP ", "separable verb prefix"],
+	  ["UC", "UC ", "(idiosyncratic) unit component"],
+	  ["VO", "VO ", "vocative"]
+	];
+
+	ah["corenlp/c=DL-"] = [
+	  ["AC", "AC ", "adpositional case marker"],
+	  ["ADC", "ADC ", "adjective component"],
+	  ["AMS", "AMS ", "measure argument of adj"],
+	  ["APP", "APP ", "apposition"],
+	  ["AVC", "AVC ", "adverbial phrase component"],
+	  ["CC", "CC ", "comparative complement"],
+	  ["CD", "CD ", "coordinating conjunction"],
+	  ["CJ", "CJ ", "conjunct"],
+	  ["CM", "CM ", "comparative concjunction"],
+	  ["CP", "CP ", "complementizer"],
+	  ["DA", "DA ", "dative"],
+	  ["DH", "DH ", "discourse-level head"],
+	  ["DM", "DM ", "discourse marker"],
+	  ["GL", "GL ", "prenominal genitive"],
+	  ["GR", "GR ", "postnominal genitive"],
+	  ["HD", "HD ", "head"],
+	  ["JU", "JU ", "junctor"],
+	  ["MC", "MC ", "comitative"],
+	  ["MI", "MI ", "instrumental"],
+	  ["ML", "ML ", "locative"],
+	  ["MNR", "MNR ", "postnominal modifier"],
+	  ["MO", "MO ", "modifier"],
+	  ["MR", "MR ", "rhetorical modifier"],
+	  ["MW", "MW ", "way (directional modifier)"],
+	  ["NG", "NG ", "negation"],
+	  ["NK", "NK ", "noun kernel modifier"],
+	  ["NMC", "NMC ", "numerical component"],
+	  ["OA", "OA ", "accusative object"],
+	  ["OA2", "OA2 ", "second accusative object"],
+	  ["OC", "OC ", "clausal object"],
+	  ["OG", "OG ", "genitive object"],
+	  ["PD", "PD ", "predicate"],
+	  ["PG", "PG ", "pseudo-genitive"],
+	  ["PH", "PH ", "placeholder"],
+	  ["PM", "PM ", "morphological particle"],
+	  ["PNC", "PNC ", "proper noun component"],
+	  ["RC", "RC ", "relative clause"],
+	  ["RE", "RE ", "repeated element"],
+	  ["RS", "RS ", "reported speech"],
+	  ["SB", "SB ", "subject"],
+	  ["SBP", "SBP ", "passivised subject (PP)"],
+	  ["SP", "SP ", "subject or predicate"],
+	  ["SVP", "SVP ", "separable verb prefix"],
+	  ["UC", "UC ", "(idiosyncratic) unit component"],
+	  ["VO", "VO ", "vocative"]
+	];
+
+	ah["corenlp/c=ISU-"] = [
+	  ["AC", "AC ", "adpositional case marker"],
+	  ["ADC", "ADC ", "adjective component"],
+	  ["AMS", "AMS ", "measure argument of adj"],
+	  ["APP", "APP ", "apposition"],
+	  ["AVC", "AVC ", "adverbial phrase component"],
+	  ["CC", "CC ", "comparative complement"],
+	  ["CD", "CD ", "coordinating conjunction"],
+	  ["CJ", "CJ ", "conjunct"],
+	  ["CM", "CM ", "comparative concjunction"],
+	  ["CP", "CP ", "complementizer"],
+	  ["DA", "DA ", "dative"],
+	  ["DH", "DH ", "discourse-level head"],
+	  ["DM", "DM ", "discourse marker"],
+	  ["GL", "GL ", "prenominal genitive"],
+	  ["GR", "GR ", "postnominal genitive"],
+	  ["HD", "HD ", "head"],
+	  ["JU", "JU ", "junctor"],
+	  ["MC", "MC ", "comitative"],
+	  ["MI", "MI ", "instrumental"],
+	  ["ML", "ML ", "locative"],
+	  ["MNR", "MNR ", "postnominal modifier"],
+	  ["MO", "MO ", "modifier"],
+	  ["MR", "MR ", "rhetorical modifier"],
+	  ["MW", "MW ", "way (directional modifier)"],
+	  ["NG", "NG ", "negation"],
+	  ["NK", "NK ", "noun kernel modifier"],
+	  ["NMC", "NMC ", "numerical component"],
+	  ["OA", "OA ", "accusative object"],
+	  ["OA2", "OA2 ", "second accusative object"],
+	  ["OC", "OC ", "clausal object"],
+	  ["OG", "OG ", "genitive object"],
+	  ["PD", "PD ", "predicate"],
+	  ["PG", "PG ", "pseudo-genitive"],
+	  ["PH", "PH ", "placeholder"],
+	  ["PM", "PM ", "morphological particle"],
+	  ["PNC", "PNC ", "proper noun component"],
+	  ["RC", "RC ", "relative clause"],
+	  ["RE", "RE ", "repeated element"],
+	  ["RS", "RS ", "reported speech"],
+	  ["SB", "SB ", "subject"],
+	  ["SBP", "SBP ", "passivised subject (PP)"],
+	  ["SP", "SP ", "subject or predicate"],
+	  ["SVP", "SVP ", "separable verb prefix"],
+	  ["UC", "UC ", "(idiosyncratic) unit component"],
+	  ["VO", "VO ", "vocative"]
+	];
+
+	ah["corenlp/c=MPN-"] = [
+	  ["AC", "AC ", "adpositional case marker"],
+	  ["ADC", "ADC ", "adjective component"],
+	  ["AMS", "AMS ", "measure argument of adj"],
+	  ["APP", "APP ", "apposition"],
+	  ["AVC", "AVC ", "adverbial phrase component"],
+	  ["CC", "CC ", "comparative complement"],
+	  ["CD", "CD ", "coordinating conjunction"],
+	  ["CJ", "CJ ", "conjunct"],
+	  ["CM", "CM ", "comparative concjunction"],
+	  ["CP", "CP ", "complementizer"],
+	  ["DA", "DA ", "dative"],
+	  ["DH", "DH ", "discourse-level head"],
+	  ["DM", "DM ", "discourse marker"],
+	  ["GL", "GL ", "prenominal genitive"],
+	  ["GR", "GR ", "postnominal genitive"],
+	  ["HD", "HD ", "head"],
+	  ["JU", "JU ", "junctor"],
+	  ["MC", "MC ", "comitative"],
+	  ["MI", "MI ", "instrumental"],
+	  ["ML", "ML ", "locative"],
+	  ["MNR", "MNR ", "postnominal modifier"],
+	  ["MO", "MO ", "modifier"],
+	  ["MR", "MR ", "rhetorical modifier"],
+	  ["MW", "MW ", "way (directional modifier)"],
+	  ["NG", "NG ", "negation"],
+	  ["NK", "NK ", "noun kernel modifier"],
+	  ["NMC", "NMC ", "numerical component"],
+	  ["OA", "OA ", "accusative object"],
+	  ["OA2", "OA2 ", "second accusative object"],
+	  ["OC", "OC ", "clausal object"],
+	  ["OG", "OG ", "genitive object"],
+	  ["PD", "PD ", "predicate"],
+	  ["PG", "PG ", "pseudo-genitive"],
+	  ["PH", "PH ", "placeholder"],
+	  ["PM", "PM ", "morphological particle"],
+	  ["PNC", "PNC ", "proper noun component"],
+	  ["RC", "RC ", "relative clause"],
+	  ["RE", "RE ", "repeated element"],
+	  ["RS", "RS ", "reported speech"],
+	  ["SB", "SB ", "subject"],
+	  ["SBP", "SBP ", "passivised subject (PP)"],
+	  ["SP", "SP ", "subject or predicate"],
+	  ["SVP", "SVP ", "separable verb prefix"],
+	  ["UC", "UC ", "(idiosyncratic) unit component"],
+	  ["VO", "VO ", "vocative"]
+	];
+
+	ah["corenlp/c=MTA-"] = [
+	  ["AC", "AC ", "adpositional case marker"],
+	  ["ADC", "ADC ", "adjective component"],
+	  ["AMS", "AMS ", "measure argument of adj"],
+	  ["APP", "APP ", "apposition"],
+	  ["AVC", "AVC ", "adverbial phrase component"],
+	  ["CC", "CC ", "comparative complement"],
+	  ["CD", "CD ", "coordinating conjunction"],
+	  ["CJ", "CJ ", "conjunct"],
+	  ["CM", "CM ", "comparative concjunction"],
+	  ["CP", "CP ", "complementizer"],
+	  ["DA", "DA ", "dative"],
+	  ["DH", "DH ", "discourse-level head"],
+	  ["DM", "DM ", "discourse marker"],
+	  ["GL", "GL ", "prenominal genitive"],
+	  ["GR", "GR ", "postnominal genitive"],
+	  ["HD", "HD ", "head"],
+	  ["JU", "JU ", "junctor"],
+	  ["MC", "MC ", "comitative"],
+	  ["MI", "MI ", "instrumental"],
+	  ["ML", "ML ", "locative"],
+	  ["MNR", "MNR ", "postnominal modifier"],
+	  ["MO", "MO ", "modifier"],
+	  ["MR", "MR ", "rhetorical modifier"],
+	  ["MW", "MW ", "way (directional modifier)"],
+	  ["NG", "NG ", "negation"],
+	  ["NK", "NK ", "noun kernel modifier"],
+	  ["NMC", "NMC ", "numerical component"],
+	  ["OA", "OA ", "accusative object"],
+	  ["OA2", "OA2 ", "second accusative object"],
+	  ["OC", "OC ", "clausal object"],
+	  ["OG", "OG ", "genitive object"],
+	  ["PD", "PD ", "predicate"],
+	  ["PG", "PG ", "pseudo-genitive"],
+	  ["PH", "PH ", "placeholder"],
+	  ["PM", "PM ", "morphological particle"],
+	  ["PNC", "PNC ", "proper noun component"],
+	  ["RC", "RC ", "relative clause"],
+	  ["RE", "RE ", "repeated element"],
+	  ["RS", "RS ", "reported speech"],
+	  ["SB", "SB ", "subject"],
+	  ["SBP", "SBP ", "passivised subject (PP)"],
+	  ["SP", "SP ", "subject or predicate"],
+	  ["SVP", "SVP ", "separable verb prefix"],
+	  ["UC", "UC ", "(idiosyncratic) unit component"],
+	  ["VO", "VO ", "vocative"]
+	];
+
+	ah["corenlp/c=NM-"] = [
+	  ["AC", "AC ", "adpositional case marker"],
+	  ["ADC", "ADC ", "adjective component"],
+	  ["AMS", "AMS ", "measure argument of adj"],
+	  ["APP", "APP ", "apposition"],
+	  ["AVC", "AVC ", "adverbial phrase component"],
+	  ["CC", "CC ", "comparative complement"],
+	  ["CD", "CD ", "coordinating conjunction"],
+	  ["CJ", "CJ ", "conjunct"],
+	  ["CM", "CM ", "comparative concjunction"],
+	  ["CP", "CP ", "complementizer"],
+	  ["DA", "DA ", "dative"],
+	  ["DH", "DH ", "discourse-level head"],
+	  ["DM", "DM ", "discourse marker"],
+	  ["GL", "GL ", "prenominal genitive"],
+	  ["GR", "GR ", "postnominal genitive"],
+	  ["HD", "HD ", "head"],
+	  ["JU", "JU ", "junctor"],
+	  ["MC", "MC ", "comitative"],
+	  ["MI", "MI ", "instrumental"],
+	  ["ML", "ML ", "locative"],
+	  ["MNR", "MNR ", "postnominal modifier"],
+	  ["MO", "MO ", "modifier"],
+	  ["MR", "MR ", "rhetorical modifier"],
+	  ["MW", "MW ", "way (directional modifier)"],
+	  ["NG", "NG ", "negation"],
+	  ["NK", "NK ", "noun kernel modifier"],
+	  ["NMC", "NMC ", "numerical component"],
+	  ["OA", "OA ", "accusative object"],
+	  ["OA2", "OA2 ", "second accusative object"],
+	  ["OC", "OC ", "clausal object"],
+	  ["OG", "OG ", "genitive object"],
+	  ["PD", "PD ", "predicate"],
+	  ["PG", "PG ", "pseudo-genitive"],
+	  ["PH", "PH ", "placeholder"],
+	  ["PM", "PM ", "morphological particle"],
+	  ["PNC", "PNC ", "proper noun component"],
+	  ["RC", "RC ", "relative clause"],
+	  ["RE", "RE ", "repeated element"],
+	  ["RS", "RS ", "reported speech"],
+	  ["SB", "SB ", "subject"],
+	  ["SBP", "SBP ", "passivised subject (PP)"],
+	  ["SP", "SP ", "subject or predicate"],
+	  ["SVP", "SVP ", "separable verb prefix"],
+	  ["UC", "UC ", "(idiosyncratic) unit component"],
+	  ["VO", "VO ", "vocative"]
+	];
+
+	ah["corenlp/c=NP-"] = [
+	  ["AC", "AC ", "adpositional case marker"],
+	  ["ADC", "ADC ", "adjective component"],
+	  ["AMS", "AMS ", "measure argument of adj"],
+	  ["APP", "APP ", "apposition"],
+	  ["AVC", "AVC ", "adverbial phrase component"],
+	  ["CC", "CC ", "comparative complement"],
+	  ["CD", "CD ", "coordinating conjunction"],
+	  ["CJ", "CJ ", "conjunct"],
+	  ["CM", "CM ", "comparative concjunction"],
+	  ["CP", "CP ", "complementizer"],
+	  ["DA", "DA ", "dative"],
+	  ["DH", "DH ", "discourse-level head"],
+	  ["DM", "DM ", "discourse marker"],
+	  ["GL", "GL ", "prenominal genitive"],
+	  ["GR", "GR ", "postnominal genitive"],
+	  ["HD", "HD ", "head"],
+	  ["JU", "JU ", "junctor"],
+	  ["MC", "MC ", "comitative"],
+	  ["MI", "MI ", "instrumental"],
+	  ["ML", "ML ", "locative"],
+	  ["MNR", "MNR ", "postnominal modifier"],
+	  ["MO", "MO ", "modifier"],
+	  ["MR", "MR ", "rhetorical modifier"],
+	  ["MW", "MW ", "way (directional modifier)"],
+	  ["NG", "NG ", "negation"],
+	  ["NK", "NK ", "noun kernel modifier"],
+	  ["NMC", "NMC ", "numerical component"],
+	  ["OA", "OA ", "accusative object"],
+	  ["OA2", "OA2 ", "second accusative object"],
+	  ["OC", "OC ", "clausal object"],
+	  ["OG", "OG ", "genitive object"],
+	  ["PD", "PD ", "predicate"],
+	  ["PG", "PG ", "pseudo-genitive"],
+	  ["PH", "PH ", "placeholder"],
+	  ["PM", "PM ", "morphological particle"],
+	  ["PNC", "PNC ", "proper noun component"],
+	  ["RC", "RC ", "relative clause"],
+	  ["RE", "RE ", "repeated element"],
+	  ["RS", "RS ", "reported speech"],
+	  ["SB", "SB ", "subject"],
+	  ["SBP", "SBP ", "passivised subject (PP)"],
+	  ["SP", "SP ", "subject or predicate"],
+	  ["SVP", "SVP ", "separable verb prefix"],
+	  ["UC", "UC ", "(idiosyncratic) unit component"],
+	  ["VO", "VO ", "vocative"]
+	];
+
+	ah["corenlp/c=PP-"] = [
+	  ["AC", "AC ", "adpositional case marker"],
+	  ["ADC", "ADC ", "adjective component"],
+	  ["AMS", "AMS ", "measure argument of adj"],
+	  ["APP", "APP ", "apposition"],
+	  ["AVC", "AVC ", "adverbial phrase component"],
+	  ["CC", "CC ", "comparative complement"],
+	  ["CD", "CD ", "coordinating conjunction"],
+	  ["CJ", "CJ ", "conjunct"],
+	  ["CM", "CM ", "comparative concjunction"],
+	  ["CP", "CP ", "complementizer"],
+	  ["DA", "DA ", "dative"],
+	  ["DH", "DH ", "discourse-level head"],
+	  ["DM", "DM ", "discourse marker"],
+	  ["GL", "GL ", "prenominal genitive"],
+	  ["GR", "GR ", "postnominal genitive"],
+	  ["HD", "HD ", "head"],
+	  ["JU", "JU ", "junctor"],
+	  ["MC", "MC ", "comitative"],
+	  ["MI", "MI ", "instrumental"],
+	  ["ML", "ML ", "locative"],
+	  ["MNR", "MNR ", "postnominal modifier"],
+	  ["MO", "MO ", "modifier"],
+	  ["MR", "MR ", "rhetorical modifier"],
+	  ["MW", "MW ", "way (directional modifier)"],
+	  ["NG", "NG ", "negation"],
+	  ["NK", "NK ", "noun kernel modifier"],
+	  ["NMC", "NMC ", "numerical component"],
+	  ["OA", "OA ", "accusative object"],
+	  ["OA2", "OA2 ", "second accusative object"],
+	  ["OC", "OC ", "clausal object"],
+	  ["OG", "OG ", "genitive object"],
+	  ["PD", "PD ", "predicate"],
+	  ["PG", "PG ", "pseudo-genitive"],
+	  ["PH", "PH ", "placeholder"],
+	  ["PM", "PM ", "morphological particle"],
+	  ["PNC", "PNC ", "proper noun component"],
+	  ["RC", "RC ", "relative clause"],
+	  ["RE", "RE ", "repeated element"],
+	  ["RS", "RS ", "reported speech"],
+	  ["SB", "SB ", "subject"],
+	  ["SBP", "SBP ", "passivised subject (PP)"],
+	  ["SP", "SP ", "subject or predicate"],
+	  ["SVP", "SVP ", "separable verb prefix"],
+	  ["UC", "UC ", "(idiosyncratic) unit component"],
+	  ["VO", "VO ", "vocative"]
+	];
+
+	ah["corenlp/c=QL-"] = [
+	  ["AC", "AC ", "adpositional case marker"],
+	  ["ADC", "ADC ", "adjective component"],
+	  ["AMS", "AMS ", "measure argument of adj"],
+	  ["APP", "APP ", "apposition"],
+	  ["AVC", "AVC ", "adverbial phrase component"],
+	  ["CC", "CC ", "comparative complement"],
+	  ["CD", "CD ", "coordinating conjunction"],
+	  ["CJ", "CJ ", "conjunct"],
+	  ["CM", "CM ", "comparative concjunction"],
+	  ["CP", "CP ", "complementizer"],
+	  ["DA", "DA ", "dative"],
+	  ["DH", "DH ", "discourse-level head"],
+	  ["DM", "DM ", "discourse marker"],
+	  ["GL", "GL ", "prenominal genitive"],
+	  ["GR", "GR ", "postnominal genitive"],
+	  ["HD", "HD ", "head"],
+	  ["JU", "JU ", "junctor"],
+	  ["MC", "MC ", "comitative"],
+	  ["MI", "MI ", "instrumental"],
+	  ["ML", "ML ", "locative"],
+	  ["MNR", "MNR ", "postnominal modifier"],
+	  ["MO", "MO ", "modifier"],
+	  ["MR", "MR ", "rhetorical modifier"],
+	  ["MW", "MW ", "way (directional modifier)"],
+	  ["NG", "NG ", "negation"],
+	  ["NK", "NK ", "noun kernel modifier"],
+	  ["NMC", "NMC ", "numerical component"],
+	  ["OA", "OA ", "accusative object"],
+	  ["OA2", "OA2 ", "second accusative object"],
+	  ["OC", "OC ", "clausal object"],
+	  ["OG", "OG ", "genitive object"],
+	  ["PD", "PD ", "predicate"],
+	  ["PG", "PG ", "pseudo-genitive"],
+	  ["PH", "PH ", "placeholder"],
+	  ["PM", "PM ", "morphological particle"],
+	  ["PNC", "PNC ", "proper noun component"],
+	  ["RC", "RC ", "relative clause"],
+	  ["RE", "RE ", "repeated element"],
+	  ["RS", "RS ", "reported speech"],
+	  ["SB", "SB ", "subject"],
+	  ["SBP", "SBP ", "passivised subject (PP)"],
+	  ["SP", "SP ", "subject or predicate"],
+	  ["SVP", "SVP ", "separable verb prefix"],
+	  ["UC", "UC ", "(idiosyncratic) unit component"],
+	  ["VO", "VO ", "vocative"]
+	];
+
+	ah["corenlp/c=ROOT-"] = [
+	  ["AC", "AC ", "adpositional case marker"],
+	  ["ADC", "ADC ", "adjective component"],
+	  ["AMS", "AMS ", "measure argument of adj"],
+	  ["APP", "APP ", "apposition"],
+	  ["AVC", "AVC ", "adverbial phrase component"],
+	  ["CC", "CC ", "comparative complement"],
+	  ["CD", "CD ", "coordinating conjunction"],
+	  ["CJ", "CJ ", "conjunct"],
+	  ["CM", "CM ", "comparative concjunction"],
+	  ["CP", "CP ", "complementizer"],
+	  ["DA", "DA ", "dative"],
+	  ["DH", "DH ", "discourse-level head"],
+	  ["DM", "DM ", "discourse marker"],
+	  ["GL", "GL ", "prenominal genitive"],
+	  ["GR", "GR ", "postnominal genitive"],
+	  ["HD", "HD ", "head"],
+	  ["JU", "JU ", "junctor"],
+	  ["MC", "MC ", "comitative"],
+	  ["MI", "MI ", "instrumental"],
+	  ["ML", "ML ", "locative"],
+	  ["MNR", "MNR ", "postnominal modifier"],
+	  ["MO", "MO ", "modifier"],
+	  ["MR", "MR ", "rhetorical modifier"],
+	  ["MW", "MW ", "way (directional modifier)"],
+	  ["NG", "NG ", "negation"],
+	  ["NK", "NK ", "noun kernel modifier"],
+	  ["NMC", "NMC ", "numerical component"],
+	  ["OA", "OA ", "accusative object"],
+	  ["OA2", "OA2 ", "second accusative object"],
+	  ["OC", "OC ", "clausal object"],
+	  ["OG", "OG ", "genitive object"],
+	  ["PD", "PD ", "predicate"],
+	  ["PG", "PG ", "pseudo-genitive"],
+	  ["PH", "PH ", "placeholder"],
+	  ["PM", "PM ", "morphological particle"],
+	  ["PNC", "PNC ", "proper noun component"],
+	  ["RC", "RC ", "relative clause"],
+	  ["RE", "RE ", "repeated element"],
+	  ["RS", "RS ", "reported speech"],
+	  ["SB", "SB ", "subject"],
+	  ["SBP", "SBP ", "passivised subject (PP)"],
+	  ["SP", "SP ", "subject or predicate"],
+	  ["SVP", "SVP ", "separable verb prefix"],
+	  ["UC", "UC ", "(idiosyncratic) unit component"],
+	  ["VO", "VO ", "vocative"]
+	];
+
+	ah["corenlp/c=S-"] = [
+	  ["AC", "AC ", "adpositional case marker"],
+	  ["ADC", "ADC ", "adjective component"],
+	  ["AMS", "AMS ", "measure argument of adj"],
+	  ["APP", "APP ", "apposition"],
+	  ["AVC", "AVC ", "adverbial phrase component"],
+	  ["CC", "CC ", "comparative complement"],
+	  ["CD", "CD ", "coordinating conjunction"],
+	  ["CJ", "CJ ", "conjunct"],
+	  ["CM", "CM ", "comparative concjunction"],
+	  ["CP", "CP ", "complementizer"],
+	  ["DA", "DA ", "dative"],
+	  ["DH", "DH ", "discourse-level head"],
+	  ["DM", "DM ", "discourse marker"],
+	  ["GL", "GL ", "prenominal genitive"],
+	  ["GR", "GR ", "postnominal genitive"],
+	  ["HD", "HD ", "head"],
+	  ["JU", "JU ", "junctor"],
+	  ["MC", "MC ", "comitative"],
+	  ["MI", "MI ", "instrumental"],
+	  ["ML", "ML ", "locative"],
+	  ["MNR", "MNR ", "postnominal modifier"],
+	  ["MO", "MO ", "modifier"],
+	  ["MR", "MR ", "rhetorical modifier"],
+	  ["MW", "MW ", "way (directional modifier)"],
+	  ["NG", "NG ", "negation"],
+	  ["NK", "NK ", "noun kernel modifier"],
+	  ["NMC", "NMC ", "numerical component"],
+	  ["OA", "OA ", "accusative object"],
+	  ["OA2", "OA2 ", "second accusative object"],
+	  ["OC", "OC ", "clausal object"],
+	  ["OG", "OG ", "genitive object"],
+	  ["PD", "PD ", "predicate"],
+	  ["PG", "PG ", "pseudo-genitive"],
+	  ["PH", "PH ", "placeholder"],
+	  ["PM", "PM ", "morphological particle"],
+	  ["PNC", "PNC ", "proper noun component"],
+	  ["RC", "RC ", "relative clause"],
+	  ["RE", "RE ", "repeated element"],
+	  ["RS", "RS ", "reported speech"],
+	  ["SB", "SB ", "subject"],
+	  ["SBP", "SBP ", "passivised subject (PP)"],
+	  ["SP", "SP ", "subject or predicate"],
+	  ["SVP", "SVP ", "separable verb prefix"],
+	  ["UC", "UC ", "(idiosyncratic) unit component"],
+	  ["VO", "VO ", "vocative"]
+	];
+
+	ah["corenlp/c=VP-"] = [
+	  ["AC", "AC ", "adpositional case marker"],
+	  ["ADC", "ADC ", "adjective component"],
+	  ["AMS", "AMS ", "measure argument of adj"],
+	  ["APP", "APP ", "apposition"],
+	  ["AVC", "AVC ", "adverbial phrase component"],
+	  ["CC", "CC ", "comparative complement"],
+	  ["CD", "CD ", "coordinating conjunction"],
+	  ["CJ", "CJ ", "conjunct"],
+	  ["CM", "CM ", "comparative concjunction"],
+	  ["CP", "CP ", "complementizer"],
+	  ["DA", "DA ", "dative"],
+	  ["DH", "DH ", "discourse-level head"],
+	  ["DM", "DM ", "discourse marker"],
+	  ["GL", "GL ", "prenominal genitive"],
+	  ["GR", "GR ", "postnominal genitive"],
+	  ["HD", "HD ", "head"],
+	  ["JU", "JU ", "junctor"],
+	  ["MC", "MC ", "comitative"],
+	  ["MI", "MI ", "instrumental"],
+	  ["ML", "ML ", "locative"],
+	  ["MNR", "MNR ", "postnominal modifier"],
+	  ["MO", "MO ", "modifier"],
+	  ["MR", "MR ", "rhetorical modifier"],
+	  ["MW", "MW ", "way (directional modifier)"],
+	  ["NG", "NG ", "negation"],
+	  ["NK", "NK ", "noun kernel modifier"],
+	  ["NMC", "NMC ", "numerical component"],
+	  ["OA", "OA ", "accusative object"],
+	  ["OA2", "OA2 ", "second accusative object"],
+	  ["OC", "OC ", "clausal object"],
+	  ["OG", "OG ", "genitive object"],
+	  ["PD", "PD ", "predicate"],
+	  ["PG", "PG ", "pseudo-genitive"],
+	  ["PH", "PH ", "placeholder"],
+	  ["PM", "PM ", "morphological particle"],
+	  ["PNC", "PNC ", "proper noun component"],
+	  ["RC", "RC ", "relative clause"],
+	  ["RE", "RE ", "repeated element"],
+	  ["RS", "RS ", "reported speech"],
+	  ["SB", "SB ", "subject"],
+	  ["SBP", "SBP ", "passivised subject (PP)"],
+	  ["SP", "SP ", "subject or predicate"],
+	  ["SVP", "SVP ", "separable verb prefix"],
+	  ["UC", "UC ", "(idiosyncratic) unit component"],
+	  ["VO", "VO ", "vocative"]
+	];
+
+	ah["corenlp/c=VZ-"] = [
+	  ["AC", "AC ", "adpositional case marker"],
+	  ["ADC", "ADC ", "adjective component"],
+	  ["AMS", "AMS ", "measure argument of adj"],
+	  ["APP", "APP ", "apposition"],
+	  ["AVC", "AVC ", "adverbial phrase component"],
+	  ["CC", "CC ", "comparative complement"],
+	  ["CD", "CD ", "coordinating conjunction"],
+	  ["CJ", "CJ ", "conjunct"],
+	  ["CM", "CM ", "comparative concjunction"],
+	  ["CP", "CP ", "complementizer"],
+	  ["DA", "DA ", "dative"],
+	  ["DH", "DH ", "discourse-level head"],
+	  ["DM", "DM ", "discourse marker"],
+	  ["GL", "GL ", "prenominal genitive"],
+	  ["GR", "GR ", "postnominal genitive"],
+	  ["HD", "HD ", "head"],
+	  ["JU", "JU ", "junctor"],
+	  ["MC", "MC ", "comitative"],
+	  ["MI", "MI ", "instrumental"],
+	  ["ML", "ML ", "locative"],
+	  ["MNR", "MNR ", "postnominal modifier"],
+	  ["MO", "MO ", "modifier"],
+	  ["MR", "MR ", "rhetorical modifier"],
+	  ["MW", "MW ", "way (directional modifier)"],
+	  ["NG", "NG ", "negation"],
+	  ["NK", "NK ", "noun kernel modifier"],
+	  ["NMC", "NMC ", "numerical component"],
+	  ["OA", "OA ", "accusative object"],
+	  ["OA2", "OA2 ", "second accusative object"],
+	  ["OC", "OC ", "clausal object"],
+	  ["OG", "OG ", "genitive object"],
+	  ["PD", "PD ", "predicate"],
+	  ["PG", "PG ", "pseudo-genitive"],
+	  ["PH", "PH ", "placeholder"],
+	  ["PM", "PM ", "morphological particle"],
+	  ["PNC", "PNC ", "proper noun component"],
+	  ["RC", "RC ", "relative clause"],
+	  ["RE", "RE ", "repeated element"],
+	  ["RS", "RS ", "reported speech"],
+	  ["SB", "SB ", "subject"],
+	  ["SBP", "SBP ", "passivised subject (PP)"],
+	  ["SP", "SP ", "subject or predicate"],
+	  ["SVP", "SVP ", "separable verb prefix"],
+	  ["UC", "UC ", "(idiosyncratic) unit component"],
+	  ["VO", "VO ", "vocative"]
+	];
+
+
+  
+//  for (var i in negraNodesArray) {
+//    ah["corenlp/c=" + negraNodesArray[i][0] + '-'] = negraEdgesArray;
+//  };
+});
diff --git a/full/src/main/resources/annotation-scripts/foundries/dereko.js b/full/src/main/resources/annotation-scripts/foundries/dereko.js
new file mode 100644
index 0000000..e31f42e
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/foundries/dereko.js
@@ -0,0 +1,9 @@
+define(["hint/foundries"], function (ah) {
+  ah["-"].push(
+    ["DeReKo", "dereko/", "Structure"]
+  );
+  
+  ah["dereko/"] = [
+	["Structure", "s="]
+  ];
+});
diff --git a/full/src/main/resources/annotation-scripts/foundries/drukola.js b/full/src/main/resources/annotation-scripts/foundries/drukola.js
new file mode 100644
index 0000000..b57f8a4
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/foundries/drukola.js
@@ -0,0 +1,104 @@
+define(["hint/foundries"], function (ah) {
+  ah["-"].push(
+    ["DRuKoLa", "drukola/", "Lemma, Morphology, Part-of-Speech"]
+  );
+
+  ah["drukola/"] = [
+    ["Lemma", "l="],
+    ["Morphology", "m="],
+    ["Part-of-Speech", "p="]      
+  ];
+
+  ah["drukola/m="] =  [
+    ["CTAG", "ctag:"]
+  ];
+
+  ah["drukola/m=ctag:"] = [
+    ["A","a ","Adjective"],
+    ["Y","y ","Abbreviation"],
+    ["AN","an ","Adjective, Indefinite"],
+    ["APRY","apry ","Adjective, Plural, Direct, Definite"],
+    ["APN","apn ","Adjective, Plural, Indefinite"],
+    ["APOY","apoy ","Adjective, Plural, Oblique, Definite"],
+    ["APON","apon ","Adjective, Plural, Oblique, Indefinite"],
+    ["ASRY","asry ","Adjective, Singular, Direct, Definite"],
+    ["ASN","asn ","Adjective, Singular, Indefinite"],
+    ["ASOY","asoy ","Adjective, Singular, Oblique, Definite"],
+    ["ASON","ason ","Adjective, Singular, Oblique, Indefinite"],
+    ["ASVY","asvy ","Adjective, Singular, Vocative, Definite"],
+    ["ASVN","asvn ","Adjective, Singular, Vocative, Indefinite"],
+    ["R","r ","Adverb"],
+    ["RC","rc ","Adverb, Portmanteau"],
+    ["TS","ts ","Article, Definite or Possessive, Singular"],
+    ["TP","tp ","Article, Indefinite or Possessive, Plural"],
+    ["TPR","tpr ","Article, non-Possessive, Plural, Direct"],
+    ["TPO","tpo ","Article, non-Possessive, Plural, Oblique"],
+    ["TSR","tsr ","Article, non-Possessive, Singular, Direct"],
+    ["TSO","tso ","Article, non-Possessive, Singular, Oblique"],
+    ["NPRY","npry ","Common Noun, Plural, Direct, Definite"],
+    ["NPN","npn ","Common Noun, Plural, Indefinite"],
+    ["NPOY","npoy ","Common Noun, Plural, Oblique, Definite"],
+    ["NPVY","npvy ","Common Noun, Plural, Vocative, Definite"],
+    ["NN","nn ","Common Noun, singular"],
+    ["NSY","nsy ","Common Noun, Singular, Definite"],
+    ["NSRY","nsry ","Common Noun, Singular, Direct, Definite"],
+    ["NSRN","nsrn ","Common Noun, Singular, Direct, Indefinite"],
+    ["NSN","nsn ","Common Noun, Singular, Indefinite"],
+    ["NSOY","nsoy ","Common Noun, Singular, Oblique, Definite"],
+    ["NSON","nson ","Common Noun, Singular, Oblique, Indefinite"],
+    ["NSVY","nsvy ","Common Noun, Singular, Vocative, Definite"],
+    ["NSVN","nsvn ","Common Noun, Singular, Vocative, Indefinite"],
+    ["CR","cr ","Conjunctio, portmanteau"],
+    ["C","c ","Conjunction"],
+    ["QF","qf ","Future Particle"],
+    ["QN","qn ","Infinitival Particle"],
+    ["I","i ","Interjection"],
+    ["QZ","qz ","Negative Particle"],
+    ["M","m ","Numeral"],
+    ["PP","pp ","Personal Pronoun"],
+    ["PPP","ppp ","Personal Pronoun, Plural"],
+    ["PPPA","pppa ","Personal Pronoun, Plural, Acc."],
+    ["PPPD","pppd ","Personal Pronoun, Plural, Dative"],
+    ["PPPR","pppr ","Personal Pronoun, Plural, Direct"],
+    ["PPPO","pppo ","Personal Pronoun, Plural, Oblique"],
+    ["PPS","pps ","Personal Pronoun, Singular"],
+    ["PPSA","ppsa ","Personal Pronoun, Singular, Accusative"],
+    ["PPSD","ppsd ","Personal Pronoun, Singular, Dative"],
+    ["PPSR","ppsr ","Personal Pronoun, Singular, Direct"],
+    ["PPSN","ppsn ","Personal Pronoun, Singular, Nominative"],
+    ["PPSO","ppso ","Personal Pronoun, Singular, Oblique"],
+    ["S","s ","Preposition"],
+    ["DMPR","dmpr ","Pronoun or Determiner, Demonstrative, Plural, Direct"],
+    ["DMPO","dmpo ","Pronoun or Determiner, Demonstrative, Plural, Oblique"],
+    ["DMSR","dmsr ","Pronoun or Determiner, Demonstrative, Singular, Direct"],
+    ["DMSO","dmso ","Pronoun or Determiner, Demonstrative, Singular, Oblique"],
+    ["PS","ps ","Pronoun or Determiner, Poss or Emph"],
+    ["PSS","pss ","Pronoun or Determiner, Poss or Emph, Singular"],
+    ["RELR","relr ","Pronoun or Determiner, Relative, Direct"],
+    ["RELO","relo ","Pronoun or Determiner, Relative, Oblique"],
+    ["NP","np ","Proper Noun"],
+    ["PI","pi ","Quantifier Pronoun or Determiner"],
+    ["PXA","pxa ","Reflexive Pronoun, Accusative"],
+    ["PXD","pxd ","Reflexive Pronoun, Dative"],
+    ["X","x ","Residual"],
+    ["QS","qs ","Subjunctive Particle"],
+    ["VA","va ","Verb, Auxiliary"],
+    ["VA1","va1 ","Verb, Auxiliary, 1st person"],
+    ["VA2P","va2p ","Verb, Auxiliary, 2nd person, Plural"],
+    ["VA2S","va2s ","Verb, Auxiliary, 2nd person, Singular"],
+    ["VA3","va3 ","Verb, Auxiliary, 3rd person"],
+    ["VA3P","va3p ","Verb, Auxiliary, 3rd person, Plural"],
+    ["VA3S","va3s ","Verb, Auxiliary, 3rd person, Singular"],
+    ["VA1P","va1p ","Verb, Auxiliary,1st person, Plural"],
+    ["VA1S","va1s ","Verb, Auxiliary,1st person, Singular"],
+    ["VG","vg ","Verb, Gerund"],
+    ["VN","vn ","Verb, Infinitive"],
+    ["V1","v1 ","Verb, Main, 1st person"],
+    ["V2","v2 ","Verb, Main, 2nd person"],
+    ["V3","v3 ","Verb, Main, 3rd person"],
+    ["VPPF","vppf ","Verb, Participle, Plural, Feminine"],
+    ["VPPM","vppm ","Verb, Participle, Plural, Masculine"],
+    ["VPSF","vpsf ","Verb, Participle, Singular, Feminine"],
+    ["VPSM","vpsm ","Verb, Participle, Singular, Masculine"]
+  ];
+});
diff --git a/full/src/main/resources/annotation-scripts/foundries/lwc.js b/full/src/main/resources/annotation-scripts/foundries/lwc.js
new file mode 100644
index 0000000..600bc57
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/foundries/lwc.js
@@ -0,0 +1,57 @@
+define(["hint/foundries","hint/foundries/negraedges"], function (ah, negraEdgesArray) {
+  ah["-"].push(
+    ["LWC", "lwc/", "Dependency"]
+  );
+
+  ah["lwc/"] = [
+    ["Dependency", "d="]
+  ];
+
+  ah["lwc/d="] = [
+	["AC","AC ","adpositional case marker"],
+    ["ADC","ADC ","adjective component"],
+    ["AMS","AMS ","measure argument of adj"],
+    ["APP","APP ","apposition"],
+    ["AVC","AVC ","adverbial phrase component"],
+    ["CC","CC ","comparative complement"],
+    ["CD","CD ","coordinating conjunction"],
+    ["CJ","CJ ","conjunct"],
+    ["CM","CM ","comparative concjunction"],
+    ["CP","CP ","complementizer"],
+    ["DA","DA ","dative"],
+    ["DH","DH ","discourse-level head"],
+    ["DM","DM ","discourse marker"],
+    ["GL","GL ","prenominal genitive"],
+    ["GR","GR ","postnominal genitive"],
+    ["HD","HD ","head"],
+    ["JU","JU ","junctor"],
+    ["MC","MC ","comitative"],
+    ["MI","MI ","instrumental"],
+    ["ML","ML ","locative"],
+    ["MNR","MNR ","postnominal modifier"],
+    ["MO","MO ","modifier"],
+    ["MR","MR ","rhetorical modifier"],
+    ["MW","MW ","way (directional modifier)"],
+    ["NG","NG ","negation"],
+    ["NK","NK ","noun kernel modifier"],
+    ["NMC","NMC ","numerical component"], 
+    ["OA","OA ","accusative object"],
+    ["OA2","OA2 ","second accusative object"], 
+    ["OC","OC ","clausal object"],
+    ["OG","OG ","genitive object"], 
+    ["PD","PD ","predicate"],
+    ["PG","PG ","pseudo-genitive"],
+    ["PH","PH ","placeholder"],
+    ["PM","PM ","morphological particle"],
+    ["PNC","PNC ","proper noun component"], 
+    ["RC","RC ","relative clause"],
+    ["RE","RE ","repeated element"],
+    ["RS","RS ","reported speech"],
+    ["SB","SB ","subject"],
+    ["SBP","SBP ","passivised subject (PP)"], 
+    ["SP","SP ","subject or predicate"],
+    ["SVP","SVP ","separable verb prefix"],
+    ["UC","UC ","(idiosyncratic) unit component"], 
+    ["VO","VO ","vocative"]
+  ];
+});
diff --git a/full/src/main/resources/annotation-scripts/foundries/malt.js b/full/src/main/resources/annotation-scripts/foundries/malt.js
new file mode 100644
index 0000000..c8c837b
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/foundries/malt.js
@@ -0,0 +1,47 @@
+define(["hint/foundries"], function (ah) {
+  ah["-"].push(
+    ["Malt", "malt/", "Dependency"]
+  );
+
+  ah["malt/"] = [
+    ["Dependency", "d="]
+  ];
+
+  ah["malt/d="] = [
+    ["-PUNCT-", "-PUNCT- ",""],
+    ["-UNKNOWN-","-UNKNOWN- ",""],
+    ["ADV","ADV ",""],
+    ["APP","APP ",""],
+    ["ATTR","ATTR ",""],
+    ["AUX","AUX ",""],
+    ["AVZ","AVZ ",""],
+    ["CJ","CJ ",""],
+    ["DET","DET ",""],
+    ["EXPL","EXPL ",""],
+    ["GMOD","GMOD ",""],
+    ["GRAD","GRAD ",""],
+    ["KOM","KOM ",""],
+    ["KON","KON ",""],
+    ["KONJ","KONJ ",""],
+    ["NEB","NEB ",""],
+    ["OBJA","OBJA ",""],
+    ["OBJC","OBJC ",""],
+    ["OBJD","OBJD ",""],
+    ["OBJG","OBJG ",""],
+    ["OBJI","OBJI ",""],
+    ["OBJP","OBJP ",""],
+    ["PAR","PAR ",""],
+    ["PART","PART ",""],
+    ["PN","PN ",""],
+    ["PP","PP ",""],
+    ["PRED","PRED ",""],
+    ["REL","REL ",""],
+    ["ROOT","ROOT ",""],
+    ["S","S ",""],
+    ["SUBJ","SUBJ ",""],
+    ["SUBJC","SUBJC ",""],
+    ["ZEIT","ZEIT ",""],
+    ["gmod-app","gmod-app ",""],
+    ["koord","koord ",""]
+  ];
+});
diff --git a/full/src/main/resources/annotation-scripts/foundries/marmot.js b/full/src/main/resources/annotation-scripts/foundries/marmot.js
new file mode 100644
index 0000000..a6e5b61
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/foundries/marmot.js
@@ -0,0 +1,120 @@
+define(["hint/foundries","hint/foundries/stts"], function (ah, sttsArray) {
+  ah["-"].push(
+    ["MarMoT", "marmot/", "Morphology, Part-of-Speech"]
+  );
+
+  ah["marmot/"] = [
+    ["Morphology", "m="],
+    ["Part-of-Speech", "p="]
+  ];
+
+  ah["marmot/p="] = [
+	["ADJA","ADJA ", "Attributive Adjective"],
+    ["ADJD","ADJD ", "Predicative Adjective"],
+    ["ADV","ADV ", "Adverb"],
+    ["APPO","APPO ", "Postposition"],
+    ["APPR","APPR ", "Preposition"],
+    ["APPRART","APPRART ", "Preposition with Determiner"],
+    ["APZR","APZR ","Right Circumposition"],
+    ["ART","ART ", "Determiner"],
+    ["CARD","CARD ", "Cardinal Number"],
+    ["FM","FM ", "Foreign Material"],
+    ["ITJ","ITJ ", "Interjection"],
+    ["KOKOM","KOKOM ", "Comparison Particle"],
+    ["KON","KON ", "Coordinating Conjuncion"],
+    ["KOUI","KOUI ", "Subordinating Conjunction with 'zu'"],
+    ["KOUS","KOUS ", "Subordinating Conjunction with Sentence"],
+    ["NE","NE ", "Named Entity"],
+    ["NN","NN ", "Normal Nomina"],
+    ["PAV", "PAV ", "Pronominal Adverb"],
+    ["PDAT","PDAT ","Attributive Demonstrative Pronoun"],
+    ["PDS","PDS ", "Substitutive Demonstrative Pronoun"],
+    ["PIAT","PIAT ", "Attributive Indefinite Pronoun without Determiner"],
+    ["PIDAT","PIDAT ", "Attributive Indefinite Pronoun with Determiner"],
+    ["PIS","PIS ", "Substitutive Indefinite Pronoun"],
+    ["PPER","PPER ", "Personal Pronoun"],
+    ["PPOSAT","PPOSAT ", "Attributive Possessive Pronoun"],
+    ["PPOSS","PPOSS ", "Substitutive Possessive Pronoun"],
+    ["PRELAT","PRELAT ", "Attributive Relative Pronoun"],
+    ["PRELS","PRELS ", "Substitutive Relative Pronoun"],
+    ["PRF","PRF ", "Reflexive Pronoun"],
+    ["PROAV","PROAV ", "Pronominal Adverb"],
+    ["PTKA","PTKA ","Particle with Adjective"],
+    ["PTKANT","PTKANT ", "Answering Particle"],
+    ["PTKNEG","PTKNEG ", "Negation Particle"],
+    ["PTKVZ","PTKVZ ", "Separated Verbal Particle"],
+    ["PTKZU","PTKZU ", "'zu' Particle"],
+    ["PWAT","PWAT ", "Attributive Interrogative Pronoun"],
+    ["PWAV","PWAV ", "Adverbial Interrogative Pronoun"],
+    ["PWS","PWS ", "Substitutive Interrogative Pronoun"],
+    ["TRUNC","TRUNC ","Truncated"],
+    ["VAFIN","VAFIN ", "Auxiliary Finite Verb"],
+    ["VAIMP","VAIMP ", "Auxiliary Finite Imperative Verb"],
+    ["VAINF","VAINF ", "Auxiliary Infinite Verb"],
+    ["VAPP","VAPP ", "Auxiliary Perfect Participle"],
+    ["VMFIN","VMFIN ", "Modal Finite Verb"],
+    ["VMINF","VMINF ", "Modal Infinite Verb"],
+    ["VMPP","VMPP ", "Modal Perfect Participle"],
+    ["VVFIN","VVFIN ","Finite Verb"],
+    ["VVIMP","VVIMP ", "Finite Imperative Verb"],
+    ["VVINF","VVINF ", "Infinite Verb"],
+    ["VVIZU","VVIZU ", "Infinite Verb with 'zu'"],
+    ["VVPP","VVPP ", "Perfect Participle"],
+    ["XY", "XY ", "Non-Word"]
+  ];	  
+
+  ah["marmot/m="] = [
+    ["Case", "case:"],
+    ["Degree", "degree:"],
+    ["Gender", "gender:"],
+    ["Mood", "mood:"],
+    ["Number", "number:"],
+    ["Person", "person:"],
+    ["Tense","tense:"],
+    ["No type", "<no-type> "]
+  ];
+
+  ah["marmot/m=case:"] = [
+    ["acc", "acc ", "Accusative"],
+    ["dat","dat ", "Dative"],
+    ["gen", "gen ","Genitive"],
+    ["nom","nom ", "Nominative"],
+    ["*","* ", "Undefined"]
+  ];
+
+  ah["marmot/m=degree:"] = [
+    ["comp","comp ", "Comparative"],
+    ["pos","pos ", "Positive"],
+    ["sup","sup ", "Superative"]
+  ];
+  
+  ah["marmot/m=gender:"] = [
+    ["fem", "fem ", "Feminium"],
+    ["masc", "masc ", "Masculinum"],
+    ["neut","neut ", "Neuter"],
+    ["*","* ","Undefined"]
+  ];
+
+  ah["marmot/m=mood:"] = [
+    ["imp","imp ", "Imperative"],
+    ["ind","ind ", "Indicative"],
+    ["subj","subj ", "Subjunctive"]
+  ];
+
+  ah["marmot/m=number:"] = [
+    ["pl","pl ","Plural"],
+    ["sg","sg ","Singular"],
+    ["*","* ","Undefined"]
+  ];
+
+  ah["marmot/m=person:"] = [
+    ["1","1 ", "First Person"],
+    ["2","2 ", "Second Person"],
+    ["3","3 ", "Third Person"]
+  ];
+
+  ah["marmot/m=tense:"] = [
+    ["past","past ", "Past"],
+    ["pres","pres ", "Present"]
+  ];
+});
diff --git a/full/src/main/resources/annotation-scripts/foundries/mate.js b/full/src/main/resources/annotation-scripts/foundries/mate.js
new file mode 100644
index 0000000..f0d5c12
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/foundries/mate.js
@@ -0,0 +1,130 @@
+define(["hint/foundries","hint/foundries/stts"], function (ah, sttsArray) {
+//  var mateSttsArray = sttsArray.slice(0);
+//  mateSttsArray.push(
+//    ["<root-POS>","<root-POS>","Root Part of Speech"]
+//  );
+
+  var ah = KorAP.annotationHelper = KorAP.annotationHelper || { "-" : [] };
+
+  ah["-"].push(
+    ["Mate", "mate/", "Lemma, Morphology, Part-of-Speech"]
+  );
+
+  ah["mate/"] = [
+    // Inactive: "d" : ["d=", "Dependency"],
+    ["Lemma", "l="],
+    ["Morphology", "m="],
+    ["Part-of-Speech", "p="]
+  ];
+
+  // Inactive: mate/d=
+  ah["mate/p="] = [
+	["ADJA","ADJA ", "Attributive Adjective"],
+    ["ADJD","ADJD ", "Predicative Adjective"],
+    ["ADV","ADV ", "Adverb"],
+    ["APPO","APPO ", "Postposition"],
+    ["APPR","APPR ", "Preposition"],
+    ["APPRART","APPRART ", "Preposition with Determiner"],
+    ["APZR","APZR ","Right Circumposition"],
+    ["ART","ART ", "Determiner"],
+    ["CARD","CARD ", "Cardinal Number"],
+    ["FM","FM ", "Foreign Material"],
+    ["ITJ","ITJ ", "Interjection"],
+    ["KOKOM","KOKOM ", "Comparison Particle"],
+    ["KON","KON ", "Coordinating Conjuncion"],
+    ["KOUI","KOUI ", "Subordinating Conjunction with 'zu'"],
+    ["KOUS","KOUS ", "Subordinating Conjunction with Sentence"],
+    ["NE","NE ", "Named Entity"],
+    ["NN","NN ", "Normal Nomina"],
+    ["PAV", "PAV ", "Pronominal Adverb"],
+    ["PDAT","PDAT ","Attributive Demonstrative Pronoun"],
+    ["PDS","PDS ", "Substitutive Demonstrative Pronoun"],
+    ["PIAT","PIAT ", "Attributive Indefinite Pronoun without Determiner"],
+    ["PIDAT","PIDAT ", "Attributive Indefinite Pronoun with Determiner"],
+    ["PIS","PIS ", "Substitutive Indefinite Pronoun"],
+    ["PPER","PPER ", "Personal Pronoun"],
+    ["PPOSAT","PPOSAT ", "Attributive Possessive Pronoun"],
+    ["PPOSS","PPOSS ", "Substitutive Possessive Pronoun"],
+    ["PRELAT","PRELAT ", "Attributive Relative Pronoun"],
+    ["PRELS","PRELS ", "Substitutive Relative Pronoun"],
+    ["PRF","PRF ", "Reflexive Pronoun"],
+    ["PROAV","PROAV ", "Pronominal Adverb"],
+    ["PTKA","PTKA ","Particle with Adjective"],
+    ["PTKANT","PTKANT ", "Answering Particle"],
+    ["PTKNEG","PTKNEG ", "Negation Particle"],
+    ["PTKVZ","PTKVZ ", "Separated Verbal Particle"],
+    ["PTKZU","PTKZU ", "'zu' Particle"],
+    ["PWAT","PWAT ", "Attributive Interrogative Pronoun"],
+    ["PWAV","PWAV ", "Adverbial Interrogative Pronoun"],
+    ["PWS","PWS ", "Substitutive Interrogative Pronoun"],
+    ["TRUNC","TRUNC ","Truncated"],
+    ["VAFIN","VAFIN ", "Auxiliary Finite Verb"],
+    ["VAIMP","VAIMP ", "Auxiliary Finite Imperative Verb"],
+    ["VAINF","VAINF ", "Auxiliary Infinite Verb"],
+    ["VAPP","VAPP ", "Auxiliary Perfect Participle"],
+    ["VMFIN","VMFIN ", "Modal Finite Verb"],
+    ["VMINF","VMINF ", "Modal Infinite Verb"],
+    ["VMPP","VMPP ", "Modal Perfect Participle"],
+    ["VVFIN","VVFIN ","Finite Verb"],
+    ["VVIMP","VVIMP ", "Finite Imperative Verb"],
+    ["VVINF","VVINF ", "Infinite Verb"],
+    ["VVIZU","VVIZU ", "Infinite Verb with 'zu'"],
+    ["VVPP","VVPP ", "Perfect Participle"],
+    ["XY", "XY ", "Non-Word"],
+    ["<root-POS>","<root-POS>","Root Part of Speech"]
+  ];
+
+  ah["mate/m="] = [
+    ["Case", "case:"],
+    ["Degree", "degree:"],
+    ["Gender", "gender:"],
+    ["Mood", "mood:"],
+    ["Number", "number:"],
+    ["Person", "person:"],
+    ["Tense","tense:"],
+    ["No type", "<no-type> "]
+  ];
+
+  ah["mate/m=case:"] = [
+    ["acc", "acc ", "Accusative"],
+    ["dat","dat ", "Dative"],
+    ["gen", "gen ","Genitive"],
+    ["nom","nom ", "Nominative"],
+    ["*","* ", "Undefined"]
+  ];
+
+  ah["mate/m=degree:"] = [
+    ["comp","comp ", "Comparative"],
+    ["pos","pos ", "Positive"],
+    ["sup","sup ", "Superative"]
+  ];
+
+  ah["mate/m=gender:"] = [
+    ["fem", "fem ", "Feminium"],
+    ["masc", "masc ", "Masculinum"],
+    ["neut","neut ", "Neuter"],
+    ["*","* ","Undefined"]
+  ];
+
+  ah["mate/m=mood:"] = [
+    ["imp","imp ", "Imperative"],
+    ["ind","ind ", "Indicative"],
+    ["subj","subj ", "Subjunctive"]
+  ];
+
+  ah["mate/m=number:"] = [
+    ["pl","pl ","Plural"],
+    ["sg","sg ","Singular"],
+    ["*","* ","Undefined"]
+  ];
+
+  ah["mate/m=person:"] = [
+    ["1","1 ", "First Person"],
+    ["2","2 ", "Second Person"],
+    ["3","3 ", "Third Person"]
+  ];
+  ah["mate/m=tense:"] = [
+    ["past","past ", "Past"],
+    ["pres","pres ", "Present"]
+  ];
+});
diff --git a/full/src/main/resources/annotation-scripts/foundries/opennlp.js b/full/src/main/resources/annotation-scripts/foundries/opennlp.js
new file mode 100644
index 0000000..d790eba
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/foundries/opennlp.js
@@ -0,0 +1,65 @@
+define(["hint/foundries","hint/foundries/stts"], function (ah, sttsArray) {
+  ah["-"].push(
+    ["OpenNLP", "opennlp/", "Part-of-Speech"]
+  );
+
+  ah["opennlp/"] = [
+    ["Part-of-Speech", "p="]
+  ];
+
+//  ah["opennlp/p="] = sttsArray;
+  ah["opennlp/p="] = [
+	  ["ADJA","ADJA ", "Attributive Adjective"],
+	  ["ADJD","ADJD ", "Predicative Adjective"],
+	  ["ADV","ADV ", "Adverb"],
+	  ["APPO","APPO ", "Postposition"],
+	  ["APPR","APPR ", "Preposition"],
+	  ["APPRART","APPRART ", "Preposition with Determiner"],
+	  ["APZR","APZR ","Right Circumposition"],
+	  ["ART","ART ", "Determiner"],
+	  ["CARD","CARD ", "Cardinal Number"],
+	  ["FM","FM ", "Foreign Material"],
+	  ["ITJ","ITJ ", "Interjection"],
+	  ["KOKOM","KOKOM ", "Comparison Particle"],
+	  ["KON","KON ", "Coordinating Conjuncion"],
+	  ["KOUI","KOUI ", "Subordinating Conjunction with 'zu'"],
+	  ["KOUS","KOUS ", "Subordinating Conjunction with Sentence"],
+	  ["NE","NE ", "Named Entity"],
+	  ["NN","NN ", "Normal Nomina"],
+	  ["PAV", "PAV ", "Pronominal Adverb"],
+	  ["PDAT","PDAT ","Attributive Demonstrative Pronoun"],
+	  ["PDS","PDS ", "Substitutive Demonstrative Pronoun"],
+	  ["PIAT","PIAT ", "Attributive Indefinite Pronoun without Determiner"],
+	  ["PIDAT","PIDAT ", "Attributive Indefinite Pronoun with Determiner"],
+	  ["PIS","PIS ", "Substitutive Indefinite Pronoun"],
+	  ["PPER","PPER ", "Personal Pronoun"],
+	  ["PPOSAT","PPOSAT ", "Attributive Possessive Pronoun"],
+	  ["PPOSS","PPOSS ", "Substitutive Possessive Pronoun"],
+	  ["PRELAT","PRELAT ", "Attributive Relative Pronoun"],
+	  ["PRELS","PRELS ", "Substitutive Relative Pronoun"],
+	  ["PRF","PRF ", "Reflexive Pronoun"],
+	  ["PROAV","PROAV ", "Pronominal Adverb"],
+	  ["PTKA","PTKA ","Particle with Adjective"],
+	  ["PTKANT","PTKANT ", "Answering Particle"],
+	  ["PTKNEG","PTKNEG ", "Negation Particle"],
+	  ["PTKVZ","PTKVZ ", "Separated Verbal Particle"],
+	  ["PTKZU","PTKZU ", "'zu' Particle"],
+	  ["PWAT","PWAT ", "Attributive Interrogative Pronoun"],
+	  ["PWAV","PWAV ", "Adverbial Interrogative Pronoun"],
+	  ["PWS","PWS ", "Substitutive Interrogative Pronoun"],
+	  ["TRUNC","TRUNC ","Truncated"],
+	  ["VAFIN","VAFIN ", "Auxiliary Finite Verb"],
+	  ["VAIMP","VAIMP ", "Auxiliary Finite Imperative Verb"],
+	  ["VAINF","VAINF ", "Auxiliary Infinite Verb"],
+	  ["VAPP","VAPP ", "Auxiliary Perfect Participle"],
+	  ["VMFIN","VMFIN ", "Modal Finite Verb"],
+	  ["VMINF","VMINF ", "Modal Infinite Verb"],
+	  ["VMPP","VMPP ", "Modal Perfect Participle"],
+	  ["VVFIN","VVFIN ","Finite Verb"],
+	  ["VVIMP","VVIMP ", "Finite Imperative Verb"],
+	  ["VVINF","VVINF ", "Infinite Verb"],
+	  ["VVIZU","VVIZU ", "Infinite Verb with 'zu'"],
+	  ["VVPP","VVPP ", "Perfect Participle"],
+	  ["XY", "XY ", "Non-Word"]
+	];
+});
diff --git a/full/src/main/resources/annotation-scripts/foundries/schreibgebrauch.js b/full/src/main/resources/annotation-scripts/foundries/schreibgebrauch.js
new file mode 100644
index 0000000..850617f
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/foundries/schreibgebrauch.js
@@ -0,0 +1,117 @@
+define(["hint/foundries","hint/foundries/stts"], function (ah, sttsArray) {
+//  var sgbrSttsArray = sttsArray.slice(0);
+
+  // Push specific information for Schreibgebrauch
+//  sgbrSttsArray.push(
+//    ["NNE", "NNE", "Normal Nomina with Named Entity"],
+//    ["ADVART","ADVART",   "Adverb with Article"],
+//    ["EMOASC","EMOASC",   "ASCII emoticon"],
+//    ["EMOIMG","EMOIMG",   "Graphic emoticon"],
+//    ["ERRTOK","ERRTOK",   "Tokenisation Error"],
+//    ["HST",     "HST",      "Hashtag"],
+//    ["KOUSPPER","KOUSPPER", "Subordinating Conjunction (with Sentence) with Personal Pronoun"],
+//    ["ONO",     "ONO",      "Onomatopoeia"],
+//    ["PPERPPER","PPERPPER", "Personal Pronoun with Personal Pronoun"],
+//    ["URL",     "URL",      "Uniform Resource Locator"],
+//    ["VAPPER",  "VAPPER",   "Finite Auxiliary Verb with Personal Pronoun"],
+//    ["VMPPER",  "VMPPER",   "Fintite Modal Verb with Personal Pronoun"],
+//    ["VVPPER",  "VVPPER",   "Finite Full Verb with Personal Pronoun"],
+//    ["AW", "AW", "Interaction Word"],
+//    ["ADR", "ADR", "Addressing Term"],
+//    ["AWIND", "AWIND", "Punctuation Indicating Addressing Term"],
+//    ["ERRAW","ERRAW", "Part of Erroneously Separated Compound"]
+//    /*
+//      As KorAP currently doesn't support these tags, they could also be ommited
+//      ["_KOMMA", "_KOMMA", "Comma"],
+//      ["_SONST", "_SONST", "Intrasentential Punctuation Mark"],
+//      ["_ENDE", "_ENDE", "Punctuation Mark at the end of the Sentence"]
+//    */
+//  );
+
+  // Sort by tag
+  sgbrSttsArray.sort(function (a,b) { return a[0].localeCompare(b[0]) });
+
+  
+  ah["-"].push(
+    ["Schreibgebrauch", "sgbr/", "Lemma, Lemma Variants, Part-of-Speech"]
+  );
+
+  ah["sgbr/"] = [
+    ["Lemma", "l="],
+    ["Lemma Variants", "lv="],
+    ["Part-of-Speech", "p="]
+  ];
+
+//  ah["sgbr/p="] = sgbrSttsArray;
+  ah["sgbr/p="] = [
+    ["ADJA","ADJA ", "Attributive Adjective"],
+    ["ADJD","ADJD ", "Predicative Adjective"],
+    ["ADV","ADV ", "Adverb"],
+    ["APPO","APPO ", "Postposition"],
+    ["APPR","APPR ", "Preposition"],
+    ["APPRART","APPRART ", "Preposition with Determiner"],
+    ["APZR","APZR ","Right Circumposition"],
+    ["ART","ART ", "Determiner"],
+    ["CARD","CARD ", "Cardinal Number"],
+    ["FM","FM ", "Foreign Material"],
+    ["ITJ","ITJ ", "Interjection"],
+    ["KOKOM","KOKOM ", "Comparison Particle"],
+    ["KON","KON ", "Coordinating Conjuncion"],
+    ["KOUI","KOUI ", "Subordinating Conjunction with 'zu'"],
+    ["KOUS","KOUS ", "Subordinating Conjunction with Sentence"],
+    ["NE","NE ", "Named Entity"],
+    ["NN","NN ", "Normal Nomina"],
+    ["PAV", "PAV ", "Pronominal Adverb"],
+    ["PDAT","PDAT ","Attributive Demonstrative Pronoun"],
+    ["PDS","PDS ", "Substitutive Demonstrative Pronoun"],
+    ["PIAT","PIAT ", "Attributive Indefinite Pronoun without Determiner"],
+    ["PIDAT","PIDAT ", "Attributive Indefinite Pronoun with Determiner"],
+    ["PIS","PIS ", "Substitutive Indefinite Pronoun"],
+    ["PPER","PPER ", "Personal Pronoun"],
+    ["PPOSAT","PPOSAT ", "Attributive Possessive Pronoun"],
+    ["PPOSS","PPOSS ", "Substitutive Possessive Pronoun"],
+    ["PRELAT","PRELAT ", "Attributive Relative Pronoun"],
+    ["PRELS","PRELS ", "Substitutive Relative Pronoun"],
+    ["PRF","PRF ", "Reflexive Pronoun"],
+    ["PROAV","PROAV ", "Pronominal Adverb"],
+    ["PTKA","PTKA ","Particle with Adjective"],
+    ["PTKANT","PTKANT ", "Answering Particle"],
+    ["PTKNEG","PTKNEG ", "Negation Particle"],
+    ["PTKVZ","PTKVZ ", "Separated Verbal Particle"],
+    ["PTKZU","PTKZU ", "'zu' Particle"],
+    ["PWAT","PWAT ", "Attributive Interrogative Pronoun"],
+    ["PWAV","PWAV ", "Adverbial Interrogative Pronoun"],
+    ["PWS","PWS ", "Substitutive Interrogative Pronoun"],
+    ["TRUNC","TRUNC ","Truncated"],
+    ["VAFIN","VAFIN ", "Auxiliary Finite Verb"],
+    ["VAIMP","VAIMP ", "Auxiliary Finite Imperative Verb"],
+    ["VAINF","VAINF ", "Auxiliary Infinite Verb"],
+    ["VAPP","VAPP ", "Auxiliary Perfect Participle"],
+    ["VMFIN","VMFIN ", "Modal Finite Verb"],
+    ["VMINF","VMINF ", "Modal Infinite Verb"],
+    ["VMPP","VMPP ", "Modal Perfect Participle"],
+    ["VVFIN","VVFIN ","Finite Verb"],
+    ["VVIMP","VVIMP ", "Finite Imperative Verb"],
+    ["VVINF","VVINF ", "Infinite Verb"],
+    ["VVIZU","VVIZU ", "Infinite Verb with 'zu'"],
+    ["VVPP","VVPP ", "Perfect Participle"],
+    ["XY", "XY ", "Non-Word"],
+    ["NNE", "NNE", "Normal Nomina with Named Entity"],
+    ["ADVART","ADVART",   "Adverb with Article"],
+    ["EMOASC","EMOASC",   "ASCII emoticon"],
+    ["EMOIMG","EMOIMG",   "Graphic emoticon"],
+    ["ERRTOK","ERRTOK",   "Tokenisation Error"],
+    ["HST",     "HST",      "Hashtag"],
+    ["KOUSPPER","KOUSPPER", "Subordinating Conjunction (with Sentence) with Personal Pronoun"],
+    ["ONO",     "ONO",      "Onomatopoeia"],
+    ["PPERPPER","PPERPPER", "Personal Pronoun with Personal Pronoun"],
+    ["URL",     "URL",      "Uniform Resource Locator"],
+    ["VAPPER",  "VAPPER",   "Finite Auxiliary Verb with Personal Pronoun"],
+    ["VMPPER",  "VMPPER",   "Fintite Modal Verb with Personal Pronoun"],
+    ["VVPPER",  "VVPPER",   "Finite Full Verb with Personal Pronoun"],
+    ["AW", "AW", "Interaction Word"],
+    ["ADR", "ADR", "Addressing Term"],
+    ["AWIND", "AWIND", "Punctuation Indicating Addressing Term"],
+    ["ERRAW","ERRAW", "Part of Erroneously Separated Compound"]
+  ];
+});
diff --git a/full/src/main/resources/annotation-scripts/foundries/treetagger.js b/full/src/main/resources/annotation-scripts/foundries/treetagger.js
new file mode 100644
index 0000000..78a692a
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/foundries/treetagger.js
@@ -0,0 +1,66 @@
+define(["hint/foundries","hint/foundries/stts"], function (ah, sttsArray) {
+  ah["-"].push(
+    ["TreeTagger", "tt/", "Lemma, Part-of-Speech"]
+  );
+
+  ah["tt/"] = [
+    ["Lemma", "l="],
+    ["Part-of-Speech", "p="]
+  ];
+
+//  ah["tt/p="] = sttsArray;
+  ah["tt/p="] = [
+    ["ADJA","ADJA ", "Attributive Adjective"],
+    ["ADJD","ADJD ", "Predicative Adjective"],
+    ["ADV","ADV ", "Adverb"],
+    ["APPO","APPO ", "Postposition"],
+    ["APPR","APPR ", "Preposition"],
+    ["APPRART","APPRART ", "Preposition with Determiner"],
+    ["APZR","APZR ","Right Circumposition"],
+    ["ART","ART ", "Determiner"],
+    ["CARD","CARD ", "Cardinal Number"],
+    ["FM","FM ", "Foreign Material"],
+    ["ITJ","ITJ ", "Interjection"],
+    ["KOKOM","KOKOM ", "Comparison Particle"],
+    ["KON","KON ", "Coordinating Conjuncion"],
+    ["KOUI","KOUI ", "Subordinating Conjunction with 'zu'"],
+    ["KOUS","KOUS ", "Subordinating Conjunction with Sentence"],
+    ["NE","NE ", "Named Entity"],
+    ["NN","NN ", "Normal Nomina"],
+    ["PAV", "PAV ", "Pronominal Adverb"],
+    ["PDAT","PDAT ","Attributive Demonstrative Pronoun"],
+    ["PDS","PDS ", "Substitutive Demonstrative Pronoun"],
+    ["PIAT","PIAT ", "Attributive Indefinite Pronoun without Determiner"],
+    ["PIDAT","PIDAT ", "Attributive Indefinite Pronoun with Determiner"],
+    ["PIS","PIS ", "Substitutive Indefinite Pronoun"],
+    ["PPER","PPER ", "Personal Pronoun"],
+    ["PPOSAT","PPOSAT ", "Attributive Possessive Pronoun"],
+    ["PPOSS","PPOSS ", "Substitutive Possessive Pronoun"],
+    ["PRELAT","PRELAT ", "Attributive Relative Pronoun"],
+    ["PRELS","PRELS ", "Substitutive Relative Pronoun"],
+    ["PRF","PRF ", "Reflexive Pronoun"],
+    ["PROAV","PROAV ", "Pronominal Adverb"],
+    ["PTKA","PTKA ","Particle with Adjective"],
+    ["PTKANT","PTKANT ", "Answering Particle"],
+    ["PTKNEG","PTKNEG ", "Negation Particle"],
+    ["PTKVZ","PTKVZ ", "Separated Verbal Particle"],
+    ["PTKZU","PTKZU ", "'zu' Particle"],
+    ["PWAT","PWAT ", "Attributive Interrogative Pronoun"],
+    ["PWAV","PWAV ", "Adverbial Interrogative Pronoun"],
+    ["PWS","PWS ", "Substitutive Interrogative Pronoun"],
+    ["TRUNC","TRUNC ","Truncated"],
+    ["VAFIN","VAFIN ", "Auxiliary Finite Verb"],
+    ["VAIMP","VAIMP ", "Auxiliary Finite Imperative Verb"],
+    ["VAINF","VAINF ", "Auxiliary Infinite Verb"],
+    ["VAPP","VAPP ", "Auxiliary Perfect Participle"],
+    ["VMFIN","VMFIN ", "Modal Finite Verb"],
+    ["VMINF","VMINF ", "Modal Infinite Verb"],
+    ["VMPP","VMPP ", "Modal Perfect Participle"],
+    ["VVFIN","VVFIN ","Finite Verb"],
+    ["VVIMP","VVIMP ", "Finite Imperative Verb"],
+    ["VVINF","VVINF ", "Infinite Verb"],
+    ["VVIZU","VVIZU ", "Infinite Verb with 'zu'"],
+    ["VVPP","VVPP ", "Perfect Participle"],
+    ["XY", "XY ", "Non-Word"]
+  ];
+});
diff --git a/full/src/main/resources/annotation-scripts/foundries/xip.js b/full/src/main/resources/annotation-scripts/foundries/xip.js
new file mode 100644
index 0000000..ff7ac5e
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/foundries/xip.js
@@ -0,0 +1,12 @@
+define(["hint/foundries"], function (ah) {
+  ah["-"].push(
+    ["Xerox Parser", "xip/", "Constituency, Lemma, Part-of-Speech"]
+  );
+
+  ah["xip/"] = [
+    ["Constituency", "c="],
+    // Inactive: ["Dependency", "d="],
+    ["Lemma", "l="],
+    ["Part-of-Speech", "p="]
+  ];
+});
diff --git a/full/src/main/resources/annotation-scripts/variables/negraedges.js b/full/src/main/resources/annotation-scripts/variables/negraedges.js
new file mode 100644
index 0000000..7dc9b0f
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/variables/negraedges.js
@@ -0,0 +1,51 @@
+define(function () {
+  // http://www.coli.uni-saarland.de/projects/sfb378/negra-corpus/negra-corpus.html
+  // http://www.coli.uni-saarland.de/projects/sfb378/negra-corpus/kanten.html
+  return [
+    ["AC","AC ","adpositional case marker"],
+    ["ADC","ADC ","adjective component"],
+    ["AMS","AMS ","measure argument of adj"],
+    ["APP","APP ","apposition"],
+    ["AVC","AVC ","adverbial phrase component"],
+    ["CC","CC ","comparative complement"],
+    ["CD","CD ","coordinating conjunction"],
+    ["CJ","CJ ","conjunct"],
+    ["CM","CM ","comparative concjunction"],
+    ["CP","CP ","complementizer"],
+    ["DA","DA ","dative"],
+    ["DH","DH ","discourse-level head"],
+    ["DM","DM ","discourse marker"],
+    ["GL","GL ","prenominal genitive"],
+    ["GR","GR ","postnominal genitive"],
+    ["HD","HD ","head"],
+    ["JU","JU ","junctor"],
+    ["MC","MC ","comitative"],
+    ["MI","MI ","instrumental"],
+    ["ML","ML ","locative"],
+    ["MNR","MNR ","postnominal modifier"],
+    ["MO","MO ","modifier"],
+    ["MR","MR ","rhetorical modifier"],
+    ["MW","MW ","way (directional modifier)"],
+    ["NG","NG ","negation"],
+    ["NK","NK ","noun kernel modifier"],
+    ["NMC","NMC ","numerical component"], 
+    ["OA","OA ","accusative object"],
+    ["OA2","OA2 ","second accusative object"], 
+    ["OC","OC ","clausal object"],
+    ["OG","OG ","genitive object"], 
+    ["PD","PD ","predicate"],
+    ["PG","PG ","pseudo-genitive"],
+    ["PH","PH ","placeholder"],
+    ["PM","PM ","morphological particle"],
+    ["PNC","PNC ","proper noun component"], 
+    ["RC","RC ","relative clause"],
+    ["RE","RE ","repeated element"],
+    ["RS","RS ","reported speech"],
+    ["SB","SB ","subject"],
+    ["SBP","SBP ","passivised subject (PP)"], 
+    ["SP","SP ","subject or predicate"],
+    ["SVP","SVP ","separable verb prefix"],
+    ["UC","UC ","(idiosyncratic) unit component"], 
+    ["VO","VO ","vocative"]
+  ];
+});
diff --git a/full/src/main/resources/annotation-scripts/variables/negranodes.js b/full/src/main/resources/annotation-scripts/variables/negranodes.js
new file mode 100644
index 0000000..b6be581
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/variables/negranodes.js
@@ -0,0 +1,32 @@
+define(function () {
+  // http://www.coli.uni-saarland.de/projects/sfb378/negra-corpus/negra-corpus.html
+  // http://www.coli.uni-saarland.de/projects/sfb378/negra-corpus/knoten.html
+  return [
+    ["AA", "AA", "superlative phrase with 'am'"],
+    ["AP","AP", "adjektive phrase"],
+    ["AVP","AVP", "adverbial phrase"],
+    ["CAP","CAP", "coordinated adjektive phrase"],
+    ["CAVP","CAVP", "coordinated adverbial phrase"],
+    ["CAC","CAC", "coordinated adposition"],
+    ["CCP","CCP", "coordinated complementiser"],
+    ["CH","CH", "chunk"],
+    ["CNP","CNP", "coordinated noun phrase"],
+    ["CO","CO", "coordination"],
+    ["CPP","CPP", "coordinated adpositional phrase"],
+    ["CS","CS", "coordinated sentence"],
+    ["CVP","CVP", "coordinated verb phrase (non-finite)"],
+    ["CVZ","CVZ", "coordinated zu-marked infinitive"],
+    ["DL","DL", "discourse level constituent"],
+    ["ISU","ISU", "idiosyncratis unit"],
+    ["MPN","MPN", "multi-word proper noun"],
+    ["MTA","MTA", "multi-token adjective"],
+    ["NM","NM", "multi-token number"],
+    ["NP","NP", "noun phrase"],
+    ["PP","PP", "adpositional phrase"],
+    ["QL","QL", "quasi-languag"],
+    ["ROOT","ROOT", "root node"],
+    ["S","S", "sentence"],
+    ["VP","VP", "verb phrase (non-finite)"],
+    ["VZ","VZ", "zu-marked infinitive"]
+  ]
+});
diff --git a/full/src/main/resources/annotation-scripts/variables/stts.js b/full/src/main/resources/annotation-scripts/variables/stts.js
new file mode 100644
index 0000000..f7d3b62
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/variables/stts.js
@@ -0,0 +1,59 @@
+define(function () {
+  return [
+    // http://www.ids-mannheim.de/cosmas2/projekt/referenz/stts/morph.html
+    // http://nachhalt.sfb632.uni-potsdam.de/owl-docu/stts.html
+    // "$.", "$(", "$,"
+    ["ADJA","ADJA ", "Attributive Adjective"],
+    ["ADJD","ADJD ", "Predicative Adjective"],
+    ["ADV","ADV ", "Adverb"],
+    ["APPO","APPO ", "Postposition"],
+    ["APPR","APPR ", "Preposition"],
+    ["APPRART","APPRART ", "Preposition with Determiner"],
+    ["APZR","APZR ","Right Circumposition"],
+    ["ART","ART ", "Determiner"],
+    ["CARD","CARD ", "Cardinal Number"],
+    ["FM","FM ", "Foreign Material"],
+    ["ITJ","ITJ ", "Interjection"],
+    ["KOKOM","KOKOM ", "Comparison Particle"],
+    ["KON","KON ", "Coordinating Conjuncion"],
+    ["KOUI","KOUI ", "Subordinating Conjunction with 'zu'"],
+    ["KOUS","KOUS ", "Subordinating Conjunction with Sentence"],
+    ["NE","NE ", "Named Entity"],
+    ["NN","NN ", "Normal Nomina"],
+    ["PAV", "PAV ", "Pronominal Adverb"],
+    ["PDAT","PDAT ","Attributive Demonstrative Pronoun"],
+    ["PDS","PDS ", "Substitutive Demonstrative Pronoun"],
+    ["PIAT","PIAT ", "Attributive Indefinite Pronoun without Determiner"],
+    ["PIDAT","PIDAT ", "Attributive Indefinite Pronoun with Determiner"],
+    ["PIS","PIS ", "Substitutive Indefinite Pronoun"],
+    ["PPER","PPER ", "Personal Pronoun"],
+    ["PPOSAT","PPOSAT ", "Attributive Possessive Pronoun"],
+    ["PPOSS","PPOSS ", "Substitutive Possessive Pronoun"],
+    ["PRELAT","PRELAT ", "Attributive Relative Pronoun"],
+    ["PRELS","PRELS ", "Substitutive Relative Pronoun"],
+    ["PRF","PRF ", "Reflexive Pronoun"],
+    ["PROAV","PROAV ", "Pronominal Adverb"],
+    ["PTKA","PTKA ","Particle with Adjective"],
+    ["PTKANT","PTKANT ", "Answering Particle"],
+    ["PTKNEG","PTKNEG ", "Negation Particle"],
+    ["PTKVZ","PTKVZ ", "Separated Verbal Particle"],
+    ["PTKZU","PTKZU ", "'zu' Particle"],
+    ["PWAT","PWAT ", "Attributive Interrogative Pronoun"],
+    ["PWAV","PWAV ", "Adverbial Interrogative Pronoun"],
+    ["PWS","PWS ", "Substitutive Interrogative Pronoun"],
+    ["TRUNC","TRUNC ","Truncated"],
+    ["VAFIN","VAFIN ", "Auxiliary Finite Verb"],
+    ["VAIMP","VAIMP ", "Auxiliary Finite Imperative Verb"],
+    ["VAINF","VAINF ", "Auxiliary Infinite Verb"],
+    ["VAPP","VAPP ", "Auxiliary Perfect Participle"],
+    ["VMFIN","VMFIN ", "Modal Finite Verb"],
+    ["VMINF","VMINF ", "Modal Infinite Verb"],
+    ["VMPP","VMPP ", "Modal Perfect Participle"],
+    ["VVFIN","VVFIN ","Finite Verb"],
+    ["VVIMP","VVIMP ", "Finite Imperative Verb"],
+    ["VVINF","VVINF ", "Infinite Verb"],
+    ["VVIZU","VVIZU ", "Infinite Verb with 'zu'"],
+    ["VVPP","VVPP ", "Perfect Participle"],
+    ["XY", "XY ", "Non-Word"]
+  ];
+});
diff --git a/full/src/main/resources/lite-config.xml b/full/src/main/resources/lite-config.xml
index 4b6a074..5c9b8ff 100644
--- a/full/src/main/resources/lite-config.xml
+++ b/full/src/main/resources/lite-config.xml
@@ -132,10 +132,10 @@
 	<!-- Initialization -->
 	<!-- <bean id="initializator" class="de.ids_mannheim.de.init.LiteInitializatorImpl"
 		init-method="init">
-	</bean> -->
+	</bean> 
 	<bean id="annotationParser" class="de.ids_mannheim.korap.annotation.AnnotationParser"
 		scope="singleton" />
-
+	-->
 	<!-- Search Engine -->
 	<bean id="search_krill" class="de.ids_mannheim.korap.web.SearchKrill">
 		<constructor-arg value="${krill.indexDir}" />
diff --git a/full/src/main/resources/service.properties b/full/src/main/resources/service.properties
new file mode 100644
index 0000000..cf2899d
--- /dev/null
+++ b/full/src/main/resources/service.properties
@@ -0,0 +1,3 @@
+kustvakt.version=${project.version}
+krill.version=${krill.version}
+koral.version=${koral.version}
\ No newline at end of file
diff --git a/full/src/main/resources/validation.properties b/full/src/main/resources/validation.properties
new file mode 100644
index 0000000..15e09cd
--- /dev/null
+++ b/full/src/main/resources/validation.properties
@@ -0,0 +1,42 @@
+# 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.password_cap=((?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{8,20})
+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.ipddress=^(?:(?: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}$
+
+# as used by apache commons validator for strings
+# Validator.string=^[\\.;:,\\=&\\*\\/\\/_()\\[\\]@\\|\\-0-9\\p{L}\\p{Space}]{0,1024}$
+
+#Validator.username=^[A-Za-z_.\\d]{6,15}$ by Hanl
+# 21.04.17/FB
+Validator.username=^[A-Za-z_.\\d]{3,20}$
+Validator.password=^((?=.*\\d)(?=.*[A-Za-z])(?!.*[\\(\\)-]).{8,20})$
+
+
+# EM
+Validator.setting=[a-zA-Z0-9_-]+
\ No newline at end of file
diff --git a/full/src/test/resources/network-output/search-result.jsonld b/full/src/test/resources/network-output/search-result.jsonld
new file mode 100644
index 0000000..da55e09
--- /dev/null
+++ b/full/src/test/resources/network-output/search-result.jsonld
@@ -0,0 +1,130 @@
+{
+    "@context": "http://korap.ids-mannheim.de/ns/KoralQuery/v0.3/context.jsonld",
+    "meta": {
+        "count": 2,
+        "startIndex": 0,
+        "timeout": 10000,
+        "context": {
+            "left": [
+                "token",
+                6
+            ],
+            "right": [
+                "token",
+                6
+            ]
+        },
+        "fields": [
+            "ID",
+            "UID",
+            "textSigle",
+            "corpusID",
+            "author",
+            "title",
+            "subTitle",
+            "textClass",
+            "pubPlace",
+            "pubDate",
+            "availability",
+            "layerInfos",
+            "docSigle",
+            "corpusSigle"
+        ],
+        "version": "0.60.4",
+        "benchmark": "1.503497374 s",
+        "totalResults": 67249248,
+        "serialQuery": "tokens:s:der",
+        "itemsPerPage": 2
+    },
+    "query": {
+        "@type": "koral:token",
+        "wrap": {
+            "@type": "koral:term",
+            "match": "match:eq",
+            "layer": "orth",
+            "key": "der",
+            "foundry": "opennlp",
+            "rewrites": [
+                {
+                    "@type": "koral:rewrite",
+                    "src": "Kustvakt",
+                    "operation": "operation:injection",
+                    "scope": "foundry"
+                }
+            ]
+        }
+    },
+    "collection": {
+        "@type": "koral:doc",
+        "match": "match:eq",
+        "type": "type:regex",
+        "value": "CC-BY.*",
+        "key": "availability",
+        "rewrites": [
+            {
+                "@type": "koral:rewrite",
+                "src": "Kustvakt",
+                "operation": "operation:insertion",
+                "scope": "availability(FREE)"
+            }
+        ]
+    },
+    "matches": [
+        {
+            "@context": "http://korap.ids-mannheim.de/ns/KoralQuery/v0.3/context.jsonld",
+            "meta": {},
+            "hasSnippet": true,
+            "hasTokens": false,
+            "matchID": "match-WUD17/B96/48580-p40-41",
+            "textClass": "staat-gesellschaft biographien-interviews fiktion vermischtes",
+            "textSigle": "WUD17/B96/48580",
+            "author": "Andy king50, u.a.",
+            "docSigle": "WUD17/B96",
+            "layerInfos": "corenlp/c=spans corenlp/p=tokens corenlp/s=spans dereko/s=spans malt/d=rels marmot/m=tokens marmot/p=tokens opennlp/p=tokens opennlp/s=spans tt/l=tokens tt/p=tokens",
+            "pubPlace": "URL:http://de.wikipedia.org",
+            "availability": "CC-BY-SA",
+            "title": "Benutzer Diskussion:Brettspieler",
+            "pubDate": "2017-07-01",
+            "corpusSigle": "WUD17",
+            "context": {
+                "left": [
+                    "token",
+                    6
+                ],
+                "right": [
+                    "token",
+                    6
+                ]
+            },
+            "snippet": "<span class=\"context-left\"><span class=\"more\"><\/span>konstruktiv mitarbeiten kannst, erfährst du auf <\/span><span class=\"match\"><mark>der<\/mark><\/span><span class=\"context-right\"> Seite  Starthilfe . Grüße,  19:05, 6. Nov.<span class=\"more\"><\/span><\/span>"
+        },
+        {
+            "@context": "http://korap.ids-mannheim.de/ns/KoralQuery/v0.3/context.jsonld",
+            "meta": {},
+            "hasSnippet": true,
+            "hasTokens": false,
+            "matchID": "match-WUD17/B96/50115-p40-41",
+            "textClass": "staat-gesellschaft biographien-interviews fiktion vermischtes",
+            "textSigle": "WUD17/B96/50115",
+            "author": "Doc.Heintz, u.a.",
+            "docSigle": "WUD17/B96",
+            "layerInfos": "corenlp/c=spans corenlp/p=tokens corenlp/s=spans dereko/s=spans malt/d=rels marmot/m=tokens marmot/p=tokens opennlp/p=tokens opennlp/s=spans tt/l=tokens tt/p=tokens",
+            "pubPlace": "URL:http://de.wikipedia.org",
+            "availability": "CC-BY-SA",
+            "title": "Benutzer Diskussion:Blkviz",
+            "pubDate": "2017-07-01",
+            "corpusSigle": "WUD17",
+            "context": {
+                "left": [
+                    "token",
+                    6
+                ],
+                "right": [
+                    "token",
+                    6
+                ]
+            },
+            "snippet": "<span class=\"context-left\"><span class=\"more\"><\/span>konstruktiv mitarbeiten kannst, erfährst du auf <\/span><span class=\"match\"><mark>der<\/mark><\/span><span class=\"context-right\"> Seite  Starthilfe . Grüße,  20:11, 7.<span class=\"more\"><\/span><\/span>"
+        }
+    ]
+}
diff --git a/full/src/test/resources/pipe-output/test-pipes.jsonld b/full/src/test/resources/pipe-output/test-pipes.jsonld
new file mode 100644
index 0000000..7fde643
--- /dev/null
+++ b/full/src/test/resources/pipe-output/test-pipes.jsonld
@@ -0,0 +1,25 @@
+{
+    "meta": {
+        "snippets": true,
+        "timeout": 10000
+    },
+    "query": {
+        "@type": "koral:token",
+        "wrap": {
+            "@type": "koral:term",
+            "match": "match:eq",
+            "key": [
+                "der",
+                "das"
+            ],
+            "layer": "orth",
+            "rewrites": [{
+                "@type": "koral:rewrite",
+                "src": "Glemm",
+                "operation": "operation:override",
+                "scope": "key"
+            }]
+        }
+    },
+    "@context": "http://korap.ids-mannheim.de/ns/koral/0.3/context.jsonld"
+}
diff --git a/full/src/test/resources/pipe-output/with-param.jsonld b/full/src/test/resources/pipe-output/with-param.jsonld
new file mode 100644
index 0000000..08c7fda
--- /dev/null
+++ b/full/src/test/resources/pipe-output/with-param.jsonld
@@ -0,0 +1,29 @@
+{
+    "meta": {
+        "snippets": true,
+        "timeout": 10000
+    },
+    "query": {
+        "@type": "koral:token",
+        "wrap": {
+            "@type": "koral:term",
+            "match": "match:eq",
+            "key": [
+                "der",
+                "das",
+		"die"
+            ],
+            "layer": "orth",
+            "rewrites": [
+                {
+                    "@type": "koral:rewrite",
+                    "src": "Glemm",
+                    "operation": "operation:override",
+                    "scope": "key"
+                }
+            ]
+        }
+    },
+    "@context": "http://korap.ids-mannheim.de/ns/koral/0.3/context.jsonld"
+}
+