package com.clarkparsia.empire.ds.impl; import com.clarkparsia.empire.ds.ResultSet; import com.clarkparsia.empire.ds.QueryException; import com.clarkparsia.empire.impl.RdfQueryFactory; import com.clarkparsia.empire.impl.sparql.SPARQLDialect; import com.complexible.common.openrdf.model.GraphIO; import com.complexible.common.openrdf.util.AdunaIterations; import com.complexible.common.web.ParameterList; import com.complexible.common.web.Request; import com.complexible.common.web.HttpResourceImpl; import com.complexible.common.web.HttpResource; import com.complexible.common.web.HttpHeaders; import com.complexible.common.web.MimeTypes; import com.complexible.common.web.Response; import java.net.ConnectException; import java.net.URL; import java.io.IOException; import org.openrdf.model.Graph; import org.openrdf.rio.RDFFormat; import org.openrdf.rio.RDFParseException; import org.openrdf.query.TupleQueryResult; import org.openrdf.query.TupleQueryResultHandlerException; import org.openrdf.query.resultio.BooleanQueryResultFormat; import org.openrdf.query.resultio.QueryResultIO; import org.openrdf.query.resultio.QueryResultParseException; import org.openrdf.query.resultio.TupleQueryResultFormat; import org.openrdf.query.resultio.UnsupportedQueryResultFormatException; /** * <p>Simple implementation of the DataSource interface for a generic read-only sparql endpoint.</p> * * @author Michael Grove * @since 0.6.5 * @version 0.7 */ public class SparqlEndpointDataSource extends AbstractDataSource { /** * Constant for the query requests */ private static final String PARAM_QUERY = "query"; /** * The URL of the endpoint */ private URL mURL; /** * Whether or not to use HTTP GET requests for queries */ private boolean mUseGetForQueries = true; /** * Create a new SparqlEndpointDataSource * @param theURL the URL of the sparql endpoint. */ public SparqlEndpointDataSource(final URL theURL) { this(theURL, true, SPARQLDialect.instance()); } /** * Create a new SparqlEndpointDataSource * @param theURL the URL of the sparql endpoint * @param theDialect the sparql query dialect to use */ public SparqlEndpointDataSource(final URL theURL, SPARQLDialect theDialect) { this(theURL, true, theDialect); } /** * Create a new SparqlEndpointDataSource * @param theURL the URL of the sparql endpoint. * @param theUseGetForQueries whether or not to use HTTP GET requests for queries */ public SparqlEndpointDataSource(final URL theURL, final boolean theUseGetForQueries) { mURL = theURL; mUseGetForQueries = theUseGetForQueries; setQueryFactory(new RdfQueryFactory(this, SPARQLDialect.instance())); } /** * Create a new SparqlEndpointDataSource * @param theURL the URL of the sparql endpoint. * @param theUseGetForQueries whether or not to use HTTP GET requests for queries * @param theDialect the query dialect to use for the endpoint */ public SparqlEndpointDataSource(final URL theURL, final boolean theUseGetForQueries, SPARQLDialect theDialect) { mURL = theURL; mUseGetForQueries = theUseGetForQueries; setQueryFactory(new RdfQueryFactory(this, theDialect)); } /** * @inheritDoc */ public void connect() throws ConnectException { setConnected(true); } /** * @inheritDoc */ public void disconnect() { setConnected(false); } /** * Return the URL of this SPARQL endpoint * @return the endpoint URL */ public URL getURL() { return mURL; } /** * @inheritDoc */ public ResultSet selectQuery(final String theQuery) throws QueryException { assertConnected(); return new AbstractResultSet(AdunaIterations.iterator(executeSPARQLQuery(theQuery, TupleQueryResult.class))) { public void close() { // no-op } }; } private <T> T executeSPARQLQuery(String theQuery, Class<T> clazz) throws QueryException { Response aResponse = null; try { aResponse = createSPARQLQueryRequest(theQuery).execute(); if (aResponse.hasErrorCode()) { throw responseToException(theQuery, aResponse); } else { try { if (Boolean.class.equals(clazz)) { return (T) parseBooleanResult(aResponse); } else { return (T) parseTupleResult(aResponse); } } catch (Exception e) { throw new QueryException("Could not parse SPARQL-XML results", e); } } } catch (IOException e) { throw new QueryException(e); } finally { if (aResponse != null) { try { aResponse.close(); } catch (IOException e) { System.err.println("There was an error while closing the http connection: " + e.getMessage()); } } } } private Request createSPARQLQueryRequest(String theQuery) { HttpResource aRes = new HttpResourceImpl(mURL); Request aQueryRequest; // auto prefix queries w/ rdf and rdfs namespaces ParameterList aParams = new ParameterList() .add(PARAM_QUERY, theQuery); if (mUseGetForQueries) { aQueryRequest = aRes.initGet() .addHeader(HttpHeaders.Accept.getName(), TupleQueryResultFormat.SPARQL.getDefaultMIMEType()) .setParameters(aParams); } else { aQueryRequest = aRes.initPost() .addHeader(HttpHeaders.ContentType.getName(), MimeTypes.FormUrlEncoded.getMimeType()) .addHeader(HttpHeaders.Accept.getName(), TupleQueryResultFormat.SPARQL.getDefaultMIMEType()) .setBody(aParams.getURLEncoded()); } return aQueryRequest; } /** * Given a response, return it as a QueryException by parsing out the errore message and content * @param theQuery the query being executed that caused the error * @param theResponse the response which indicate a server error * @return the Response as an Exception */ private QueryException responseToException(String theQuery, Response theResponse) { return new QueryException("Error evaluating query: " + theQuery + "\n(" + theResponse.getResponseCode() + ") " + theResponse.getMessage() + "\n\n" + theResponse.getContent()); } /** * @inheritDoc */ public boolean ask(final String theQuery) throws QueryException { assertConnected(); return executeSPARQLQuery(theQuery, Boolean.class); } private Boolean parseBooleanResult(Response theResponse) throws QueryResultParseException, UnsupportedQueryResultFormatException, IOException { return QueryResultIO.parse(theResponse.getContent(), BooleanQueryResultFormat.SPARQL); } private TupleQueryResult parseTupleResult(Response theResponse) throws QueryResultParseException, TupleQueryResultHandlerException, UnsupportedQueryResultFormatException, IOException { return QueryResultIO.parse(theResponse.getContent(), TupleQueryResultFormat.SPARQL); } /** * @inheritDoc */ public Graph describe(final String theQuery) throws QueryException { return graphQuery(theQuery); } /** * @inheritDoc */ public Graph graphQuery(final String theQuery) throws QueryException { assertConnected(); HttpResource aRes = new HttpResourceImpl(mURL); ParameterList aParams = new ParameterList() .add(PARAM_QUERY, theQuery); Request aQueryRequest; Response aResponse = null; try { if (mUseGetForQueries) { aQueryRequest = aRes.initGet() .addHeader(HttpHeaders.Accept.getName(), RDFFormat.TURTLE.getDefaultMIMEType()) .setParameters(aParams); } else { aQueryRequest = aRes.initPost() .addHeader(HttpHeaders.ContentType.getName(), MimeTypes.FormUrlEncoded.getMimeType()) .addHeader(HttpHeaders.Accept.getName(), RDFFormat.TURTLE.getDefaultMIMEType()) .setBody(aParams.getURLEncoded()); } aResponse = aQueryRequest.execute(); if (aResponse.hasErrorCode()) { throw responseToException(theQuery, aResponse); } else { try { return GraphIO.readGraph(aResponse.getContent(), RDFFormat.TURTLE); } catch (RDFParseException e) { throw new QueryException("Error while parsing rdf/xml-formatted query results", e); } } } catch (IOException e) { throw new QueryException(e); } finally { if (aResponse != null) { try { aResponse.close(); } catch (IOException e) { System.err.println("There was an error while closing the http connection: " + e.getMessage()); } } } } }