VIVO-281 Make extended linked data a configurable option.
This commit is contained in:
parent
3258e2ad4f
commit
ef6437faeb
5 changed files with 484 additions and 184 deletions
|
@ -207,5 +207,13 @@ public class VitroRequest extends HttpServletRequestWrapper {
|
||||||
return _req.getParameterValues(name);
|
return _req.getParameterValues(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setLanguageNeutralUnionFullModel(OntModel model) {
|
||||||
|
setAttribute("languageNeutralUnionFullModel", model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OntModel getLanguageNeutralUnionFullModel() {
|
||||||
|
return (OntModel) getAttribute("languageNeutralUnionFullModel");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,235 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.controller.individual;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import com.hp.hpl.jena.datatypes.TypeMapper;
|
||||||
|
import com.hp.hpl.jena.datatypes.xsd.XSDDatatype;
|
||||||
|
import com.hp.hpl.jena.ontology.OntModel;
|
||||||
|
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.Property;
|
||||||
|
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.Lock;
|
||||||
|
import com.hp.hpl.jena.vocabulary.RDF;
|
||||||
|
import com.hp.hpl.jena.vocabulary.RDFS;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatement;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.VClass;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.RdfResponseValues;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.utils.jena.ExtendedLinkedDataUtils;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.utils.jena.JenaOutputUtils;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.web.ContentType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO Keep this around until release 1.7, in case anyone is relying on it.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public class ExtendedRdfAssembler {
|
||||||
|
private static final Log log = LogFactory
|
||||||
|
.getLog(ExtendedRdfAssembler.class);
|
||||||
|
|
||||||
|
private static final String RICH_EXPORT_ROOT = "/WEB-INF/rich-export/";
|
||||||
|
private static final String PERSON_CLASS_URI = "http://xmlns.com/foaf/0.1/Person";
|
||||||
|
private static final String INCLUDE_ALL = "all";
|
||||||
|
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
private static final Map<String, String> namespaces = new HashMap<String, String>() {{
|
||||||
|
put("display", VitroVocabulary.DISPLAY);
|
||||||
|
put("vitro", VitroVocabulary.vitroURI);
|
||||||
|
put("vitroPublic", VitroVocabulary.VITRO_PUBLIC);
|
||||||
|
}};
|
||||||
|
|
||||||
|
private static final Property extendedLinkedDataProperty = ResourceFactory.createProperty(namespaces.get("vitro") + "extendedLinkedData");
|
||||||
|
private static final Literal xsdTrue = ResourceFactory.createTypedLiteral("true", XSDDatatype.XSDboolean);
|
||||||
|
|
||||||
|
private final VitroRequest vreq;
|
||||||
|
private final ServletContext ctx;
|
||||||
|
private final Individual individual;
|
||||||
|
private final ContentType rdfFormat;
|
||||||
|
|
||||||
|
public ExtendedRdfAssembler(VitroRequest vreq, Individual individual,
|
||||||
|
ContentType rdfFormat) {
|
||||||
|
this.vreq = vreq;
|
||||||
|
this.ctx = vreq.getSession().getServletContext();
|
||||||
|
this.individual = individual;
|
||||||
|
this.rdfFormat = rdfFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public ResponseValues assembleRdf() {
|
||||||
|
OntModel ontModel = vreq.getJenaOntModel();
|
||||||
|
|
||||||
|
String[] includes = vreq.getParameterValues("include");
|
||||||
|
Model newModel = getRDF(individual, ontModel, ModelFactory.createDefaultModel(), 0, includes);
|
||||||
|
JenaOutputUtils.setNameSpacePrefixes(newModel, vreq.getWebappDaoFactory());
|
||||||
|
return new RdfResponseValues(rdfFormat, newModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Model getRDF(Individual entity, OntModel contextModel, Model newModel, int recurseDepth, String[] includes) {
|
||||||
|
|
||||||
|
Resource subj = newModel.getResource(entity.getURI());
|
||||||
|
|
||||||
|
List<DataPropertyStatement> dstates = entity.getDataPropertyStatements();
|
||||||
|
TypeMapper typeMapper = TypeMapper.getInstance();
|
||||||
|
for (DataPropertyStatement ds: dstates) {
|
||||||
|
Property dp = newModel.getProperty(ds.getDatapropURI());
|
||||||
|
Literal lit = null;
|
||||||
|
if ((ds.getLanguage()) != null && (ds.getLanguage().length()>0)) {
|
||||||
|
lit = newModel.createLiteral(ds.getData(),ds.getLanguage());
|
||||||
|
} else if ((ds.getDatatypeURI() != null) && (ds.getDatatypeURI().length()>0)) {
|
||||||
|
lit = newModel.createTypedLiteral(ds.getData(),typeMapper.getSafeTypeByName(ds.getDatatypeURI()));
|
||||||
|
} else {
|
||||||
|
lit = newModel.createLiteral(ds.getData());
|
||||||
|
}
|
||||||
|
newModel.add(newModel.createStatement(subj, dp, lit));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recurseDepth < 5) {
|
||||||
|
List<ObjectPropertyStatement> ostates = entity.getObjectPropertyStatements();
|
||||||
|
|
||||||
|
for (ObjectPropertyStatement os: ostates) {
|
||||||
|
Property prop = newModel.getProperty(os.getPropertyURI());
|
||||||
|
Resource obj = newModel.getResource(os.getObjectURI());
|
||||||
|
newModel.add(newModel.createStatement(subj, prop, obj));
|
||||||
|
if ( includeInLinkedData(obj, contextModel)) {
|
||||||
|
newModel.add(getRDF(os.getObject(), contextModel, newModel, recurseDepth + 1, includes));
|
||||||
|
} else {
|
||||||
|
contextModel.enterCriticalSection(Lock.READ);
|
||||||
|
try {
|
||||||
|
newModel.add(contextModel.listStatements(obj, RDFS.label, (RDFNode)null));
|
||||||
|
} finally {
|
||||||
|
contextModel.leaveCriticalSection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newModel = getLabelAndTypes(entity, contextModel, newModel );
|
||||||
|
newModel = getStatementsWithUntypedProperties(subj, contextModel, vreq.getAssertionsOntModel(), newModel);
|
||||||
|
|
||||||
|
//bdc34: The following code adds all triples where entity is the Subject.
|
||||||
|
// contextModel.enterCriticalSection(Lock.READ);
|
||||||
|
// try {
|
||||||
|
// StmtIterator iter = contextModel.listStatements(subj, (Property) null, (RDFNode) null);
|
||||||
|
// while (iter.hasNext()) {
|
||||||
|
// Statement stmt = iter.next();
|
||||||
|
// if (!newModel.contains(stmt)) {
|
||||||
|
// newModel.add(stmt);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } finally {
|
||||||
|
// contextModel.leaveCriticalSection();
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (recurseDepth == 0 && includes != null && entity.isVClass(PERSON_CLASS_URI)) {
|
||||||
|
|
||||||
|
for (String include : includes) {
|
||||||
|
|
||||||
|
String rootDir = null;
|
||||||
|
if (INCLUDE_ALL.equals(include)) {
|
||||||
|
rootDir = RICH_EXPORT_ROOT;
|
||||||
|
} else {
|
||||||
|
rootDir = RICH_EXPORT_ROOT + include + "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
Model extendedModel = ExtendedLinkedDataUtils.createModelFromQueries(ctx, rootDir, contextModel, entity.getURI());
|
||||||
|
long elapsedTimeMillis = System.currentTimeMillis()-start;
|
||||||
|
log.info("Time to create rich export model: msecs = " + elapsedTimeMillis);
|
||||||
|
|
||||||
|
newModel.add(extendedModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean includeInLinkedData(Resource object, Model contextModel) {
|
||||||
|
|
||||||
|
boolean retval = false;
|
||||||
|
|
||||||
|
contextModel.enterCriticalSection(Lock.READ);
|
||||||
|
|
||||||
|
try {
|
||||||
|
StmtIterator iter = contextModel.listStatements(object, RDF.type, (RDFNode)null);
|
||||||
|
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
Statement stmt = iter.next();
|
||||||
|
|
||||||
|
if (stmt.getObject().isResource() && contextModel.contains(stmt.getObject().asResource(), extendedLinkedDataProperty, xsdTrue)) {
|
||||||
|
retval = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
contextModel.leaveCriticalSection();
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the properties that are difficult to get via a filtered WebappDaoFactory. */
|
||||||
|
private Model getLabelAndTypes(Individual entity, Model ontModel, Model newModel){
|
||||||
|
for( VClass vclass : entity.getVClasses()){
|
||||||
|
newModel.add(newModel.getResource(entity.getURI()), RDF.type, newModel.getResource(vclass.getURI()));
|
||||||
|
}
|
||||||
|
|
||||||
|
ontModel.enterCriticalSection(Lock.READ);
|
||||||
|
try {
|
||||||
|
newModel.add(ontModel.listStatements(ontModel.getResource(entity.getURI()), RDFS.label, (RDFNode)null));
|
||||||
|
} finally {
|
||||||
|
ontModel.leaveCriticalSection();
|
||||||
|
}
|
||||||
|
|
||||||
|
return newModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This method adds in statements in which the property does not
|
||||||
|
* have an rdf type in the asserted model.
|
||||||
|
* This was added for release 1.5 to handle cases such as the
|
||||||
|
* reasoning-plugin inferred dcterms:creator assertion
|
||||||
|
*/
|
||||||
|
private Model getStatementsWithUntypedProperties(Resource subject, OntModel contextModel, OntModel assertionsModel, Model newModel) {
|
||||||
|
contextModel.enterCriticalSection(Lock.READ);
|
||||||
|
try {
|
||||||
|
StmtIterator iter = contextModel.listStatements(subject, (Property) null, (RDFNode) null);
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
Statement stmt = iter.next();
|
||||||
|
Property property = stmt.getPredicate();
|
||||||
|
assertionsModel.enterCriticalSection(Lock.READ);
|
||||||
|
try {
|
||||||
|
if (!assertionsModel.contains(property, RDF.type) && !newModel.contains(stmt)) {
|
||||||
|
newModel.add(stmt);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
assertionsModel.leaveCriticalSection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
contextModel.leaveCriticalSection();
|
||||||
|
}
|
||||||
|
|
||||||
|
return newModel;
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet;
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ExceptionResponseValues;
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ExceptionResponseValues;
|
||||||
|
@ -30,10 +31,14 @@ public class IndividualController extends FreemarkerHttpServlet {
|
||||||
|
|
||||||
private static final String TEMPLATE_HELP = "individual-help.ftl";
|
private static final String TEMPLATE_HELP = "individual-help.ftl";
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
private static final String PROPERTY_EXTENDED_LOD = "serveExtendedLinkedData";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use this map to decide which MIME type is suited for the "accept" header.
|
* Use this map to decide which MIME type is suited for the "accept" header.
|
||||||
*/
|
*/
|
||||||
public static final Map<String, Float> ACCEPTED_CONTENT_TYPES = initializeContentTypes();
|
public static final Map<String, Float> ACCEPTED_CONTENT_TYPES = initializeContentTypes();
|
||||||
|
|
||||||
private static Map<String, Float> initializeContentTypes() {
|
private static Map<String, Float> initializeContentTypes() {
|
||||||
HashMap<String, Float> map = new HashMap<String, Float>();
|
HashMap<String, Float> map = new HashMap<String, Float>();
|
||||||
map.put(HTML_MIMETYPE, 0.5f);
|
map.put(HTML_MIMETYPE, 0.5f);
|
||||||
|
@ -82,9 +87,15 @@ public class IndividualController extends FreemarkerHttpServlet {
|
||||||
* If they are asking for RDF using the preferred URL, give it
|
* If they are asking for RDF using the preferred URL, give it
|
||||||
* to them.
|
* to them.
|
||||||
*/
|
*/
|
||||||
|
if (useExtendedLOD(vreq)) {
|
||||||
|
return new ExtendedRdfAssembler(vreq,
|
||||||
|
requestInfo.getIndividual(),
|
||||||
|
requestInfo.getRdfFormat()).assembleRdf();
|
||||||
|
} else {
|
||||||
return new IndividualRdfAssembler(vreq,
|
return new IndividualRdfAssembler(vreq,
|
||||||
requestInfo.getIndividual(), requestInfo.getRdfFormat())
|
requestInfo.getIndividual().getURI(),
|
||||||
.assembleRdf();
|
requestInfo.getRdfFormat()).assembleRdf();
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
/*
|
/*
|
||||||
* Otherwise, prepare an HTML response for the requested
|
* Otherwise, prepare an HTML response for the requested
|
||||||
|
@ -113,6 +124,11 @@ public class IndividualController extends FreemarkerHttpServlet {
|
||||||
HttpServletResponse.SC_NOT_FOUND);
|
HttpServletResponse.SC_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean useExtendedLOD(HttpServletRequest req) {
|
||||||
|
ConfigurationProperties props = ConfigurationProperties.getBean(req);
|
||||||
|
return Boolean.valueOf(props.getProperty(PROPERTY_EXTENDED_LOD));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doPost(HttpServletRequest request, HttpServletResponse response)
|
public void doPost(HttpServletRequest request, HttpServletResponse response)
|
||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
|
|
|
@ -2,233 +2,270 @@
|
||||||
|
|
||||||
package edu.cornell.mannlib.vitro.webapp.controller.individual;
|
package edu.cornell.mannlib.vitro.webapp.controller.individual;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.Set;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.servlet.ServletContext;
|
import javax.servlet.ServletContext;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import com.hp.hpl.jena.datatypes.TypeMapper;
|
|
||||||
import com.hp.hpl.jena.datatypes.xsd.XSDDatatype;
|
|
||||||
import com.hp.hpl.jena.ontology.OntModel;
|
import com.hp.hpl.jena.ontology.OntModel;
|
||||||
import com.hp.hpl.jena.rdf.model.Literal;
|
import com.hp.hpl.jena.ontology.OntModelSpec;
|
||||||
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.rdf.model.ModelFactory;
|
||||||
import com.hp.hpl.jena.rdf.model.Property;
|
|
||||||
import com.hp.hpl.jena.rdf.model.RDFNode;
|
import com.hp.hpl.jena.rdf.model.RDFNode;
|
||||||
import com.hp.hpl.jena.rdf.model.Resource;
|
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.Statement;
|
||||||
import com.hp.hpl.jena.rdf.model.StmtIterator;
|
import com.hp.hpl.jena.rdf.model.StmtIterator;
|
||||||
import com.hp.hpl.jena.shared.Lock;
|
|
||||||
import com.hp.hpl.jena.vocabulary.RDF;
|
import com.hp.hpl.jena.vocabulary.RDF;
|
||||||
import com.hp.hpl.jena.vocabulary.RDFS;
|
import com.hp.hpl.jena.vocabulary.RDFS;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.auth.policy.PolicyHelper;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.display.DisplayDataPropertyStatement;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.display.DisplayObjectPropertyStatement;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.ifaces.RequestedAction;
|
||||||
import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatement;
|
import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatement;
|
||||||
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
|
import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatementImpl;
|
||||||
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement;
|
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement;
|
||||||
import edu.cornell.mannlib.vitro.webapp.beans.VClass;
|
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatementImpl;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.RdfResponseValues;
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.RdfResponseValues;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
|
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceUtils;
|
||||||
import edu.cornell.mannlib.vitro.webapp.utils.jena.ExtendedLinkedDataUtils;
|
import edu.cornell.mannlib.vitro.webapp.utils.jena.ExtendedLinkedDataUtils;
|
||||||
import edu.cornell.mannlib.vitro.webapp.utils.jena.JenaOutputUtils;
|
import edu.cornell.mannlib.vitro.webapp.utils.jena.JenaOutputUtils;
|
||||||
import edu.cornell.mannlib.vitro.webapp.web.ContentType;
|
import edu.cornell.mannlib.vitro.webapp.web.ContentType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO See where this can be improved.
|
* Write a smaller set of Linked Data. It consists of:
|
||||||
|
*
|
||||||
|
* 1) The data properties of the entity
|
||||||
|
*
|
||||||
|
* 2) The object properties in which the entity is either subject or object
|
||||||
|
*
|
||||||
|
* 3) The labels and types of the objects that are linked by those properties.
|
||||||
|
*
|
||||||
|
* If the request comes with an Accept-language header, use an appropriately
|
||||||
|
* language-aware data source to filter the data properties and labels.
|
||||||
|
* Otherwise, show all triples, regardless of language.
|
||||||
|
*
|
||||||
|
* Filter the result based on the policy, removing any triples that should not
|
||||||
|
* be displayed to the public (or to the user, if logged in). Also remove any
|
||||||
|
* objects which can only be reached by excluded triples.
|
||||||
|
*
|
||||||
|
* ----------------
|
||||||
|
*
|
||||||
|
* This still permits the use of rich export, by "include" options on the
|
||||||
|
* request. The only difference from earlier implementations is that the result
|
||||||
|
* may be made language-aware.
|
||||||
*/
|
*/
|
||||||
public class IndividualRdfAssembler {
|
public class IndividualRdfAssembler {
|
||||||
private static final Log log = LogFactory
|
private static final Log log = LogFactory
|
||||||
.getLog(IndividualRdfAssembler.class);
|
.getLog(IndividualRdfAssembler.class);
|
||||||
|
|
||||||
private static final String RICH_EXPORT_ROOT = "/WEB-INF/rich-export/";
|
private static final String RICH_EXPORT_ROOT = "/WEB-INF/rich-export/";
|
||||||
private static final String PERSON_CLASS_URI = "http://xmlns.com/foaf/0.1/Person";
|
|
||||||
private static final String INCLUDE_ALL = "all";
|
private static final String INCLUDE_ALL = "all";
|
||||||
|
|
||||||
@SuppressWarnings("serial")
|
|
||||||
private static final Map<String, String> namespaces = new HashMap<String, String>() {{
|
|
||||||
put("display", VitroVocabulary.DISPLAY);
|
|
||||||
put("vitro", VitroVocabulary.vitroURI);
|
|
||||||
put("vitroPublic", VitroVocabulary.VITRO_PUBLIC);
|
|
||||||
}};
|
|
||||||
|
|
||||||
private static final Property extendedLinkedDataProperty = ResourceFactory.createProperty(namespaces.get("vitro") + "extendedLinkedData");
|
|
||||||
private static final Literal xsdTrue = ResourceFactory.createTypedLiteral("true", XSDDatatype.XSDboolean);
|
|
||||||
|
|
||||||
private final VitroRequest vreq;
|
private final VitroRequest vreq;
|
||||||
private final ServletContext ctx;
|
private final ServletContext ctx;
|
||||||
private final Individual individual;
|
private final String individualUri;
|
||||||
private final ContentType rdfFormat;
|
private final ContentType rdfFormat;
|
||||||
|
private final String[] richExportIncludes;
|
||||||
|
private final RDFService rdfService;
|
||||||
|
private final OntModel contentModel;
|
||||||
|
private final WebappDaoFactory wadf;
|
||||||
|
|
||||||
public IndividualRdfAssembler(VitroRequest vreq, Individual individual,
|
public IndividualRdfAssembler(VitroRequest vreq, String individualUri,
|
||||||
ContentType rdfFormat) {
|
ContentType rdfFormat) {
|
||||||
this.vreq = vreq;
|
this.vreq = vreq;
|
||||||
this.ctx = vreq.getSession().getServletContext();
|
this.ctx = vreq.getSession().getServletContext();
|
||||||
this.individual = individual;
|
|
||||||
this.rdfFormat = rdfFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
this.individualUri = individualUri;
|
||||||
* @return
|
this.rdfFormat = rdfFormat;
|
||||||
*/
|
|
||||||
public ResponseValues assembleRdf() {
|
|
||||||
OntModel ontModel = vreq.getJenaOntModel();
|
|
||||||
|
|
||||||
String[] includes = vreq.getParameterValues("include");
|
String[] includes = vreq.getParameterValues("include");
|
||||||
Model newModel = getRDF(individual, ontModel, ModelFactory.createDefaultModel(), 0, includes);
|
this.richExportIncludes = (includes == null) ? new String[0] : includes;
|
||||||
JenaOutputUtils.setNameSpacePrefixes(newModel, vreq.getWebappDaoFactory());
|
|
||||||
|
if (isLanguageAware()) {
|
||||||
|
this.rdfService = vreq.getRDFService();
|
||||||
|
this.contentModel = vreq.getJenaOntModel();
|
||||||
|
} else {
|
||||||
|
this.rdfService = vreq.getUnfilteredRDFService();
|
||||||
|
this.contentModel = vreq.getLanguageNeutralUnionFullModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
wadf = vreq.getWebappDaoFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResponseValues assembleRdf() {
|
||||||
|
OntModel newModel = getRdf();
|
||||||
|
newModel.add(getRichExportRdf());
|
||||||
|
JenaOutputUtils.setNameSpacePrefixes(newModel, wadf);
|
||||||
return new RdfResponseValues(rdfFormat, newModel);
|
return new RdfResponseValues(rdfFormat, newModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Model getRDF(Individual entity, OntModel contextModel, Model newModel, int recurseDepth, String[] includes) {
|
private boolean isLanguageAware() {
|
||||||
|
return StringUtils.isNotEmpty(vreq.getHeader("Accept-Language"));
|
||||||
Resource subj = newModel.getResource(entity.getURI());
|
|
||||||
|
|
||||||
List<DataPropertyStatement> dstates = entity.getDataPropertyStatements();
|
|
||||||
TypeMapper typeMapper = TypeMapper.getInstance();
|
|
||||||
for (DataPropertyStatement ds: dstates) {
|
|
||||||
Property dp = newModel.getProperty(ds.getDatapropURI());
|
|
||||||
Literal lit = null;
|
|
||||||
if ((ds.getLanguage()) != null && (ds.getLanguage().length()>0)) {
|
|
||||||
lit = newModel.createLiteral(ds.getData(),ds.getLanguage());
|
|
||||||
} else if ((ds.getDatatypeURI() != null) && (ds.getDatatypeURI().length()>0)) {
|
|
||||||
lit = newModel.createTypedLiteral(ds.getData(),typeMapper.getSafeTypeByName(ds.getDatatypeURI()));
|
|
||||||
} else {
|
|
||||||
lit = newModel.createLiteral(ds.getData());
|
|
||||||
}
|
|
||||||
newModel.add(newModel.createStatement(subj, dp, lit));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recurseDepth < 5) {
|
private OntModel getRdf() {
|
||||||
List<ObjectPropertyStatement> ostates = entity.getObjectPropertyStatements();
|
OntModel o = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM);
|
||||||
|
o.add(getStatementsAboutEntity());
|
||||||
|
o.add(getLabelsAndTypesOfRelatedObjects());
|
||||||
|
filterByPolicy(o);
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
for (ObjectPropertyStatement os: ostates) {
|
/**
|
||||||
Property prop = newModel.getProperty(os.getPropertyURI());
|
* Get all statements that have the entity as either the subject or the
|
||||||
Resource obj = newModel.getResource(os.getObjectURI());
|
* object.
|
||||||
newModel.add(newModel.createStatement(subj, prop, obj));
|
*/
|
||||||
if ( includeInLinkedData(obj, contextModel)) {
|
private Model getStatementsAboutEntity() {
|
||||||
newModel.add(getRDF(os.getObject(), contextModel, newModel, recurseDepth + 1, includes));
|
Model m = runConstructQuery(String
|
||||||
|
.format("CONSTRUCT { <%1$s> ?predicate ?object . } "
|
||||||
|
+ "WHERE { <%1$s> ?predicate ?object } ", individualUri));
|
||||||
|
m.add(runConstructQuery(String.format(
|
||||||
|
"CONSTRUCT { ?s ?predicate <%1$s> . } "
|
||||||
|
+ "WHERE { ?s ?predicate <%1$s> } ", individualUri)));
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the labels and types of all related objects.
|
||||||
|
*/
|
||||||
|
private Model getLabelsAndTypesOfRelatedObjects() {
|
||||||
|
Model m = runConstructQuery(String
|
||||||
|
.format("CONSTRUCT { ?object <%2$s> ?type . } "
|
||||||
|
+ "WHERE { <%1$s> ?predicate ?object ."
|
||||||
|
+ " ?object <%2$s> ?type . } ", individualUri, RDF.type));
|
||||||
|
m.add(runConstructQuery(String.format(
|
||||||
|
"CONSTRUCT { ?object <%2$s> ?label . } "
|
||||||
|
+ "WHERE { <%1$s> ?predicate ?object ."
|
||||||
|
+ " ?object <%2$s> ?label . } ", individualUri,
|
||||||
|
RDFS.label)));
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove any triples that we aren't allowed to see. Then remove any objects
|
||||||
|
* that we no longer have access to.
|
||||||
|
*/
|
||||||
|
private void filterByPolicy(OntModel o) {
|
||||||
|
removeProhibitedTriples(o);
|
||||||
|
Set<String> okObjects = determineAccessibleUris(o);
|
||||||
|
removeOrphanedObjects(o, okObjects);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the triples that we aren't allowed to see.
|
||||||
|
*/
|
||||||
|
private void removeProhibitedTriples(OntModel o) {
|
||||||
|
StmtIterator stmts = o.listStatements();
|
||||||
|
while (stmts.hasNext()) {
|
||||||
|
Statement stmt = stmts.next();
|
||||||
|
String subjectUri = stmt.getSubject().getURI();
|
||||||
|
String predicateUri = stmt.getPredicate().getURI();
|
||||||
|
if (stmt.getObject().isLiteral()) {
|
||||||
|
String value = stmt.getObject().asLiteral().getString();
|
||||||
|
DataPropertyStatement dps = new DataPropertyStatementImpl(
|
||||||
|
subjectUri, predicateUri, value);
|
||||||
|
RequestedAction ddps = new DisplayDataPropertyStatement(dps);
|
||||||
|
if (!PolicyHelper.isAuthorizedForActions(vreq, ddps)) {
|
||||||
|
log.debug("not authorized: " + ddps);
|
||||||
|
stmts.remove();
|
||||||
|
}
|
||||||
|
} else if (stmt.getObject().isURIResource()) {
|
||||||
|
String objectUri = stmt.getObject().asResource().getURI();
|
||||||
|
ObjectPropertyStatement ops = new ObjectPropertyStatementImpl(
|
||||||
|
subjectUri, predicateUri, objectUri);
|
||||||
|
RequestedAction dops = new DisplayObjectPropertyStatement(ops);
|
||||||
|
if (!PolicyHelper.isAuthorizedForActions(vreq, dops)) {
|
||||||
|
log.debug("not authorized: " + dops);
|
||||||
|
stmts.remove();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
contextModel.enterCriticalSection(Lock.READ);
|
log.warn("blank node: + stmt");
|
||||||
|
stmts.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collect the URIs of all objects that are accessible through permitted
|
||||||
|
* triples.
|
||||||
|
*/
|
||||||
|
private Set<String> determineAccessibleUris(OntModel o) {
|
||||||
|
Resource i = o.getResource(individualUri);
|
||||||
|
Set<String> uris = new HashSet<>();
|
||||||
|
uris.add(individualUri);
|
||||||
|
|
||||||
|
StmtIterator stmts;
|
||||||
|
|
||||||
|
stmts = o.listStatements(i, null, (RDFNode) null);
|
||||||
|
while (stmts.hasNext()) {
|
||||||
|
Statement stmt = stmts.next();
|
||||||
|
if (stmt.getObject().isURIResource()) {
|
||||||
|
uris.add(stmt.getObject().asResource().getURI());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stmts = o.listStatements(null, null, i);
|
||||||
|
while (stmts.hasNext()) {
|
||||||
|
Statement stmt = stmts.next();
|
||||||
|
uris.add(stmt.getSubject().getURI());
|
||||||
|
}
|
||||||
|
|
||||||
|
return uris;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove any statements about objects that cannot be reached through
|
||||||
|
* permitted triples.
|
||||||
|
*/
|
||||||
|
private void removeOrphanedObjects(OntModel o, Set<String> okObjects) {
|
||||||
|
StmtIterator stmts = o.listStatements();
|
||||||
|
while (stmts.hasNext()) {
|
||||||
|
Statement stmt = stmts.next();
|
||||||
|
if (!okObjects.contains(stmt.getSubject().getURI())) {
|
||||||
|
log.debug("removing orphan triple: " + stmt);
|
||||||
|
stmts.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Model runConstructQuery(String query) {
|
||||||
try {
|
try {
|
||||||
newModel.add(contextModel.listStatements(obj, RDFS.label, (RDFNode)null));
|
return RDFServiceUtils.parseModel(rdfService.sparqlConstructQuery(
|
||||||
} finally {
|
query, RDFService.ModelSerializationFormat.N3),
|
||||||
contextModel.leaveCriticalSection();
|
RDFService.ModelSerializationFormat.N3);
|
||||||
}
|
} catch (RDFServiceException e) {
|
||||||
}
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newModel = getLabelAndTypes(entity, contextModel, newModel );
|
private Model getRichExportRdf() {
|
||||||
newModel = getStatementsWithUntypedProperties(subj, contextModel, vreq.getAssertionsOntModel(), newModel);
|
Model richExportModel = ModelFactory.createDefaultModel();
|
||||||
|
|
||||||
//bdc34: The following code adds all triples where entity is the Subject.
|
for (String include : richExportIncludes) {
|
||||||
// contextModel.enterCriticalSection(Lock.READ);
|
String rootDir = RICH_EXPORT_ROOT;
|
||||||
// try {
|
if (!INCLUDE_ALL.equals(include)) {
|
||||||
// StmtIterator iter = contextModel.listStatements(subj, (Property) null, (RDFNode) null);
|
rootDir += include + "/";
|
||||||
// while (iter.hasNext()) {
|
|
||||||
// Statement stmt = iter.next();
|
|
||||||
// if (!newModel.contains(stmt)) {
|
|
||||||
// newModel.add(stmt);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } finally {
|
|
||||||
// contextModel.leaveCriticalSection();
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (recurseDepth == 0 && includes != null && entity.isVClass(PERSON_CLASS_URI)) {
|
|
||||||
|
|
||||||
for (String include : includes) {
|
|
||||||
|
|
||||||
String rootDir = null;
|
|
||||||
if (INCLUDE_ALL.equals(include)) {
|
|
||||||
rootDir = RICH_EXPORT_ROOT;
|
|
||||||
} else {
|
|
||||||
rootDir = RICH_EXPORT_ROOT + include + "/";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
Model extendedModel = ExtendedLinkedDataUtils.createModelFromQueries(ctx, rootDir, contextModel, entity.getURI());
|
richExportModel.add(ExtendedLinkedDataUtils.createModelFromQueries(
|
||||||
long elapsedTimeMillis = System.currentTimeMillis()-start;
|
ctx, rootDir, contentModel, individualUri));
|
||||||
log.info("Time to create rich export model: msecs = " + elapsedTimeMillis);
|
long elapsedTimeMillis = System.currentTimeMillis() - start;
|
||||||
|
log.debug("Time to create rich export model: msecs = "
|
||||||
newModel.add(extendedModel);
|
+ elapsedTimeMillis);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return newModel;
|
return richExportModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean includeInLinkedData(Resource object, Model contextModel) {
|
|
||||||
|
|
||||||
boolean retval = false;
|
|
||||||
|
|
||||||
contextModel.enterCriticalSection(Lock.READ);
|
|
||||||
|
|
||||||
try {
|
|
||||||
StmtIterator iter = contextModel.listStatements(object, RDF.type, (RDFNode)null);
|
|
||||||
|
|
||||||
while (iter.hasNext()) {
|
|
||||||
Statement stmt = iter.next();
|
|
||||||
|
|
||||||
if (stmt.getObject().isResource() && contextModel.contains(stmt.getObject().asResource(), extendedLinkedDataProperty, xsdTrue)) {
|
|
||||||
retval = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
contextModel.leaveCriticalSection();
|
|
||||||
}
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the properties that are difficult to get via a filtered WebappDaoFactory. */
|
|
||||||
private Model getLabelAndTypes(Individual entity, Model ontModel, Model newModel){
|
|
||||||
for( VClass vclass : entity.getVClasses()){
|
|
||||||
newModel.add(newModel.getResource(entity.getURI()), RDF.type, newModel.getResource(vclass.getURI()));
|
|
||||||
}
|
|
||||||
|
|
||||||
ontModel.enterCriticalSection(Lock.READ);
|
|
||||||
try {
|
|
||||||
newModel.add(ontModel.listStatements(ontModel.getResource(entity.getURI()), RDFS.label, (RDFNode)null));
|
|
||||||
} finally {
|
|
||||||
ontModel.leaveCriticalSection();
|
|
||||||
}
|
|
||||||
|
|
||||||
return newModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This method adds in statements in which the property does not
|
|
||||||
* have an rdf type in the asserted model.
|
|
||||||
* This was added for release 1.5 to handle cases such as the
|
|
||||||
* reasoning-plugin inferred dcterms:creator assertion
|
|
||||||
*/
|
|
||||||
private Model getStatementsWithUntypedProperties(Resource subject, OntModel contextModel, OntModel assertionsModel, Model newModel) {
|
|
||||||
contextModel.enterCriticalSection(Lock.READ);
|
|
||||||
try {
|
|
||||||
StmtIterator iter = contextModel.listStatements(subject, (Property) null, (RDFNode) null);
|
|
||||||
while (iter.hasNext()) {
|
|
||||||
Statement stmt = iter.next();
|
|
||||||
Property property = stmt.getPredicate();
|
|
||||||
assertionsModel.enterCriticalSection(Lock.READ);
|
|
||||||
try {
|
|
||||||
if (!assertionsModel.contains(property, RDF.type) && !newModel.contains(stmt)) {
|
|
||||||
newModel.add(stmt);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
assertionsModel.leaveCriticalSection();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
contextModel.leaveCriticalSection();
|
|
||||||
}
|
|
||||||
|
|
||||||
return newModel;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,6 +143,10 @@ public class RequestModelsPrep implements Filter {
|
||||||
|
|
||||||
setRawModels(vreq, dataset);
|
setRawModels(vreq, dataset);
|
||||||
|
|
||||||
|
// We need access to the language-ignorant version of this model.
|
||||||
|
// Grab it before it gets wrapped in language awareness.
|
||||||
|
vreq.setLanguageNeutralUnionFullModel(ModelAccess.on(vreq).getOntModel(ModelID.UNION_FULL));
|
||||||
|
|
||||||
wrapModelsWithLanguageAwareness(vreq);
|
wrapModelsWithLanguageAwareness(vreq);
|
||||||
|
|
||||||
setWebappDaoFactories(vreq, rdfService);
|
setWebappDaoFactories(vreq, rdfService);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue