VIVO-823 Big changes to RDFServiceModelMaker

The "Metadata Model" goes away. A ListCachingModelMaker will provide similar functionality,
although empty models will not persist across a restart.
The BlankNodeFiltering gets pushed into the BlankNodeFilteringModelMaker, a decorator.
This commit is contained in:
Jim Blake 2014-07-18 17:18:15 -04:00
parent 58148630ad
commit b4395fcb31
4 changed files with 259 additions and 302 deletions

View file

@ -31,7 +31,6 @@ import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.dao.ModelAccess;
import edu.cornell.mannlib.vitro.webapp.dao.ModelAccess.ModelID;
import edu.cornell.mannlib.vitro.webapp.dao.jena.JenaModelUtils;
import edu.cornell.mannlib.vitro.webapp.dao.jena.RDFServiceModelMaker;
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException;
@ -262,21 +261,6 @@ public class JenaExportController extends BaseEditController {
}
}
private OntModel getOntModelFromAttribute( String attributeName, VitroRequest vreq ) {
Object o = vreq.getAttribute( attributeName );
if ( (o != null) && (o instanceof OntModel) ) {
return (OntModel) o;
} else {
o = getServletContext().getAttribute( attributeName );
if ( (o != null) && (o instanceof OntModel) ) {
return (OntModel) o;
} else {
throw new RuntimeException("Unable to find OntModel in request or context attribute "+attributeName);
}
}
}
static final String FULL_GRAPH = "?g";
static Map<String,String> formatToExtension;
static Map<String,String> formatToMimetype;
@ -301,14 +285,12 @@ public class JenaExportController extends BaseEditController {
private static final String ABOX_FULL_CONSTRUCT = "CONSTRUCT { ?s ?p ?o } " +
"WHERE { GRAPH ?g { ?s ?p ?o } FILTER (!regex(str(?g), \"tbox\")) " +
"FILTER (?g != <" + ModelNames.APPLICATION_METADATA + ">) " +
"FILTER (?g != <" + RDFServiceModelMaker.METADATA_MODEL_URI + ">) }";
"FILTER (?g != <" + ModelNames.APPLICATION_METADATA + ">) }";
private static final String ABOX_ASSERTED_CONSTRUCT = "CONSTRUCT { ?s ?p ?o } " +
"WHERE { GRAPH ?g { ?s ?p ?o } FILTER (!regex(str(?g), \"tbox\")) " +
"FILTER (?g != <" + ModelNames.ABOX_INFERENCES + ">) " +
"FILTER (?g != <" + ModelNames.APPLICATION_METADATA + ">) " +
"FILTER (?g != <" + RDFServiceModelMaker.METADATA_MODEL_URI + ">) }";
"FILTER (?g != <" + ModelNames.APPLICATION_METADATA + ">) }";
private static final String FULL_FULL_CONSTRUCT = "CONSTRUCT { ?s ?p ?o } " +
"WHERE { ?s ?p ?o }";

View file

@ -0,0 +1,125 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.dao.jena;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.hp.hpl.jena.graph.Graph;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.ModelMaker;
import com.hp.hpl.jena.rdf.model.ModelReader;
import com.hp.hpl.jena.shared.CannotCreateException;
import edu.cornell.mannlib.vitro.webapp.modelaccess.adapters.AbstractModelMakerDecorator;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService.ModelSerializationFormat;
import edu.cornell.mannlib.vitro.webapp.rdfservice.adapters.VitroModelFactory;
/**
* Still not sure why this is needed, but...
*
* Let's assume that there are some model operations that are implemented by
* multiple SPARQL queries against the RDFService. Those multiple queries might
* return different values for the same blank node, so when the results of the
* queries were combined, the relationships would be lost.
*
* To avoid this, we assume that all of the statements involving blank nodes
* will fit nicely into memory, and we fetch them all at once. After that, all
* of our operations are against the union of the actual model minus blank nodes
* and the memory model.
*
* The models do retain the same ID for each blank node on successive
* operations, so we can execute repeated queries and it will work fine.
*
* Writing blank nodes is probably a different matter, unless unrelated to
* existing blank nodes.
*/
public class BlankNodeFilteringModelMaker extends AbstractModelMakerDecorator {
private static final Log log = LogFactory
.getLog(BlankNodeFilteringModelMaker.class);
private final RDFService rdfService;
public BlankNodeFilteringModelMaker(RDFService rdfService, ModelMaker inner) {
super(inner);
this.rdfService = rdfService;
}
@Override
public Model createModel(String name) {
return wrapModelWithFilter(name, super.createModel(name));
}
@Override
public Model createModel(String name, boolean strict) {
return wrapModelWithFilter(name, super.createModel(name, strict));
}
@Override
public Model openModel(String name) {
return wrapModelWithFilter(name, super.openModel(name));
}
@Override
public Model openModelIfPresent(String name) {
return wrapModelWithFilter(name, super.openModelIfPresent(name));
}
@Override
public Model getModel(String name) {
return wrapModelWithFilter(name, super.getModel(name));
}
@Override
public Model getModel(String name, ModelReader loadIfAbsent) {
return wrapModelWithFilter(name, super.getModel(name, loadIfAbsent));
}
@Override
public Model openModel(String name, boolean strict) {
return wrapModelWithFilter(name, super.openModel(name, strict));
}
public Model wrapModelWithFilter(String name, Model model) {
if (model == null) {
return null;
}
String bnodeQuery = String.format("construct { ?s ?p ?o } \n" //
+ "where { \n" //
+ " graph <%s> { \n" //
+ " ?s ?p ?o \n "
+ " filter (isBlank(?s) || isBlank(?o)) \n" + " } \n" //
+ "}", name);
Model bnodeModel = ModelFactory.createDefaultModel();
long start = System.currentTimeMillis();
try {
bnodeModel.read(rdfService.sparqlConstructQuery(bnodeQuery,
ModelSerializationFormat.N3), null, "N3");
log.debug("constructed a model of blank nodes of size: "
+ bnodeModel.size() + " for graph " + name);
} catch (Exception e) {
log.error("error trying to create a blank node model: ", e);
throw new CannotCreateException(name);
}
long timeElapsedMillis = System.currentTimeMillis() - start;
log.debug("msecs to find blank nodes for graph " + name + " "
+ timeElapsedMillis);
Graph bnodeFilteringGraph = new BlankNodeFilteringGraph(
model.getGraph());
Model bnodeFilteringModel = ModelFactory
.createModelForGraph(bnodeFilteringGraph);
Model specialUnionModel = VitroModelFactory.createUnion(
bnodeFilteringModel, bnodeModel);
bnodeFilteringModel
.register(new BlankNodeStatementListener(bnodeModel));
return specialUnionModel;
}
}

View file

@ -2,320 +2,162 @@
package edu.cornell.mannlib.vitro.webapp.dao.jena;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.hp.hpl.jena.graph.BulkUpdateHandler;
import com.hp.hpl.jena.graph.Graph;
import com.hp.hpl.jena.graph.GraphMaker;
import com.hp.hpl.jena.query.Dataset;
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.ModelMaker;
import com.hp.hpl.jena.rdf.model.ModelReader;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.ResourceFactory;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.shared.AlreadyExistsException;
import com.hp.hpl.jena.shared.CannotCreateException;
import com.hp.hpl.jena.shared.DoesNotExistException;
import com.hp.hpl.jena.util.iterator.ExtendedIterator;
import com.hp.hpl.jena.util.iterator.WrappedIterator;
import com.ibm.icu.text.Collator;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService.ModelSerializationFormat;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceFactory;
import edu.cornell.mannlib.vitro.webapp.rdfservice.adapters.VitroModelFactory;
public class RDFServiceModelMaker implements ModelMaker {
private final static Log log = LogFactory.getLog(RDFServiceModelMaker.class);
private final static Log log = LogFactory.getLog(RDFServiceModelMaker.class);
private RDFServiceFactory rdfServiceFactory;
private RDFService service;
private RDFServiceDataset dataset;
public static final String METADATA_MODEL_URI =
"http://vitro.mannlib.cornell.edu/ns/vitro/sdb/metadata";
public RDFServiceModelMaker(RDFService service) {
this.service = service;
this.dataset = new RDFServiceDataset(service);
}
public static final String HAS_NAMED_MODEL_URI =
"http://vitro.mannlib.cornell.edu/ns/vitro/sdb/hasNamedModel";
@Override
public void close() {
dataset.close();
// service.close(); ?
}
private Resource dbResource; // a resource representing the triple store
@Override
public Model createModel(String name) {
Model model = getModel(name);
if (model == null) {
throw new CannotCreateException(name);
} else {
return model;
}
}
public RDFServiceModelMaker(RDFServiceFactory rdfServiceFactory) {
this.rdfServiceFactory = rdfServiceFactory;
@Override
public Model createModel(String name, boolean strict) {
if (this.hasModel(name) && strict) {
throw new AlreadyExistsException(name);
} else {
return createModel(name);
}
}
RDFService rdfService = rdfServiceFactory.getRDFService();
if (rdfService != null) {
try {
setUpMetadata(rdfService);
} catch (RDFServiceException se) {
log.error("unable to set up the cache of graph names");
} finally {
rdfService.close();
}
}
}
@Override
public GraphMaker getGraphMaker() {
throw new UnsupportedOperationException("GraphMaker not supported by "
+ this.getClass().getName());
}
protected RDFService getRDFService() {
return rdfServiceFactory.getRDFService();
}
private Set<String> getModelNames() {
try {
@SuppressWarnings("unchecked")
Set<String> names = new TreeSet<>(Collator.getInstance());
names.addAll(service.getGraphURIs());
return names;
} catch (RDFServiceException e) {
throw new RuntimeException(e);
}
}
Model getMetadataModel() {
return getModel(METADATA_MODEL_URI);
}
@Override
public boolean hasModel(String name) {
return getModelNames().contains(name);
}
public void close() {
// getRDFService().close(); ?
}
@Override
public ExtendedIterator<String> listModels() {
return WrappedIterator.create(getModelNames().iterator());
}
public Model createModel(String modelName) {
@Override
public Model openModel(String name, boolean strict) {
if (strict && !this.hasModel(name)) {
throw new DoesNotExistException(name);
} else {
return getModel(name);
}
}
Model model = getModel(modelName);
Model metadataModel = getMetadataModel();
/**
* The contract says to disassociate the name while leaving the model
* undisturbed. However, you should then be able to create a new model with
* the same name, and that doesn't make any sense in this context.
*/
@Override
public void removeModel(String name) {
Model m = getModel(name);
m.removeAll(null, null, null);
}
Literal modelNameLiteral = ResourceFactory.createPlainLiteral(modelName);
Statement metadataStatment = ResourceFactory.createStatement(dbResource,metadataModel.getProperty(
HAS_NAMED_MODEL_URI), modelNameLiteral);
@Override
public Model createDefaultModel() {
return dataset.getDefaultModel();
}
// to get around blank node filtering on BlankNodeFiltering graph
List<Statement> stmtList = new ArrayList<Statement>();
stmtList.add(metadataStatment);
@Override
public Model createFreshModel() {
throw new UnsupportedOperationException(
"createFreshModel not supported by "
+ this.getClass().getName());
}
try {
metadataModel.add(stmtList);
} finally {
metadataModel.close();
}
return model;
}
@Override
public Model openModel(String name) {
Model m = getModel(name);
if (m == null) {
throw new DoesNotExistException(name);
} else {
return m;
}
}
public Model createModel(String arg0, boolean arg1) {
// TODO Figure out if we can offer a "create if not found" option using SDB
return createModel(arg0);
}
@Override
public Model openModelIfPresent(String name) {
if (this.hasModel(name)) {
return getModel(name);
} else {
return null;
}
}
public GraphMaker getGraphMaker() {
throw new UnsupportedOperationException(
"GraphMaker not supported by " + this.getClass().getName());
}
@Override
public Model getModel(String name, ModelReader loadIfNotAbsent) {
Model m = getModel(name);
if (m == null) {
// Ignore the ModelReader. If the model is not present, give up.
throw new CannotCreateException(name);
} else {
return m;
}
}
public boolean hasModel(String arg0) {
Model metadataModel = getMetadataModel();
try {
StmtIterator stmtIt = metadataModel.listStatements(
dbResource, metadataModel.getProperty(
HAS_NAMED_MODEL_URI), arg0);
try {
return stmtIt.hasNext();
} finally {
if (stmtIt != null) {
stmtIt.close();
}
}
} finally {
metadataModel.close();
}
}
/**
* Return a model from the RDFService. If the model does not exist, create
* an empty one.
*/
@Override
public Model getModel(String name) {
if (name == null) {
return null;
}
return dataset.getNamedModel(name);
}
public ExtendedIterator<String> listModels() {
Model metadataModel = getMetadataModel();
try {
return listModelNames(metadataModel);
} finally {
metadataModel.close();
}
}
private ExtendedIterator<String> listModelNames(Model metadataModel) {
Set<String> modelNameSet = new HashSet<String>();
Iterator<RDFNode> metadataNameIt = metadataModel.listObjectsOfProperty(
metadataModel.getProperty(HAS_NAMED_MODEL_URI));
while (metadataNameIt.hasNext()) {
RDFNode rdfNode = metadataNameIt.next();
if (rdfNode.isLiteral()) {
modelNameSet.add(((Literal) rdfNode).getLexicalForm());
}
}
RDFService service = getRDFService();
try {
modelNameSet.addAll(service.getGraphURIs());
} catch (RDFServiceException e) {
throw new RuntimeException(e);
} finally {
service.close();
}
List<String> modelNameList = new ArrayList<String>();
modelNameList.addAll(modelNameSet);
Collections.sort(modelNameList, Collator.getInstance());
return WrappedIterator.create(modelNameList.iterator());
}
public Model openModel(String graph, boolean arg1) {
RDFService rdfService = getRDFService();
String bnodeQuery = "construct { ?s ?p ?o } where { ";
bnodeQuery += (graph != null) ? "graph <" + graph + "> {" : "";
bnodeQuery += "?s ?p ?o filter (isBlank(?s) || isBlank(?o)) }";
bnodeQuery += (graph != null) ? "}" : "";
Model bnodeModel = ModelFactory.createDefaultModel();
long start = System.currentTimeMillis();
try {
bnodeModel.read(rdfService.sparqlConstructQuery(bnodeQuery, ModelSerializationFormat.N3), null, "N3");
log.debug("constructed a model of blank nodes of size: " + bnodeModel.size() + " for graph " + graph);
} catch (RDFServiceException se) {
log.error("Error trying to create blank node model.", se);
return null;
} catch (Exception e) {
log.error("error trying to create a blank node model: " + e.getMessage());
return null;
}
long timeElapsedMillis = System.currentTimeMillis() - start;
log.debug("msecs to find blank nodes for graph " + graph + " " + timeElapsedMillis);
Model model = null;
try {
Dataset dataset = new RDFServiceDataset(rdfService);
model = dataset.getNamedModel(graph);
} finally {
rdfService.close();
}
Graph bnodeFilteringGraph = new BlankNodeFilteringGraph(model.getGraph());
Model bnodeFilteringModel = ModelFactory.createModelForGraph(bnodeFilteringGraph);
Model specialUnionModel = VitroModelFactory.createUnion(bnodeFilteringModel, bnodeModel);
bnodeFilteringModel.register(new BlankNodeStatementListener(bnodeModel));
return specialUnionModel;
}
public void removeModel(String arg0) {
Model m = getModel(arg0);
m.removeAll(null,null,null);
Model metadataModel = getMetadataModel();
try {
metadataModel.remove(dbResource, metadataModel.getProperty(
HAS_NAMED_MODEL_URI),metadataModel.createLiteral(arg0));
} finally {
metadataModel.close();
}
}
public Model addDescription(Model arg0, Resource arg1) {
throw new UnsupportedOperationException(
"addDescription not supported by " + this.getClass().getName());
}
public Model createModelOver(String arg0) {
throw new UnsupportedOperationException(
"createModelOver not supported by " + this.getClass().getName());
}
public Model getDescription() {
throw new UnsupportedOperationException(
"createModelOver not supported by " + this.getClass().getName());
}
public Model getDescription(Resource arg0) {
throw new UnsupportedOperationException(
"getDescription not supported by "+this.getClass().getName());
}
public Model openModel() {
RDFService service = getRDFService();
try {
Dataset dataset = new RDFServiceDataset(service);
return dataset.getDefaultModel();
} finally {
service.close();
}
}
public Model createDefaultModel() {
return openModel();
}
public Model createFreshModel() {
throw new UnsupportedOperationException(
"createFreshModel not supported by " + this.getClass().getName());
}
/**
* @deprecated
*/
public Model createModel() {
return openModel();
}
/**
* @deprecated
*/
public Model getModel() {
return openModel();
}
public Model openModel(String arg0) {
return openModel();
}
public Model openModelIfPresent(String arg0) {
return (this.hasModel(arg0))
? openModel(arg0, false)
: null;
}
public Model getModel(String modelName) {
return openModel(modelName, true);
}
public Model getModel(String arg0, ModelReader arg1) {
throw new UnsupportedOperationException(
"getModel(String, ModelReader) not supported by " +
this.getClass().getName());
}
private void setUpMetadata(RDFService rdfService) throws RDFServiceException {
Model metadataModel = getMetadataModel();
if (metadataModel.size() == 0) {
// set up the model name metadata to avoid expensive calls to
// listNames()
Resource resource = metadataModel.createResource();
this.dbResource = resource;
List<String> graphNames = rdfService.getGraphURIs();
Iterator<String> nameIt = graphNames.iterator();
while (nameIt.hasNext()) {
String name = nameIt.next();
metadataModel.add(dbResource,metadataModel.getProperty(
HAS_NAMED_MODEL_URI),name);
}
} else {
StmtIterator stmtIt = metadataModel.listStatements(
(Resource) null, metadataModel.getProperty(
HAS_NAMED_MODEL_URI),(RDFNode) null);
if (stmtIt.hasNext()) {
Statement stmt = stmtIt.nextStatement();
dbResource = stmt.getSubject();
}
stmtIt.close();
}
}
}

View file

@ -8,11 +8,19 @@ package edu.cornell.mannlib.vitro.webapp.modelaccess;
public class ModelNames {
public static final String ABOX_ASSERTIONS = "http://vitro.mannlib.cornell.edu/default/vitro-kb-2";
public static final String ABOX_INFERENCES = "http://vitro.mannlib.cornell.edu/default/vitro-kb-inf";
public static final String ABOX_UNION = "vitro:aboxOntModel";
public static final String TBOX_ASSERTIONS = "http://vitro.mannlib.cornell.edu/default/asserted-tbox";
public static final String TBOX_INFERENCES = "http://vitro.mannlib.cornell.edu/default/inferred-tbox";
public static final String TBOX_UNION = "vitro:tboxOntModel";
public static final String FULL_ASSERTIONS = "vitro:baseOntModel";
public static final String FULL_INFERENCES = "vitro:inferenceOntModel";
public static final String FULL_UNION = "vitro:jenaOntModel";
public static final String APPLICATION_METADATA = "http://vitro.mannlib.cornell.edu/default/vitro-kb-applicationMetadata";
public static final String USER_ACCOUNTS = "http://vitro.mannlib.cornell.edu/default/vitro-kb-userAccounts";
public static final String DISPLAY = "http://vitro.mannlib.cornell.edu/default/vitro-kb-displayMetadata";
public static final String DISPLAY_TBOX = "http://vitro.mannlib.cornell.edu/default/vitro-kb-displayMetadataTBOX";
public static final String DISPLAY_DISPLAY = "http://vitro.mannlib.cornell.edu/default/vitro-kb-displayMetadata-displayModel";
public static final String APPLICATION_METADATA = "http://vitro.mannlib.cornell.edu/default/vitro-kb-applicationMetadata";
}