VIVO-907 Handle RDFService quirks for TDB and Virtuoso

Modify signature for isEquivalentGraph() - might throw RDFServiceException
Modify RDFServiceTDB to work around the nonNegativeIntegerBug
Implement toString() for the entire RDFServiceImpl hierarchy
Parameterize ContentTripleSourceSPARQL.createRDFService() so ContentTripleSourceVirtuoso can override it.
Create ContentTripleSourceVirtuoso and RDFServiceVirtuoso, to work around the nonNegativeInteger bug
   and the "INSERT DATA" problem.
This commit is contained in:
Jim Blake 2015-02-17 16:28:45 -05:00
parent 066d013360
commit ab60341355
14 changed files with 179 additions and 27 deletions

View file

@ -169,7 +169,8 @@ public interface RDFService {
* @param serializedGraph - the contents to be compared with the existing graph. May not be null.
* @param serializationFormat - May not be null.
*/
public boolean isEquivalentGraph(String graphURI, InputStream serializedGraph, ModelSerializationFormat serializationFormat);
public boolean isEquivalentGraph(String graphURI, InputStream serializedGraph,
ModelSerializationFormat serializationFormat) throws RDFServiceException;
/**
* Registers a listener to listen to changes in any graph in

View file

@ -338,7 +338,7 @@ public class LanguageFilteringRDFService implements RDFService {
@Override
public boolean isEquivalentGraph(String graphURI,
InputStream serializedGraph,
ModelSerializationFormat serializationFormat) {
ModelSerializationFormat serializationFormat) throws RDFServiceException {
return s.isEquivalentGraph(graphURI, serializedGraph, serializationFormat);
}

View file

@ -255,7 +255,7 @@ public class SameAsFilteringRDFServiceFactory implements RDFServiceFactory {
@Override
public boolean isEquivalentGraph(String graphURI,
InputStream serializedGraph,
ModelSerializationFormat serializationFormat) {
ModelSerializationFormat serializationFormat) throws RDFServiceException {
return s.isEquivalentGraph(graphURI, serializedGraph, serializationFormat);
}

View file

@ -128,7 +128,7 @@ public class RDFServiceFactorySingle implements RDFServiceFactory {
@Override
public boolean isEquivalentGraph(String graphURI,
InputStream serializedGraph,
ModelSerializationFormat serializationFormat) {
ModelSerializationFormat serializationFormat) throws RDFServiceException {
return s.isEquivalentGraph(graphURI, serializedGraph, serializationFormat);
}

View file

@ -11,7 +11,6 @@ import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.hp.hpl.jena.graph.Graph;
import com.hp.hpl.jena.graph.Node;
import com.hp.hpl.jena.graph.NodeFactory;
import com.hp.hpl.jena.graph.Triple;
@ -32,6 +31,7 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeSet;
import edu.cornell.mannlib.vitro.webapp.rdfservice.ModelChange;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException;
import edu.cornell.mannlib.vitro.webapp.utils.logging.ToString;
public abstract class RDFServiceImpl implements RDFService {
@ -287,4 +287,9 @@ public abstract class RDFServiceImpl implements RDFService {
return q;
}
@Override
public String toString() {
return ToString.simpleName(this) + "[" + ToString.hashHex(this) + "]";
}
}

View file

@ -562,7 +562,7 @@ public abstract class RDFServiceJena extends RDFServiceImpl implements RDFServic
*/
@Override
public boolean isEquivalentGraph(String graphURI, InputStream serializedGraph,
ModelSerializationFormat serializationFormat) {
ModelSerializationFormat serializationFormat) throws RDFServiceException {
Model fileModel = RDFServiceUtils.parseModel(serializedGraph, serializationFormat);
Model tripleStoreModel = new RDFServiceDataset(this).getNamedModel(graphURI);
Model fromTripleStoreModel = ModelFactory.createDefaultModel().add(tripleStoreModel);

View file

@ -27,7 +27,6 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeSet;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException;
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.jena.RDFServiceJena;
import edu.cornell.mannlib.vitro.webapp.utils.logging.ToString;
public class RDFServiceSDB extends RDFServiceJena implements RDFService {
@ -172,9 +171,4 @@ public class RDFServiceSDB extends RDFServiceJena implements RDFService {
}
}
@Override
public String toString() {
return "RDFServiceSDB[" + ToString.hashHex(this) + "]";
}
}

View file

@ -2,6 +2,7 @@
package edu.cornell.mannlib.vitro.webapp.rdfservice.impl.jena.tdb;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@ -10,6 +11,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -22,7 +24,6 @@ import edu.cornell.mannlib.vitro.webapp.dao.jena.DatasetWrapper;
import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeSet;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException;
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.jena.RDFServiceJena;
import edu.cornell.mannlib.vitro.webapp.utils.logging.ToString;
/**
* An implementation that is based on Jena TDB.
@ -177,8 +178,43 @@ public class RDFServiceTDB extends RDFServiceJena {
}
}
/**
* TDB has a bug: if given a literal of type xsd:nonNegativeInteger, it
* stores a literal of type xsd:integer.
*
* To determine whether this serialized graph is equivalent to what's in
* TDB, we need to do the same.
*/
@Override
public String toString() {
return "RDFServiceTDB[" + ToString.hashHex(this) + "]";
public boolean isEquivalentGraph(String graphURI,
InputStream serializedGraph,
ModelSerializationFormat serializationFormat)
throws RDFServiceException {
return super.isEquivalentGraph(graphURI,
adjustForNonNegativeIntegers(serializedGraph),
serializationFormat);
}
/**
* Convert all of the references to "nonNegativeInteger" to "integer" in
* this serialized graph.
*
* This isn't rigorous: it could fail if another property contained the text
* "nonNegativeInteger" in its name, or if that text were used as part of a
* string literal. If that happens before this TDB bug is fixed, we'll need
* to improve this method.
*
* It also isn't scalable: if we wanted real scalability, we would write to
* a temporary file as we converted.
*/
private InputStream adjustForNonNegativeIntegers(InputStream serializedGraph)
throws RDFServiceException {
try {
String raw = IOUtils.toString(serializedGraph, "UTF-8");
String modified = raw.replace("nonNegativeInteger", "integer");
return new ByteArrayInputStream(modified.getBytes("UTF-8"));
} catch (IOException e) {
throw new RDFServiceException(e);
}
}
}

View file

@ -86,7 +86,8 @@ public class LoggingRDFService implements RDFService {
@Override
public boolean isEquivalentGraph(String graphURI,
InputStream serializedGraph,
ModelSerializationFormat serializationFormat) {
ModelSerializationFormat serializationFormat)
throws RDFServiceException {
try (RDFServiceLogger l = new RDFServiceLogger(graphURI)) {
return innerService.isEquivalentGraph(graphURI, serializedGraph,
serializationFormat);

View file

@ -849,7 +849,7 @@ public class RDFServiceSparql extends RDFServiceImpl implements RDFService {
*/
@Override
public boolean isEquivalentGraph(String graphURI, InputStream serializedGraph,
ModelSerializationFormat serializationFormat) {
ModelSerializationFormat serializationFormat) throws RDFServiceException {
Model fileModel = RDFServiceUtils.parseModel(serializedGraph, serializationFormat);
Model tripleStoreModel = new RDFServiceDataset(this).getNamedModel(graphURI);
Model fromTripleStoreModel = ModelFactory.createDefaultModel().add(tripleStoreModel);

View file

@ -0,0 +1,83 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.rdfservice.impl.virtuoso;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.io.IOUtils;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException;
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.sparql.RDFServiceSparql;
/**
* For now, at least, it is just like an RDFServiceSparql except for:
*
* A small change in the syntax of an UPDATE request.
*
* Allow for the nonNegativeInteger bug when checking to see whether a graph has
* changed.
*/
public class RDFServiceVirtuoso extends RDFServiceSparql {
public RDFServiceVirtuoso(String readEndpointURI, String updateEndpointURI,
String defaultWriteGraphURI) {
super(readEndpointURI, updateEndpointURI, defaultWriteGraphURI);
}
public RDFServiceVirtuoso(String readEndpointURI, String updateEndpointURI) {
super(readEndpointURI, updateEndpointURI);
}
public RDFServiceVirtuoso(String endpointURI) {
super(endpointURI);
}
@Override
protected void executeUpdate(String updateString)
throws RDFServiceException {
super.executeUpdate(updateString.replace("INSERT DATA", "INSERT"));
}
/**
* Virtuoso has a bug, which it shares with TDB: if given a literal of type
* xsd:nonNegativeInteger, it stores a literal of type xsd:integer.
*
* To determine whether this serialized graph is equivalent to what is
* already in Virtuoso, we need to do the same.
*/
@Override
public boolean isEquivalentGraph(String graphURI,
InputStream serializedGraph,
ModelSerializationFormat serializationFormat)
throws RDFServiceException {
return super.isEquivalentGraph(graphURI,
adjustForNonNegativeIntegers(serializedGraph),
serializationFormat);
}
/**
* Convert all of the references to "nonNegativeInteger" to "integer" in
* this serialized graph.
*
* This isn't rigorous: it could fail if another property contained the text
* "nonNegativeInteger" in its name, or if that text were used as part of a
* string literal. If that happens before this Virtuoso bug is fixed, we'll
* need to improve this method.
*
* It also isn't scalable: if we wanted real scalability, we would write to
* a temporary file as we converted.
*/
private InputStream adjustForNonNegativeIntegers(InputStream serializedGraph)
throws RDFServiceException {
try {
String raw = IOUtils.toString(serializedGraph, "UTF-8");
String modified = raw.replace("nonNegativeInteger", "integer");
return new ByteArrayInputStream(modified.getBytes("UTF-8"));
} catch (IOException e) {
throw new RDFServiceException(e);
}
}
}

View file

@ -40,6 +40,7 @@ import edu.cornell.mannlib.vitro.webapp.dao.jena.RDFServiceDataset;
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess;
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService.ModelSerializationFormat;
import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus;
@ -206,7 +207,7 @@ public class FileGraphSetup implements ServletContextListener {
* Otherwise, if a graph with the given name is in the DB and is isomorphic with
* the graph that was read from the files system, then do nothing.
*/
public boolean updateGraphInDB(RDFService rdfService, Model fileModel, String type, Path path) {
public boolean updateGraphInDB(RDFService rdfService, Model fileModel, String type, Path path) throws RDFServiceException {
String graphURI = pathToURI(path,type);
ByteArrayOutputStream buffer = new ByteArrayOutputStream();

View file

@ -73,20 +73,22 @@ public class ContentTripleSourceSPARQL extends ContentTripleSource {
@Override
public void startup(Application application, ComponentStartupStatus ss) {
this.rdfServiceFactory = createRDFServiceFactory(createRDFService(ss));
this.rdfServiceFactory = createRDFServiceFactory(createRDFService(ss,
endpointURI, updateEndpointURI));
this.rdfService = this.rdfServiceFactory.getRDFService();
this.dataset = createDataset();
this.modelMaker = createModelMaker();
}
private RDFService createRDFService(ComponentStartupStatus ss) {
if (updateEndpointURI == null) {
ss.info("Using endpoint at " + endpointURI);
return new RDFServiceSparql(endpointURI);
protected RDFService createRDFService(ComponentStartupStatus ss,
String endpoint, String updateEndpoint) {
if (updateEndpoint == null) {
ss.info("Using endpoint at " + endpoint);
return new RDFServiceSparql(endpoint);
} else {
ss.info("Using read endpoint at " + endpointURI
+ " and update endpoint at " + updateEndpointURI);
return new RDFServiceSparql(endpointURI, updateEndpointURI);
ss.info("Using read endpoint at " + endpoint
+ " and update endpoint at " + updateEndpoint);
return new RDFServiceSparql(endpoint, updateEndpoint);
}
}

View file

@ -0,0 +1,29 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.triplesource.impl.virtuoso;
import edu.cornell.mannlib.vitro.webapp.modules.ComponentStartupStatus;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.virtuoso.RDFServiceVirtuoso;
import edu.cornell.mannlib.vitro.webapp.triplesource.impl.sparql.ContentTripleSourceSPARQL;
/**
* So far, it's just like a ContentTripleSourceSPARQL but it uses an instance of
* RDFServiceVirtuoso.
*/
public class ContentTripleSourceVirtuoso extends ContentTripleSourceSPARQL {
@Override
protected RDFService createRDFService(ComponentStartupStatus ss,
String endpoint, String updateEndpoint) {
if (updateEndpoint == null) {
ss.info("Using endpoint at " + endpoint);
return new RDFServiceVirtuoso(endpoint);
} else {
ss.info("Using read endpoint at " + endpoint
+ " and update endpoint at " + updateEndpoint);
return new RDFServiceVirtuoso(endpoint, updateEndpoint);
}
}
}