// This file is part of the Attempto Parsing Engine (APE).
// Copyright 2008-2012, Attempto Group, University of Zurich (see http://attempto.ifi.uzh.ch).
//
// The Attempto Parsing Engine (APE) is free software: you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by the Free Software
// Foundation, either version 3 of the License, or (at your option) any later version.
//
// The Attempto Parsing Engine (APE) is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along with the Attempto
// Parsing Engine (APE). If not, see http://www.gnu.org/licenses/.
package ch.uzh.ifi.attempto.ape;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
/**
* This class provides an interface to the Attempto Parsing Engine (APE) webservice
* (i.e. HTTP server).
* The HTTP server implementation is provided by ape.exe
. To start
* a server, execute for example:
*
*
* ./ape.exe -httpserver -port 8000
*
*
* @author Kaarel Kaljurand
* @author Tobias Kuhn
*/
public class APEWebservice extends ACEParser {
private static final int MAX_HTTP_GET_LENGTH = 1000;
private static final String ERROR_MESSAGE = "Accessing APE webservice failed";
private String wsUrl;
private HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
/**
* Creates a new parser object based on the URL of a running APE webservice.
*
* @param wsUrl The URL of the APE webservice.
*/
public APEWebservice(String wsUrl) {
this.wsUrl = wsUrl;
}
/**
* Sets the URL of the APE webservice to be used.
*
* @param wsUrl The URL of the APE webservice.
*/
public void setWebserviceUrl(String wsUrl) {
if (wsUrl == null) {
throw new NullPointerException("APE webservice URL cannot be null");
}
this.wsUrl = wsUrl;
}
public String getSoloOutput(String aceText, Lexicon lexicon, OutputType outputType) throws ACEParserException {
List nvps = prepareRequestParameters(aceText, lexicon);
nvps.add(getSoloParam(outputType));
return checkForErrors(getResponseAsString(nvps));
}
public ACEParserResult getMultiOutput(String aceText, Lexicon lexicon, OutputType... outputTypes) {
List nvps = prepareRequestParameters(aceText, lexicon);
for (OutputType t : outputTypes) {
nvps.add(new BasicNameValuePair(t.toMultiFlag(), "on"));
}
return new ACEParserResult(getResponseAsString(nvps));
}
private List prepareRequestParameters(String aceText, Lexicon lexicon) {
List nvps = new ArrayList();
nvps.add(new BasicNameValuePair("text", aceText));
if (lexicon != null) {
nvps.add(new BasicNameValuePair("ulextext", lexicon.toString()));
}
String uri = getURI();
if (uri != null) {
nvps.add(new BasicNameValuePair("uri", uri));
}
nvps.add(getNamedBooleanParameter("guess", isGuessingEnabled()));
nvps.add(getNamedBooleanParameter("noclex", !isClexEnabled()));
return nvps;
}
private NameValuePair getSoloParam(OutputType solo) {
return new BasicNameValuePair("solo", solo.toSoloFlag());
}
private BasicNameValuePair getNamedBooleanParameter(String key, boolean value) {
return new BasicNameValuePair(key, value ? "on" : "off");
}
private String getResponseAsString(List nvps) {
HttpClient client = httpClientBuilder.build();
HttpUriRequest request = getHttpUriRequest(nvps);
return getEntity(client, request);
}
/**
* We create an HTTP GET query from the given parameters. If it turns out to be
* too long (which we expect to happen very infrequently) then we fall back to creating
* HTTP POST.
*
* BUG: Old versions of the SWI-Prolog HTTP server do not handle POST queries correctly.
* Hopefully this has been fixed in v5.11.9 (but I haven't tested it).
* See the bug report in the SWI-Prolog mailing list in 2010-11-03.
*
* @param nvps List of name-value pairs
* @return HTTP request (either GET or POST)
*/
private HttpUriRequest getHttpUriRequest(List nvps) {
String getQuery = wsUrl + "?" + URLEncodedUtils.format(nvps, HTTP.UTF_8);
if (getQuery.length() > MAX_HTTP_GET_LENGTH) {
HttpPost httppost = new HttpPost(wsUrl);
try {
httppost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
} catch (UnsupportedEncodingException e) {
// BUG: Assuming that this cannot happen
}
return httppost;
}
return new HttpGet(getQuery);
}
private String getEntity(HttpClient httpclient, HttpUriRequest httpRequest) {
try {
HttpResponse response = httpclient.execute(httpRequest);
HttpEntity entity = response.getEntity();
if (entity == null) {
throw new RuntimeException(ERROR_MESSAGE + ": " + response.getStatusLine());
}
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
throw new RuntimeException(ERROR_MESSAGE + ": " + response.getStatusLine());
}
// The APE webservice returns the data in UTF8, even if it doesn't declare it.
if (entity.getContentEncoding() == null) {
return EntityUtils.toString(entity, HTTP.UTF_8);
}
return EntityUtils.toString(entity);
} catch (ClientProtocolException e) {
throw new RuntimeException(ERROR_MESSAGE + ": " + e.getMessage());
} catch (IOException e) {
throw new RuntimeException(ERROR_MESSAGE + ": " + e.getMessage());
} finally {
httpclient.getConnectionManager().shutdown();
}
}
}