com.tinkerpop.blueprints.impls.sail.SailGraph Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of blueprints-sail-graph Show documentation
Show all versions of blueprints-sail-graph Show documentation
Blueprints property graph implementation for Sesame RDF Sail
package com.tinkerpop.blueprints.impls.sail;
import com.tinkerpop.blueprints.Edge;
import com.tinkerpop.blueprints.Features;
import com.tinkerpop.blueprints.GraphQuery;
import com.tinkerpop.blueprints.MetaGraph;
import com.tinkerpop.blueprints.TransactionalGraph;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.blueprints.util.DefaultGraphQuery;
import com.tinkerpop.blueprints.util.ExceptionFactory;
import com.tinkerpop.blueprints.util.PropertyFilteredIterable;
import com.tinkerpop.blueprints.util.StringFactory;
import info.aduna.iteration.CloseableIteration;
import org.apache.log4j.PropertyConfigurator;
import org.openrdf.model.Literal;
import org.openrdf.model.Namespace;
import org.openrdf.model.Resource;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.model.impl.BNodeImpl;
import org.openrdf.model.impl.StatementImpl;
import org.openrdf.model.impl.URIImpl;
import org.openrdf.query.Binding;
import org.openrdf.query.BindingSet;
import org.openrdf.query.QueryEvaluationException;
import org.openrdf.query.impl.MapBindingSet;
import org.openrdf.query.parser.ParsedQuery;
import org.openrdf.query.parser.sparql.SPARQLParser;
import org.openrdf.rio.RDFFormat;
import org.openrdf.rio.RDFHandler;
import org.openrdf.rio.RDFHandlerException;
import org.openrdf.rio.RDFParser;
import org.openrdf.rio.RDFWriter;
import org.openrdf.rio.Rio;
import org.openrdf.sail.Sail;
import org.openrdf.sail.SailConnection;
import org.openrdf.sail.SailException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Logger;
/**
* A Blueprints implementation of the RDF-based Sail interfaces by Aduna (http://openrdf.org).
*
* @author Marko A. Rodriguez (http://markorodriguez.com)
* @author Joshua Shinavier (http://fortytwo.net)
*/
public class SailGraph implements TransactionalGraph, MetaGraph {
private static final Logger LOGGER = Logger.getLogger(SailGraph.class.getName());
public static final Map formats = new HashMap();
private static final Features FEATURES = new Features();
static {
FEATURES.supportsDuplicateEdges = false;
FEATURES.supportsSelfLoops = true;
FEATURES.isPersistent = false;
FEATURES.supportsVertexIteration = false;
FEATURES.supportsEdgeIteration = true;
FEATURES.supportsVertexIndex = false;
FEATURES.supportsEdgeIndex = false;
FEATURES.ignoresSuppliedIds = false;
FEATURES.supportsEdgeRetrieval = false;
FEATURES.supportsVertexProperties = false;
FEATURES.supportsEdgeProperties = false;
FEATURES.supportsTransactions = true;
FEATURES.supportsEdgeKeyIndex = false;
FEATURES.supportsVertexKeyIndex = false;
FEATURES.supportsKeyIndices = false;
FEATURES.isWrapper = false;
FEATURES.supportsIndices = false;
FEATURES.supportsSerializableObjectProperty = false;
FEATURES.supportsBooleanProperty = false;
FEATURES.supportsDoubleProperty = false;
FEATURES.supportsFloatProperty = false;
FEATURES.supportsIntegerProperty = false;
FEATURES.supportsPrimitiveArrayProperty = false;
FEATURES.supportsUniformListProperty = false;
FEATURES.supportsMixedListProperty = false;
FEATURES.supportsLongProperty = false;
FEATURES.supportsMapProperty = false;
FEATURES.supportsStringProperty = false;
FEATURES.supportsThreadedTransactions = false;
}
static {
formats.put("n3", RDFFormat.N3);
formats.put("n-quads", RDFFormat.NQUADS);
formats.put("n-triples", RDFFormat.NTRIPLES);
formats.put("rdf-json", RDFFormat.RDFJSON);
formats.put("rdf-xml", RDFFormat.RDFXML);
formats.put("trix", RDFFormat.TRIX);
formats.put("trig", RDFFormat.TRIG);
formats.put("turtle", RDFFormat.TURTLE);
}
public static RDFFormat getFormat(final String format) {
RDFFormat ret = formats.get(format);
if (null == ret)
throw new IllegalArgumentException(format + " is an unsupported RDF file format. Use rdf-xml, n-triples, n-quads, turtle, n3, trix, or trig");
else
return ret;
}
private final List connections = new LinkedList();
protected final Sail rawGraph;
protected final ThreadLocal sailConnection = new ThreadLocal() {
protected SailConnection initialValue() {
SailConnection sc = null;
try {
sc = createConnection();
} catch (SailException e) {
e.printStackTrace(System.err);
}
return sc;
}
};
private static final String LOG4J_PROPERTIES = "log4j.properties";
/**
* Construct a new SailGraph with an uninitialized Sail.
*
* @param sail a not-yet-initialized Sail instance
*/
public SailGraph(final Sail sail) {
try {
PropertyConfigurator.configure(SailGraph.class.getResource(LOG4J_PROPERTIES));
} catch (Throwable e) {
LOGGER.warning("failed to configure Log4j: " + e.getMessage());
}
try {
this.rawGraph = sail;
sail.initialize();
} catch (SailException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
/**
* Defines a few commonly-used namespace prefixes.
*/
public void addDefaultNamespaces() {
this.addNamespace(SailTokens.RDF_PREFIX, SailTokens.RDF_NS);
this.addNamespace(SailTokens.RDFS_PREFIX, SailTokens.RDFS_NS);
this.addNamespace(SailTokens.OWL_PREFIX, SailTokens.OWL_NS);
this.addNamespace(SailTokens.XSD_PREFIX, SailTokens.XSD_NS);
this.addNamespace(SailTokens.FOAF_PREFIX, SailTokens.FOAF_NS);
}
public Sail getRawGraph() {
return this.rawGraph;
}
private Vertex createVertex(String resource) {
Literal literal;
if (SailHelper.isBNode(resource)) {
return new SailVertex(new BNodeImpl(resource.substring(2)), this);
} else if ((literal = SailHelper.makeLiteral(resource, this)) != null) {
return new SailVertex(literal, this);
} else if (resource.contains(SailTokens.NAMESPACE_SEPARATOR) || resource.contains(SailTokens.FORWARD_SLASH) || resource.contains(SailTokens.POUND)) {
resource = this.expandPrefix(resource);
return new SailVertex(new URIImpl(resource), this);
} else {
throw new RuntimeException(resource + " is not a valid URI, blank node, or literal value");
}
//return new SailVertex(NTriplesUtil.parseValue(resource, new ValueFactoryImpl()), this.sailConnection);
}
public Vertex addVertex(Object id) {
if (null == id)
id = SailTokens.URN_UUID_PREFIX + UUID.randomUUID().toString();
return createVertex(id.toString());
}
public Vertex getVertex(final Object id) {
if (null == id)
throw ExceptionFactory.vertexIdCanNotBeNull();
try {
return createVertex(id.toString());
} catch (RuntimeException re) {
return null;
}
}
public Edge getEdge(final Object id) {
throw new UnsupportedOperationException();
}
public Iterable getVertices() {
throw new UnsupportedOperationException("RDF is an edge based graph model");
}
public Iterable getVertices(final String key, final Object value) {
throw new UnsupportedOperationException("RDF is an edge based graph model");
}
public Iterable getEdges() {
return new SailEdgeIterable(null, null, null, this);
}
public Iterable getEdges(final String key, final Object value) {
// TODO: Make this efficient using a SPARQL query
return new PropertyFilteredIterable(key, value, new SailEdgeIterable(null, null, null, this));
}
public void removeVertex(final Vertex vertex) {
Value vertexValue = ((SailVertex) vertex).getRawVertex();
try {
if (vertexValue instanceof Resource) {
this.sailConnection.get().removeStatements((Resource) vertexValue, null, null);
}
this.sailConnection.get().removeStatements(null, null, vertexValue);
} catch (SailException e) {
throw ExceptionFactory.vertexWithIdDoesNotExist(vertex.getId());
}
}
public Edge addEdge(final Object id, final Vertex outVertex, final Vertex inVertex, final String label) {
if (label == null)
throw ExceptionFactory.edgeLabelCanNotBeNull();
Value outVertexValue = ((SailVertex) outVertex).getRawVertex();
Value inVertexValue = ((SailVertex) inVertex).getRawVertex();
if (!(outVertexValue instanceof Resource)) {
throw new IllegalArgumentException(outVertex.toString() + " is not a legal URI or blank node");
}
try {
URI labelURI = new URIImpl(this.expandPrefix(label));
Statement statement = new StatementImpl((Resource) outVertexValue, labelURI, inVertexValue);
SailHelper.addStatement(statement, this.sailConnection.get());
return new SailEdge(statement, this);
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
public void removeEdge(final Edge edge) {
Statement statement = ((SailEdge) edge).getRawEdge();
try {
SailHelper.removeStatement(statement, this.sailConnection.get());
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
/**
* Get the Sail connection currently being used by the graph.
*
* @return the Sail connection
*/
public ThreadLocal getSailConnection() {
return this.sailConnection;
}
/**
* Add a prefix-to-namespace mapping to the Sail connection of this graph.
*
* @param prefix the prefix (e.g. tg)
* @param namespace the namespace (e.g. http://tinkerpop.com#)
*/
public void addNamespace(final String prefix, final String namespace) {
try {
this.sailConnection.get().setNamespace(prefix, namespace);
} catch (SailException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
/**
* Remove a prefix-to-namespace mapping from the Sail connection of this graph.
*
* @param prefix the prefix of the prefix-to-namespace mapping to remove
*/
public void removeNamespace(final String prefix) {
try {
this.sailConnection.get().removeNamespace(prefix);
} catch (SailException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
/**
* Get all the prefix-to-namespace mappings of the graph.
*
* @return a map of the prefix-to-namespace mappings
*/
public Map getNamespaces() {
Map namespaces = new HashMap();
try {
final CloseableIteration extends Namespace, SailException> results = this.sailConnection.get().getNamespaces();
while (results.hasNext()) {
Namespace namespace = results.next();
namespaces.put(namespace.getPrefix(), namespace.getName());
}
results.close();
} catch (SailException e) {
throw new RuntimeException(e.getMessage(), e);
}
return namespaces;
}
/**
* Load RDF data into the SailGraph. Supported formats include rdf-xml, n-triples, turtle, n3, trix, or trig.
* Before loading data, the current transaction is successfully committed.
*
* @param input the InputStream of RDF data
* @param baseURI the baseURI for RDF data
* @param format supported formats include rdf-xml, n-triples, turtle, n3, trix, or trig
* @param baseGraph the baseGraph to insert the data into
*/
public void loadRDF(final InputStream input, final String baseURI, final String format, final String baseGraph) {
try {
this.commit();
final SailConnection c = this.rawGraph.getConnection();
try {
c.begin();
RDFParser p = Rio.createParser(getFormat(format));
RDFHandler h = null == baseGraph
? new SailAdder(c)
: new SailAdder(c, new URIImpl(baseGraph));
p.setRDFHandler(h);
p.parse(input, baseURI);
c.commit();
} finally {
c.rollback();
c.close();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Save RDF data from the SailGraph.
* Supported formats include rdf-xml, n-triples, turtle, n3, trix, or trig.
*
* @param output the OutputStream to which to write RDF data
* @param format supported formats include rdf-xml, n-triples, turtle, n3, trix, or trig
*/
public void saveRDF(final OutputStream output, final String format) {
try {
this.commit();
final SailConnection c = this.rawGraph.getConnection();
try {
c.begin();
RDFWriter w = Rio.createWriter(getFormat(format), output);
w.startRDF();
CloseableIteration extends Statement, SailException> iter = c.getStatements(null, null, null, false);
try {
while (iter.hasNext()) {
w.handleStatement(iter.next());
}
} finally {
iter.close();
}
w.endRDF();
} finally {
c.rollback();
c.close();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private synchronized SailConnection createConnection() throws SailException {
cleanupConnections();
final SailConnection sc = rawGraph.getConnection();
sc.begin();
connections.add(sc);
return sc;
}
private void cleanupConnections() throws SailException {
Collection toRemove = new LinkedList();
for (SailConnection sc : connections) {
if (!sc.isOpen()) {
toRemove.add(sc);
}
}
for (SailConnection sc : toRemove) {
connections.remove(sc);
}
}
private void closeAllConnections() throws SailException {
for (SailConnection sc : connections) {
if (null != sc) {
if (sc.isOpen()) {
sc.rollback();
sc.close();
}
}
}
}
public synchronized void shutdown() {
try {
this.commit();
closeAllConnections();
this.rawGraph.shutDown();
} catch (Throwable e) {
throw new RuntimeException(e.getMessage(), e);
}
}
/**
* Given a URI, expand it to its full URI.
*
* @param uri the compressed URI (e.g. tg:knows)
* @return the expanded URI (e.g. http://tinkerpop.com#knows)
*/
public String expandPrefix(String uri) {
try {
if (uri.contains(SailTokens.NAMESPACE_SEPARATOR)) {
String namespace = this.sailConnection.get().getNamespace(uri.substring(0, uri.indexOf(SailTokens.NAMESPACE_SEPARATOR)));
if (null != namespace)
uri = namespace + uri.substring(uri.indexOf(SailTokens.NAMESPACE_SEPARATOR) + 1);
}
} catch (SailException e) {
throw new RuntimeException(e.getMessage(), e);
}
return uri;
}
/**
* Given a URI, compress it to its prefixed URI.
*
* @param uri the expanded URI (e.g. http://tinkerpop.com#knows)
* @return the prefixed URI (e.g. tg:knows)
*/
public String prefixNamespace(String uri) {
try {
CloseableIteration extends Namespace, SailException> namespaces = this.sailConnection.get().getNamespaces();
while (namespaces.hasNext()) {
Namespace namespace = namespaces.next();
if (uri.contains(namespace.getName()))
uri = uri.replace(namespace.getName(), namespace.getPrefix() + SailTokens.NAMESPACE_SEPARATOR);
}
namespaces.close();
} catch (SailException e) {
throw new RuntimeException(e.getMessage(), e);
}
return uri;
}
public void stopTransaction(Conclusion conclusion) {
if (Conclusion.SUCCESS == conclusion) {
commit();
} else {
rollback();
}
}
public void commit() {
try {
SailConnection sc = this.sailConnection.get();
sc.commit();
sc.begin();
} catch (SailException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
public void rollback() {
try {
SailConnection sc = this.sailConnection.get();
sc.rollback();
sc.begin();
} catch (SailException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
public String toString() {
return StringFactory.graphString(this, this.rawGraph.getClass().getSimpleName().toLowerCase());
}
private String getPrefixes() {
String prefixString = "";
final Map namespaces = this.getNamespaces();
for (Map.Entry entry : namespaces.entrySet()) {
prefixString = prefixString + SailTokens.PREFIX_SPACE + entry.getKey() + SailTokens.COLON_LESSTHAN + entry.getValue() + SailTokens.GREATERTHAN_NEWLINE;
}
return prefixString;
}
public Features getFeatures() {
return FEATURES;
}
public GraphQuery query() {
return new DefaultGraphQuery(this);
}
/**
* Evaluate a SPARQL query against the SailGraph (http://www.w3.org/TR/rdf-sparql-query/). The result is a mapping between the ?-bindings and the bound URI, blank node, or literal represented as a Vertex.
*
* @param sparqlQuery the SPARQL query to evaluate
* @return the mapping between a ?-binding and the URI, blank node, or literal as a Vertex
* @throws RuntimeException if an error occurs in the SPARQL query engine
*/
public List
© 2015 - 2024 Weber Informatics LLC | Privacy Policy