package edu.kit.aifb.cumulus.webapp;
import static edu.kit.aifb.cumulus.framework.util.Strings.isNotNullOrEmptyString;
import static edu.kit.aifb.cumulus.framework.util.Strings.isNullOrEmptyString;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import org.openrdf.query.resultio.BooleanQueryResultFormat;
import org.openrdf.query.resultio.TupleQueryResultFormat;
import org.openrdf.rio.RDFFormat;
import org.slf4j.LoggerFactory;
import edu.kit.aifb.cumulus.framework.util.Strings;
import edu.kit.aifb.cumulus.log.Log;
import edu.kit.aifb.cumulus.log.MessageCatalog;
import edu.kit.aifb.cumulus.util.Util;
/**
* Provides various HTTP utilities and attributes used in
* CumulusRDF HTTP servlets.
*
* @author Andrea Gazzarini
* @author Andreas Wagner
* @since 1.0.1
*/
public class HttpProtocol {
/**
*
* HTTP header fields, which are used in CumulusRDF servlets.
*
* @author Andreas Wagner
* @since 1.1
* @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html">http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html</a>
*
*/
public interface Headers {
static final String CONTENT_TYPE = Parameters.CONTENT_TYPE, ACCEPT = Parameters.ACCEPT, BASE_URI = Parameters.BASE_URI;
}
/**
*
* HTTP methods that are used in the CumulusRDF servlets.
*
* @author Andreas Wagner
* @since 1.1
*
*/
public interface Methods {
static final String PUT = "PUT", POST = "POST", DELETE = "DELETE", GET = "GET";
}
/**
* MIME types for RDF serialization as well as result serialization.
*
* @author Andreas Wagner
* @since 1.1
* @see <a href="http://www.iana.org/assignments/media-types/media-types.xhtml">http://www.iana.org/assignments/media-types/media-types.xhtml</a>
*
*/
public interface MimeTypes {
/*
* MIME types for RDF serializations
*/
static final String RDF_XML = "application/rdf+xml",
BINARY = "application/x-binary-rdf",
N_TRIPLES = "text/plain", //application/n-triples
N_QUADS = "text/x-nquads", //application/n-quads
JSON_LD = "application/ld+json",
RDF_JSON = "application/rdf+json",
TRIG = "application/x-trig",
TRIX = "application/trix",
TURTLE = "text/turtle",
TURTLE_ALT = "application/x-turtle",
N3 = "text/n3",
N3_ALT = "text/rdf+n3",
TEXT_PLAIN = "text/plain",
TEXT_HTML = "text/html";
static final String[] RDF_SERIALIZATIONS = new String[] { RDF_XML, BINARY, N_TRIPLES, N_QUADS, JSON_LD, RDF_JSON, TRIG, TRIX, TURTLE, TURTLE_ALT, N3, N3_ALT, TEXT_PLAIN };
/*
* MIME types for result serializations
*/
static final String
SPARQL_XML = "application/sparql-results+xml",
SPARQL_BINARY = "application/x-binary-rdf-results-table",
SPARQL_JSON = "application/sparql-results+json",
SPARQL_CSV = "text/csv",
SPARQL_TSV = "text/tab-separated-values",
SPARQL_BOOLEAN = "text/boolean";
static final String[] RESULT_SERIALIZATIONS = new String[] { SPARQL_XML, SPARQL_BINARY, SPARQL_JSON, SPARQL_CSV, SPARQL_TSV, SPARQL_BOOLEAN };
}
/**
* Parameters used in CumulusRDF servlets.
*
* @author Andreas Wagner
* @since 1.1
*/
public interface Parameters {
static final String URI = "uri",
S = "s",
P = "p",
O = "o",
C = "c",
S2 = "s2",
P2 = "p2",
O2 = "o2",
C2 = "c2",
ACCEPT = "accept",
QUERY = "query",
UPDATE = "update",
BASE_URI = "base-uri",
CONTENT_TYPE = "content-type";
}
private static final String DEFAULT_URL_ENCODING = "UTF-8";
private static final Log _log = new Log(LoggerFactory.getLogger(HttpProtocol.class));
/**
*
* Retrieves and decodes (if HTTP request uses GET or DELETE method) a parameter value from the HTTP request.
*
* @author Andreas Wagner
* @since 1.1
*
* @param request - HTTP request
* @param parameter - parameter name
* @return parameter value
*/
public static String getParameterValue(final HttpServletRequest request, String parameter) {
boolean url_encoded = request.getMethod().equals(Methods.GET) || request.getMethod().equals(Methods.DELETE) ? true : false;
String value = request.getParameter(parameter), encoding = request.getCharacterEncoding() == null ? DEFAULT_URL_ENCODING : request.getCharacterEncoding();
try {
return (value == null) || value.isEmpty() || !url_encoded ? value : URLDecoder.decode(value, encoding);
} catch (UnsupportedEncodingException e) {
_log.warning(MessageCatalog._00025_CUMULUS_SYSTEM_INTERNAL_FAILURE_MSG + " Could not decode HTTP parameter-value '" + value + "' for paramter '" + parameter + "'", e);
return null;
}
}
/**
* <p>Retrieves the MIME type for the accept parameter.
* The accept parameter could be specified by the client as a parameter or
* a header in the HTTP request.
* HTTP header has a higher priority.</p>
* <p> Default (if no accept parameter is given): RDF/XML.</p>
*
* @author Andreas Wagner
* @since 1.1
*
* @param request - HTTP request.
* @return MIME type of the accept parameter.
*/
public static String parseAcceptHeader(final HttpServletRequest request) {
String accept = request.getHeader(Headers.ACCEPT);
if (isNullOrEmptyString(accept)) {
accept = getParameterValue(request, Parameters.ACCEPT);
}
if (isNotNullOrEmptyString(accept)) {
accept = accept.trim().toLowerCase();
}
if ((BooleanQueryResultFormat.forMIMEType(accept) == null) &&
(TupleQueryResultFormat.forMIMEType(accept) == null) &&
(RDFFormat.forMIMEType(accept) == null)) {
/*
* guess MIME type
*/
/*
* default: MimeTypes.RDF_XML
*/
if ((accept == null) || accept.isEmpty()) {
accept = MimeTypes.RDF_XML;
}
/*
* MIME type for result serialization
*/
else if (accept.contains("sparql") || accept.contains("result") || accept.contains("tsv") || accept.contains("tab") || accept.contains("csv")
|| accept.contains("bool")) {
if (accept.contains("xml")) {
accept = MimeTypes.SPARQL_XML;
} else if (accept.contains("binary")) {
accept = MimeTypes.SPARQL_BINARY;
} else if (accept.contains("json")) {
accept = MimeTypes.SPARQL_JSON;
} else if (accept.contains("csv")) {
accept = MimeTypes.SPARQL_CSV;
} else if (accept.contains("tsv") || accept.contains("tab")) {
accept = MimeTypes.SPARQL_TSV;
} else if (accept.contains("bool")) {
accept = MimeTypes.SPARQL_BOOLEAN;
}
/*
* default: SPARQL/XML
*/
else {
accept = MimeTypes.SPARQL_XML;
}
}
/*
* MIME type for RDF serialization
*/
else {
if (accept.contains("htm")) {
accept = MimeTypes.TEXT_HTML;
} else if (accept.contains("xml")) {
accept = MimeTypes.RDF_XML;
} else if (accept.contains("rdf+json")) {
accept = MimeTypes.RDF_JSON;
} else if (accept.contains("json")) {
accept = MimeTypes.JSON_LD;
} else if (accept.contains("trix")) {
accept = MimeTypes.TRIX;
} else if (accept.contains("trig")) {
accept = MimeTypes.TRIG;
} else if (accept.contains("n3")) {
accept = MimeTypes.N3;
} else if (accept.contains("turtle") || accept.contains("ttl")) {
accept = MimeTypes.TURTLE;
} else if (accept.contains("triple")) {
accept = MimeTypes.TEXT_PLAIN;
} else if (accept.contains("quad")) {
accept = MimeTypes.N_QUADS;
} else if (accept.contains("plain")) {
accept = MimeTypes.TEXT_PLAIN;
} else if (accept.contains("binary")) {
accept = MimeTypes.BINARY;
}
/*
* default: RDF/XML
*/
else {
accept = MimeTypes.RDF_XML;
}
}
}
return accept;
}
/**
* Parses the base URI from the header or as request parameter.
*
* @author Andreas Wagner
* @since 1.1
*
* @param request - HTTP request
* @return base URI
*
*/
public static String parseBaseURI(final HttpServletRequest request) {
return parseBaseURI(request, Strings.EMPTY_STRING);
}
/**
* Parses the base URI from the header or as request parameter.
*
* @author Andreas Wagner
* @since 1.1
*
* @param request - HTTP request
* @param default_base_URI - default value
* @return base URI
*
*/
public static String parseBaseURI(final HttpServletRequest request, final String default_base_URI) {
String base_URI = getParameterValue(request, Parameters.BASE_URI);
if (isNullOrEmptyString(base_URI)) {
base_URI = request.getHeader(Headers.BASE_URI);
}
if (isNotNullOrEmptyString(base_URI)) {
return Util.isValidURI(base_URI) ? base_URI : default_base_URI;
} else {
return default_base_URI;
}
}
/**
* <p>Retrieves the MIME type for the content-type parameter.
* The content-type parameter could be specified by the client as a parameter or
* a header in the HTTP request.
* HTTP header has a higher priority.</p>
* <p> Default (if no content-type parameter is given): RDF/XML.</p>
*
* @param request - the HTTP request.
* @return MIME type of the content-type parameter.
*/
public static String parseContentTypeHeader(final HttpServletRequest request) {
String content_type = request.getHeader(Headers.CONTENT_TYPE);
if (isNullOrEmptyString(content_type)) {
content_type = getParameterValue(request, Parameters.CONTENT_TYPE);
}
if (isNotNullOrEmptyString(content_type)) {
content_type = content_type.trim().toLowerCase();
}
if (RDFFormat.forMIMEType(content_type) == null) {
/*
* default: MimeTypes.RDF_XML
*/
if ((content_type == null) || content_type.isEmpty()) {
content_type = MimeTypes.RDF_XML;
}
/*
* guess MIME type
*/
else if (content_type.contains("htm")) {
content_type = MimeTypes.TEXT_HTML;
} else if (content_type.contains("xml")) {
content_type = MimeTypes.RDF_XML;
} else if (content_type.contains("rdf+json")) {
content_type = MimeTypes.RDF_JSON;
} else if (content_type.contains("json")) {
content_type = MimeTypes.JSON_LD;
} else if (content_type.contains("trix")) {
content_type = MimeTypes.TRIX;
} else if (content_type.contains("trig")) {
content_type = MimeTypes.TRIG;
} else if (content_type.contains("n3")) {
content_type = MimeTypes.N3;
} else if (content_type.contains("turtle") || content_type.contains("ttl")) {
content_type = MimeTypes.TURTLE;
} else if (content_type.contains("triple")) {
content_type = MimeTypes.TEXT_PLAIN;
} else if (content_type.contains("quad")) {
content_type = MimeTypes.N_QUADS;
} else if (content_type.contains("plain")) {
content_type = MimeTypes.TEXT_PLAIN;
} else if (content_type.contains("binary")) {
content_type = MimeTypes.BINARY;
}
/*
* default: RDF/XML
*/
else {
content_type = MimeTypes.RDF_XML;
}
}
return content_type;
}
private HttpProtocol() {
}
}