com.bigdata.rdf.sail.webapp.client.RemoteRepository Maven / Gradle / Ivy
/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2014. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
[email protected]
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.bigdata.rdf.sail.webapp.client;
import info.aduna.io.IOUtil;
import java.io.Closeable;
import java.io.File;
import java.io.InputStream;
import java.io.Reader;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.mime.FormBodyPart;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.ByteArrayBody;
import org.apache.log4j.Logger;
import org.openrdf.model.Resource;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.query.GraphQueryResult;
import org.openrdf.query.TupleQueryResult;
import org.openrdf.rio.RDFFormat;
import com.bigdata.rdf.sail.remote.BigdataSailRemoteRepository;
/**
* Java API to the Nano Sparql Server.
*
* Note: The {@link RemoteRepository} object SHOULD be reused for multiple
* operations against the same end point.
*
* @see NanoSparqlServer REST API
*
* @see Create
* a bigdata-client jar for the NSS REST API
*/
public class RemoteRepository extends RemoteRepositoryBase {
private static final transient Logger log = Logger
.getLogger(RemoteRepository.class);
/**
* The {@link RemoteRepositoryManager} object use to manage all access to the
* service backing the {@link #sparqlEndpointURL}.
*/
private final RemoteRepositoryManager mgr;
/**
* The service end point for the default data set.
*/
private final String sparqlEndpointURL;
/**
* When non-null
the operations against the
* {@link #sparqlEndpointURL} will be isolated by the transaction.
*/
private final IRemoteTx tx;
/**
* Return the SPARQL end point.
*/
public String getSparqlEndPoint() {
return sparqlEndpointURL;
}
@Override
public String toString() {
return super.toString() + "{sparqlEndpoint=" + sparqlEndpointURL
+ ", mgr=" + getRemoteRepositoryManager()
+ (tx == null ? "" : ", tx=" + tx) + "}";
}
/**
* The {@link RemoteRepositoryManager} object use to manage all access to the
* service backing the {@link #sparqlEndpointURL}.
*/
public RemoteRepositoryManager getRemoteRepositoryManager() {
return mgr;
}
/**
* Create a connection to a remote repository. This can be used with any
* SPARQL end point as long as you restrict yourself to SPARQL QUERY or
* SPARQL UPDATE. The other methods can only be used with a blazegraph
* backend.
*
* @param mgr
* @param sparqlEndpointURL
* The SPARQL end point.
*
* @see RemoteRepositoryManager
* @see HttpClientConfigurator
* @see
* HALoadBalancer
*/
RemoteRepository(final RemoteRepositoryManager mgr,
final String sparqlEndpointURL, final IRemoteTx tx) {
if (mgr == null)
throw new IllegalArgumentException();
if (sparqlEndpointURL == null)
throw new IllegalArgumentException();
this.mgr = mgr;
this.sparqlEndpointURL = sparqlEndpointURL;
this.tx = tx;
}
/**
* Flyweight method returns a wrapper for the sparql end point associated
* with this instance.
*/
public BigdataSailRemoteRepository getBigdataSailRemoteRepository() {
return new BigdataSailRemoteRepository(this);
}
/**
* Post a GraphML file to the blueprints layer of the remote bigdata instance.
*/
public long postGraphML(final String path) throws Exception {
// TODO Allow client to specify UUID for postGraphML. See #1254.
final UUID uuid = UUID.randomUUID();
final ConnectOptions opts = mgr.newConnectOptions(sparqlEndpointURL, uuid, tx);
opts.addRequestParam("blueprints");
JettyResponseListener response = null;
try {
final File file = new File(path);
if (!file.exists()) {
throw new RuntimeException("cannot locate file: " + file.getAbsolutePath());
}
final byte[] data = IOUtil.readBytes(file);
final ByteArrayEntity entity = new ByteArrayEntity(data);
entity.setContentType(ConnectOptions.MIME_GRAPH_ML);
opts.entity = entity;
opts.setAcceptHeader(ConnectOptions.MIME_APPLICATION_XML);
checkResponseCode(response = doConnect(opts));
final MutationResult result = mutationResults(response);
return result.mutationCount;
} finally {
if (response != null)
response.abort();
}
}
/**
* Return the SPARQL 1.1 Service Description for the end point.
*/
public GraphQueryResult getServiceDescription() throws Exception {
return getServiceDescription(UUID.randomUUID());
}
public GraphQueryResult getServiceDescription(final UUID uuid)
throws Exception {
// TODO Unit test when isolated by a transaction. The server is already
// creating a tx for this so it might hit a fence post.
final ConnectOptions opts = mgr.newConnectOptions(sparqlEndpointURL, uuid, tx);
opts.method = "GET";
opts.setAcceptHeader(ConnectOptions.DEFAULT_GRAPH_ACCEPT_HEADER);
return mgr
.graphResults(opts, uuid, null/* preparedListener */);
}
/**
* Prepare a tuple (select) query.
*
* @param query
* the query string
*
* @return The {@link IPreparedTupleQuery}.
*/
public IPreparedTupleQuery prepareTupleQuery(final String query)
throws Exception {
return prepareTupleQuery(query, UUID.randomUUID());
}
/**
* Prepare a tuple (select) query.
*
* @param query
* the query string
*
* @param uuid
* The {@link UUID} used to identify this query.
*
* @return The {@link IPreparedTupleQuery}.
*/
public IPreparedTupleQuery prepareTupleQuery(final String query,
final UUID uuid) throws Exception {
return new TupleQuery(
mgr.newQueryConnectOptions(sparqlEndpointURL, uuid, tx), uuid, query);
}
/**
* Prepare a graph query.
*
* @param query
* the query string
*
* @return The {@link IPreparedGraphQuery}
*/
public IPreparedGraphQuery prepareGraphQuery(final String query)
throws Exception {
return prepareGraphQuery(query, UUID.randomUUID());
}
/**
* Prepare a graph query.
*
* @param query
* the query string
* @param uuid
* The {@link UUID} used to identify this query.
*
* @return The {@link IPreparedGraphQuery}
*/
public IPreparedGraphQuery prepareGraphQuery(final String query,
final UUID uuid) throws Exception {
return new GraphQuery(
mgr.newQueryConnectOptions(sparqlEndpointURL, uuid, tx), uuid, query);
}
/**
* Prepare a boolean (ask) query.
*
* @param query
* the query string
*
* @return the {@link IPreparedBooleanQuery}
*/
public IPreparedBooleanQuery prepareBooleanQuery(final String query)
throws Exception {
return prepareBooleanQuery(query, UUID.randomUUID());
}
/**
* Prepare a boolean (ask) query.
*
* @param query
* the query string
*
* @param uuid
* The {@link UUID} used to identify this query.
*
* @return the {@link IPreparedBooleanQuery}
*/
public IPreparedBooleanQuery prepareBooleanQuery(final String query, final UUID uuid)
throws Exception {
return new BooleanQuery(mgr.newQueryConnectOptions(sparqlEndpointURL, uuid, tx), uuid, query);
}
/**
* Prepare a SPARQL UPDATE request.
*
* @param updateStr
* The SPARQL UPDATE request.
*
* @return The {@link SparqlUpdate} operation.
*
* @throws Exception
*/
public IPreparedSparqlUpdate prepareUpdate(final String updateStr)
throws Exception {
return prepareUpdate(updateStr, UUID.randomUUID());
}
/**
* Prepare a SPARQL UPDATE request.
*
* @param updateStr
* The SPARQL UPDATE request.
* @param uuid
* The {@link UUID} used to identify this query.
*
* @return The {@link SparqlUpdate} operation.
*
* @throws Exception
*/
public IPreparedSparqlUpdate prepareUpdate(final String updateStr, final UUID uuid)
throws Exception {
return new SparqlUpdate(mgr.newUpdateConnectOptions(sparqlEndpointURL, uuid,
tx), uuid, updateStr);
}
/**
* Return all matching statements.
*
* @param subj
* @param pred
* @param obj
* @param includeInferred
* @param contexts
* @return
* @throws Exception
*/
public GraphQueryResult getStatements(final Resource subj, final URI pred,
final Value obj, final boolean includeInferred,
final Resource... contexts) throws Exception {
if (contexts == null) {
// Note: May not be a null Resource[] reference.
// MAY be Resource[null], which is the openrdf nullGraph.
// See #1177
throw new IllegalArgumentException();
}
final UUID uuid = UUID.randomUUID();
final ConnectOptions opts = mgr.newQueryConnectOptions(sparqlEndpointURL, uuid, tx);
opts.addRequestParam("GETSTMTS");
opts.addRequestParam(RemoteRepositoryDecls.INCLUDE_INFERRED,
Boolean.toString(includeInferred));
if (subj != null) {
opts.addRequestParam("s", EncodeDecodeValue.encodeValue(subj));
}
if (pred != null) {
opts.addRequestParam("p", EncodeDecodeValue.encodeValue(pred));
}
if (obj != null) {
opts.addRequestParam("o", EncodeDecodeValue.encodeValue(obj));
}
opts.addRequestParam("c", EncodeDecodeValue.encodeContexts(contexts));
opts.setAcceptHeader(ConnectOptions.DEFAULT_GRAPH_ACCEPT_HEADER);
JettyResponseListener resp = null;
try {
checkResponseCode(resp = doConnect(opts));
GraphQueryResult result = mgr.graphResults(opts, null, null);
return result;
} finally {
if (resp != null)
resp.abort();
}
}
/**
* Method to line up with the Sesame interface.
*
* @param s
* The subject (optional).
* @param p
* The predicate (optional).
* @param o
* The value (optional).
* @param includeInferred
* when true
inferred statements will also be
* considered.
* @param c
* The contexts (optional, BUT may not be a null Resource[]).
*
* @return true
iff a statement exists that matches the request.
*
* @throws Exception
*
* @see hasStatements can
* overestimate and ignores includeInferred (REST API)
* @see Resource... contexts
* not encoded/decoded according to openrdf semantics (REST API)
*/
public boolean hasStatement(final Resource s, final URI p, final Value o,
final boolean includeInferred, final Resource... c) throws Exception {
if (c == null) {
// Note: May not be a null Resource[] reference.
// MAY be Resource[null], which is the openrdf nullGraph.
// See #1177
throw new IllegalArgumentException();
}
final UUID uuid = UUID.randomUUID();
final ConnectOptions opts = mgr.newQueryConnectOptions(sparqlEndpointURL, uuid, tx);
opts.addRequestParam("HASSTMT");
opts.addRequestParam(RemoteRepositoryDecls.INCLUDE_INFERRED,
Boolean.toString(includeInferred));
if (s != null) {
opts.addRequestParam("s", EncodeDecodeValue.encodeValue(s));
}
if (p != null) {
opts.addRequestParam("p", EncodeDecodeValue.encodeValue(p));
}
if (o != null) {
opts.addRequestParam("o", EncodeDecodeValue.encodeValue(o));
}
opts.addRequestParam("c", EncodeDecodeValue.encodeContexts(c));
JettyResponseListener resp = null;
try {
opts.setAcceptHeader(ConnectOptions.MIME_APPLICATION_XML);
checkResponseCode(resp = doConnect(opts));
final BooleanResult result = RemoteRepositoryManager.booleanResults(resp);
return result.result;
} finally {
if (resp != null)
resp.abort();
}
}
private String asConstOrVar(final AST2SPARQLUtil util, final String var,
final Value val) {
if (val == null)
return var;
return util.toExternal(val);
}
/**
* Cancel a query running remotely on the server.
*
* @param queryID
* the UUID of the query to cancel
*/
public void cancel(final UUID queryId) throws Exception {
mgr.cancel(queryId);
}
/**
* Perform a fast range count on the statement indices for a given triple
* (quad) pattern.
*
* @param s
* the subject (can be null)
* @param p
* the predicate (can be null)
* @param o
* the object (can be null)
* @param c
* the context (can be null)
*
* @return the range count
*
* @see Add REST API method
* for exact range counts
*/
public long rangeCount(final Resource s, final URI p, final Value o,
final Resource... c) throws Exception {
return rangeCount(false/* exact */, s, p, o, c);
}
/**
* Perform a range count on the statement indices for a given triple (quad)
* pattern.
*
* Note: fast range counts are *fast*. They require two key probes into the
* indices. Exact range counts are only fast when the indices do not support
* isolation or fused views. Isolation is used if the namespace supports full
* read/write transactions. Fused views are used in scale-out to model shards
* and are also used in full read/write transaction support.
*
* @param exact
* if true
then an exact range count is requested,
* otherwise a fast range count is requested.
* @param s
* the subject (can be null)
* @param p
* the predicate (can be null)
* @param o
* the object (can be null)
* @param c
* the context (can be null)
*
* @return the range count
*
* @see Add REST API method
* for exact range counts
* @see Resource... contexts
* not encoded/decoded according to openrdf semantics (REST API)
*/
public long rangeCount(final boolean exact, final Resource s, final URI p,
final Value o, final Resource... c) throws Exception {
if (c == null) {
// Note: May not be a null Resource[] reference.
// MAY be Resource[null], which is the openrdf nullGraph.
// See #1177
throw new IllegalArgumentException();
}
// TODO Allow client to specify UUID for ESTCARD. See #1254.
final UUID uuid = UUID.randomUUID();
final ConnectOptions opts = mgr.newQueryConnectOptions(sparqlEndpointURL, uuid, tx);
opts.addRequestParam("ESTCARD");
if (exact) {
opts.addRequestParam("exact", "true");
}
if (s != null) {
opts.addRequestParam("s", EncodeDecodeValue.encodeValue(s));
}
if (p != null) {
opts.addRequestParam("p", EncodeDecodeValue.encodeValue(p));
}
if (o != null) {
opts.addRequestParam("o", EncodeDecodeValue.encodeValue(o));
}
opts.addRequestParam("c", EncodeDecodeValue.encodeContexts(c));
JettyResponseListener resp = null;
try {
opts.setAcceptHeader(ConnectOptions.MIME_APPLICATION_XML);
checkResponseCode(resp = doConnect(opts));
final RangeCountResult result = rangeCountResults(resp);
return result.rangeCount;
} finally {
if (resp != null)
resp.abort();
}
}
/**
* Perform a fast range count on the statement indices. This reports an
* estimate of the number of statements in the namespace. That estimate is
* exact unless the namespace is provisioned for full read/write transactions
* or the endpoint is scale-out.
*
* @return the range count (#of statements in the database).
*/
public long size() throws Exception {
return rangeCount(/* s */null, /* p */null, /* o */null);
}
/**
* Return a list of contexts in use in a remote quads database.
*
* FIXME This should be a streaming response for scalability. That will
* require us to change the return type for the method. E.g., to something
* that implements {@link Closeable}. Callers will then have to invoke
* {@link Closeable#close()} to avoid leaking resources. (This change could
* be made when making the CONTEXTS an operation that can be given a UUID
* for cancellation by the client.)
*/
public Collection getContexts() throws Exception {
// TODO Allow client to specify UUID for CONTEXTS. See #1254.
final UUID uuid = UUID.randomUUID();
final ConnectOptions opts = mgr.newQueryConnectOptions(sparqlEndpointURL, uuid, tx);
opts.addRequestParam("CONTEXTS");
JettyResponseListener resp = null;
try {
opts.setAcceptHeader(ConnectOptions.MIME_APPLICATION_XML);
checkResponseCode(resp = doConnect(opts));
final ContextsResult result = contextsResults(resp);
return result.contexts;
} finally {
if (resp != null)
resp.abort();
}
}
/**
* Adds RDF data to the remote repository.
*
* @param add
* The RDF data to be added.
*
* @return The mutation count.
*/
public long add(final AddOp add) throws Exception {
return add(add, UUID.randomUUID()/*queryId*/);
}
/**
* Adds RDF data to the remote repository.
*
* @param add
* The RDF data to be added.
* @param uuid
* The {@link UUID} used to identify this query.
*
* @return The mutation count.
*
* @see See #1254 / BLZG-1259
*/
public long add(final AddOp add, final UUID uuid) throws Exception {
if (add == null)
throw new IllegalArgumentException();
final ConnectOptions opts = mgr.newUpdateConnectOptions(
sparqlEndpointURL, uuid, tx);
add.prepareForWire();
if (add.format != null) {
final ByteArrayEntity entity = new ByteArrayEntity(add.data);
entity.setContentType(add.format.getDefaultMIMEType());
opts.entity = entity;
}
if (add.uris != null) {
// set the resource(s) to load
opts.addRequestParam("uri", add.uris.toArray(new String[0]));
}
if (add.context != null && add.context.length > 0) {
// set the default context.
opts.addRequestParam("context-uri", toStrings(add.context));
}
opts.setAcceptHeader(ConnectOptions.MIME_APPLICATION_XML);
JettyResponseListener response = null;
boolean ok = false;
try {
checkResponseCode(response = doConnect(opts));
final MutationResult result = mutationResults(response);
ok = true;
return result.mutationCount;
} finally {
if (response != null) {
// Abort the http response handling.
response.abort();
if (!ok) {
try {
/*
* POST back to the server to cancel the request in case
* it is still running on the server.
*/
cancel(uuid);
} catch (Exception ex) {
log.warn(ex);
}
}
}
}
}
/**
* Removes RDF data from the remote repository.
*
* @param remove
* The RDF data to be removed.
*
* @return The mutation count.
*
* @see Resource... contexts
* not encoded/decoded according to openrdf semantics (REST API)
*/
public long remove(final RemoveOp remove) throws Exception {
return remove(remove, UUID.randomUUID());
}
/**
* Removes RDF data from the remote repository.
*
* @param remove
* The RDF data to be removed.
*
* @return The mutation count.
*
* @see Resource... contexts
* not encoded/decoded according to openrdf semantics (REST API)
* @see #1254 / BLZG-1259
*/
public long remove(final RemoveOp remove, final UUID uuid) throws Exception {
if (remove == null)
throw new IllegalArgumentException();
final ConnectOptions opts = mgr.newUpdateConnectOptions(sparqlEndpointURL, uuid, tx);
remove.prepareForWire();
if (remove.format != null) {
opts.method = "POST";
opts.addRequestParam("delete");
final ByteArrayEntity entity = new ByteArrayEntity(remove.data);
entity.setContentType(remove.format.getDefaultMIMEType());
opts.entity = entity;
if (remove.context != null && remove.context.length > 0) {
// set the default context.
opts.addRequestParam("context-uri", toStrings(remove.context));
}
} else {
opts.method = "DELETE";
if (remove.query != null) {
opts.addRequestParam("query", remove.query);
}
if (remove.s != null) {
opts.addRequestParam("s", EncodeDecodeValue.encodeValue(remove.s));
}
if (remove.p != null) {
opts.addRequestParam("p", EncodeDecodeValue.encodeValue(remove.p));
}
if (remove.o != null) {
opts.addRequestParam("o", EncodeDecodeValue.encodeValue(remove.o));
}
if (remove.c != null) {
/*
* Note: Due to the way in which the RemoveOp declares [c] even when
* it is not a "delete-by-accesspath" request, we have to check for
* [c!=null] here.
*
* TODO This could be fixed if we had a factory for RemoveOp such
* that the concrete instance only declared [c] when it was a
* delete-by-access-path request. See #1177
*/
opts.addRequestParam("c",
EncodeDecodeValue.encodeContexts(remove.c));
}
}
opts.setAcceptHeader(ConnectOptions.MIME_APPLICATION_XML);
JettyResponseListener response = null;
boolean ok = false;
try {
checkResponseCode(response = doConnect(opts));
final MutationResult result = mutationResults(response);
ok = true;
return result.mutationCount;
} finally {
if (response != null) {
// Abort the http response handling.
response.abort();
if (!ok) {
try {
/*
* POST back to the server to cancel the request in case
* it is still running on the server.
*/
cancel(uuid);
} catch (Exception ex) {
log.warn(ex);
}
}
}
}
}
/**
* Perform an ACID update (delete+insert) per the semantics of the NanoSparqlServer.
*
* Currently, the only combination supported is delete by query with add by
* post (Iterable and File). You can embed statements you want to
* delete inside a construct query without a where clause.
*
* @param remove
* The RDF data to be removed.
* @param add
* The RDF data to be added.
*
* @return The mutation count.
*/
public long update(final RemoveOp remove, final AddOp add) throws Exception {
return update(remove, add, UUID.randomUUID());
}
/**
* Perform an ACID update
*
* There are two different patterns which are supported:
*
* - UPDATE (DELETE statements selected by a QUERY plus INSERT statements
* from Request Body using PUT)
* - Where query is a CONSTRUCT or DESCRIBE query.
* Note: The QUERY + DELETE operation is ACID.
* Note: You MAY specify a CONSTRUCT query with an empty WHERE clause in
* order to specify a set of statements to be removed without reference to
* statements already existing in the database.
* - UPDATE (POST with Multi-Part Request Body)
* - You can specify two sets of serialized statements - one to be removed
* and one to be added..
*
*
* @param remove
* The RDF data to be removed (either a collection of statements
* or a CONSTRUCT or DESCRIBE QUERY identifying the data to be
* deleted).
* @param add
* The RDF data to be added (must be a collection of statements).
*
* @return The mutation count.
* @see http
* ://wiki.blazegraph.com/wiki/index.php/NanoSparqlServer#UPDATE_.28D
* ELETE_.2B_INSERT.29
*/
public long update(final RemoveOp remove, final AddOp add, final UUID uuid)
throws Exception {
if(remove == null)
throw new IllegalArgumentException();
if(add == null)
throw new IllegalArgumentException();
remove.prepareForWire();
add.prepareForWire();
final ConnectOptions opts = mgr.newUpdateConnectOptions(
sparqlEndpointURL, uuid, tx);
if (remove.format != null) {
// Code path when caller specifies data to be removed.
opts.method = "POST";
opts.addRequestParam("update");
// Note: Multi-part MIME request entity.
final MultipartEntity entity = new MultipartEntity();
// The data to be removed.
entity.addPart(new FormBodyPart("remove", new ByteArrayBody(
remove.data, remove.format.getDefaultMIMEType(), "remove")));
// The data to be added.
entity.addPart(new FormBodyPart("add", new ByteArrayBody(add.data,
add.format.getDefaultMIMEType(), "add")));
// The multi-part request entity.
opts.entity = entity;
} else {
// Code path when caller specifies CONSTRUCT or DESCRIBE query
// identifying the data to be removed.
opts.method = "PUT";
// QUERY specifying the data to be removed.
opts.addRequestParam("query", remove.query);
// The data to be added.
final ByteArrayEntity entity = new ByteArrayEntity(add.data);
entity.setContentType(add.format.getDefaultMIMEType());
opts.entity = entity;
}
if (add.context != null) {
// set the default context for insert.
opts.addRequestParam("context-uri-insert", toStrings(add.context));
}
if (remove.context != null) {
// set the default context for delete.
opts.addRequestParam("context-uri-delete", toStrings(remove.context));
}
opts.setAcceptHeader(ConnectOptions.MIME_APPLICATION_XML);
JettyResponseListener response = null;
boolean ok = false;
try {
checkResponseCode(response = doConnect(opts));
final MutationResult result = mutationResults(response);
ok = true;
return result.mutationCount;
} finally {
if (response != null) {
// Abort the http response handling.
response.abort();
if (!ok) {
try {
/*
* POST back to the server to cancel the request in case
* it is still running on the server.
*/
cancel(uuid);
} catch (Exception ex) {
log.warn(ex);
}
}
}
}
}
/**
* A prepared query will hold metadata for a particular query instance.
*
* Right now, the only metadata is the query ID.
*/
protected abstract class QueryOrUpdate implements IPreparedOperation, IPreparedQuery {
protected final ConnectOptions opts;
private final UUID uuid;
protected final String query;
private final boolean update;
public QueryOrUpdate(final ConnectOptions opts, final UUID id,
final String query) {
this(opts, id, query, false/* update */);
}
/**
*
* @param id
* The query id.
* @param query
* The SPARQL query or update string.
* @param update
* true
iff this is a SPARQL update.
*/
public QueryOrUpdate(final ConnectOptions opts, final UUID uuid,
final String query, final boolean update) {
if (opts == null)
throw new IllegalArgumentException();
if (query == null)
throw new IllegalArgumentException();
if (uuid == null)
throw new IllegalArgumentException();
this.opts = opts;
this.uuid = uuid;
this.query = query;
this.update = update;
}
@Override
final public UUID getQueryId() {
return uuid;
}
@Override
public final boolean isUpdate() {
return update;
}
public void addRequestParam(String name, String... val) {
opts.addRequestParam(name, val);
}
/**
* Setup the connection options.
*/
protected void setupConnectOptions() {
opts.method = mgr.getQueryMethod();
if(update) {
opts.addRequestParam("update", query);
} else {
opts.addRequestParam("query", query);
}
final UUID queryId = getQueryId();
if (queryId != null)
opts.addRequestParam(QUERYID, queryId.toString());
}
@Override
public void setAcceptHeader(final String value) {
opts.setAcceptHeader(value);
}
@Override
public void setHeader(final String name, final String value) {
opts.setHeader(name, value);
}
@Override
public void setMaxQueryMillis(final long timeout) {
opts.setHeader(HTTP_HEADER_BIGDATA_MAX_QUERY_MILLIS,
Long.toString(timeout));
}
/**
* {@inheritDoc}
*
* Note: -1L
is returned if the http header is not
* specified.
*/
@Override
public long getMaxQueryMillis() {
final String s = opts
.getHeader(HTTP_HEADER_BIGDATA_MAX_QUERY_MILLIS);
if (s == null) {
return -1L;
}
return StringUtil.toLong(s);
}
@Override
public String getHeader(final String name) {
return opts.getHeader(name);
}
}
private final class TupleQuery extends QueryOrUpdate implements IPreparedTupleQuery {
public TupleQuery(final ConnectOptions opts, final UUID id,
final String query) {
super(opts, id, query);
}
@Override
protected void setupConnectOptions() {
super.setupConnectOptions();
if (opts.getAcceptHeader() == null)
opts.setAcceptHeader(ConnectOptions.DEFAULT_SOLUTIONS_ACCEPT_HEADER);
}
@Override
public TupleQueryResult evaluate() throws Exception {
return evaluate(null);
}
@Override
public TupleQueryResult evaluate(final IPreparedQueryListener listener)
throws Exception {
setupConnectOptions();
return mgr.tupleResults(opts, getQueryId(), listener);
}
}
private final class GraphQuery extends QueryOrUpdate implements IPreparedGraphQuery {
public GraphQuery(final ConnectOptions opts, final UUID id,
final String query) {
super(opts, id, query);
}
@Override
protected void setupConnectOptions() {
super.setupConnectOptions();
if (opts.getAcceptHeader() == null)
opts.setAcceptHeader(ConnectOptions.DEFAULT_GRAPH_ACCEPT_HEADER);
}
@Override
public GraphQueryResult evaluate() throws Exception {
return evaluate(null);
}
@Override
public GraphQueryResult evaluate(final IPreparedQueryListener listener)
throws Exception {
setupConnectOptions();
return mgr.graphResults(opts, getQueryId(), listener);
}
}
private final class BooleanQuery extends QueryOrUpdate implements
IPreparedBooleanQuery {
public BooleanQuery(final ConnectOptions opts, final UUID id,
final String query) {
super(opts, id, query);
}
@Override
protected void setupConnectOptions() {
super.setupConnectOptions();
if (opts.getAcceptHeader() == null)
opts.setAcceptHeader(ConnectOptions.DEFAULT_BOOLEAN_ACCEPT_HEADER);
}
@Override
public boolean evaluate() throws Exception {
return evaluate(null);
}
@Override
public boolean evaluate(final IPreparedQueryListener listener)
throws Exception {
setupConnectOptions();
return mgr.booleanResults(opts, getQueryId(), listener);
}
}
private final class SparqlUpdate extends QueryOrUpdate implements
IPreparedSparqlUpdate {
public SparqlUpdate(final ConnectOptions opts, final UUID uuid,
final String updateStr) {
super(opts, uuid, updateStr, true/*update*/);
}
@Override
public void evaluate() throws Exception {
evaluate(null);
}
@Override
public void evaluate(final IPreparedQueryListener listener)
throws Exception {
setupConnectOptions();
mgr.sparqlUpdateResults(opts, getQueryId(), listener);
}
}
/**
* Add by URI, statements, or file.
*/
public static class AddOp {
private List uris;
private Iterable extends Statement> stmts;
private byte[] data;
private File file;
private InputStream is;
private Reader reader;
private RDFFormat format;
private Resource[] context;
public AddOp(final String uri) {
if (uri == null)
throw new IllegalArgumentException();
this.uris = Collections.singletonList(uri);
}
public AddOp(final Collection uris) {
if (uris == null)
throw new IllegalArgumentException();
if (uris.isEmpty())
throw new IllegalArgumentException();
this.uris = new LinkedList(uris);
}
public AddOp(final Iterable extends Statement> stmts) {
this.stmts = stmts;
}
public AddOp(final File file, final RDFFormat format) {
this.file = file;
this.format = format;
}
public AddOp(final InputStream is, final RDFFormat format) {
this.is = is;
this.format = format;
}
public AddOp(final Reader reader, final RDFFormat format) {
this.reader = reader;
this.format = format;
}
/**
* This ctor is for the test cases.
*/
public AddOp(final byte[] data, final RDFFormat format) {
this.data = data;
this.format = format;
}
public void setContext(final Resource... context) {
this.context = context;
}
private void prepareForWire() throws Exception {
if (file != null) {
// set the data
data = IOUtil.readBytes(file);
} else if (is != null) {
// set the data
data = IOUtil.readBytes(is);
} else if (reader != null) {
// set the data
data = IOUtil.readString(reader).getBytes();
} else if (stmts != null) {
// set the data and content type (TRIG by default)
format = RDFFormat.TRIG;
data = serialize(stmts, format);
}
}
}
/**
* Remove by query, access path, statements, or file.
*/
public static class RemoveOp {
private String query;
private Iterable extends Statement> stmts;
private Value s, p, o;
private Resource[] c;
private byte[] data;
private File file;
private RDFFormat format;
private Resource[] context;
public RemoveOp(final String query) {
this.query = query;
}
public RemoveOp(final Iterable extends Statement> stmts) {
this.stmts = stmts;
}
/**
*
* @param s
* The subject (optional).
* @param p
* The predicate (optional).
* @param o
* The value (optional).
* @param c
* The contexts (optional, BUT may not be a null Resource[]).
*/
public RemoveOp(final Resource s, final URI p, final Value o, final Resource... c) {
if (c == null)
throw new IllegalArgumentException();
this.s = s;
this.p = p;
this.o = o;
this.c = c;
}
public RemoveOp(final File file, final RDFFormat format) {
this.file = file;
this.format = format;
}
/**
* This ctor is for the test cases.
*/
public RemoveOp(final byte[] data, final RDFFormat format) {
this.data = data;
this.format = format;
}
public void setContext(final Resource... context) {
this.context = context;
}
private void prepareForWire() throws Exception {
if (file != null) {
// set the data
data = IOUtil.readBytes(file);
} else if (stmts != null) {
// set the data and content type (TRIG by default)
format = RDFFormat.TRIG;
data = serialize(stmts, format);
}
}
}
/**
* Connect to a SPARQL end point (GET or POST query only).
*
* @param opts
* The connection options.
*
* @return The connection.
*/
private JettyResponseListener doConnect(final ConnectOptions opts) throws Exception {
return mgr.doConnect(opts);
}
}