Commit 7d00d653 authored by bseeger's avatar bseeger
Browse files

New entailment service and endpoint

parent 82b39507
Repository PCDM object extension
================================
This extension operates on LDP resources, performing
RDFS entailment for the specified ontologies and returns
the entailed triples for the resource.
For example:
curl http://localhost:9109/entailment?context=http://localhost:8080/fcrepo/rest/a/b/c
Building
--------
To build this project use
gradle install
Deploying in OSGi
-----------------
This projects can be deployed in an OSGi container. For example using
[Apache Karaf](http://karaf.apache.org) version 4.x or better, you can run the following
command from its shell:
feature:repo-add mvn:edu.amherst.acdc/acrepo-karaf/LATEST/xml/features
feature:install acrepo-exts-entailment
feature:install acrepo-services-entailment
Configuration
-------------
The application can be configured by creating the following configuration
file `$KARAF_HOME/etc/edu.amherst.acdc.exts.entailment.cfg`. The following values
are available for configuration:
The base url of the fedora repository
fcrepo.baseUrl=localhost:8080/fcrepo/rest
The port on which the service is made availalbe
rest.port=9109
The hostname for the service
rest.host=localhost
The REST prefix
rest.prefix=/entailment
By editing this file, any currently running routes will be immediately redeployed
with the new values.
For more help see the [Apache Camel](http://camel.apache.org) documentation
apply plugin: 'osgi'
description = 'Entailed Triple Builder'
dependencies {
compile group: 'org.apache.camel', name: 'camel-core', version: camelVersion
compile group: 'org.apache.camel', name: 'camel-blueprint', version: camelVersion
compile group: 'org.apache.camel', name: 'camel-jetty9', version: camelVersion
compile(group: 'org.fcrepo.camel', name: 'fcrepo-camel', version: fcrepoCamelVersion) {
exclude(module: 'slf4j-log4j12')
}
compile group: 'org.apache.jena', name: 'jena-osgi', version: jenaVersion
compile project(':acrepo-services-entailment')
testCompile group: 'junit', name: 'junit', version: junitVersion
testCompile group: 'org.apache.camel', name: 'camel-test-blueprint', version: camelVersion
testCompile group: 'ch.qos.logback', name: 'logback-classic', version: logbackVersion
}
jar {
manifest {
description project.description
docURL project.docURL
vendor project.vendor
license project.license
instruction 'Import-Package', "org.apache.camel,org.fcrepo.camel,edu.amherst.acdc.services.entailment,${defaultOsgiImports}"
instruction 'Export-Package', "edu.amherst.acdc.exts.entailment;version=${projectOsgiVersion}"
}
}
artifacts {
archives (file('build/cfg/main/edu.amherst.acdc.exts.entailment.cfg')) {
classifier 'configuration'
type 'cfg'
}
}
# HTTP Port
rest.host=localhost
rest.port=9109
rest.prefix=/entailment
# Concurrency level (this MUST be > 1)
entailment.concurrency=10
# Repository Base URL (it MUST start with http:// or https://)
fcrepo.baseUrl=http://localhost:8080/fcrepo/rest
fcrepo.authUsername=
fcrepo.authPassword=
# API-X configuration
extension.load=false
extension.load.uri=http://apix/services//apix:load
extension.load.maximumRedeliveries=60
/*
* Copyright 2016 Amherst College
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package edu.amherst.acdc.exts.entailment;
//import static java.util.Optional.ofNullable;
import static org.apache.camel.Exchange.CONTENT_TYPE;
import static org.apache.camel.Exchange.HTTP_METHOD;
//import static org.apache.camel.Exchange.HTTP_RESPONSE_CODE;
import static org.fcrepo.camel.FcrepoHeaders.FCREPO_BASE_URL;
import static org.fcrepo.camel.FcrepoHeaders.FCREPO_URI;
//import java.util.Optional;
import org.apache.camel.builder.RouteBuilder;
//import org.apache.jena.riot.Lang;
//import org.apache.jena.riot.RDFLanguages;
/**
* A content router for handling ORE extension requests
*
* @author Bethany Seeger
*/
public class EntailmentRouter extends RouteBuilder {
private final static String DEFAULT_CONTENT_TYPE = "text/turtle";
private final static String HTTP_QUERY_CONTEXT = "context";
/**
* Configure the message route workflow.
*/
public void configure() throws Exception {
from("jetty:http://{{rest.host}}:{{rest.port}}{{rest.prefix}}?" +
"sendServerVersion=false&httpMethodRestrict=GET,OPTIONS")
.routeId("EntailmentRouter")
.removeHeader("User-Agent")
.process(e -> e.getIn().setHeader(FCREPO_URI,
e.getIn().getHeader(HTTP_QUERY_CONTEXT,
e.getIn().getHeader("Apix-Ldp-Resource"))))
.setHeader(FCREPO_BASE_URL).simple("{{fcrepo.baseUrl}}")
.choice()
.when(header(HTTP_METHOD).isEqualTo("OPTIONS"))
.setHeader(CONTENT_TYPE).constant("text/turtle")
.setHeader("Allow").constant("GET,OPTIONS")
.to("language:simple:resource:classpath:options.ttl")
.when(header(HTTP_QUERY_CONTEXT).startsWith(header(FCREPO_BASE_URL)))
.to("direct:get");
// first get resource
// then pass it to getTriples... which is defined in the blueprint script.
//
from("direct:get").routeId("EntailementGetTriples")
.log("Building RDFS Entailment Info ${body}")
.to("fcrepo:{{fcrepo.baseUrl}}?throwExceptionOnFailure=false")
.to("direct:getTriples");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
xsi:schemaLocation="
http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0 http://aries.apache.org/schemas/blueprint-cm/blueprint-cm-1.1.0.xsd
http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd">
<!-- OSGI blueprint property placeholder -->
<cm:property-placeholder persistent-id="edu.amherst.acdc.exts.entailment" update-strategy="reload">
<cm:default-properties>
<cm:property name="rest.port" value="9109"/>
<cm:property name="rest.prefix" value="/entailment"/>
<cm:property name="rest.host" value="localhost"/>
<cm:property name="entailment.concurrency" value="10"/>
<cm:property name="fcrepo.baseUrl" value="http://localhost:8080/fcrepo/rest"/>
<cm:property name="fcrepo.authUsername" value=""/>
<cm:property name="fcrepo.authPassword" value=""/>
<cm:property name="extension.load" value="false" />
<cm:property name="extension.load.uri" value="http://apix/services//apix:load" />
<cm:property name="extension.load.maximumRedeliveries" value="60" />
</cm:default-properties>
</cm:property-placeholder>
<reference id="entailmentService" interface="edu.amherst.acdc.services.entailment.EntailmentService" filter="(osgi.jndi.service.name=acrepo/Entailment)" />
<bean id="fcrepo" class="org.fcrepo.camel.FcrepoComponent">
<property name="authUsername" value="${fcrepo.authUsername}"/>
<property name="authPassword" value="${fcrepo.authPassword}"/>
<property name="baseUrl" value="${fcrepo.baseUrl}"/>
</bean>
<camelContext id="AcrepoExtEntailment" xmlns="http://camel.apache.org/schema/blueprint">
<package>edu.amherst.acdc.exts.entailment</package>
<route id="EntailmentTriples">
<from uri="direct:getTriples"/>
<setBody>
<method ref="entailmentService" method="getEntailedTriples(${header[CamelFcrepoUri]}, ${body}, ${header[Content-Type]})"/>
</setBody>
</route>
<!-- Self-register the loader service as an extension -->
<route id="load-extension">
<from uri="timer:register?repeatCount=1" />
<onException>
<exception>java.lang.Exception</exception>
<redeliveryPolicy maximumRedeliveries="{{extension.load.maximumRedeliveries}}"
logRetryAttempted="true" retryAttemptedLogLevel="INFO" />
</onException>
<setHeader headerName="Content-Type">
<constant>text/plain</constant>
</setHeader>
<setHeader headerName="CamelHttpMethod">
<constant>POST</constant>
</setHeader>
<setBody>
<simple>http://{{rest.host}}:{{rest.port}}{{rest.prefix}}</simple>
</setBody>
<choice>
<when>
<simple>{{extension.load}}</simple>
<to
uri="jetty:{{extension.load.uri}}?okStatusCodeRange=200-399" />
</when>
</choice>
</route>
</camelContext>
</blueprint>
@prefix owl:<http://www.w3.org/2002/07/owl#> .
@prefix rdfs:<http://www.w3.org/2000/01/rdf-schema#> .
@prefix registry:<http://acdc.amherst.edu/ns/registry#> .
@prefix apix:<http://fedora.info/definitions/v4/api-extension#> .
@prefix ldp: <http://www.w3.org/ns/ldp#> .
<> a apix:Extension;
rdfs:label "RDFS Entailment extension";
rdfs:comment "An extension that returns RDFS entailed triples for a resource";
apix:exposesService registry:EntailmentService
apix:exposesServiceAt "svc:entailment";
apix:bindsTo ldp:RDFSource .
@prefix premis: <http://www.loc.gov/premis/rdf/v1#> .
@prefix pcdm: <http://pcdm.org/models#> .
@prefix fedora: <http://fedora.info/definitions/v4/repository#> .
@prefix ebucore: <http://www.ebu.ch/metadata/ontologies/ebucore/ebucore#> .
@prefix ldp: <http://www.w3.org/ns/ldp#> .
@prefix iana: <http://www.iana.org/assignments/relation/> .
<http://localhost:8080/fcrepo/rest/pcdm/members/page1/files/file1> a fedora:Binary , fedora:Resource , ldp:NonRDFSource , pcdm:File ;
ebucore:filename "IMG_0107.jpg" ;
ebucore:hasMimeType "image/jpeg" ;
premis:hasSize "1565421"^^<http://www.w3.org/2001/XMLSchema#long> ;
premis:hasMessageDigest <urn:sha1:c9790c378f3589bfcfe092f834d212685f44fff9> ;
iana:describedby <http://localhost:8080/fcrepo/rest/pcdm/members/page1/files/file1/fcr:metadata> ;
fedora:lastModifiedBy "bypassAdmin" ;
fedora:createdBy "bypassAdmin" ;
fedora:created "2016-06-28T00:21:22.621Z"^^<http://www.w3.org/2001/XMLSchema#dateTime> ;
fedora:lastModified "2016-06-28T00:21:22.621Z"^^<http://www.w3.org/2001/XMLSchema#dateTime> ;
fedora:writable "true"^^<http://www.w3.org/2001/XMLSchema#boolean> ;
fedora:hasParent <http://localhost:8080/fcrepo/rest/pcdm/members/page1/files> ;
fedora:numberOfChildren "0"^^<http://www.w3.org/2001/XMLSchema#long> ;
fedora:hasFixityService <http://localhost:8080/fcrepo/rest/pcdm/members/page1/files/file1/fcr:fixity> .
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%p %d{HH:mm:ss.SSS} \(%c{0}\) %m%n</pattern>
</encoder>
</appender>
<logger name="edu.amherst.acdc.exts.pcdm" additivity="false" level="INFO">
<appender-ref ref="STDOUT"/>
</logger>
<logger name="org.apache.camel" additivity="false" level="INFO">
<appender-ref ref="STDOUT"/>
</logger>
<logger name="org.fcrepo.camel" additivity="false" level="INFO">
<appender-ref ref="STDOUT"/>
</logger>
<root additivity="false" level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
@prefix pcdm: <http://pcdm.org/models#> .
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
@prefix fedora: <http://fedora.info/definitions/v4/repository#> .
@prefix ldp: <http://www.w3.org/ns/ldp#> .
<http://localhost:8080/fcrepo/rest/pcdm> a fedora:Container , fedora:Resource , ldp:RDFSource , ldp:Container, pcdm:Object ;
skos:prefLabel "Top level pcdm:Object"@en ;
fedora:lastModifiedBy "bypassAdmin" ;
fedora:createdBy "bypassAdmin" ;
fedora:created "2016-06-28T00:14:06.677Z"^^<http://www.w3.org/2001/XMLSchema#dateTime> ;
fedora:lastModified "2016-06-28T00:16:25.55Z"^^<http://www.w3.org/2001/XMLSchema#dateTime> ;
fedora:writable "true"^^<http://www.w3.org/2001/XMLSchema#boolean> ;
fedora:hasParent <http://localhost:8080/fcrepo/rest/> ;
fedora:numberOfChildren "1"^^<http://www.w3.org/2001/XMLSchema#long> ;
ldp:contains <http://localhost:8080/fcrepo/rest/pcdm/members> ;
pcdm:hasMember <http://localhost:8080/fcrepo/rest/pcdm/members/page1> .
@prefix pcdm: <http://pcdm.org/models#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix fedora: <http://fedora.info/definitions/v4/repository#> .
@prefix ldp: <http://www.w3.org/ns/ldp#> .
<http://localhost:8080/fcrepo/rest/pcdm/members/page1> a fedora:Container , fedora:Resource , ldp:RDFSource , ldp:Container , pcdm:Object ;
rdfs:label "Page 1" ;
fedora:lastModifiedBy "bypassAdmin" ;
fedora:createdBy "bypassAdmin" ;
fedora:created "2016-06-28T00:16:25.552Z"^^<http://www.w3.org/2001/XMLSchema#dateTime> ;
fedora:lastModified "2016-06-28T00:56:43.618Z"^^<http://www.w3.org/2001/XMLSchema#dateTime> ;
fedora:writable "true"^^<http://www.w3.org/2001/XMLSchema#boolean> ;
fedora:hasParent <http://localhost:8080/fcrepo/rest/pcdm/members> ;
fedora:numberOfChildren "1"^^<http://www.w3.org/2001/XMLSchema#long> ;
ldp:contains <http://localhost:8080/fcrepo/rest/pcdm/members/page1/files> ;
pcdm:hasFile <http://localhost:8080/fcrepo/rest/pcdm/members/page1/files/file1> .
@prefix pcdm: <http://pcdm.org/models#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix fedora: <http://fedora.info/definitions/v4/repository#> .
@prefix ldp: <http://www.w3.org/ns/ldp#> .
<http://localhost:8080/fcrepo/rest/pcdm/members/page2> a fedora:Container , fedora:Resource , ldp:RDFSource , ldp:Container , pcdm:Object ;
rdfs:label "Page 2" ;
fedora:lastModifiedBy "bypassAdmin" ;
fedora:createdBy "bypassAdmin" ;
fedora:created "2016-06-28T00:16:25.552Z"^^<http://www.w3.org/2001/XMLSchema#dateTime> ;
fedora:lastModified "2016-06-28T00:56:43.618Z"^^<http://www.w3.org/2001/XMLSchema#dateTime> ;
fedora:writable "true"^^<http://www.w3.org/2001/XMLSchema#boolean> ;
fedora:hasParent <http://localhost:8080/fcrepo/rest/pcdm/members> ;
fedora:numberOfChildren "1"^^<http://www.w3.org/2001/XMLSchema#long> ;
ldp:contains <http://localhost:8080/fcrepo/rest/pcdm/members/page2/files> ;
pcdm:hasFile <http://localhost:8080/fcrepo/rest/pcdm/members/page2/files/file1> .
......@@ -46,6 +46,21 @@
<configfile finalname="/etc/org.ops4j.datasource-idiomatic.cfg">mvn:edu.amherst.acdc/acrepo-connector-idiomatic-pgsql/${project.version}/cfg/configuration</configfile>
</feature>
<feature name="acrepo-exts-entailment" version="${project.version}">
<details>Installs the RDFS entailment extension</details>
<feature version="${camelVersionRange}">camel</feature>
<feature version="${camelVersionRange}">camel-blueprint</feature>
<feature version="${camelVersionRange}">camel-jetty9</feature>
<feature version="${fcrepoCamelVersionRange}">fcrepo-camel</feature>
<feature version="${project.version}">acrepo-services-entailment</feature>
<feature version="${project.version}">acrepo-libs-jena</feature>
<bundle>mvn:edu.amherst.acdc/acrepo-exts-entailment/${project.version}</bundle>
<configfile finalname="/etc/edu.amherst.acdc.exts.entailment.cfg">mvn:edu.amherst.acdc/acrepo-exts-entailment/${project.version}/cfg/configuration</configfile>
</feature>
<feature name="acrepo-exts-image" version="${project.version}">
<details>Installs the image service</details>
......@@ -106,6 +121,16 @@
<configfile finalname="/etc/edu.amherst.acdc.exts.pcdm.cfg">mvn:edu.amherst.acdc/acrepo-exts-pcdm/${project.version}/cfg/configuration</configfile>
</feature>
<feature name="acrepo-services-entailment" version="${project.version}">
<details>Installs the RDFS entailment service</details>
<feature version="${project.version}">acrepo-libs-jena</feature>
<bundle>mvn:edu.amherst.acdc/acrepo-services-entailment/${project.version}</bundle>
<configfile finalname="/etc/edu.amherst.acdc.services.entailment.cfg">mvn:edu.amherst.acdc/acrepo-services-entailment/${project.version}/cfg/configuration</configfile>
</feature>
<feature name="acrepo-services-mint" version="${project.version}">
<details>Installs the id minter</details>
......
Repository Inference Service
============================
This OSGi service can do object typing of RDF graphs using RDFS entailment.
Building
--------
To build this project use
gradle install
Deploying in OSGi
-----------------
Each of these projects can be deployed in an OSGi container. For example using
[Apache Karaf](http://karaf.apache.org) version 4.x or better, you can run the following
command from its shell:
feature:repo-add mvn:edu.amherst.acdc/acrepo-karaf/LATEST/xml/features
feature:install acrepo-services-entailment
apply plugin: 'osgi'
description = 'RDF simple entailment service bundle'
dependencies {
compile group: 'org.apache.jena', name: 'jena-osgi', version: jenaVersion
compile group: 'org.slf4j', name: 'slf4j-api', version: slf4jVersion
testCompile group: 'xerces', name: 'xercesImpl', version: xercesVersion
testCompile group: 'junit', name: 'junit', version: junitVersion
testCompile group: 'ch.qos.logback', name: 'logback-classic', version: logbackVersion
}
jar {
manifest {
description project.description
docURL project.docURL
vendor project.vendor
license project.license
instruction 'Import-Package', "org.apache.camel,${defaultOsgiImports}"
instruction 'Export-Package', "edu.amherst.acdc.services.entailment;version=${projectOsgiVersion}"
}
}
artifacts {
archives (file('build/cfg/main/edu.amherst.acdc.services.entailment.cfg')) {
classifier 'configuration'
type 'cfg'
}
}
# Comma separated list of ontologies that should pulled in for entailment to be done on them
rdfs.ontologies= "http://purl.org/dc/terms/,http://purl.org/dc/elements/1.1/,https://www.w3.org/2009/08/skos-reference/skos.rdf,https://www.w3.org/2000/01/rdf-schema.rdf"
output.lang="text/turtle"
/*
* Copyright 2016 Amherst College
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package edu.amherst.acdc.services.entailment;
import java.io.InputStream;
/**
* @author bseeger
* @since 3/23/2017
*/
public interface EntailmentService {
/**
* Take in a RDF resource and perform RDFS entailment on it, returning only the new triples
* created by entailment.
* @param subject Subject of the triples
* @param input Input stream containing the information for the resource
* @param contentType The content type of the stream
*/
InputStream getEntailedTriples(final String subject, final InputStream input, final String contentType);
}
/*
* Copyright 2016 Amherst College
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package edu.amherst.acdc.services.entailment;
import static java.util.Optional.ofNullable;
import static org.apache.http.impl.client.HttpClientBuilder.create;
import static org.apache.jena.rdf.model.ModelFactory.createDefaultModel;
import static org.apache.jena.rdf.model.ModelFactory.createRDFSModel;
import static org.apache.jena.riot.RDFDataMgr.read;
import static org.apache.jena.riot.RDFDataMgr.write;
import static org.apache.jena.riot.RDFLanguages.contentTypeToLang;
import static org.apache.jena.riot.Lang.TURTLE;
import static org.apache.jena.riot.web.HttpOp.setDefaultHttpClient;
import static org.slf4j.LoggerFactory.getLogger;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.io.StringWriter;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.apache.jena.rdf.model.InfModel;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.Resource;
import org.apache.jena.rdf.model.Statement;
import org.apache.jena.riot.Lang;
import org.apache.jena.riot.RDFLanguages;
import org.slf4j.Logger;
/**
* @author bseeger
* @since 3/23/2017
*/
public class EntailmentServiceImpl implements EntailmentService {
private static final Logger LOGGER = getLogger(EntailmentServiceImpl.class);
private List<String> rdfsOntologies;
private static final Lang DEFAULT_LANG = TURTLE;
private InfModel rdfsModel = createRDFSModel(createDefaultModel());
static {
setDefaultHttpClient(create().setRedirectStrategy(new LaxRedirectStrategy()).build());
}
/**
* Implementation of an Entailment Service to return the
* RDFS entailed triples for a resource.
*/
public EntailmentServiceImpl(final String ontologies) {
LOGGER.info("EntailmentServiceImpl: ontologies: {}", ontologies);
rdfsOntologies = new ArrayList<>(Arrays.asList(ontologies.split(",")));
rdfsOntologies.forEach(