package edu.kit.aifb.cumulus.webapp; import static edu.kit.aifb.cumulus.framework.util.Strings.isNullOrEmptyString; import static edu.kit.aifb.cumulus.webapp.HttpProtocol.*; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Iterator; import java.util.Locale; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.openrdf.model.Statement; import org.openrdf.query.BindingSet; import org.openrdf.query.BooleanQuery; import org.openrdf.query.GraphQuery; import org.openrdf.query.GraphQueryResult; import org.openrdf.query.MalformedQueryException; import org.openrdf.query.Query; import org.openrdf.query.QueryEvaluationException; import org.openrdf.query.QueryLanguage; import org.openrdf.query.QueryResult; import org.openrdf.query.QueryResultHandlerException; import org.openrdf.query.TupleQuery; import org.openrdf.query.TupleQueryResult; import org.openrdf.query.UpdateExecutionException; import org.openrdf.query.resultio.BooleanQueryResultFormat; import org.openrdf.query.resultio.QueryResultIO; import org.openrdf.query.resultio.TupleQueryResultFormat; import org.openrdf.repository.RepositoryConnection; import org.openrdf.repository.RepositoryException; import org.openrdf.repository.sail.SailRepository; import org.openrdf.rio.RDFFormat; import org.openrdf.rio.RDFHandlerException; import edu.kit.aifb.cumulus.framework.Environment.ConfigParams; import edu.kit.aifb.cumulus.log.MessageCatalog; import edu.kit.aifb.cumulus.store.CumulusStoreException; import edu.kit.aifb.cumulus.webapp.HttpProtocol.MimeTypes; import edu.kit.aifb.cumulus.webapp.HttpProtocol.Parameters; /** * Servlet that executes <a href="http://www.w3.org/TR/sparql11-overview/">SPARQL 1.1</a> updates and queries. * * @see {@link edu.kit.aifb.cumulus.webapp.HttpProtocol.Parameters} * @see <a href="http://www.w3.org/TR/sparql11-overview/">http://www.w3.org/TR/sparql11-overview/</a> * * @param query - HTTP parameter 'query', which holds a SPARQL query. * @param update - HTTP parameter 'update', which holds a SPARQL update. * @param base-uri - HTTP parameter 'base-uri', which is specifies the base URI used for SPARQL updates. * * @author Andreas Harth * @author Andreas Wagner * @author Andrea Gazzarini * * @since 0.6 */ public class SPARQLServlet extends AbstractCumulusServlet { private static final long serialVersionUID = -8252614862256454962L; private ThreadLocal<SimpleDateFormat> _rfc822Formatters = new ThreadLocal<SimpleDateFormat>() { @Override protected SimpleDateFormat initialValue() { final SimpleDateFormat rfc822 = new SimpleDateFormat("EEE', 'dd' 'MMM' 'yyyy' 'HH:mm:ss' 'Z", Locale.US); rfc822.setLenient(false); return rfc822; } }; @Override public void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { final String accept = parseAcceptHeader(request); final String query = getParameterValue(request, Parameters.QUERY), update = getParameterValue(request, Parameters.UPDATE); if (isNullOrEmptyString(query) && isNullOrEmptyString(update)) { sendError( request, response, HttpServletResponse.SC_BAD_REQUEST, MessageCatalog._00045_MISSING_QUERY_OR_UPDATE_PARAM); return; } RepositoryConnection connection = null; @SuppressWarnings("rawtypes") QueryResult resultset = null; try { final SailRepository repository = (SailRepository) getServletContext().getAttribute(ConfigParams.SESAME_REPO); if ((repository == null) || !repository.isInitialized()) { _log.error(MessageCatalog._00025_CUMULUS_SYSTEM_INTERNAL_FAILURE_MSG + " Repository was null or not initialized."); sendError( request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, MessageCatalog._00025_CUMULUS_SYSTEM_INTERNAL_FAILURE_MSG); return; } connection = repository.getConnection(); if (query != null) { final Query parsedQuery = connection.prepareQuery(QueryLanguage.SPARQL, query, parseBaseURI(request, null)); if (accept.equals(MimeTypes.TEXT_HTML)) { if (parsedQuery instanceof BooleanQuery) { request.setAttribute("isThereAResult", true); request.setAttribute("booleanQuery", true); request.setAttribute("query", query); request.setAttribute("result", ((BooleanQuery) parsedQuery).evaluate()); forwardTo(request, response, "query.vm"); } else if (parsedQuery instanceof TupleQuery) { resultset = ((TupleQuery) parsedQuery).evaluate(); final TupleQueryResult res = (TupleQueryResult) resultset; request.setAttribute("tupleQuery", true); request.setAttribute("query", query); if (resultset.hasNext()) { request.setAttribute("isThereAResult", true); request.setAttribute("tupleResult", resultset); request.setAttribute("wrappedResult", new Iterator<BindingSet>() { @Override public boolean hasNext() { try { return res.hasNext(); } catch (Exception exception) { return false; } } @Override public BindingSet next() { try { return res.next(); } catch (Exception exception) { return null; } } @Override public void remove() { // Nothing to be done... } }); } forwardTo(request, response, "query.vm"); } else if (parsedQuery instanceof GraphQuery) { resultset = ((GraphQuery) parsedQuery).evaluate(); request.setAttribute("graphQuery", true); request.setAttribute("query", query); if (resultset.hasNext()) { final GraphQueryResult res = (GraphQueryResult) resultset; request.setAttribute("isThereAResult", true); request.setAttribute("graphResult", resultset); request.setAttribute("wrappedResult", new Iterator<Statement>() { @Override public boolean hasNext() { try { return res.hasNext(); } catch (Exception exception) { return false; } } @Override public Statement next() { try { return res.next(); } catch (Exception exception) { return null; } } @Override public void remove() { // Nothing to be done... } }); } forwardTo(request, response, "query.vm"); } } else { final Calendar c = Calendar.getInstance(); c.add(Calendar.DATE, 7); response.setHeader("Cache-Control", "public"); response.setHeader("Expires", _rfc822Formatters.get().format(c.getTime())); response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Vary", "Accept"); if (parsedQuery instanceof BooleanQuery) { BooleanQueryResultFormat format = BooleanQueryResultFormat.forMIMEType(accept, BooleanQueryResultFormat.SPARQL); response.setContentType(format.getDefaultMIMEType()); QueryResultIO.writeBoolean( ((BooleanQuery) parsedQuery).evaluate(), format, response.getOutputStream()); } else if (parsedQuery instanceof TupleQuery) { TupleQueryResultFormat format = TupleQueryResultFormat.forMIMEType(accept, TupleQueryResultFormat.SPARQL); response.setContentType(format.getDefaultMIMEType()); resultset = ((TupleQuery) parsedQuery).evaluate(); QueryResultIO.write( (TupleQueryResult) resultset, format, response.getOutputStream()); } else if (parsedQuery instanceof GraphQuery) { RDFFormat format = RDFFormat.forMIMEType(accept, RDFFormat.RDFXML); response.setContentType(format.getDefaultMIMEType()); resultset = ((GraphQuery) parsedQuery).evaluate(); QueryResultIO.write( (GraphQueryResult) resultset, format, response.getOutputStream()); } response.setStatus(HttpServletResponse.SC_OK); } } else if (update != null) { connection.prepareUpdate(QueryLanguage.SPARQL, update, parseBaseURI(request)).execute(); response.setStatus(HttpServletResponse.SC_OK); } } catch (final QueryResultHandlerException e) { _log.debug(MessageCatalog._00115_WEB_MODULE_REQUEST_NOT_VALID, e); sendError(request, response, HttpServletResponse.SC_BAD_REQUEST, MessageCatalog._00115_WEB_MODULE_REQUEST_NOT_VALID, e); } catch (final RDFHandlerException e) { _log.debug(MessageCatalog._00115_WEB_MODULE_REQUEST_NOT_VALID, e); sendError(request, response, HttpServletResponse.SC_BAD_REQUEST, MessageCatalog._00115_WEB_MODULE_REQUEST_NOT_VALID, e); } catch (final IOException e) { _log.error(MessageCatalog._00025_CUMULUS_SYSTEM_INTERNAL_FAILURE_MSG, e); sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, MessageCatalog._00025_CUMULUS_SYSTEM_INTERNAL_FAILURE_MSG, e); } catch (QueryEvaluationException e) { _log.debug(MessageCatalog._00115_WEB_MODULE_REQUEST_NOT_VALID, e); sendError(request, response, HttpServletResponse.SC_BAD_REQUEST, MessageCatalog._00115_WEB_MODULE_REQUEST_NOT_VALID, e); } catch (RepositoryException e) { _log.error(MessageCatalog._00025_CUMULUS_SYSTEM_INTERNAL_FAILURE_MSG, e); sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, MessageCatalog._00025_CUMULUS_SYSTEM_INTERNAL_FAILURE_MSG, e); } catch (MalformedQueryException e) { _log.debug(MessageCatalog._00115_WEB_MODULE_REQUEST_NOT_VALID, e); sendError(request, response, HttpServletResponse.SC_BAD_REQUEST, MessageCatalog._00115_WEB_MODULE_REQUEST_NOT_VALID, e); } catch (UpdateExecutionException e) { _log.debug(MessageCatalog._00115_WEB_MODULE_REQUEST_NOT_VALID, e); sendError(request, response, HttpServletResponse.SC_BAD_REQUEST, MessageCatalog._00115_WEB_MODULE_REQUEST_NOT_VALID, e); } catch (final CumulusStoreException e) { _log.error(MessageCatalog._00025_CUMULUS_SYSTEM_INTERNAL_FAILURE, e); sendError( request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, MessageCatalog._00025_CUMULUS_SYSTEM_INTERNAL_FAILURE_MSG, e); } catch (final Exception e) { _log.error(MessageCatalog._00025_CUMULUS_SYSTEM_INTERNAL_FAILURE, e); e.printStackTrace(); sendError( request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, MessageCatalog._00025_CUMULUS_SYSTEM_INTERNAL_FAILURE, e); } finally { // CHECKSTYLE:OFF // @formatter:off if (resultset != null) { try { resultset.close();} catch (final Exception ignore) {}}; if (connection != null) { try { connection.close();} catch (final Exception ignore) {}}; // @formatter:on // CHECKSTYLE:ON } } }