From ef6437faebeabce938f04fa3ca7c72ed4eb01a4f Mon Sep 17 00:00:00 2001 From: j2blake Date: Thu, 12 Sep 2013 16:44:43 -0400 Subject: [PATCH] VIVO-281 Make extended linked data a configurable option. --- .../vitro/webapp/controller/VitroRequest.java | 10 +- .../individual/ExtendedRdfAssembler.java | 235 +++++++++++ .../individual/IndividualController.java | 22 +- .../individual/IndividualRdfAssembler.java | 397 ++++++++++-------- .../webapp/filters/RequestModelsPrep.java | 4 + 5 files changed, 484 insertions(+), 184 deletions(-) create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/ExtendedRdfAssembler.java diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/VitroRequest.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/VitroRequest.java index 6d1e10cc1..0eeeed0a0 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/VitroRequest.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/VitroRequest.java @@ -205,7 +205,15 @@ public class VitroRequest extends HttpServletRequestWrapper { @Override public String[] getParameterValues(String name) { return _req.getParameterValues(name); - } + } + + public void setLanguageNeutralUnionFullModel(OntModel model) { + setAttribute("languageNeutralUnionFullModel", model); + } + public OntModel getLanguageNeutralUnionFullModel() { + return (OntModel) getAttribute("languageNeutralUnionFullModel"); + } + } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/ExtendedRdfAssembler.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/ExtendedRdfAssembler.java new file mode 100644 index 000000000..07678d2db --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/ExtendedRdfAssembler.java @@ -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 namespaces = new HashMap() {{ + 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 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 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; + } +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualController.java index 8c10623b2..cab2a0a23 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualController.java @@ -14,6 +14,7 @@ import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; 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.freemarker.FreemarkerHttpServlet; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ExceptionResponseValues; @@ -29,11 +30,15 @@ public class IndividualController extends FreemarkerHttpServlet { .getLog(IndividualController.class); 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. */ public static final Map ACCEPTED_CONTENT_TYPES = initializeContentTypes(); + private static Map initializeContentTypes() { HashMap map = new HashMap(); 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 * to them. */ - return new IndividualRdfAssembler(vreq, - requestInfo.getIndividual(), requestInfo.getRdfFormat()) - .assembleRdf(); + if (useExtendedLOD(vreq)) { + return new ExtendedRdfAssembler(vreq, + requestInfo.getIndividual(), + requestInfo.getRdfFormat()).assembleRdf(); + } else { + return new IndividualRdfAssembler(vreq, + requestInfo.getIndividual().getURI(), + requestInfo.getRdfFormat()).assembleRdf(); + } default: /* * Otherwise, prepare an HTML response for the requested @@ -113,6 +124,11 @@ public class IndividualController extends FreemarkerHttpServlet { HttpServletResponse.SC_NOT_FOUND); } + private boolean useExtendedLOD(HttpServletRequest req) { + ConfigurationProperties props = ConfigurationProperties.getBean(req); + return Boolean.valueOf(props.getProperty(PROPERTY_EXTENDED_LOD)); + } + @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualRdfAssembler.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualRdfAssembler.java index c104d091b..50d4d40c2 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualRdfAssembler.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/individual/IndividualRdfAssembler.java @@ -2,233 +2,270 @@ package edu.cornell.mannlib.vitro.webapp.controller.individual; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.HashSet; +import java.util.Set; import javax.servlet.ServletContext; +import org.apache.commons.lang.StringUtils; 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.ontology.OntModelSpec; 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.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.Individual; +import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatementImpl; 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.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.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.JenaOutputUtils; 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 { private static final Log log = LogFactory .getLog(IndividualRdfAssembler.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 namespaces = new HashMap() {{ - 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 static final String RICH_EXPORT_ROOT = "/WEB-INF/rich-export/"; + private static final String INCLUDE_ALL = "all"; + private final VitroRequest vreq; private final ServletContext ctx; - private final Individual individual; + private final String individualUri; 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) { this.vreq = vreq; this.ctx = vreq.getSession().getServletContext(); - this.individual = individual; - this.rdfFormat = rdfFormat; - } - /** - * @return - */ - public ResponseValues assembleRdf() { - OntModel ontModel = vreq.getJenaOntModel(); + this.individualUri = individualUri; + this.rdfFormat = rdfFormat; String[] includes = vreq.getParameterValues("include"); - Model newModel = getRDF(individual, ontModel, ModelFactory.createDefaultModel(), 0, includes); - JenaOutputUtils.setNameSpacePrefixes(newModel, vreq.getWebappDaoFactory()); + this.richExportIncludes = (includes == null) ? new String[0] : includes; + + 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); } - private Model getRDF(Individual entity, OntModel contextModel, Model newModel, int recurseDepth, String[] includes) { - - Resource subj = newModel.getResource(entity.getURI()); - - List 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 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); - } + private boolean isLanguageAware() { + return StringUtils.isNotEmpty(vreq.getHeader("Accept-Language")); + } + + private OntModel getRdf() { + OntModel o = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM); + o.add(getStatementsAboutEntity()); + o.add(getLabelsAndTypesOfRelatedObjects()); + filterByPolicy(o); + return o; + } + + /** + * Get all statements that have the entity as either the subject or the + * object. + */ + private Model getStatementsAboutEntity() { + 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 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 { + log.warn("blank node: + stmt"); + stmts.remove(); + } } - - 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; - } + /** + * Collect the URIs of all objects that are accessible through permitted + * triples. + */ + private Set determineAccessibleUris(OntModel o) { + Resource i = o.getResource(individualUri); + Set uris = new HashSet<>(); + uris.add(individualUri); - /* 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); + 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 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 { - newModel.add(ontModel.listStatements(ontModel.getResource(entity.getURI()), RDFS.label, (RDFNode)null)); - } finally { - ontModel.leaveCriticalSection(); + return RDFServiceUtils.parseModel(rdfService.sparqlConstructQuery( + query, RDFService.ModelSerializationFormat.N3), + RDFService.ModelSerializationFormat.N3); + } catch (RDFServiceException e) { + throw new RuntimeException(e); } - - 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; - } + } + + private Model getRichExportRdf() { + Model richExportModel = ModelFactory.createDefaultModel(); + + for (String include : richExportIncludes) { + String rootDir = RICH_EXPORT_ROOT; + if (!INCLUDE_ALL.equals(include)) { + rootDir += include + "/"; + } + + long start = System.currentTimeMillis(); + richExportModel.add(ExtendedLinkedDataUtils.createModelFromQueries( + ctx, rootDir, contentModel, individualUri)); + long elapsedTimeMillis = System.currentTimeMillis() - start; + log.debug("Time to create rich export model: msecs = " + + elapsedTimeMillis); + } + + return richExportModel; + } + } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/filters/RequestModelsPrep.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/filters/RequestModelsPrep.java index 0c8e41fa0..f83432551 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/filters/RequestModelsPrep.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/filters/RequestModelsPrep.java @@ -143,6 +143,10 @@ public class RequestModelsPrep implements Filter { 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); setWebappDaoFactories(vreq, rdfService);