Importing WformServices and GlemmServices

Change-Id: Ifa95576d69e0d3863f63d3fdedb48c2c21cf64bc
diff --git a/GlemmClient/.classpath b/GlemmClient/.classpath
new file mode 100644
index 0000000..0915405
--- /dev/null
+++ b/GlemmClient/.classpath
@@ -0,0 +1,28 @@
+<?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 exported="true" kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+			<attribute name="org.eclipse.jst.component.nondependency" value=""/>
+		</attributes>
+	</classpathentry>
+	<classpathentry combineaccessrules="false" kind="src" path="/GlemmServices"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/JsonTraverse"/>
+	<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.utility"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/jdk1.8.0_231">
+		<attributes>
+			<attribute name="owner.project.facets" value="java"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/GlemmClient/.project b/GlemmClient/.project
new file mode 100644
index 0000000..23dcb98
--- /dev/null
+++ b/GlemmClient/.project
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>GlemmClient</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.wst.common.project.facet.core.builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</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.jem.workbench.JavaEMFNature</nature>
+		<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
+		<nature>org.eclipse.m2e.core.maven2Nature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
+	</natures>
+</projectDescription>
diff --git a/GlemmClient/.settings/org.eclipse.jdt.core.prefs b/GlemmClient/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..4e4a3ad
--- /dev/null
+++ b/GlemmClient/.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/GlemmClient/.settings/org.eclipse.m2e.core.prefs b/GlemmClient/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 0000000..f897a7f
--- /dev/null
+++ b/GlemmClient/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/GlemmClient/.settings/org.eclipse.wst.common.component b/GlemmClient/.settings/org.eclipse.wst.common.component
new file mode 100644
index 0000000..96f1e14
--- /dev/null
+++ b/GlemmClient/.settings/org.eclipse.wst.common.component
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
+    <wb-module deploy-name="GlemmClient">
+        <wb-resource deploy-path="/" source-path="/src"/>
+    </wb-module>
+</project-modules>
diff --git a/GlemmClient/.settings/org.eclipse.wst.common.project.facet.core.xml b/GlemmClient/.settings/org.eclipse.wst.common.project.facet.core.xml
new file mode 100644
index 0000000..3eca3e1
--- /dev/null
+++ b/GlemmClient/.settings/org.eclipse.wst.common.project.facet.core.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<faceted-project>
+  <runtime name="Apache Tomcat v9.0"/>
+  <installed facet="java" version="1.8"/>
+  <installed facet="jst.utility" version="1.0"/>
+</faceted-project>
diff --git a/GlemmClient/.settings/org.eclipse.wst.validation.prefs b/GlemmClient/.settings/org.eclipse.wst.validation.prefs
new file mode 100644
index 0000000..04cad8c
--- /dev/null
+++ b/GlemmClient/.settings/org.eclipse.wst.validation.prefs
@@ -0,0 +1,2 @@
+disabled=06target
+eclipse.preferences.version=1
diff --git a/GlemmClient/pom.xml b/GlemmClient/pom.xml
new file mode 100644
index 0000000..c5030f9
--- /dev/null
+++ b/GlemmClient/pom.xml
@@ -0,0 +1,53 @@
+<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.glemm.client</groupId>
+  <artifactId>GlemmClient</artifactId>
+  <version>0.2-Test</version>
+  <name>Glemm Client library</name>
+  <description>Client for communication with Glemm Services</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>
+    </plugins>
+  </build>
+  
+ <dependencies>
+   <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>compile</scope>
+	</dependency>
+	<dependency>
+		<groupId>de.korap.json</groupId>
+		<artifactId>JsonTraverse</artifactId>
+		<version>0.1-SNAPSHOT</version>
+	</dependency>
+		<dependency>
+		<groupId>de.korap.services</groupId>
+		<artifactId>utils</artifactId>
+		<version>0.1-SNAPSHOT</version>
+		<scope>compile</scope>
+	</dependency>
+ </dependencies>
+  
+</project>
\ No newline at end of file
diff --git a/GlemmClient/src/de/korap/services/glemm/client/GlemmClient.java b/GlemmClient/src/de/korap/services/glemm/client/GlemmClient.java
new file mode 100644
index 0000000..2befa42
--- /dev/null
+++ b/GlemmClient/src/de/korap/services/glemm/client/GlemmClient.java
@@ -0,0 +1,343 @@
+package de.korap.services.glemm.client;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.net.*;
+import java.util.Properties;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import de.korap.services.LemmaResponse;
+import de.korap.services.utils.Utils;
+
+/*
+ * Notes on encoding URLs:
+ * 
+ * -> http://server:port/GlemmServices-0.2-test/lemma/info/Haus?opts=comp+other
+ * - in this URL, 'Haus' is not part of the query but part of the path.
+ * - the query starts behind the '?'.
+ * -> http://server:port/GlemmServices-0.2-test/lemma/info?lemma=Haus&opts=comp+other
+ * - that would be correct: now lemma=Haus is part of the query.
+ * - But fortunatly, URI.toASCIIString() will also urlencode lemma 'Haus' as part
+ *   of the path in the 1st URL above.
+ */
+
+public class GlemmClient {
+	
+	/* constants that may be rewritten by properties */
+	String URLGlemmServer	= "http://c0r4ft";
+	int    GlemmPort		= 40913;
+	String URLGlemmServices	= "GlemmServices-0.1-SNAPSHOT";
+	String LemmaRequestPath	= "index/info/lemma";
+	
+	public PrintStream fout	= null;
+	public PrintStream ferr	= null;		
+		
+	public GlemmClient(PrintStream fout, PrintStream ferr)
+	
+	{
+	this.fout = fout;
+	this.ferr = ferr;
+	
+	fout.printf("Debug: GlemmClient: constructor called.\n");	
+	}
+	
+	/* GlemmClient constructor:
+	 * 
+	 * init constants from properties.
+	 * 
+	 * 22.10.21/FB
+	 */
+	
+	public GlemmClient(Properties props, PrintStream fout, PrintStream ferr)
+	
+	{
+	String
+		tmp;
+	
+	this.fout = fout;
+	this.ferr = ferr;
+	
+	URLGlemmServer	= Utils.getConstantFromProperties(props, "URLGlemmServer", "http://c0r4ft");
+	tmp				= Utils.getConstantFromProperties(props, "GlemmPort", 	   "40913");
+	GlemmPort		= Integer.parseInt(tmp);
+	URLGlemmServices= Utils.getConstantFromProperties(props, "URLGlemmServices", "GlemmServices-0.1-SNAPSHOT");
+	LemmaRequestPath= Utils.getConstantFromProperties(props, "LemmaRequestPath", "index/info/lemma");		
+	}
+	
+	public int GlemmConnect()
+	
+	{
+	final String func = "GlemmConnect";
+	
+	fout.printf("Debug: %s: starting to connect to GLEMM Services...\n", func);
+	
+	return 0;
+	} // GlemmConnect
+
+	/*
+	 * sendQuery:
+	 * - sends a lemma query over the network to the Glemm Services.
+	 * Notes:
+	 * - classical use of URLs, HttpURLConnections, etc. out of the box of
+	 *   the java library; no other library is needed.
+	 * 
+	 * Arguments:
+	 * lemmaQuery: e.g. Schuh or Schuh?opts=flex+comp+other
+	 * 
+	 * Returns:
+	 * - list of wordforms (= lemma expansions).
+	 *
+	 * 27.04.20/FB
+	 * 10.05.21/FB :
+	 * - converting lemmaQuery using urlencoding.
+	 * note: - JVM on COSMAS II-lunix is using ISO-8859-1 per default.
+	 *       - currently, modifying default character encoding (at JVM start) is discouraged.
+	 * - sendQuery() get JSON [UTF-8] back from the GlemmServices.
+	 * - list of wfs returned [UTF-8] have to be converted to [ISO8859-1] before being converted
+	 *   by transform_JSON2Obj() to JSON [UTF-8]. Otherwise UTF-8 chars will be wrongly
+	 *   converted to UTF-8 (convert UTF-8 to UTF-8 = carbage).
+	 * - lemmaQuery: using URI + uri.toASCIIString() for "lemma?opts=flex+comp": 
+	 *   this urlencodes the lemma part but not the query parameters behind the '?'. 
+	 * 28.10.21/FB
+	 */
+	
+	public LemmaResponse sendQuery(String lemmaQuery)
+	
+	{
+	final String func = "sendQuery";
+	String
+		URLasString;
+	URL 
+		url 		= null;
+	URI
+		uri			= null;
+	HttpURLConnection 
+		con;
+	int
+		status;
+	String
+		responseContent;
+	LemmaResponse
+		lemResp		= null;
+	final boolean
+		bURLencode	= true;	// if true: urlencode query.
+	final boolean
+		bLat1		= true; // if true: convert from UTF-8 to ISO8859-1
+
+	this.fout.printf("Debug: %s: requested lemma = '%s'.\n", func, lemmaQuery);
+
+	if( bURLencode )
+		{
+		URLasString = String.format("%s:%d/%s/%s/%s", 
+								URLGlemmServer, 
+								GlemmPort, 
+								URLGlemmServices, 
+								LemmaRequestPath, 	// = path without lemma which is part of the path. 
+								lemmaQuery);  		// not urlencoded yet!
+		try {
+			uri = new URI(URLasString);
+			// uri.toASCIIString: urlencodes only the lemma (OK). 
+			this.fout.printf("Debug: %s: uri als ASCII string = '%s'.\n",  func, uri.toASCIIString());
+			} 
+		catch (URISyntaxException e1) {
+			this.ferr.printf("Error: %s: cannot convert to URI: '%s'!\n", func, URLasString);
+			e1.printStackTrace();
+			return null;
+			}
+		
+		try { 
+			url = new URL(uri.toASCIIString()); // which translates the lemma, but not the query part behind the '?' to urlencoding.
+			this.fout.printf("Debug: %s: creating URL='%s'.\n", func, url.toString());
+			} 
+		catch (MalformedURLException  e) {
+			this.ferr.printf("Error: %s: cannot build URL from '%s'!\n", func, uri.toASCIIString());
+			e.printStackTrace();
+			return null;
+			} 
+		}
+	else
+		{
+		// formulate request as URL:
+		URLasString = String.format("%s:%d/%s/%s/%s", 
+						URLGlemmServer, 
+						GlemmPort, 
+						URLGlemmServices, 
+						LemmaRequestPath, 
+						lemmaQuery); 		
+		
+		try { 
+			url = new URL(URLasString);
+			this.fout.printf("Debug: %s: creating URL='%s'.\n", func, url.toString());
+			} 
+		catch (MalformedURLException  e) {
+			this.ferr.printf("Error: %s: cannot build URL from '%s'!\n", func, URLasString);
+			e.printStackTrace();
+			return null;
+			}
+		}
+
+	// build the connection:
+	
+	try {
+		con = (HttpURLConnection) url.openConnection();
+		} 
+	catch (IOException e) {
+		this.ferr.printf("Error: %s: cannot connect to URL='%s'!\n", func, URLasString);
+		e.printStackTrace();
+		return null;
+		}
+	
+	try {
+		con.setRequestMethod("GET");
+		con.setRequestProperty("Content-Type", "application/json");
+		con.setRequestProperty("Accept",       "application/json"); 
+		con.setUseCaches(false);
+		} 
+	catch (ProtocolException e) {
+		e.printStackTrace();
+		return null;
+		}
+	
+	// sending the request & waiting for the response with the wf lists:
+
+	try {
+		if( (status = con.getResponseCode()) == HttpURLConnection.HTTP_OK )
+			{
+			this.fout.printf("Debug: %s: HTTP return status = OK.\n", func);
+			responseContent = getResponseContent(con);
+			if( responseContent == null )
+				return null;
+			this.fout.printf("Debug: %s: HTTP Response Content = '%s'.\n", func, responseContent);
+			// 28.10.21/FB
+			if( bLat1 )
+				{
+				String
+					responseContentLat1 = new String(responseContent.getBytes("ISO-8859-1"), "utf-8");
+				this.fout.printf("Debug: %s: Response Content = '%s' [Lat1].\n", func, responseContentLat1);
+				responseContent = responseContentLat1;
+				}
+			}	
+		else
+			{
+			this.fout.printf("Debug: %s: HTTP return status = %d (%s)!\n",  func, status, con.getResponseMessage());
+			con.disconnect();
+			return null;
+			}
+		} 
+	catch (IOException e) {
+		this.ferr.printf("Error: %s: while sending request to GlemmServices!\n", func);
+		e.printStackTrace();
+		con.disconnect();
+		return null;
+		}
+	
+	// converting JSON Response Content to object:
+	lemResp = transform_JSON2Obj(responseContent);
+	
+	this.fout.printf("Debug: %s: lemResp='%s'.\n", func, lemResp.toString());
+	
+	// disconnecting:
+	con.disconnect();
+	
+	// returning lemma Response Object:
+	return lemResp; // ok.
+	
+	} // sendQuery
+	
+
+	/* getResponseContent:
+	 * 
+	 * - returns the HTTP Response Content on con.
+	 * Returns:
+	 *  - either the Content as String.
+	 *  - or null if an error occures.
+	 * 29.04.20/FB
+	 */
+	
+	private String getResponseContent(HttpURLConnection con)
+	
+	{
+	final String func = "getResponseContent";
+	BufferedReader 
+		in;
+	String 
+		inputLine;
+	StringBuffer 
+		content;
+			
+	try {
+		in = new BufferedReader(
+				  new InputStreamReader(con.getInputStream()));
+		} 
+	catch (IOException e) {
+		this.ferr.printf("Error: %s: cannot read returned HTTP Content!\n", func);
+		e.printStackTrace();
+		return null;
+		}
+	
+	content = new StringBuffer();
+	
+	try {
+		while ((inputLine = in.readLine()) != null) 
+			{
+		    content.append(inputLine);
+			}
+		} 
+	catch (IOException e1) {
+		e1.printStackTrace();
+		}
+	
+	try {
+		in.close();
+		} 
+	catch (IOException e) {
+		e.printStackTrace();
+		}
+	
+	// return Response Content:
+	return content.toString();
+	
+	} // getResponseContent		
+			
+	/* transform_JSON2Obj
+	 * - transforms the JSON formated Glemm Response content to an
+	 *   object.
+	 * 29.04.20/FB
+	 */
+	
+	private LemmaResponse transform_JSON2Obj(String jsonString)
+	
+	{
+	ObjectMapper
+		objMapper = null;	
+	LemmaResponse
+		lemResp;
+	
+	objMapper = new ObjectMapper();
+
+	try {
+		lemResp   = objMapper.readValue(jsonString, LemmaResponse.class);
+		} 
+	catch (JsonParseException e) {
+		e.printStackTrace();
+		return null;
+		} 
+	catch (JsonMappingException e) {
+		e.printStackTrace();
+		return null;
+		} 
+	catch (IOException e) {
+		e.printStackTrace();
+		return null;
+		}
+	
+	return lemResp;	
+	} // transform_JSON2Obj
+	 
+} // class GlemmClient