package com.mysema.rdfbean.sparql;
import java.io.IOException;
import java.util.Locale;
import javax.annotation.Nullable;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.mysema.rdfbean.model.Format;
import com.mysema.rdfbean.model.QueryLanguage;
import com.mysema.rdfbean.model.RDFConnection;
import com.mysema.rdfbean.model.Repository;
import com.mysema.rdfbean.model.SPARQLQuery;
/**
* SPARQLServlet provides a Servlet based SPARQL HTTP access point for RDFBean
* repositories
*
* @author tiwe
*
*/
public class SPARQLServlet extends HttpServlet {
private static final Logger logger = LoggerFactory.getLogger(SPARQLServlet.class);
private static final long serialVersionUID = 5726683938555535282L;
private static final ResultProducer xmlProducer = new XMLResultProducer();
private static final ResultProducer jsonProducer = new JSONResultProducer();
public static final String SPARQL_RESULTS_JSON = "application/sparql-results+json";
public static final String SPARQL_RESULTS_XML = "application/sparql-results+xml";
@Nullable
private Repository repository;
@Nullable
private Integer maxLimit;
@Nullable
private Integer maxQueryTime;
public SPARQLServlet(Repository repository, Integer limit, Integer maxQueryTime) {
this.repository = repository;
this.maxLimit = limit;
this.maxQueryTime = maxQueryTime;
}
public SPARQLServlet(Repository repository, Integer limit) {
this.repository = repository;
this.maxLimit = limit;
}
public SPARQLServlet(Repository repository) {
this.repository = repository;
}
public SPARQLServlet() {
}
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
if (repository == null) {
repository = (Repository) config.getServletContext().getAttribute(Repository.class.getName());
}
}
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
response.setDateHeader("Last-Modified", System.currentTimeMillis());
response.setCharacterEncoding("UTF-8");
String queryString = request.getParameter("query");
if (queryString == null) {
response.sendError(400, "No query given");
return;
}
String normalized = queryString.toLowerCase(Locale.ENGLISH).replaceAll("\\s+", " ");
if (!normalized.startsWith("ask") && !normalized.contains(" ask ")) {
String limit = request.getParameter("limit");
String offset = request.getParameter("offset");
if (maxLimit != null) {
if (limit != null) {
limit = String.valueOf(Math.min(Integer.valueOf(limit), maxLimit));
} else {
limit = maxLimit.toString();
}
}
if (limit != null) {
queryString += "\nLIMIT " + limit;
}
if (offset != null) {
queryString += "\nOFFSET " + offset;
}
}
try {
handleRequest(request, response, queryString);
} catch (Exception e) {
if (e.getMessage() != null) {
logger.error(e.getMessage(), e);
response.sendError(400, e.getMessage() + "\n\n" + request.getParameter("query")); // NOSONAR
} else {
logger.error("Caught Exception", e);
response.sendError(400);
}
}
}
protected void handleRequest(HttpServletRequest request,
HttpServletResponse response, String queryString)
throws IOException {
RDFConnection connection = repository.openConnection();
try {
SPARQLQuery query = connection.createQuery(QueryLanguage.SPARQL, queryString);
if (maxQueryTime != null) {
query.setMaxQueryTime(maxQueryTime);
}
String type = request.getParameter("type");
if (query.getResultType() == SPARQLQuery.ResultType.TRIPLES) {
String contentType = Format.RDFXML.getMimetype();
if ("turtle".equals(type)) {
contentType = Format.TURTLE.getMimetype();
} else if ("ntriples".equals(type)) {
contentType = Format.NTRIPLES.getMimetype();
} else {
contentType = getAcceptedType(request, contentType);
}
// normalize
contentType = Format.getFormat(contentType, Format.RDFXML).getMimetype();
response.setContentType(contentType);
query.streamTriples(response.getWriter(), contentType);
} else {
String contentType = SPARQL_RESULTS_XML;
if ("json".equals(type)) {
contentType = SPARQL_RESULTS_JSON;
} else {
contentType = getAcceptedType(request, contentType);
}
// normalize
if (!contentType.equals(SPARQL_RESULTS_JSON) && !contentType.equals(SPARQL_RESULTS_XML)) {
contentType = SPARQL_RESULTS_XML;
}
response.setContentType(contentType);
if (contentType.equals(SPARQL_RESULTS_JSON)) {
String jsonpCallback = request.getParameter("callback");
if (jsonpCallback != null) {
response.getWriter().write(jsonpCallback + "(");
}
jsonProducer.stream(query, response.getWriter());
if (jsonpCallback != null) {
response.getWriter().write(")");
}
} else {
xmlProducer.stream(query, response.getWriter());
}
}
} finally {
connection.close();
}
}
// TODO : make sure this works correctly
private String getAcceptedType(HttpServletRequest request, String defaultType) {
String accept = request.getHeader("Accept");
if (accept != null) {
return accept.contains(",") ? accept.substring(0, accept.indexOf(',')) : accept;
} else {
return defaultType;
}
}
}