Importing WformServices and GlemmServices

Change-Id: Ifa95576d69e0d3863f63d3fdedb48c2c21cf64bc
diff --git a/WformServices/.classpath b/WformServices/.classpath
new file mode 100644
index 0000000..ec33d97
--- /dev/null
+++ b/WformServices/.classpath
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" output="target/classes" path="src">
+		<attributes>
+			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="src" path="json"/>
+	<classpathentry kind="src" path="services"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.jst.server.core.container/org.eclipse.jst.server.tomcat.runtimeTarget/Apache Tomcat v9.0">
+		<attributes>
+			<attribute name="owner.project.facets" value="jst.web"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry combineaccessrules="false" exported="true" kind="src" path="/JsonTraverse"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/GlemmClient"/>
+	<classpathentry kind="lib" path="C:/Users/Franck/.m2/repository/de/korap/json/JsonTraverse/0.1-SNAPSHOT/JsonTraverse-0.1-SNAPSHOT.jar"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/utils"/>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/WformServices/.project b/WformServices/.project
new file mode 100644
index 0000000..856db24
--- /dev/null
+++ b/WformServices/.project
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>wformServices</name>
+	<comment></comment>
+	<projects>
+		<project>utils</project>
+		<project>JsonTraverse</project>
+		<project>GlemmClient</project>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.wst.common.project.facet.core.builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.wst.validation.validationbuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.m2e.core.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.m2e.core.maven2Nature</nature>
+		<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
+		<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
+		<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
+	</natures>
+	<linkedResources>
+		<link>
+			<name>json</name>
+			<type>2</type>
+			<location>C:/myIDS/Daten/KorAP/Services-branch-0.1/JsonTraverse/src/de/korap/json</location>
+		</link>
+		<link>
+			<name>services</name>
+			<type>2</type>
+			<location>C:/myIDS/Daten/KorAP/Services-branch-0.1/GlemmServices/src/de/korap/services</location>
+		</link>
+		<link>
+			<name>src-GlemmServices</name>
+			<type>2</type>
+			<location>C:/myIDS/Daten/KorAP/Services/GlemmServices/src</location>
+		</link>
+	</linkedResources>
+</projectDescription>
diff --git a/WformServices/.settings/.jsdtscope b/WformServices/.settings/.jsdtscope
new file mode 100644
index 0000000..92e666d
--- /dev/null
+++ b/WformServices/.settings/.jsdtscope
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry excluding="**/bower_components/*|**/node_modules/*|**/*.min.js" kind="src" path="WebContent"/>
+	<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.WebProject">
+		<attributes>
+			<attribute name="hide" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.baseBrowserLibrary"/>
+	<classpathentry kind="output" path=""/>
+</classpath>
diff --git a/WformServices/.settings/org.eclipse.jdt.core.prefs b/WformServices/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..4e4a3ad
--- /dev/null
+++ b/WformServices/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,9 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.release=disabled
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/WformServices/.settings/org.eclipse.m2e.core.prefs b/WformServices/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 0000000..f897a7f
--- /dev/null
+++ b/WformServices/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/WformServices/.settings/org.eclipse.wst.common.component b/WformServices/.settings/org.eclipse.wst.common.component
new file mode 100644
index 0000000..a48f70e
--- /dev/null
+++ b/WformServices/.settings/org.eclipse.wst.common.component
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
+    <wb-module deploy-name="wformServices-0.1-SNAPSHOT">
+        <wb-resource deploy-path="/" source-path="/target/m2e-wtp/web-resources"/>
+        <wb-resource deploy-path="/" source-path="/WebContent" tag="defaultRootSource"/>
+        <wb-resource deploy-path="/WEB-INF/classes" source-path="/src"/>
+        <wb-resource deploy-path="/WEB-INF/classes" source-path="/services"/>
+        <wb-resource deploy-path="/WEB-INF/classes" source-path="/json"/>
+        <dependent-module archiveName="utils-0.1-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/utils/utils">
+            <dependency-type>uses</dependency-type>
+        </dependent-module>
+        <dependent-module archiveName="JsonTraverse-0.1-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/JsonTraverse/JsonTraverse">
+            <dependency-type>uses</dependency-type>
+        </dependent-module>
+        <dependent-module archiveName="GlemmClient-0.2-Test.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/GlemmClient/GlemmClient">
+            <dependency-type>uses</dependency-type>
+        </dependent-module>
+        <property name="java-output-path" value="/wformServices/build/classes"/>
+        <property name="context-root" value="wformServices"/>
+    </wb-module>
+</project-modules>
diff --git a/WformServices/.settings/org.eclipse.wst.common.project.facet.core.xml b/WformServices/.settings/org.eclipse.wst.common.project.facet.core.xml
new file mode 100644
index 0000000..24124f7
--- /dev/null
+++ b/WformServices/.settings/org.eclipse.wst.common.project.facet.core.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<faceted-project>
+  <runtime name="Apache Tomcat v9.0"/>
+  <fixed facet="jst.web"/>
+  <fixed facet="wst.jsdt.web"/>
+  <fixed facet="java"/>
+  <installed facet="java" version="1.8"/>
+  <installed facet="jst.web" version="4.0"/>
+  <installed facet="wst.jsdt.web" version="1.0"/>
+</faceted-project>
diff --git a/WformServices/.settings/org.eclipse.wst.jsdt.ui.superType.container b/WformServices/.settings/org.eclipse.wst.jsdt.ui.superType.container
new file mode 100644
index 0000000..3bd5d0a
--- /dev/null
+++ b/WformServices/.settings/org.eclipse.wst.jsdt.ui.superType.container
@@ -0,0 +1 @@
+org.eclipse.wst.jsdt.launching.baseBrowserLibrary
\ No newline at end of file
diff --git a/WformServices/.settings/org.eclipse.wst.jsdt.ui.superType.name b/WformServices/.settings/org.eclipse.wst.jsdt.ui.superType.name
new file mode 100644
index 0000000..05bd71b
--- /dev/null
+++ b/WformServices/.settings/org.eclipse.wst.jsdt.ui.superType.name
@@ -0,0 +1 @@
+Window
\ No newline at end of file
diff --git a/WformServices/.settings/org.eclipse.wst.validation.prefs b/WformServices/.settings/org.eclipse.wst.validation.prefs
new file mode 100644
index 0000000..04cad8c
--- /dev/null
+++ b/WformServices/.settings/org.eclipse.wst.validation.prefs
@@ -0,0 +1,2 @@
+disabled=06target
+eclipse.preferences.version=1
diff --git a/WformServices/WebContent/META-INF/MANIFEST.MF b/WformServices/WebContent/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..254272e
--- /dev/null
+++ b/WformServices/WebContent/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Class-Path: 
+
diff --git a/WformServices/WebContent/WEB-INF/WformServices.properties b/WformServices/WebContent/WEB-INF/WformServices.properties
new file mode 100644
index 0000000..8df4067
--- /dev/null
+++ b/WformServices/WebContent/WEB-INF/WformServices.properties
@@ -0,0 +1,22 @@
+# WordformServices properties:
+# expected in /WEB-INF/.
+
+## General properties:
+GS_WorkingPath = /home/bodmer/KorAP/GlemmServices/work-live
+
+## Loging:
+
+# fnameOut = GS_WorkingPath + "/WformmServices.log";
+#fnameOut = WformServices-t.log
+fnameOut = WformServices.log
+# fnameErr = GS_WorkingPath + "/WformServices.err";
+#fnameErr = WformServices-t.err
+fnameErr = WformServices.err
+
+## Communicating with the GlemmServices
+URLGlemmServer		= http://c0r4ft
+GlemmPort			= 40913
+#URLGlemmServices	= GlemmServices-0.2-Test
+URLGlemmServices	= GlemmServices
+LemmaRequestPath	= index/info/lemma
+	
\ No newline at end of file
diff --git a/WformServices/WebContent/WEB-INF/plugin.html b/WformServices/WebContent/WEB-INF/plugin.html
new file mode 100644
index 0000000..9936067
--- /dev/null
+++ b/WformServices/WebContent/WEB-INF/plugin.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <title>Wordform Services (including Glemm Services)</title>
+  <!-- load client javascript library -->
+  <!--  live-version: script src="https://korap.ids-mannheim.de/js/korap-plugin-0.2.js"
+          data-server="https://korap.ids-mannheim.de/"></script -->
+  <!--  Testversion: -->
+  <script src="https://korap.ids-mannheim.de/instance/test/js/korap-plugin-0.2.js"
+          data-server="https://korap.ids-mannheim.de/instance/test"></script>
+ </head>
+ <body>
+  <script>
+       let data = {
+         'action' : 'pipe',
+         <!-- KoralQuery mit POST Request an diesen service schicken; dahinter steht der Wordform Service, dahinter der Glemm Service. -->
+         <!-- ohne proxy: 'service' : 'http://c0r4ft.ids-mannheim.de:40913/wformServices-0.1-SNAPSHOT/wformServices/query' -->
+         <!-- mit proxy : 'service' : 'https://korap.ids-mannheim.de/proxy/c0r4ft/wformServices-0.1-SNAPSHOT/wformServices/query' -->
+         <!-- ab Version 0.2 der WformServices enthält die URL zu den Services keine Versionsnummer mehr: -->
+         'service' : 'https://korap.ids-mannheim.de/proxy/c0r4ft/wformServices/wformServices/query'
+       };
+
+       function pluginit (p) {
+         p.onMessage = function(msg) {
+           if (msg.key == 'glemm') {
+             if (msg.value) {
+               data['job'] = 'add';
+             }
+             else {
+               data['job'] = 'del';
+             };
+             KorAPlugin.sendMsg(data);
+           };
+         };
+       };
+     </script>
+   </body>
+</html> 
\ No newline at end of file
diff --git a/WformServices/WebContent/WEB-INF/web.xml b/WformServices/WebContent/WEB-INF/web.xml
new file mode 100644
index 0000000..1b3ae97
--- /dev/null
+++ b/WformServices/WebContent/WEB-INF/web.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- kopiert aus https://blog.dejavu.sk/registering-resources-and-providers-in-jersey-2/ , 09.12.19/FB -->
+
+<web-app version="2.5"
+        xmlns="http://java.sun.com/xml/ns/javaee"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+
+    <servlet>
+        <servlet-name>WordformServices mit Jersey REST</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+
+        <!-- Register JAX-RS Application, if needed. -->
+        <!-- init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>my.package.MyApplication</param-value>
+        </init-param -->
+
+        <!-- Register resources and providers under my.package. -->
+        <init-param>
+            <param-name>jersey.config.server.provider.packages</param-name>
+            <param-value>de.korap.services</param-value>
+        </init-param>
+
+        <!-- Register my custom provider (not needed if it's in my.package) AND LoggingFilter. -->
+        <init-param>
+            <param-name>jersey.config.server.provider.classnames</param-name>
+            <param-value>de.korap.services</param-value>
+        </init-param>
+
+        <!-- Enable Tracing support. -->
+        <init-param>
+            <param-name>jersey.config.server.tracing</param-name>
+            <param-value>ALL</param-value>
+        </init-param>
+
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>WordformServices mit Jersey REST</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
\ No newline at end of file
diff --git a/WformServices/pom.xml b/WformServices/pom.xml
new file mode 100644
index 0000000..b3eb297
--- /dev/null
+++ b/WformServices/pom.xml
@@ -0,0 +1,83 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>de.korap.services</groupId>
+  <artifactId>wformServices</artifactId>
+  <!-- version>0.2-Test</version-->
+  <version>0.2-live</version>
+  <name>wformServices</name>
+  <description>Wordform Services for Rewriting Wordform Queries and Handling Wordform Lists.</description>
+  <build>
+    <sourceDirectory>src</sourceDirectory>
+    <plugins>
+      <plugin>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <version>3.7.0</version>
+        <configuration>
+          <source>1.8</source>
+          <target>1.8</target>
+        </configuration>
+      </plugin>
+      <plugin>
+        <artifactId>maven-war-plugin</artifactId>
+        <version>3.2.1</version>
+        <configuration>
+          <warSourceDirectory>WebContent</warSourceDirectory>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  
+    <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>3.8.1</version>
+      <scope>test</scope>
+    </dependency>
+   <dependency>
+    <groupId>javax.servlet</groupId>
+    <artifactId>servlet-api</artifactId>
+    <version>2.5</version>
+   </dependency>
+   <!-- https://mvnrepository.com/artifact/javax.ws.rs/javax.ws.rs-api -->
+   <dependency>
+    <groupId>javax.ws.rs</groupId>
+    <artifactId>javax.ws.rs-api</artifactId>
+    <version>2.0</version>
+   </dependency>
+   <dependency>
+    <groupId>com.fasterxml.jackson.core</groupId>
+    <artifactId>jackson-core</artifactId>
+    <version>2.9.6</version>
+    <scope>compile</scope>
+   </dependency>
+	<dependency>
+	  <groupId>com.fasterxml.jackson.core</groupId>
+	  <artifactId>jackson-annotations</artifactId>
+	  <version>2.9.6</version>
+	</dependency>
+	<dependency>
+	  <groupId>com.fasterxml.jackson.core</groupId>
+	  <artifactId>jackson-databind</artifactId>
+	  <version>2.9.6</version>
+	  <scope>provided</scope>
+	</dependency>
+	<dependency>
+		<groupId>de.korap.services</groupId>
+		<artifactId>utils</artifactId>
+		<version>0.1-SNAPSHOT</version>
+	</dependency>
+	<dependency>
+		<groupId>de.korap.json</groupId>
+		<artifactId>JsonTraverse</artifactId>
+	   <version>0.1-SNAPSHOT</version>
+	</dependency>
+	<dependency>
+		<groupId>de.korap.services.glemm.client</groupId>
+		<artifactId>GlemmClient</artifactId>
+		<version>0.2-Test</version>
+	</dependency>
+    </dependencies>
+  
+    <packaging>war</packaging>
+</project>
\ No newline at end of file
diff --git a/WformServices/src/de/korap/services/WordformRewrite.java b/WformServices/src/de/korap/services/WordformRewrite.java
new file mode 100644
index 0000000..b575f84
--- /dev/null
+++ b/WformServices/src/de/korap/services/WordformRewrite.java
@@ -0,0 +1,129 @@
+package de.korap.services;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Properties;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import de.korap.json.JsonTraverse;
+import de.korap.json.TraversedPath;
+import de.korap.services.glemm.client.GlemmClient;
+import de.korap.services.utils.Utils;
+import de.korap.services.LemmaResponse;
+
+public class WordformRewrite {
+
+
+	/* 
+	 * traverse_and_rewrite_KoralQuery:
+	 * Parameters:
+	 * query     : JSON KoralQuery.
+	 * props	 : wformServices.properties.
+	 * 
+	 * Notes:
+	 * - 
+	 * Returns	: 0 = rewrite done. 1 = no rewrite needed. > 1: error!
+	 * - rewriten new Query if rewritten done.
+	 * - null + no exception: no rewriting needed.
+	 * - null + exception   : error.
+	 * 04.03.20/FB 27.04.20/FB 
+	 */
+		 
+	public static String traverse_and_rewrite_KoralQuery(
+			String 		query, 
+			Properties 	props, 
+			PrintStream	fout,
+			PrintStream ferr )
+		
+					throws IOException
+	
+	{
+	final String
+		func = "traverse_and_rewrite_KoralQuery";
+	ObjectMapper
+		objMapper = null;
+	JsonNode
+		jsonNode = null;
+	TraversedPath
+		tPath 	= null;
+	LemmaResponse
+		lemResp	= null;
+	final boolean
+		bDebug = true;
+	int
+		mode = JsonTraverse.REWRITE_EXP; // rewrite lemma query by expansion list.
+	
+	fout.printf("Debug: %s: ready for traversing query...\n", func);
+	
+	objMapper = new ObjectMapper();
+	
+	// get full query as a JsonNode tree:
+	try {
+		jsonNode = objMapper.readTree(query);
+		
+		// traverse query and collect lemma subqueries in a TraversedPath object:
+		tPath = new TraversedPath(objMapper);
+		JsonTraverse.traverseJsonTree(jsonNode, tPath, JsonTraverse.COLLECT, fout, ferr);
+		} 
+	catch (IOException e) {
+		e.printStackTrace();
+		throw e;
+		}
+	
+	fout.printf("Debug: %s: query traversed.\n", func);
+	
+	// Exit if no lemma subqueries found:
+	if( tPath == null || tPath.getLemmaCount() == 0 )
+		{
+		fout.printf("Info: %s: no lemma subquery found -> no rewrite needed.\n", func);
+		return null; // no rewrite.
+		}
+
+	/* - - - Call GlemmServices to resolve lemma-Subqueries - - - 
+	 * 
+	 * This returns for each lemma a reference to a list of wordform instantiations
+	 * (= instantiation list).
+	 */ 
+	
+	GlemmClient
+		glemmClient = new GlemmClient(props, fout, ferr);
+	
+	for(int i=0; i<tPath.getLemmaCount(); i++)
+		{
+		lemResp = glemmClient.sendQuery(tPath.getLemma(i));
+		tPath.addLemmaResponse(lemResp);
+		
+		fout.printf("Debug: %s: lemma='%s': %d wfs found: %s.\n", func, 
+				tPath.getLemma(i),
+				lemResp.head_nWfs,
+				lemResp.printPartialList());
+		}
+
+	// TODO glemmClient.close ?
+	
+	/*
+	 *  - - - Rewriting KoralQuery with lists of wordforms:
+	 *  
+	 * - Rewrite KoralQuery: use either JsonTraverse.REWRITE or .REWRITE_EXP:
+	 * - REWRITE_EXP = rewrite with expansion lists.
+	 */
+	
+	JsonTraverse.traverseJsonTree(jsonNode, tPath, JsonTraverse.REWRITE_EXP, fout, ferr); 
+	
+	// OK: return newQuery formated in pretty json:
+	fout.printf("Debug: %s: returning OK with jsonNode as a String.\n", func);
+	
+	return objMapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonNode);
+		
+	//System.out.printf("Debug: %s: returning OK and null String.\n", func);
+	
+	//return null; // OK
+
+	} // traverse_and_rewrite_KoralQuery
+
+
+
+} // class WordformRewrite
+
diff --git a/WformServices/src/de/korap/services/WordformServices.java b/WformServices/src/de/korap/services/WordformServices.java
new file mode 100644
index 0000000..0a26350
--- /dev/null
+++ b/WformServices/src/de/korap/services/WordformServices.java
@@ -0,0 +1,284 @@
+package de.korap.services;
+
+import javax.servlet.ServletContext;
+import javax.ws.rs.Consumes;
+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.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.charset.Charset;
+import java.time.LocalDateTime;
+import java.util.Locale;
+import java.util.Properties;
+
+import de.korap.services.WordformRewrite;
+import de.korap.services.utils.Utils;
+
+
+/*
+ * v-0.1: 29.06.20/FB: 1st version.
+ * v-0.1: 28.10.21/FB: 
+ * 		- writes request time to log;
+ *		- resets fnameOut/Err after call to GlemmServices. 
+ * Notes:
+ * - When submitting a KoralQuery [UTF-8] to the WformServices (if redirected
+ *   to the GlemmServices, only chars that may be translated to ISO8859-1 (=Lat1)
+ *   will be processed correctly. Glemm behind the scene can only treat
+ *   German words, so this restriction is OK.
+ */
+
+@Path("/wformServices")
+
+public class WordformServices {
+	
+	final private static String versionDate			= "28.10.21";
+	final private static String version     		= "0.2";
+	final private String 		fnameProps			= "/WEB-INF/WformServices.properties";
+	      private static String GS_WorkingPath 		= "/home/bodmer/KorAP/GlemmServices/Tests"; // linux
+	      private static String fnameOut 			= GS_WorkingPath + "/wformServices-t.log";
+	      private static String fnameErr	 		= GS_WorkingPath + "/wformServices-t.err";
+	final private static String filename_pluginHTML	= "WEB-INF/plugin.html";
+
+	@Context 
+	private ServletContext servletContext;
+	
+	Properties
+		props = null; // properties in WEB-INF/WformServices.properties
+	
+	public static PrintStream fout	= null;
+	public static PrintStream ferr	= null;		
+		
+	/*
+	 *  log_Request:
+	 *  
+	 *  - write info about API Request.
+	 *  Parameters:
+	 *  requestPath	: path of webapp addressed by the request.
+	 *  func		: name of the function which implements the request.
+	 *  
+	 *  19.10.21/FB
+	 */
+	
+	private void log_Request(String requestPath, String func)
+	
+	{
+	// transform "dateTtime" -> "date time":
+	
+	fout.printf("###\n");
+	fout.printf("### %s: %s : vers='%s', vdate='%s' request='%s'.\n", func, requestPath, version, versionDate, 
+						LocalDateTime.now().toString().replace("T",  " "));
+	fout.printf("###\n");
+	} // log_Request
+		
+	/* 
+ 	 * 
+	 * - after loadProperties, sets the class variable with loaded property values.
+	 * 
+	 * 15.10.21/FB
+	 */
+	
+	private void set_ConstantsFromProperties(Properties props)
+	
+	{
+	GS_WorkingPath 	= Utils.getConstantFromProperties(props, "GS_WorkingPath", "/home/bodmer/KorAP/GlemmServices/Tests");
+	fnameOut		= Utils.getConstantFromProperties(props, "fnameOut", 	   "WformServices-t.log");
+	fnameOut		= GS_WorkingPath + "/" + fnameOut;
+	fnameErr		= Utils.getConstantFromProperties(props, "fnameErr", 	   "WformServices-t.err");
+	fnameErr		= GS_WorkingPath + "/" + fnameErr;
+	
+	} // set_ConstantsFromProperties
+	
+	/* init_and_log_Request
+	 * 
+	 * - load GlemmServices.properties, sets GlemmServices constants
+	 *   and logs the request.
+	 * - load properties and set constants before logging anything, because
+	 *   logging will be done into the last stdout/stderr opened in glassfish's JVM,
+	 *   e.g. in the log of another Service.
+	 *    
+	 * 19.10.21/FB
+	 */
+	
+	private void init_and_log_Request(String func, String requestPath)
+	
+	{
+	String
+		realPath = servletContext.getRealPath(fnameProps);
+
+	// load GlemmServices properties and log them (if last param = true):
+	props = Utils.loadProperties(servletContext.getResourceAsStream(fnameProps), fnameProps, realPath, false);
+	
+	set_ConstantsFromProperties(props);
+	
+	// Set Standard File output streams for GlemmServices:
+	// Utils.setStdFileStreams(fnameErr, fnameOut);
+	
+	// open fout and ferr as replacement for stdout/stderr for use in GlemmServices.
+	// do not use or redirect System.out and System.err, as they will be redirected
+	// by other Services in the same glassfish JVM, e.g. by the WformServices.
+	fout = Utils.setLogStream(fnameOut);
+	ferr = Utils.setLogStream(fnameErr);
+	
+	log_Request(requestPath, func);
+
+	} // init_and_log_Request
+
+	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+	 * GET: /wformServices/status/
+	 * 
+	 * returns status of Wordform Services in HTML.
+	 * 
+	 * Parameters:
+	 *  -
+	 * Notes:
+	 * - 
+	 * 12.06.20/FB 
+	 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+	@Path("status")
+	@GET
+	@Produces("text/html")
+	
+	public String doGET_status()
+	
+	{
+	final String func = "doGET_status";
+	String
+		status;
+
+	init_and_log_Request(func, "/wformServices/status/");
+	
+	status = String.format(	"<html>\n" +
+						   	" <h3>Wordform Services</h3>\n" +
+						   	" <p><b>Version</b>     : %s</p>" +
+							" <p><b>Date   </b>     : %s</p>" +
+							" <p><b>status</b>      : ready.</p>" +
+							" <p><b>Request time</b>: %s</p>" +
+							" <p><b>properties</b>: %s </p>" +
+							"</html>",
+							version,
+							versionDate,
+							LocalDateTime.now().toString().replace("T", " "),
+							servletContext.getResourceAsStream(fnameProps) == null ? "not found" :
+								props.isEmpty() ? "empty" : "loaded");
+
+	fout.printf("Debug: %s: done.\n", func);
+
+	return status;
+		
+	} // doGET_status
+	
+	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+	 * GET: /wformServices/plugin.html
+	 * 
+	 * returns plugin.html file for registration in Kalamar.
+	 * 
+	 * Parameters:
+	 *  -
+	 * Notes:
+	 * - 
+	 * 29.06.20/FB 
+	 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+	@Path("plugin.html")
+	@GET
+	@Produces("text/html")
+	
+	public String doGET_pluginHTML(@Context ServletContext servletContext)
+	
+	{
+	final String func = "doGET_pluginHTML";
+	String
+		pluginHTML;
+	String
+		webAppRoot;
+
+	init_and_log_Request(func, "/wformServices/plugin.html");
+	
+	webAppRoot = servletContext.getRealPath("/");
+	
+	fout.printf("Debug: %s: Root Path of webApp = '%s'.\n",  func, webAppRoot);
+	fout.printf("Debug: %s: loading = '%s'.\n",  func, webAppRoot + filename_pluginHTML);
+	
+	try {
+		pluginHTML = Utils.readFromInputStream(webAppRoot + filename_pluginHTML);
+		fout.printf("Debug: %s: loading plugin.html: OK'.\n",  func);
+		fout.printf("Debug: %s: plugin.html = '%s'.\n",  func, pluginHTML);
+		} 
+	catch (IOException e) {
+		e.printStackTrace();
+		pluginHTML = String.format("<html>\n<body><h3>wformServices Error: cannot load plugin.html!</h3></body></html>\n");
+		fout.printf("Debug: %s: loading plugin.html: failed!\n",  func);
+		}
+	
+	fout.printf("Debug: %s: done.\n", func);
+	return pluginHTML;
+		
+	} // doGET_pluginHTML
+	
+	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+	 * POST: /wformServices/query/: application/json.
+	 * submits a Koral Query which might contain 1 or more lemma subqueries [UTF-8].
+	 * Each subquery is searched in the Glemm DB and the resulting wordform list rewrites
+	 * the original subquery.
+	 * Parameters:
+	 *  query    : content of POST body: full KoralQuery in Json.
+	 * Notes:
+	 * - HTTP POST Body can be read once only. Probably Jersey reads it once and
+	 *   passes it to the POST method, i.e. String query.
+	 * - if we try to read the POST Body by either HttpServletRequest.getReader()
+	 *   or HttpServletRequest.getInputStream(), we get an exception, as this 
+	 *   would be the 2nd access to the body data.
+	 * 17.02.20/FB 
+	 * 20.02.20/FB Body Data access by parameter query.
+	 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+	@Path("query") 
+	@POST 
+	@Consumes("application/json") 
+	@Produces("application/json")
+	 
+	public String doPOST_handleWordformQueries(String query) 
+	 
+	{
+	final String
+		func = "doPOST_handleWordformQueries";
+	String
+		newQuery = "";
+	
+	init_and_log_Request(func, "/wformServices/query/");
+	
+	try	{
+		newQuery = WordformRewrite.traverse_and_rewrite_KoralQuery(query, props, fout, ferr);
+		if( newQuery == null )
+			{
+			fout.printf("Debug: %s: ending time: %s.\n", func, LocalDateTime.now().toString().replace("T",  " "));
+			fout.printf("Debug: %s: nothing to rewrite.\n", func);
+			return query; // nothing to rewrite, return original query.
+			}
+		}
+	catch( Exception e)
+		{
+		e.printStackTrace();
+		fout.printf("Debug: %s: ending time: %s.\n", func, LocalDateTime.now().toString().replace("T",  " "));
+		fout.printf("Debug: %s: Exception occured: returning unrewritten query!\n", func);
+		return query; // error occured, but returning original, unrewritten query.
+		}
+	
+	fout.printf("Debug: %s: ending time: %s.\n", func, LocalDateTime.now().toString().replace("T",  " "));
+	fout.printf("Debug: %s: returning rewritten query: size=%d chars.\n", func, newQuery.length());
+	
+	return newQuery;
+	
+	} // doPOST_handleWordformQueries
+
+}