fix to allow for batch handling of more complex SPARQL updates
This commit is contained in:
parent
ce1ec1158f
commit
6e3a256596
2 changed files with 150 additions and 23 deletions
|
@ -5,9 +5,8 @@ package edu.cornell.mannlib.vitro.webapp.dao.jena;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
@ -26,6 +25,7 @@ import com.hp.hpl.jena.graph.impl.SimpleEventManager;
|
||||||
import com.hp.hpl.jena.query.QuerySolution;
|
import com.hp.hpl.jena.query.QuerySolution;
|
||||||
import com.hp.hpl.jena.rdf.listeners.StatementListener;
|
import com.hp.hpl.jena.rdf.listeners.StatementListener;
|
||||||
import com.hp.hpl.jena.rdf.model.Model;
|
import com.hp.hpl.jena.rdf.model.Model;
|
||||||
|
import com.hp.hpl.jena.rdf.model.ModelFactory;
|
||||||
import com.hp.hpl.jena.shared.AddDeniedException;
|
import com.hp.hpl.jena.shared.AddDeniedException;
|
||||||
import com.hp.hpl.jena.shared.Command;
|
import com.hp.hpl.jena.shared.Command;
|
||||||
import com.hp.hpl.jena.shared.DeleteDeniedException;
|
import com.hp.hpl.jena.shared.DeleteDeniedException;
|
||||||
|
@ -54,9 +54,8 @@ public class RDFServiceGraph implements GraphWithPerform {
|
||||||
private GraphEventManager eventManager;
|
private GraphEventManager eventManager;
|
||||||
|
|
||||||
private boolean queueWrites = false;
|
private boolean queueWrites = false;
|
||||||
private ConcurrentLinkedQueue<Triple> addTripleQueue = new ConcurrentLinkedQueue<Triple>();
|
private Graph additionsGraph = ModelFactory.createDefaultModel().getGraph();
|
||||||
private ConcurrentLinkedQueue<Triple> removeTripleQueue = new ConcurrentLinkedQueue<Triple>();
|
private Graph removalsGraph = ModelFactory.createDefaultModel().getGraph();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a SparqlGraph for the union of named graphs in a remote repository
|
* Returns a SparqlGraph for the union of named graphs in a remote repository
|
||||||
|
@ -64,6 +63,7 @@ public class RDFServiceGraph implements GraphWithPerform {
|
||||||
*/
|
*/
|
||||||
public RDFServiceGraph(RDFService rdfService) {
|
public RDFServiceGraph(RDFService rdfService) {
|
||||||
this(rdfService, null);
|
this(rdfService, null);
|
||||||
|
log.info("using graph implementation: " + additionsGraph.getClass().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -97,17 +97,17 @@ public class RDFServiceGraph implements GraphWithPerform {
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void flush() {
|
public synchronized void flush() {
|
||||||
log.debug("Flushing a batch");
|
log.debug("Flushing a batch");
|
||||||
ChangeSet changeSet = rdfService.manufactureChangeSet();
|
ChangeSet changeSet = rdfService.manufactureChangeSet();
|
||||||
try {
|
try {
|
||||||
if(!removeTripleQueue.isEmpty()) {
|
if(!removalsGraph.isEmpty()) {
|
||||||
String removals = serializeQueue(removeTripleQueue);
|
String removals = serializeGraph(removalsGraph);
|
||||||
changeSet.addRemoval(RDFServiceUtils.toInputStream(removals),
|
changeSet.addRemoval(RDFServiceUtils.toInputStream(removals),
|
||||||
RDFService.ModelSerializationFormat.N3, graphURI);
|
RDFService.ModelSerializationFormat.N3, graphURI);
|
||||||
}
|
}
|
||||||
if(!addTripleQueue.isEmpty()) {
|
if(!additionsGraph.isEmpty()) {
|
||||||
String additions = serializeQueue(addTripleQueue);
|
String additions = serializeGraph(additionsGraph);
|
||||||
changeSet.addAddition(RDFServiceUtils.toInputStream(additions),
|
changeSet.addAddition(RDFServiceUtils.toInputStream(additions),
|
||||||
RDFService.ModelSerializationFormat.N3, graphURI);
|
RDFService.ModelSerializationFormat.N3, graphURI);
|
||||||
}
|
}
|
||||||
|
@ -117,25 +117,34 @@ public class RDFServiceGraph implements GraphWithPerform {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String serializeQueue(Queue<Triple> tripleQueue) {
|
private synchronized String serializeGraph(Graph graph) {
|
||||||
String triples = "";
|
String triples = "";
|
||||||
while(!tripleQueue.isEmpty()) {
|
Iterator<Triple> tripIt = graph.find(null, null, null);
|
||||||
triples += " \n" + serialize(tripleQueue.poll());
|
while(tripIt.hasNext()) {
|
||||||
|
triples += " \n" + serialize(tripIt.next());
|
||||||
}
|
}
|
||||||
return triples;
|
return triples;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void performAdd(Triple t) {
|
public synchronized void performAdd(Triple t) {
|
||||||
addTripleQueue.add(t);
|
if(removalsGraph.contains(t)) {
|
||||||
|
removalsGraph.remove(t.getSubject(), t.getPredicate(), t.getObject());
|
||||||
|
} else {
|
||||||
|
additionsGraph.add(t);
|
||||||
|
}
|
||||||
if(!queueWrites) {
|
if(!queueWrites) {
|
||||||
flush();
|
flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void performDelete(Triple t) {
|
public synchronized void performDelete(Triple t) {
|
||||||
removeTripleQueue.add(t);
|
if(additionsGraph.contains(t)) {
|
||||||
|
additionsGraph.remove(t.getSubject(), t.getPredicate(), t.getObject());
|
||||||
|
} else {
|
||||||
|
removalsGraph.add(t);
|
||||||
|
}
|
||||||
if(!queueWrites) {
|
if(!queueWrites) {
|
||||||
flush();
|
flush();
|
||||||
}
|
}
|
||||||
|
@ -191,7 +200,13 @@ public class RDFServiceGraph implements GraphWithPerform {
|
||||||
|
|
||||||
ResultSetConsumer.HasResult consumer = new ResultSetConsumer.HasResult();
|
ResultSetConsumer.HasResult consumer = new ResultSetConsumer.HasResult();
|
||||||
execSelect(containsQuery.toString(), consumer);
|
execSelect(containsQuery.toString(), consumer);
|
||||||
return consumer.hasResult();
|
boolean initialResult = consumer.hasResult();
|
||||||
|
if(!queueWrites) {
|
||||||
|
return initialResult;
|
||||||
|
} else {
|
||||||
|
Triple t = Triple.create(subject, predicate, object);
|
||||||
|
return (initialResult || additionsGraph.contains(t)) && !removalsGraph.contains(t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -284,7 +299,11 @@ public class RDFServiceGraph implements GraphWithPerform {
|
||||||
String queryString = findQuery.toString();
|
String queryString = findQuery.toString();
|
||||||
|
|
||||||
final List<Triple> triplist = new ArrayList<Triple>();
|
final List<Triple> triplist = new ArrayList<Triple>();
|
||||||
|
if(queueWrites) {
|
||||||
|
addAdditions(triplist, additionsGraph.find(subject, predicate, object));
|
||||||
|
subtractRemovals(triplist, removalsGraph.find(subject, predicate, object));
|
||||||
|
}
|
||||||
|
|
||||||
execSelect(queryString, new ResultSetConsumer() {
|
execSelect(queryString, new ResultSetConsumer() {
|
||||||
@Override
|
@Override
|
||||||
protected void processQuerySolution(QuerySolution qs) {
|
protected void processQuerySolution(QuerySolution qs) {
|
||||||
|
@ -311,6 +330,24 @@ public class RDFServiceGraph implements GraphWithPerform {
|
||||||
return WrappedIterator.create(triplist.iterator());
|
return WrappedIterator.create(triplist.iterator());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addAdditions(List<Triple> tripList, ExtendedIterator<Triple> tripIt) {
|
||||||
|
while(tripIt.hasNext()) {
|
||||||
|
Triple t = tripIt.next();
|
||||||
|
if(!tripList.contains(t)) {
|
||||||
|
tripList.add(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void subtractRemovals(List<Triple> tripList, ExtendedIterator<Triple> tripIt) {
|
||||||
|
while(tripIt.hasNext()) {
|
||||||
|
Triple t = tripIt.next();
|
||||||
|
if(tripList.contains(t)) {
|
||||||
|
tripList.remove(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isVar(Node node) {
|
private boolean isVar(Node node) {
|
||||||
return (node == null || node.isVariable() || node == Node.ANY);
|
return (node == null || node.isVariable() || node == Node.ANY);
|
||||||
}
|
}
|
||||||
|
@ -430,10 +467,10 @@ public class RDFServiceGraph implements GraphWithPerform {
|
||||||
|
|
||||||
private final TransactionHandler transactionHandler = new TransactionHandler() {
|
private final TransactionHandler transactionHandler = new TransactionHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void abort() {
|
public synchronized void abort() {
|
||||||
queueWrites = false;
|
queueWrites = false;
|
||||||
removeTripleQueue.clear();
|
removalsGraph.clear();
|
||||||
addTripleQueue.clear();
|
additionsGraph.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -442,7 +479,7 @@ public class RDFServiceGraph implements GraphWithPerform {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void commit() {
|
public synchronized void commit() {
|
||||||
flush();
|
flush();
|
||||||
queueWrites = false;
|
queueWrites = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.controller.api;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.io.StringReader;
|
||||||
|
|
||||||
|
import junit.framework.Assert;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.hp.hpl.jena.query.Dataset;
|
||||||
|
import com.hp.hpl.jena.query.DatasetFactory;
|
||||||
|
import com.hp.hpl.jena.query.ReadWrite;
|
||||||
|
import com.hp.hpl.jena.rdf.model.Model;
|
||||||
|
import com.hp.hpl.jena.rdf.model.ModelFactory;
|
||||||
|
import com.hp.hpl.jena.update.GraphStore;
|
||||||
|
import com.hp.hpl.jena.update.GraphStoreFactory;
|
||||||
|
import com.hp.hpl.jena.update.UpdateAction;
|
||||||
|
import com.hp.hpl.jena.update.UpdateFactory;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.testing.AbstractTestClass;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.jena.RDFServiceDataset;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.jena.model.RDFServiceModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that the SparqlQueryApiExecutor can handle all query types and all
|
||||||
|
* formats.
|
||||||
|
*/
|
||||||
|
public class SparqlUpdateApiTest extends AbstractTestClass {
|
||||||
|
|
||||||
|
private final String GRAPH_URI = "http://example.org/graph";
|
||||||
|
|
||||||
|
private final String updateStr1 =
|
||||||
|
"INSERT DATA { GRAPH <" + GRAPH_URI + "> { \n" +
|
||||||
|
" <http://here.edu/n1> a <http://here.edu/Class1> . \n" +
|
||||||
|
"} } ; \n" +
|
||||||
|
"INSERT { GRAPH <" + GRAPH_URI + "> { \n " +
|
||||||
|
" ?x a <http://here.edu/Class2> . \n " +
|
||||||
|
"} } WHERE { \n" +
|
||||||
|
" GRAPH <" + GRAPH_URI + "> { ?x a <http://here.edu/Class1> } \n " +
|
||||||
|
"}";
|
||||||
|
|
||||||
|
private final String result1 =
|
||||||
|
"<http://here.edu/n1> a <http://here.edu/Class1> . \n" +
|
||||||
|
"<http://here.edu/n1> a <http://here.edu/Class2> ." ;
|
||||||
|
|
||||||
|
// look at how the SimpleReasoner is set up.
|
||||||
|
|
||||||
|
private Model model;
|
||||||
|
private RDFService rdfService;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
model = ModelFactory.createDefaultModel();
|
||||||
|
Dataset ds = DatasetFactory.createMem();
|
||||||
|
ds.addNamedModel(GRAPH_URI, model);
|
||||||
|
rdfService = new RDFServiceModel(ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Tests
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void nullRdfService() throws Exception {
|
||||||
|
model.removeAll();
|
||||||
|
Model desiredResults = ModelFactory.createDefaultModel();
|
||||||
|
desiredResults.read(new StringReader(result1), null, "N3");
|
||||||
|
Dataset ds = new RDFServiceDataset(rdfService);
|
||||||
|
GraphStore graphStore = GraphStoreFactory.create(ds);
|
||||||
|
try {
|
||||||
|
if(ds.supportsTransactions()) {
|
||||||
|
ds.begin(ReadWrite.WRITE);
|
||||||
|
System.out.println("yep");
|
||||||
|
}
|
||||||
|
UpdateAction.execute(UpdateFactory.create(updateStr1), graphStore);
|
||||||
|
} finally {
|
||||||
|
if(ds.supportsTransactions()) {
|
||||||
|
ds.commit();
|
||||||
|
ds.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEquals("updateStr1 yields result1", desiredResults.toString(), model.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue