Merged lite into full

Change-Id: I8e496cebcfa91de5077bc938ec00617136037e22
diff --git a/full/src/test/java/de/ids_mannheim/korap/config/LiteJerseyTest.java b/full/src/test/java/de/ids_mannheim/korap/config/LiteJerseyTest.java
new file mode 100644
index 0000000..525d24b
--- /dev/null
+++ b/full/src/test/java/de/ids_mannheim/korap/config/LiteJerseyTest.java
@@ -0,0 +1,63 @@
+package de.ids_mannheim.korap.config;
+
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.support.GenericApplicationContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.web.context.support.GenericWebApplicationContext;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.servlet.ServletContainer;
+import org.glassfish.jersey.test.DeploymentContext;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.ServletDeploymentContext;
+import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
+import org.glassfish.jersey.test.spi.TestContainerException;
+import org.glassfish.jersey.test.spi.TestContainerFactory;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration("classpath:test-config-lite.xml")
+public abstract class LiteJerseyTest extends JerseyTest{
+    
+    public static final String API_VERSION = "v1.0";
+    
+    @Autowired
+    protected GenericApplicationContext applicationContext;
+    
+    public static String[] classPackages =
+            new String[] {
+                    "de.ids_mannheim.korap.core.web",
+                    "de.ids_mannheim.korap.web.filter", 
+                    "de.ids_mannheim.korap.web.utils",
+                    "de.ids_mannheim.korap.test",
+                    "com.fasterxml.jackson.jaxrs.json"};
+    
+    @Override
+    protected TestContainerFactory getTestContainerFactory ()
+            throws TestContainerException {
+        return new GrizzlyWebTestContainerFactory();
+    }
+
+    @Override
+    public void setUp () throws Exception {
+
+        GenericWebApplicationContext genericContext =
+                new GenericWebApplicationContext();
+
+        genericContext.setParent(this.applicationContext);
+        genericContext.setClassLoader(this.applicationContext.getClassLoader());
+
+        StaticContextLoaderListener.applicationContext = genericContext;
+        super.setUp();
+    }
+    
+    @Override
+    protected DeploymentContext configureDeployment() {
+        return ServletDeploymentContext
+                .forServlet(new ServletContainer(new ResourceConfig().packages(classPackages)))
+                .addListener(StaticContextLoaderListener.class)
+                .contextParam("adminToken", "secret")
+                .build();
+    }
+}
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/lite/InfoControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/lite/InfoControllerTest.java
new file mode 100644
index 0000000..6416879
--- /dev/null
+++ b/full/src/test/java/de/ids_mannheim/korap/web/lite/InfoControllerTest.java
@@ -0,0 +1,50 @@
+package de.ids_mannheim.korap.web.lite;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.config.LiteJerseyTest;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.utils.JsonUtils;
+import de.ids_mannheim.korap.utils.ServiceInfo;
+import de.ids_mannheim.korap.web.SearchKrill;
+
+public class InfoControllerTest extends LiteJerseyTest {
+
+    @Autowired
+    private KustvaktConfiguration config;
+
+    @Autowired
+    private SearchKrill krill;
+    
+    @Test
+    public void testInfo () throws KustvaktException {
+        Response response = target().path(API_VERSION).path("info")
+                .request()
+                .get();
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(config.getCurrentVersion(),
+                node.at("/latest_api_version").asText());
+        assertEquals(config.getSupportedVersions().size(),
+                node.at("/supported_api_versions").size());
+
+        assertEquals(ServiceInfo.getInfo().getVersion(),
+                node.at("/kustvakt_version").asText());
+        assertEquals(krill.getIndex().getVersion(),
+                node.at("/krill_version").asText());
+        assertEquals(ServiceInfo.getInfo().getKoralVersion(),
+                node.at("/koral_version").asText());
+    }
+}
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/lite/LiteMultipleCorpusQueryTest.java b/full/src/test/java/de/ids_mannheim/korap/web/lite/LiteMultipleCorpusQueryTest.java
new file mode 100644
index 0000000..56b861f
--- /dev/null
+++ b/full/src/test/java/de/ids_mannheim/korap/web/lite/LiteMultipleCorpusQueryTest.java
@@ -0,0 +1,89 @@
+package de.ids_mannheim.korap.web.lite;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import de.ids_mannheim.korap.config.LiteJerseyTest;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.utils.JsonUtils;
+
+public class LiteMultipleCorpusQueryTest extends LiteJerseyTest {
+
+    @Test
+    public void testSearchGet () throws KustvaktException {
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "das").queryParam("ql", "poliqarp")
+                .queryParam("cq", "pubPlace=München")
+                .queryParam("cq", "textSigle=\"GOE/AGA/01784\"")
+                .request()
+                .get();
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        node = node.at("/collection");
+        assertEquals("koral:docGroup", node.at("/@type").asText());
+        assertEquals("operation:and", node.at("/operation").asText());
+        assertEquals(2, node.at("/operands").size());
+        assertEquals("koral:doc", node.at("/operands/0/@type").asText());
+        assertEquals("match:eq", node.at("/operands/0/match").asText());
+        assertEquals("pubPlace", node.at("/operands/0/key").asText());
+        assertEquals("München", node.at("/operands/0/value").asText());
+        assertEquals("textSigle", node.at("/operands/1/key").asText());
+        assertEquals("GOE/AGA/01784", node.at("/operands/1/value").asText());
+    }
+
+    @Test
+    public void testStatisticsWithMultipleCq ()
+            throws ProcessingException,
+            KustvaktException {
+        Response response = target().path(API_VERSION).path("statistics")
+                .queryParam("cq", "textType=Abhandlung")
+                .queryParam("cq", "corpusSigle=GOE")
+                .request()
+                .method("GET");
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(2, node.at("/documents").asInt());
+        assertEquals(138180, node.at("/tokens").asInt());
+        assertEquals(5687, node.at("/sentences").asInt());
+        assertEquals(258, node.at("/paragraphs").asInt());
+
+        assertTrue(node.at("/warnings").isMissingNode());
+    }
+
+    @Test
+    public void testStatisticsWithMultipleCorpusQuery ()
+            throws ProcessingException,
+            KustvaktException {
+        Response response = target().path(API_VERSION).path("statistics")
+                .queryParam("corpusQuery", "textType=Autobiographie")
+                .queryParam("corpusQuery", "corpusSigle=GOE")
+                .request()
+                .method("GET");
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(9, node.at("/documents").asInt());
+        assertEquals(527662, node.at("/tokens").asInt());
+        assertEquals(19387, node.at("/sentences").asInt());
+        assertEquals(514, node.at("/paragraphs").asInt());
+
+        assertEquals(StatusCodes.DEPRECATED,
+                node.at("/warnings/0/0").asInt());
+        assertEquals("Parameter corpusQuery is deprecated in favor of cq.",
+                node.at("/warnings/0/1").asText());
+    }
+}
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/lite/LiteSearchControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/lite/LiteSearchControllerTest.java
new file mode 100644
index 0000000..a052c70
--- /dev/null
+++ b/full/src/test/java/de/ids_mannheim/korap/web/lite/LiteSearchControllerTest.java
@@ -0,0 +1,591 @@
+package de.ids_mannheim.korap.web.lite;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.Iterator;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.eclipse.jetty.http.HttpStatus;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.net.HttpHeaders;
+
+import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
+import de.ids_mannheim.korap.config.Attributes;
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.config.LiteJerseyTest;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.query.serialize.QuerySerializer;
+import de.ids_mannheim.korap.utils.JsonUtils;
+import de.ids_mannheim.korap.web.SearchKrill;
+
+public class LiteSearchControllerTest extends LiteJerseyTest {
+
+    @Autowired
+    private SearchKrill searchKrill;
+    @Autowired
+    private KustvaktConfiguration config;
+
+//  EM: The API is disabled
+    @Ignore   
+    @Test
+    public void testGetJSONQuery () throws KustvaktException {
+        Response response = target().path(API_VERSION).path("query")
+                .queryParam("q", "[orth=das]").queryParam("ql", "poliqarp")
+                .queryParam("context", "sentence").queryParam("count", "13")
+                .request()
+                .method("GET");
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String query = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(query);
+        assertNotNull(node);
+        assertEquals("orth", node.at("/query/wrap/layer").asText());
+        assertEquals("opennlp", node.at("/query/wrap/foundry").asText());
+        assertEquals("sentence", node.at("/meta/context").asText());
+        assertEquals("13", node.at("/meta/count").asText());
+    }
+    
+//  EM: The API is disabled
+    @Ignore
+    @Test
+    public void testbuildAndPostQuery () throws KustvaktException {
+        Response response = target().path(API_VERSION).path("query")
+                .queryParam("q", "[orth=das]").queryParam("ql", "poliqarp")
+                .queryParam("cq", "corpusSigle=WPD | corpusSigle=GOE")
+                .request()
+                .method("GET");
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+
+        String query = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(query);
+        assertNotNull(node);
+
+        response = target().path(API_VERSION).path("search")
+                .request()
+                .post(Entity.json(query));
+
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String matches = response.readEntity(String.class);
+        JsonNode match_node = JsonUtils.readTree(matches);
+        assertNotEquals(0, match_node.path("matches").size());
+    }
+
+    @Test
+    public void testApiWelcomeMessage () {
+        Response response = target().path(API_VERSION).path("")
+                .request()
+                .get();
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String message = response.readEntity(String.class);
+        assertEquals(
+            "Wes8Bd4h1OypPqbWF5njeQ==",
+            response.getHeaders().getFirst("X-Index-Revision")
+            );
+        assertEquals(message, config.getApiWelcomeMessage());
+    }
+
+    @Test
+    public void testQueryGet () throws KustvaktException {
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=das]").queryParam("ql", "poliqarp")
+                .queryParam("context", "sentence").queryParam("count", "13")
+                .request()
+                .get();
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String query = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(query);
+        assertNotNull(node);
+        assertEquals("orth", node.at("/query/wrap/layer").asText());
+        assertEquals("base/s:s", node.at("/meta/context").asText());
+        assertEquals("13", node.at("/meta/count").asText());
+        assertNotEquals(0, node.at("/matches").size());
+    }
+
+    @Test
+    public void testQueryFailure () throws KustvaktException {
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=das").queryParam("ql", "poliqarp")
+                .queryParam("cq", "corpusSigle=WPD | corpusSigle=GOE")
+                .queryParam("count", "13")
+                .request()
+                .get();
+        assertEquals(Status.BAD_REQUEST.getStatusCode(),
+                response.getStatus());
+        String query = response.readEntity(String.class);
+
+        JsonNode node = JsonUtils.readTree(query);
+        assertNotNull(node);
+        assertEquals(302, node.at("/errors/0/0").asInt());
+        assertEquals(302, node.at("/errors/1/0").asInt());
+        assertTrue(node.at("/errors/2").isMissingNode());
+        assertFalse(node.at("/collection").isMissingNode());
+        assertEquals(13, node.at("/meta/count").asInt());
+    }
+
+    @Test
+    public void testFoundryRewrite () throws KustvaktException {
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=das]").queryParam("ql", "poliqarp")
+                .queryParam("context", "sentence").queryParam("count", "13")
+                .request()
+                .get();
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String query = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(query);
+        assertNotNull(node);
+        assertEquals("orth", node.at("/query/wrap/layer").asText());
+        assertEquals("opennlp", node.at("/query/wrap/foundry").asText());
+    }
+
+//  EM: The API is disabled
+    @Test
+    @Ignore
+    public void testQueryPost () throws KustvaktException {
+        QuerySerializer s = new QuerySerializer();
+        s.setQuery("[orth=das]", "poliqarp");
+
+        Response response = target().path(API_VERSION).path("search")
+                .request()
+                .post(Entity.json(s.toJSON()));
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String query = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(query);
+        assertNotNull(node);
+        assertEquals("orth", node.at("/query/wrap/layer").asText());
+        assertNotEquals(0, node.at("/matches").size());
+    }
+
+    @Test
+    public void testParameterField () throws KustvaktException {
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=das]").queryParam("ql", "poliqarp")
+                .queryParam("fields", "author,docSigle")
+                .queryParam("context", "sentence").queryParam("count", "13")
+                .request()
+                .get();
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String query = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(query);
+        assertNotNull(node);
+        assertEquals("orth", node.at("/query/wrap/layer").asText());
+        assertNotEquals(0, node.at("/matches").size());
+        assertEquals("[\"author\",\"docSigle\"]",
+                node.at("/meta/fields").toString());
+    }
+
+    @Test
+    public void testMatchInfoGetWithoutSpans () throws KustvaktException {
+        Response response = target().path(API_VERSION)
+                .path("corpus/GOE/AGA/01784/p36-46(5)37-45(2)38-42/matchInfo")
+                .queryParam("foundry", "*").queryParam("spans", "false")
+                .request()
+                .get();
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String ent = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(ent);
+        assertNotNull(node);
+        assertEquals("GOE/AGA/01784", node.at("/textSigle").asText());
+        assertEquals("match-GOE/AGA/01784-p36-46(5)37-45(2)38-42",
+                node.at("/matchID").asText());
+        assertEquals("Belagerung von Mainz", node.at("/title").asText());
+    };
+
+    @Test
+    public void testMatchInfoGetWithoutHighlights () throws KustvaktException {
+        Response response = target().path(API_VERSION)
+                .path("corpus/GOE/AGA/01784/p36-46(5)37-45(2)38-42/matchInfo")
+                .queryParam("foundry", "xy").queryParam("spans", "false")
+                .request()
+                .get();
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String ent = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(ent);
+        assertNotNull(node);
+        assertEquals(
+                "<span class=\"context-left\"></span><span class=\"match\">der alte freie Weg nach Mainz war gesperrt, ich mußte über die Schiffbrücke bei Rüsselsheim; in Ginsheim ward <mark>gefüttert; der Ort ist sehr zerschossen; dann über die Schiffbrücke</mark> auf die Nonnenaue, wo viele Bäume niedergehauen lagen, sofort auf dem zweiten Teil der Schiffbrücke über den größern Arm des Rheins.</span><span class=\"context-right\"></span>",
+                node.at("/snippet").asText());
+        assertEquals("GOE/AGA/01784", node.at("/textSigle").asText());
+        assertEquals("match-GOE/AGA/01784-p36-46(5)37-45(2)38-42",
+                node.at("/matchID").asText());
+        assertEquals("Belagerung von Mainz", node.at("/title").asText());
+    };
+
+    @Test
+    public void testMatchInfoWithoutExtension () throws KustvaktException {
+        Response response = target().path(API_VERSION)
+                .path("corpus/GOE/AGA/01784/p36-46(5)37-45(2)38-42")
+                .queryParam("foundry", "-").queryParam("spans", "false")
+                .queryParam("expand","false")
+                .request()
+                .get();
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String ent = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(ent);
+        assertNotNull(node);
+        assertEquals("GOE/AGA/01784", node.at("/textSigle").asText());
+        assertEquals("match-GOE/AGA/01784-p36-46(5)37-45(2)38-42",
+                node.at("/matchID").asText());
+        assertEquals("<span class=\"context-left\"><span class=\"more\"></span></span><span class=\"match\"><mark>gefüttert; der Ort ist sehr zerschossen; dann über die Schiffbrücke</mark></span><span class=\"context-right\"><span class=\"more\"></span></span>",
+                node.at("/snippet").asText());
+        assertEquals("Belagerung von Mainz", node.at("/title").asText());
+    };
+
+    
+    @Test
+    public void testMatchInfoGetWithHighlights () throws KustvaktException {
+        Response response = target().path(API_VERSION)
+                .path("corpus/GOE/AGA/01784/p36-46(5)37-45(2)38-42/matchInfo")
+                .queryParam("foundry", "xy").queryParam("spans", "false")
+                .queryParam("hls", "true")
+                .request()
+                .get();
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String ent = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(ent);
+        assertNotNull(node);
+        assertEquals("GOE/AGA/01784", node.at("/textSigle").asText());
+        assertEquals(
+                "<span class=\"context-left\"></span><span class=\"match\">"
+                        + "der alte freie Weg nach Mainz war gesperrt, ich mußte über die "
+                        + "Schiffbrücke bei Rüsselsheim; in Ginsheim ward <mark>gefüttert; "
+                        + "<mark class=\"class-5 level-0\">der <mark class=\"class-2 level-1\">"
+                        + "Ort ist sehr zerschossen; dann</mark> über die Schiffbrücke</mark></mark> "
+                        + "auf die Nonnenaue, wo viele Bäume niedergehauen lagen, sofort auf dem "
+                        + "zweiten Teil der Schiffbrücke über den größern Arm des Rheins.</span>"
+                        + "<span class=\"context-right\"></span>",
+                node.at("/snippet").asText());
+        assertEquals("match-GOE/AGA/01784-p36-46(5)37-45(2)38-42",
+                node.at("/matchID").asText());
+        assertEquals("Belagerung von Mainz", node.at("/title").asText());
+    };
+
+    @Test
+    public void testMatchInfoGet2 () throws KustvaktException {
+        Response response = target().path(API_VERSION)
+
+                .path("corpus/GOE/AGA/01784/p36-46/matchInfo")
+                .queryParam("foundry", "*")
+                .request()
+                .get();
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String ent = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(ent);
+        assertNotNull(node);
+        assertEquals("GOE/AGA/01784", node.at("/textSigle").asText());
+        assertEquals("Belagerung von Mainz", node.at("/title").asText());
+    };
+    
+//  EM: The API is disabled
+    @Ignore
+    @Test
+    public void testCollectionQueryParameter () throws KustvaktException {
+        Response response = target().path(API_VERSION).path("query")
+                .queryParam("q", "[orth=das]").queryParam("ql", "poliqarp")
+                .queryParam("fields", "author, docSigle")
+                .queryParam("context", "sentence").queryParam("count", "13")
+                .queryParam("cq", "textClass=Politik & corpus=WPD")
+                .request()
+                .method("GET");
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String query = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(query);
+        assertNotNull(node);
+        assertEquals("orth", node.at("/query/wrap/layer").asText());
+        assertEquals("Politik",
+                node.at("/collection/operands/0/value").asText());
+        assertEquals("WPD", node.at("/collection/operands/1/value").asText());
+
+        response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=das]").queryParam("ql", "poliqarp")
+                .queryParam("fields", "author, docSigle")
+                .queryParam("context", "sentence").queryParam("count", "13")
+                .queryParam("cq", "textClass=Politik & corpus=WPD")
+                .request()
+                .get();
+        // String version =
+        // LucenePackage.get().getImplementationVersion();;
+        // System.out.println("VERSION "+ version);
+        // System.out.println("RESPONSE "+ response);
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        query = response.readEntity(String.class);
+        node = JsonUtils.readTree(query);
+        assertNotNull(node);
+        assertEquals("orth", node.at("/query/wrap/layer").asText());
+        assertEquals("Politik",
+                node.at("/collection/operands/0/value").asText());
+        assertEquals("WPD", node.at("/collection/operands/1/value").asText());
+    }
+
+    @Test
+    public void testTokenRetrieval () throws KustvaktException {
+        Response response =
+                target().path(API_VERSION).path("/corpus/GOE/AGA/01784/p104-105/")
+                        .request()
+                        .method("GET");
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String resp = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(resp);
+        assertTrue(node.at("/hasSnippet").asBoolean());
+        assertFalse(node.at("/hasTokens").asBoolean());
+        assertTrue(node.at("/tokens").isMissingNode());
+        assertEquals(
+            "<span class=\"context-left\"><span class=\"more\"></span></span>"+
+            "<span class=\"match\"><mark>die</mark></span>"+
+            "<span class=\"context-right\"><span class=\"more\"></span></span>",
+            node.at("/snippet").asText());
+        
+        // Tokens
+        response =
+             target().path(API_VERSION).path("/corpus/GOE/AGA/01784/p104-105")
+             .queryParam("show-snippet", "false")
+             .queryParam("show-tokens", "true")
+             .queryParam("expand", "false")
+             .request()
+             .method("GET");
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        resp = response.readEntity(String.class);
+        node = JsonUtils.readTree(resp);
+
+        assertFalse(node.at("/hasSnippet").asBoolean());
+        assertTrue(node.at("/hasTokens").asBoolean());
+        assertTrue(node.at("/snippet").isMissingNode());
+        assertEquals(
+            "die",
+            node.at("/tokens/match/0").asText());
+        assertTrue(node.at("/tokens/match/1").isMissingNode());
+    };
+
+    
+    @Test
+    public void testMetaFields () throws KustvaktException {
+        Response response =
+                target().path(API_VERSION).path("/corpus/GOE/AGA/01784")
+                        .request()
+                        .method("GET");
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String resp = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(resp);
+        // System.err.println(node.toString());
+
+        Iterator<JsonNode> fieldIter = node.at("/document/fields").elements();
+
+        int checkC = 0;
+        while (fieldIter.hasNext()) {
+            JsonNode field = (JsonNode) fieldIter.next();
+
+            String key = field.at("/key").asText();
+
+            assertEquals("koral:field", field.at("/@type").asText());
+
+            switch (key) {
+                case "textSigle":
+                    assertEquals("type:string", field.at("/type").asText());
+                    assertEquals("GOE/AGA/01784", field.at("/value").asText());
+                    checkC++;
+                    break;
+                case "author":
+                    assertEquals("type:text", field.at("/type").asText());
+                    assertEquals("Goethe, Johann Wolfgang von",
+                            field.at("/value").asText());
+                    checkC++;
+                    break;
+                case "docSigle":
+                    assertEquals("type:string", field.at("/type").asText());
+                    assertEquals("GOE/AGA", field.at("/value").asText());
+                    checkC++;
+                    break;
+                case "docTitle":
+                    assertEquals("type:text", field.at("/type").asText());
+                    assertEquals(
+                            "Goethe: Autobiographische Schriften II, (1817-1825, 1832)",
+                            field.at("/value").asText());
+                    checkC++;
+                    break;
+                case "pubDate":
+                    assertEquals("type:date", field.at("/type").asText());
+                    assertEquals(1982, field.at("/value").asInt());
+                    checkC++;
+                    break;
+            };
+        };
+        assertEquals(5, checkC);
+    };
+
+    @Test
+    public void testSearchWithoutVersion () throws KustvaktException {
+        Response response = target().path("api").path("search")
+                .queryParam("q", "[orth=der]").queryParam("ql", "poliqarp")
+                .request()
+                .accept(MediaType.APPLICATION_JSON).get();
+        assertEquals(HttpStatus.PERMANENT_REDIRECT_308, response.getStatus());
+        URI location = response.getLocation();
+        assertEquals("/api/v1.0/search", location.getPath());
+    }
+
+    @Test
+    public void testSearchWrongVersion () throws KustvaktException {
+        Response response = target().path("api").path("v0.2")
+                .path("search").queryParam("q", "[orth=der]")
+                .queryParam("ql", "poliqarp")
+                .request()
+                .accept(MediaType.APPLICATION_JSON)
+                .get();
+        assertEquals(HttpStatus.PERMANENT_REDIRECT_308, response.getStatus());
+        URI location = response.getLocation();
+        assertEquals("/api/v1.0/search", location.getPath());
+    }
+
+    @Test
+    public void testSearchWithIP () throws KustvaktException {
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "Wasser").queryParam("ql", "poliqarp")
+                .request()
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .get();
+
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertTrue(node.at("/collection").isMissingNode());
+    }
+
+    @Test
+    public void testSearchWithAuthorizationHeader () throws KustvaktException {
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "Wasser").queryParam("ql", "poliqarp")
+                .request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue("test", "pwd"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .get();
+
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertTrue(node.at("/collection").isMissingNode());
+    }
+    
+    @Test
+    public void testSearchPublicMetadata () throws KustvaktException {
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=das]").queryParam("ql", "poliqarp")
+                .queryParam("access-rewrite-disabled", "true")
+                .request()
+                .get();
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String query = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(query);
+
+        assertTrue(node.at("/matches/0/snippet").isMissingNode());
+    }
+    
+    @Test
+    public void testSearchPublicMetadataWithCustomFields () throws KustvaktException {
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "Sonne").queryParam("ql", "poliqarp")
+                .queryParam("fields", "author,title")
+                .queryParam("access-rewrite-disabled", "true")
+                .request()
+                .get();
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        
+        assertTrue(node.at("/matches/0/snippet").isMissingNode());
+        assertEquals("Goethe, Johann Wolfgang von",
+                node.at("/matches/0/author").asText());
+        assertEquals("Italienische Reise",
+                node.at("/matches/0/title").asText());
+//        assertEquals(3, node.at("/matches/0").size());
+    }
+    
+    @Test
+    public void testSearchPublicMetadataWithNonPublicField () throws KustvaktException {
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "Sonne").queryParam("ql", "poliqarp")
+                .queryParam("fields", "author,title,snippet")
+                .queryParam("access-rewrite-disabled", "true")
+                .request()
+                .get();
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+
+        assertEquals(StatusCodes.NON_PUBLIC_FIELD_IGNORED,
+                node.at("/warnings/0/0").asInt());
+        assertEquals("The requested non public fields are ignored",
+                node.at("/warnings/0/1").asText());
+        assertEquals("snippet",
+                node.at("/warnings/0/2").asText());
+    }
+    
+    @Test
+    public void testSearchWithInvalidPage () throws KustvaktException {
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=die]").queryParam("ql", "poliqarp")
+                .queryParam("page", "0")
+                .request()
+                .get();
+        assertEquals(Status.BAD_REQUEST.getStatusCode(),
+                response.getStatus());
+        String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(StatusCodes.INVALID_ARGUMENT, node.at("/errors/0/0").asInt());
+        assertEquals("page must start from 1",node.at("/errors/0/1").asText());
+    }
+    
+    @Test
+    public void testCloseIndex () throws IOException, KustvaktException {
+        searchKrill.getStatistics(null);
+        assertEquals(true, searchKrill.getIndex().isReaderOpen());
+
+        Form form = new Form();
+        form.param("token", "secret");
+
+        Response response = target().path(API_VERSION).path("index")
+                .path("close")
+                .request()
+                .post(Entity.form(form));
+
+        assertEquals(HttpStatus.OK_200, response.getStatus());
+        assertEquals(false, searchKrill.getIndex().isReaderOpen());
+    }
+}
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/lite/LiteSearchPipeTest.java b/full/src/test/java/de/ids_mannheim/korap/web/lite/LiteSearchPipeTest.java
new file mode 100644
index 0000000..80c52ca
--- /dev/null
+++ b/full/src/test/java/de/ids_mannheim/korap/web/lite/LiteSearchPipeTest.java
@@ -0,0 +1,382 @@
+package de.ids_mannheim.korap.web.lite;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockserver.integration.ClientAndServer.startClientAndServer;
+import static org.mockserver.model.HttpRequest.request;
+import static org.mockserver.model.HttpResponse.response;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockserver.client.MockServerClient;
+import org.mockserver.integration.ClientAndServer;
+import org.mockserver.model.Header;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import de.ids_mannheim.korap.config.LiteJerseyTest;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.utils.JsonUtils;
+
+public class LiteSearchPipeTest extends LiteJerseyTest {
+
+    private ClientAndServer mockServer;
+    private MockServerClient mockClient;
+
+    private int port = 6070;
+    private String pipeJson, pipeWithParamJson;
+    private String glemmUri = "http://localhost:"+port+"/glemm";
+
+    public LiteSearchPipeTest () throws IOException{
+        pipeJson = IOUtils.toString(
+                ClassLoader.getSystemResourceAsStream(
+                        "pipe-output/test-pipes.jsonld"),
+                StandardCharsets.UTF_8);
+
+        pipeWithParamJson = IOUtils.toString(
+                ClassLoader.getSystemResourceAsStream(
+                        "pipe-output/with-param.jsonld"),
+                StandardCharsets.UTF_8);
+    }
+
+    @Before
+    public void startMockServer () {
+        mockServer = startClientAndServer(port);
+        mockClient = new MockServerClient("localhost", mockServer.getPort());
+    }
+
+    @After
+    public void stopMockServer () {
+        mockServer.stop();
+    }
+
+    @Test
+    public void testMockServer () throws IOException {
+        mockClient.reset()
+                .when(request().withMethod("POST").withPath("/test")
+                        .withHeader(new Header("Content-Type",
+                                "application/json; charset=utf-8")))
+                .respond(response()
+                        .withHeader(new Header("Content-Type",
+                                "application/json; charset=utf-8"))
+                        .withBody("{test}").withStatusCode(200));
+
+        URL url = new URL("http://localhost:"+port+"/test");
+        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);
+
+        String json = "{\"name\" : \"dory\"}";
+        try (OutputStream os = connection.getOutputStream()) {
+            byte[] input = json.getBytes("utf-8");
+            os.write(input, 0, input.length);
+        }
+
+        assertEquals(200, connection.getResponseCode());
+
+        BufferedReader br = new BufferedReader(
+                new InputStreamReader(connection.getInputStream(), "utf-8"));
+        assertEquals("{test}", br.readLine());
+
+    }
+
+    @Test
+    public void testSearchWithPipes ()
+            throws IOException, KustvaktException, URISyntaxException {
+        mockClient.reset()
+                .when(request().withMethod("POST").withPath("/glemm")
+                        .withHeaders(
+                                new Header("Content-Type",
+                                        "application/json; charset=utf-8"),
+                                new Header("Accept", "application/json")))
+                .respond(response()
+                        .withHeader(new Header("Content-Type",
+                                "application/json; charset=utf-8"))
+                        .withBody(pipeJson).withStatusCode(200));
+
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=der]").queryParam("ql", "poliqarp")
+                .queryParam("pipes", glemmUri)
+                .request()
+                .get();
+
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+
+        String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(2, node.at("/query/wrap/key").size());
+
+        node = node.at("/query/wrap/rewrites");
+        assertEquals(2, node.size());
+        assertEquals("Glemm", node.at("/0/src").asText());
+        assertEquals("operation:override", node.at("/0/operation").asText());
+        assertEquals("key", node.at("/0/scope").asText());
+
+        assertEquals("Kustvakt", node.at("/1/src").asText());
+        assertEquals("operation:injection", node.at("/1/operation").asText());
+        assertEquals("foundry", node.at("/1/scope").asText());
+    }
+
+    @Test
+    public void testSearchWithUrlEncodedPipes ()
+            throws IOException, KustvaktException {
+
+        mockClient.reset()
+                .when(request().withMethod("POST").withPath("/glemm")
+                        .withHeaders(
+                                new Header("Content-Type",
+                                        "application/json; charset=utf-8"),
+                                new Header("Accept", "application/json")))
+                .respond(response()
+                        .withHeader(new Header("Content-Type",
+                                "application/json; charset=utf-8"))
+                        .withBody(pipeJson).withStatusCode(200));
+
+        glemmUri = URLEncoder.encode(glemmUri, "utf-8");
+
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=der]").queryParam("ql", "poliqarp")
+                .queryParam("pipes", glemmUri)
+                .request()
+                .get();
+
+        String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(2, node.at("/query/wrap/key").size());
+    }
+
+    @Test
+    public void testSearchWithMultiplePipes () throws KustvaktException {
+
+        mockClient.reset()
+                .when(request().withMethod("POST").withPath("/glemm")
+                        .withQueryStringParameter("param").withHeaders(
+                                new Header("Content-Type",
+                                        "application/json; charset=utf-8"),
+                                new Header("Accept", "application/json")))
+                .respond(response()
+                        .withHeader(new Header("Content-Type",
+                                "application/json; charset=utf-8"))
+                        .withBody(pipeWithParamJson).withStatusCode(200));
+
+        String glemmUri2 = glemmUri + "?param=blah";
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=der]").queryParam("ql", "poliqarp")
+                .queryParam("pipes", glemmUri + "," + glemmUri2)
+                .request()
+                .get();
+
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+
+        String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(3, node.at("/query/wrap/key").size());
+    }
+
+    @Test
+    public void testSearchWithUnknownURL ()
+            throws IOException, KustvaktException {
+        String url =
+                target().getUri().toString() + API_VERSION + "/test/tralala";
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=der]").queryParam("ql", "poliqarp")
+                .queryParam("pipes", url)
+                .request()
+                .get();
+
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String entity = response.readEntity(String.class);
+
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(StatusCodes.PIPE_FAILED, node.at("/warnings/0/0").asInt());
+        assertEquals("404 Not Found", node.at("/warnings/0/3").asText());
+    }
+
+    @Test
+    public void testSearchWithUnknownHost () throws KustvaktException {
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=der]").queryParam("ql", "poliqarp")
+                .queryParam("pipes", "http://glemm")
+                .request()
+                .get();
+
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+
+        String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+
+        assertEquals(StatusCodes.PIPE_FAILED, node.at("/warnings/0/0").asInt());
+        assertEquals("glemm", node.at("/warnings/0/3").asText());
+    }
+
+    @Test
+    public void testSearchUnsupportedMediaType () throws KustvaktException {
+        mockClient.reset()
+                .when(request().withMethod("POST").withPath("/non-json-pipe"))
+                .respond(response().withStatusCode(415));
+
+        String pipeUri = "http://localhost:"+port+"/non-json-pipe";
+
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=der]").queryParam("ql", "poliqarp")
+                .queryParam("pipes", pipeUri)
+                .request()
+                .get();
+
+        String entity = response.readEntity(String.class);
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(StatusCodes.PIPE_FAILED, node.at("/warnings/0/0").asInt());
+        assertEquals("415 Unsupported Media Type",
+                node.at("/warnings/0/3").asText());
+    }
+
+    @Test
+    public void testSearchWithMultiplePipeWarnings () throws KustvaktException {
+        String url =
+                target().getUri().toString() + API_VERSION + "/test/tralala";
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=der]").queryParam("ql", "poliqarp")
+                .queryParam("pipes", url + "," + "http://glemm")
+                .request()
+                .get();
+
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+
+        String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+
+        assertEquals(2, node.at("/warnings").size());
+        assertEquals(StatusCodes.PIPE_FAILED, node.at("/warnings/0/0").asInt());
+        assertEquals(url, node.at("/warnings/0/2").asText());
+        assertEquals("404 Not Found", node.at("/warnings/0/3").asText());
+
+        assertEquals(StatusCodes.PIPE_FAILED, node.at("/warnings/1/0").asInt());
+        assertEquals("http://glemm", node.at("/warnings/1/2").asText());
+        assertEquals("glemm", node.at("/warnings/1/3").asText());
+
+    }
+
+    @Test
+    public void testSearchWithInvalidJsonResponse () throws KustvaktException {
+        mockClient.reset()
+                .when(request().withMethod("POST").withPath("/invalid-response")
+                        .withHeaders(
+                                new Header("Content-Type",
+                                        "application/json; charset=utf-8"),
+                                new Header("Accept", "application/json")))
+                .respond(response().withBody("{blah:}").withStatusCode(200)
+                        .withHeaders(new Header("Content-Type",
+                                "application/json; charset=utf-8")));
+
+        String pipeUri = "http://localhost:"+port+"/invalid-response";
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=der]").queryParam("ql", "poliqarp")
+                .queryParam("pipes", pipeUri)
+                .request()
+                .get();
+
+        String entity = response.readEntity(String.class);
+        assertEquals(Status.BAD_REQUEST.getStatusCode(),
+                response.getStatus());
+
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(StatusCodes.DESERIALIZATION_FAILED,
+                node.at("/errors/0/0").asInt());
+    }
+
+    @Test
+    public void testSearchWithPlainTextResponse () throws KustvaktException {
+        mockClient.reset()
+                .when(request().withMethod("POST").withPath("/plain-text")
+                        .withHeaders(
+                                new Header("Content-Type",
+                                        "application/json; charset=utf-8"),
+                                new Header("Accept", "application/json")))
+                .respond(response().withBody("blah").withStatusCode(200));
+
+        String pipeUri = "http://localhost:"+port+"/plain-text";
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=der]").queryParam("ql", "poliqarp")
+                .queryParam("pipes", pipeUri)
+                .request()
+                .get();
+
+        String entity = response.readEntity(String.class);
+        assertEquals(Status.BAD_REQUEST.getStatusCode(),
+                response.getStatus());
+
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(StatusCodes.DESERIALIZATION_FAILED,
+                node.at("/errors/0/0").asInt());
+    }
+
+    @Test
+    public void testSearchWithMultipleAndUnknownPipes ()
+            throws KustvaktException {
+
+        mockClient.reset()
+                .when(request().withMethod("POST").withPath("/glemm")
+                        .withHeaders(
+                                new Header("Content-Type",
+                                        "application/json; charset=utf-8"),
+                                new Header("Accept", "application/json")))
+                .respond(response()
+                        .withHeader(new Header("Content-Type",
+                                "application/json; charset=utf-8"))
+                        .withBody(pipeJson).withStatusCode(200));
+
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=der]").queryParam("ql", "poliqarp")
+                .queryParam("pipes", "http://unknown" + "," + glemmUri)
+                .request()
+                .get();
+
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+
+        String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(2, node.at("/query/wrap/key").size());
+        assertTrue(node.at("/warnings").isMissingNode());
+
+        response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=der]").queryParam("ql", "poliqarp")
+                .queryParam("pipes", glemmUri + ",http://unknown")
+                .request()
+                .get();
+
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+
+        entity = response.readEntity(String.class);
+        node = JsonUtils.readTree(entity);
+        assertEquals(StatusCodes.PIPE_FAILED, node.at("/warnings/0/0").asInt());
+    }
+}
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/lite/LiteSearchTokenSnippetTest.java b/full/src/test/java/de/ids_mannheim/korap/web/lite/LiteSearchTokenSnippetTest.java
new file mode 100644
index 0000000..5a521f8
--- /dev/null
+++ b/full/src/test/java/de/ids_mannheim/korap/web/lite/LiteSearchTokenSnippetTest.java
@@ -0,0 +1,83 @@
+package de.ids_mannheim.korap.web.lite;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import de.ids_mannheim.korap.config.LiteJerseyTest;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.utils.JsonUtils;
+
+public class LiteSearchTokenSnippetTest extends LiteJerseyTest{
+
+    @Test
+    public void testSearchWithTokens () throws KustvaktException {
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=das]").queryParam("ql", "poliqarp")
+                .queryParam("show-tokens", "true")
+                .queryParam("context", "sentence").queryParam("count", "13")
+                .request()
+                .get();
+
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String ent = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(ent);
+
+        assertTrue(node.at("/matches/0/hasSnippet").asBoolean());
+        assertTrue(node.at("/matches/0/hasTokens").asBoolean());
+        assertTrue(node.at("/matches/0/tokens/left").size()>0);
+        assertTrue(node.at("/matches/0/tokens/right").size()>0);
+        assertEquals(1, node.at("/matches/0/tokens/match").size());
+    }
+    
+    @Test
+    public void testSearchWithoutTokens () throws KustvaktException {
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=das]").queryParam("ql", "poliqarp")
+                .queryParam("show-tokens", "false")
+                .queryParam("context", "sentence").queryParam("count", "13")
+                .request()
+                .get();
+
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String ent = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(ent);
+
+        assertTrue(node.at("/matches/0/hasSnippet").asBoolean());
+        assertFalse(node.at("/matches/0/hasTokens").asBoolean());
+        assertTrue(node.at("/matches/0/tokens").isMissingNode());
+    }
+    
+    @Test
+    public void testSearchPublicMetadataWithTokens () throws KustvaktException {
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=das]").queryParam("ql", "poliqarp")
+                .queryParam("access-rewrite-disabled", "true")
+                .queryParam("show-tokens", "true")
+                .queryParam("context", "sentence").queryParam("count", "13")
+                .request()
+                .get();
+
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String ent = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(ent);
+
+        assertFalse(node.at("/matches/0/hasSnippet").asBoolean());
+        assertFalse(node.at("/matches/0/hasTokens").asBoolean());
+        assertTrue(node.at("/matches/0/snippet").isMissingNode());
+        assertTrue(node.at("/matches/0/tokens").isMissingNode());
+        
+        assertEquals(StatusCodes.NOT_ALLOWED, node.at("/warnings/0/0").asInt());
+    }
+}
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/lite/LiteStatisticControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/lite/LiteStatisticControllerTest.java
new file mode 100644
index 0000000..b116e1e
--- /dev/null
+++ b/full/src/test/java/de/ids_mannheim/korap/web/lite/LiteStatisticControllerTest.java
@@ -0,0 +1,206 @@
+package de.ids_mannheim.korap.web.lite;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.junit.Test;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import de.ids_mannheim.korap.config.LiteJerseyTest;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.utils.JsonUtils;
+
+public class LiteStatisticControllerTest extends LiteJerseyTest{
+
+    @Test
+    public void testStatisticsWithCq () throws KustvaktException{
+        Response response = target().path(API_VERSION)
+                .path("statistics")
+                .queryParam("cq", "textType=Abhandlung & corpusSigle=GOE")
+                .request()
+                .method("GET");
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+
+        assertEquals(
+            "Wes8Bd4h1OypPqbWF5njeQ==",
+            response.getHeaders().getFirst("X-Index-Revision")
+            );
+        
+        String query = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(query);
+        assertEquals(2, node.at("/documents").asInt());
+        assertEquals(138180, node.at("/tokens").asInt());
+        assertEquals(5687, node.at("/sentences").asInt());
+        assertEquals(258, node.at("/paragraphs").asInt());
+        
+        assertTrue(node.at("/warnings").isMissingNode());
+    }
+    
+    @Test
+    public void testStatisticsWithCqAndCorpusQuery () throws KustvaktException{
+        Response response = target().path(API_VERSION)
+                .path("statistics")
+                .queryParam("cq", "textType=Abhandlung & corpusSigle=GOE")
+                .queryParam("corpusQuery", "textType=Autobiographie & corpusSigle=GOE")
+                .request()
+                .method("GET");
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String query = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(query);
+        assertEquals(2, node.at("/documents").asInt());
+        assertEquals(138180, node.at("/tokens").asInt());
+        assertEquals(5687, node.at("/sentences").asInt());
+        assertEquals(258, node.at("/paragraphs").asInt());
+        
+        assertTrue(node.at("/warnings").isMissingNode());
+    }
+    
+    @Test
+    public void testStatisticsWithCorpusQuery () throws KustvaktException{
+        Response response = target().path(API_VERSION)
+                .path("statistics")
+                .queryParam("corpusQuery", "textType=Autobiographie & corpusSigle=GOE")
+                .request()
+                .method("GET");
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String query = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(query);
+        assertEquals(9, node.at("/documents").asInt());
+        assertEquals(527662, node.at("/tokens").asInt());
+        assertEquals(19387, node.at("/sentences").asInt());
+        assertEquals(514, node.at("/paragraphs").asInt());
+        
+        assertEquals(StatusCodes.DEPRECATED,
+                node.at("/warnings/0/0").asInt());
+        assertEquals("Parameter corpusQuery is deprecated in favor of cq.",
+                node.at("/warnings/0/1").asText());
+    }
+
+    @Test
+    public void testEmptyStatistics () throws KustvaktException{
+        Response response = target().path(API_VERSION)
+            .path("statistics")
+            .queryParam("corpusQuery", "")
+            .request()
+            .method("GET");
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        String query = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(query);
+        assertEquals(11, node.at("/documents").asInt());
+        assertEquals(665842, node.at("/tokens").asInt());
+        assertEquals(25074, node.at("/sentences").asInt());
+        assertEquals(772, node.at("/paragraphs").asInt());
+
+        response = target().path(API_VERSION)
+                .path("statistics")
+                .request()
+                .method("GET");
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        query = response.readEntity(String.class);
+        node = JsonUtils.readTree(query);
+        assertEquals(11, node.at("/documents").asInt());
+        assertEquals(665842, node.at("/tokens").asInt());
+        assertEquals(25074, node.at("/sentences").asInt());
+        assertEquals(772, node.at("/paragraphs").asInt());
+    }
+    
+    @Test
+    public void testGetStatisticsWithKoralQuery ()
+            throws IOException, KustvaktException {
+        Response response = target().path(API_VERSION)
+                .path("statistics")
+                .request()
+                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
+                .post(Entity.json("{ \"collection\" : {\"@type\": "
+                        + "\"koral:doc\", \"key\": \"availability\", \"match\": "
+                        + "\"match:eq\", \"type\": \"type:regex\", \"value\": "
+                        + "\"CC-BY.*\"} }"));
+
+        assertEquals(Status.OK.getStatusCode(),
+                     response.getStatus());
+        String ent = response.readEntity(String.class);
+
+        assertEquals(
+            "Wes8Bd4h1OypPqbWF5njeQ==",
+            response.getHeaders().getFirst("X-Index-Revision")
+            );
+        
+        JsonNode node = JsonUtils.readTree(ent);
+        assertEquals(2, node.at("/documents").asInt());
+        assertEquals(72770, node.at("/tokens").asInt());
+        assertEquals(2985, node.at("/sentences").asInt());
+        assertEquals(128, node.at("/paragraphs").asInt());
+    }
+    
+    @Test
+    public void testGetStatisticsWithEmptyCollection ()
+            throws IOException, KustvaktException {
+        Response response = target().path(API_VERSION)
+                .path("statistics")
+                .request()
+                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
+                .post(Entity.json("{}"));
+
+        assertEquals(Status.BAD_REQUEST.getStatusCode(),
+                     response.getStatus());
+        String ent = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(ent);
+        assertEquals(node.at("/errors/0/0").asInt(),
+                de.ids_mannheim.korap.util.StatusCodes.MISSING_COLLECTION);
+        assertEquals(node.at("/errors/0/1").asText(),
+                "Collection is not found");
+    }
+    
+    @Test
+    public void testGetStatisticsWithIncorrectJson ()
+            throws IOException, KustvaktException {
+        Response response = target().path(API_VERSION)
+                .path("statistics")
+                .request()
+                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
+                .post(Entity.json("{ \"collection\" : }"));
+
+        assertEquals(Status.BAD_REQUEST.getStatusCode(),
+                     response.getStatus());
+        String ent = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(ent);
+        assertEquals(StatusCodes.DESERIALIZATION_FAILED,
+                node.at("/errors/0/0").asInt());
+        assertEquals("Failed deserializing json object: { \"collection\" : }",
+                node.at("/errors/0/1").asText());
+    }
+    
+    @Test
+    public void testGetStatisticsWithoutKoralQuery ()
+            throws IOException, KustvaktException {
+        Response response = target().path(API_VERSION)
+                .path("statistics")
+                .request()
+                .post(Entity.json(""));
+        
+        String ent = response.readEntity(String.class);
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+        
+        JsonNode node = JsonUtils.readTree(ent);
+        assertEquals(11, node.at("/documents").asInt());
+        assertEquals(665842, node.at("/tokens").asInt());
+        assertEquals(25074, node.at("/sentences").asInt());
+        assertEquals(772, node.at("/paragraphs").asInt());
+    }
+}
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/lite/SearchNetworkEndpointTest.java b/full/src/test/java/de/ids_mannheim/korap/web/lite/SearchNetworkEndpointTest.java
new file mode 100644
index 0000000..f90dde0
--- /dev/null
+++ b/full/src/test/java/de/ids_mannheim/korap/web/lite/SearchNetworkEndpointTest.java
@@ -0,0 +1,130 @@
+package de.ids_mannheim.korap.web.lite;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockserver.integration.ClientAndServer.startClientAndServer;
+import static org.mockserver.model.HttpRequest.request;
+import static org.mockserver.model.HttpResponse.response;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockserver.client.MockServerClient;
+import org.mockserver.integration.ClientAndServer;
+import org.mockserver.model.Header;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.config.LiteJerseyTest;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.utils.JsonUtils;
+
+public class SearchNetworkEndpointTest extends LiteJerseyTest {
+
+    @Autowired
+    private KustvaktConfiguration config;
+
+    private ClientAndServer mockServer;
+    private MockServerClient mockClient;
+
+    private int port = 6080;
+    private String searchResult;
+    private String endpointURL = "http://localhost:"+port+"/searchEndpoint";
+
+    public SearchNetworkEndpointTest () throws IOException {
+        searchResult = IOUtils.toString(
+                ClassLoader.getSystemResourceAsStream(
+                        "network-output/search-result.jsonld"),
+                StandardCharsets.UTF_8);
+    }
+
+
+    @Before
+    public void startMockServer () {
+        mockServer = startClientAndServer(port);
+        mockClient = new MockServerClient("localhost", mockServer.getPort());
+    }
+
+
+    @After
+    public void stopMockServer () {
+        mockServer.stop();
+    }
+
+
+    @Test
+    public void testSearchNetwork ()
+            throws IOException, KustvaktException, URISyntaxException {
+        config.setNetworkEndpointURL(endpointURL);
+        mockClient.reset()
+                .when(request().withMethod("POST").withPath("/searchEndpoint")
+                        .withHeaders(
+                                new Header("Content-Type",
+                                        "application/json; charset=utf-8"),
+                                new Header("Accept", "application/json")))
+                .respond(response()
+                        .withHeader(new Header("Content-Type",
+                                "application/json; charset=utf-8"))
+                        .withBody(searchResult).withStatusCode(200));
+
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=der]").queryParam("ql", "poliqarp")
+                .queryParam("engine", "network")
+                .request()
+                .get();
+
+        assertEquals(Status.OK.getStatusCode(),
+                response.getStatus());
+
+        String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+
+        assertEquals(2, node.at("/matches").size());
+    }
+
+
+    @Test
+    public void testSearchWithUnknownURL ()
+            throws IOException, KustvaktException {
+        config.setNetworkEndpointURL("http://localhost:1040/search");
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=der]").queryParam("ql", "poliqarp")
+                .queryParam("engine", "network")
+                .request()
+                .get();
+        
+        String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(StatusCodes.SEARCH_NETWORK_ENDPOINT_FAILED,
+                node.at("/errors/0/0").asInt());
+        assertEquals(Status.BAD_REQUEST.getStatusCode(),
+                response.getStatus());
+    }
+    
+    @Test
+    public void testSearchWithUnknownHost () throws KustvaktException {
+        config.setNetworkEndpointURL("http://search.com");
+        
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=der]").queryParam("ql", "poliqarp")
+                .queryParam("engine", "network")
+                .request()
+                .get();
+
+        String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(StatusCodes.SEARCH_NETWORK_ENDPOINT_FAILED,
+                node.at("/errors/0/0").asInt());
+        assertEquals(Status.BAD_REQUEST.getStatusCode(),
+                response.getStatus());
+    }
+}
diff --git a/full/src/test/resources/test-config-lite.xml b/full/src/test/resources/test-config-lite.xml
new file mode 100644
index 0000000..46f46a2
--- /dev/null
+++ b/full/src/test/resources/test-config-lite.xml
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
+	xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans
+           http://www.springframework.org/schema/beans/spring-beans.xsd
+           http://www.springframework.org/schema/tx
+           http://www.springframework.org/schema/tx/spring-tx.xsd
+           http://www.springframework.org/schema/context
+           http://www.springframework.org/schema/context/spring-context.xsd
+           http://www.springframework.org/schema/util
+           http://www.springframework.org/schema/util/spring-util.xsd">
+	
+	<context:component-scan base-package="
+		de.ids_mannheim.korap.core.service,
+		de.ids_mannheim.korap.core.web,
+		de.ids_mannheim.korap.web.filter, 
+		de.ids_mannheim.korap.web.utils,
+		de.ids_mannheim.korap.authentication.http" />
+	<context:annotation-config />
+
+	<bean id="placeholders"
+		class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
+		<property name="ignoreResourceNotFound" value="true" />
+		<property name="locations">
+			<array>
+				<value>classpath:test-jdbc.properties</value>
+				<value>file:./test-jdbc.properties</value>
+				<value>classpath:test-hibernate.properties</value>
+				<value>classpath:kustvakt-lite.conf</value>
+				<value>file:./kustvakt-lite.conf</value>
+			</array>
+		</property>
+	</bean>
+
+	<bean id="properties"
+		class="org.springframework.beans.factory.config.PropertiesFactoryBean">
+		<property name="ignoreResourceNotFound" value="true" />
+		<property name="locations">
+			<array>
+				<value>classpath:kustvakt-lite.conf</value>
+				<value>file:./kustvakt-lite.conf</value>
+			</array>
+		</property>
+	</bean>
+
+	<bean id="config" class="de.ids_mannheim.korap.config.KustvaktConfiguration">
+		<constructor-arg index="0" name="properties" ref="properties" />
+	</bean>
+
+	<!-- Database -->
+
+	<bean id="sqliteDataSource"
+		class="org.springframework.jdbc.datasource.SingleConnectionDataSource"
+		lazy-init="true">
+		<property name="driverClassName" value="${jdbc.driverClassName}" />
+		<property name="url" value="${jdbc.url}" />
+		<property name="username" value="${jdbc.username}" />
+		<property name="password" value="${jdbc.password}" />
+		<property name="connectionProperties">
+			<props>
+				<prop key="date_string_format">yyyy-MM-dd HH:mm:ss</prop>
+			</props>
+		</property>
+
+		<!-- relevant for single connection datasource and sqlite -->
+		<property name="suppressClose">
+			<value>true</value>
+		</property>
+	</bean>
+
+	<bean id="flywayConfig" class="org.flywaydb.core.api.configuration.ClassicConfiguration">
+	    <property name="baselineOnMigrate" value="true" />
+		<!-- <property name="validateOnMigrate" value="false" /> -->
+		<!-- <property name="cleanOnValidationError" value="true" /> -->
+		<property name="locations" value="#{'${jdbc.schemaPath}'.split(',')}"/>
+		<property name="dataSource" ref="sqliteDataSource" />
+		<property name="outOfOrder" value="true" />
+	</bean>
+	
+	<bean id="flyway" class="org.flywaydb.core.Flyway" init-method="migrate">
+	    <constructor-arg ref="flywayConfig"/>
+	</bean>
+
+	<bean id="entityManagerFactory"
+		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
+		<property name="dataSource" ref="sqliteDataSource" />
+
+		<property name="packagesToScan">
+			<array>
+				<value>de.ids_mannheim.korap.core.entity</value>
+			</array>
+		</property>
+		<property name="jpaVendorAdapter">
+			<bean id="jpaVendorAdapter"
+				class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
+				<property name="databasePlatform" value="${hibernate.dialect}" />
+			</bean>
+		</property>
+		<property name="jpaProperties">
+			<props>
+				<prop key="hibernate.dialect">${hibernate.dialect}</prop>
+				<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
+				<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
+				<prop key="hibernate.cache.use_query_cache">${hibernate.cache.use_query_cache}</prop>
+				<prop key="hibernate.cache.use_second_level_cache">${hibernate.cache.use_second_level_cache}
+				</prop>
+				<prop key="hibernate.cache.provider_class">${hibernate.cache.provider}</prop>
+				<prop key="hibernate.cache.region.factory_class">${hibernate.cache.region.factory}</prop>
+				<prop key="hibernate.jdbc.time_zone">${hibernate.jdbc.time_zone}</prop>
+			</props>
+		</property>
+	</bean>
+	<tx:annotation-driven proxy-target-class="true"
+		transaction-manager="transactionManager" />
+
+	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
+		<property name="entityManagerFactory" ref="entityManagerFactory" />
+	</bean>
+
+	<bean id="transactionTemplate"
+		class="org.springframework.transaction.support.TransactionTemplate">
+		<constructor-arg ref="transactionManager" />
+	</bean>
+	<bean id="txManager"
+		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
+		<property name="dataSource" ref="sqliteDataSource" />
+	</bean>
+
+	<!-- Search Engine -->
+	<bean id="search_krill" class="de.ids_mannheim.korap.web.SearchKrill">
+		<constructor-arg value="${krill.indexDir}" />
+	</bean>
+	
+
+	<!-- Filters -->
+	<!-- <bean id="APIVersionFilter" class="de.ids_mannheim.korap.web.APIVersionFilter" 
+		scope="singleton" /> -->
+	<!-- Authentication -->
+	<bean id="authenticationManager"
+		class="de.ids_mannheim.korap.authentication.DummyAuthenticationManager" />
+
+	<!-- Response handler -->
+	<bean id="kustvaktResponseHandler" class="de.ids_mannheim.korap.web.KustvaktResponseHandler">
+		<constructor-arg index="0" name="iface" ref="kustvakt_auditing" />
+	</bean>
+
+	<!-- Controllers -->
+	<!-- added via component-scan 
+	<bean id="annotationController"
+		class="de.ids_mannheim.korap.web.controller.AnnotationController" />
+	<bean id="searchController" class="de.ids_mannheim.korap.web.controller.SearchController" />
+	<bean id="statisticController"
+		class="de.ids_mannheim.korap.web.controller.StatisticController" />
+ -->
+	<!-- Services -->
+	<bean id="scopeService"
+		class="de.ids_mannheim.korap.oauth2.service.DummyOAuth2ScopeServiceImpl" />
+
+	<!-- DAO -->
+	<bean id="adminDao" class="de.ids_mannheim.korap.dao.DummyAdminDaoImpl" />
+	<bean id="annotationDao" class="de.ids_mannheim.korap.dao.AnnotationDao" />
+
+	<!-- DTO Converter -->
+	<bean id="annotationConverter" class="de.ids_mannheim.korap.dto.converter.AnnotationConverter" />
+
+	<!-- Rewrite -->
+	<bean id="layerMapper" class="de.ids_mannheim.korap.rewrite.LayerMapper" />
+	<bean id="foundryInject" class="de.ids_mannheim.korap.rewrite.FoundryInject" />
+
+	<util:list id="rewriteTasks" value-type="de.ids_mannheim.korap.rewrite.RewriteTask">
+		<ref bean="foundryInject" />
+	</util:list>
+
+	<bean id="rewriteHandler" class="de.ids_mannheim.korap.rewrite.RewriteHandler">
+		<constructor-arg ref="rewriteTasks" />
+	</bean>
+
+
+
+
+	<bean id="kustvakt_auditing"
+		class="de.ids_mannheim.korap.interfaces.defaults.DefaultAuditing">
+	</bean>
+
+</beans>
\ No newline at end of file