Skip to content

Commit

Permalink
Merge pull request #50 from TAMULib/B-03653-fedora-rdf
Browse files Browse the repository at this point in the history
Fedora RDF PCDM REST node traversal
  • Loading branch information
jcreel committed Jan 8, 2019
2 parents 3807736 + 1d2380d commit 14948cb
Show file tree
Hide file tree
Showing 52 changed files with 951 additions and 1,541 deletions.
76 changes: 43 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,47 +18,60 @@
- Triplestore
- tested with [Fuseki](https://jena.apache.org/documentation/fuseki2/)
- Fedora
- API-X
- Amherst PCDM service and extensions
## [IIIF](http://iiif.io/) Image Server
- Image resolution by identifier
- ```http://[iiif image server]/iiif/2/[base 64 encoded path]/full/full/0/default.jpg```
- currently identifier is base 64 encoded path prefixed with IR type
- path for Fedora is the resource path
- e.g. ```fedora:9b/e3/2a/4b/9be32a4b-b506-4913-9939-9c7921c00e21/38/63/cb/f5/3863cbf5-6139-4a2b-b679-e92376231732```
- path for DSpace is the webapp bitstream path
- e.g. ```dspace:xmlui/bitstream/123456789/158319/11/primary.tif```
- ```http://[iiif image server]/iiif/2/[UUID redis key]/full/full/0/default.jpg```
- UUID resource location resolution via resources interface
- [Presentation API v2](http://iiif.io/api/presentation/2.1/)
- [Image API v2](http://iiif.io/api/image/2.1/)

<details>
<summary>Example Cantaloupe resolver delegate</summary>
<summary>Example Cantaloupe custom delegate</summary>

<br/>

```
module HttpResolver
require 'base64'
class CustomDelegate
##
# @param identifier [String] Image identifier
# @return [String,nil] URL of the image corresponding to the given
# identifier, or nil if not found.
# Returns one of the following:
#
def self.get_url(_identifier)
irid = Base64.decode64(_identifier)
if irid.include? ":"
parts = irid.split(':')
ir = parts[0]
path = parts[1]
if ir == 'fedora'
uri = '<%=@fedora_url%>' + path
elsif ir == 'dspace'
uri = '<%=@dspace_url%>' + path
# 1. String URI
# 2. Hash with the following keys:
# * `uri` [String] (required)
# * `username` [String] For HTTP Basic authentication (optional).
# * `secret` [String] For HTTP Basic authentication (optional).
# * `headers` [Hash<String,String>] Hash of request headers (optional).
# 3. nil if not found.
#
# @param options [Hash] Empty hash.
# @return See above.
#
def httpsource_resource_info(options = {})
id = context['identifier']
puts id
if ( id =~ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/ )
uri = '<%= @iiif_service_url %>resources/' + id + '/redirect'
elsif
irid = Base64.decode64(id)
puts irid
if irid.include? ":"
parts = irid.split(':')
ir = parts[0]
path = parts[1]
if ir == 'fedora'
uri = '<%= @fedora_url %>' + path
elsif ir == 'dspace'
uri = '<%= @dspace_url %>' + path
else
uri = irid
end
else
uri = irid
uri = id
end
else
uri = '<%=@fedora_url%>' + irid
end
puts uri
return uri
end
end
```
Expand All @@ -70,8 +83,6 @@
- [RDF](https://wiki.duraspace.org/display/DSDOC6x/Linked+%28Open%29+Data)
## [Fedora](https://fedorarepository.org/)
- [Installation](https://wiki.duraspace.org/display/FEDORA4x/Quick+Start)
- [API-X](https://github.com/fcrepo4-labs/fcrepo-api-x/blob/master/src/site/markdown/apix-design-overview.md)
- [Amherst PCDM](https://github.com/birkland/repository-extension-services/tree/apix-demo/acrepo-exts-pcdm)
# Configuration
> Configuration for this service is done in [application.yaml](https://github.com/TAMULib/IRIIIFService/blob/master/src/main/resources/application.yaml) file located in src/main/resrouces directory.
Expand Down Expand Up @@ -103,15 +114,14 @@
| iiif.dspace.webapp | string | DSpace UI webapp. | xmlui |
| iiif.fedora.identifier.fedora-pcm | string | Fedora PCDM RDF identifier. | fedora |
| iiif.fedora.url | url | Fedora REST URL. | http://localhost:9000/fcrepo/rest |
| iiif.fedora.pcdm.ext.url | url | Fedora Amherst PCDM service URL. | http://localhost:9107/pcdm |



</details>

# REST API

> All REST endpoints have these optional URL query parameters.
> All manifest REST endpoints have these optional URL query parameters.
| **Query Parameter** | **Value** | **Functionality** |
| :----------- | :--------- | :---------------- |
Expand All @@ -128,19 +138,19 @@
| **URL** | ```/fedora/collection/**/*``` |
| **Method** | **GET** |
| **URL Parameters** | **Optional:**<br/>```update=[boolean]```<br/>```allow=[semicolon separated string of MIME types]```<br/>```disallow=[semicolon separated string of MIME types]``` |
| **Success Response** | **Code:** 200 OK<br/>**Content:**<br/>```{ ```<br/>&emsp;```"@context" : "http://iiif.io/api/presentation/2/context.json", ```<br/>&emsp;```"@id" : "http://localhost:8080/fedora/collection/cars_pcdm", ```<br/>&emsp;```"@type" : "sc:Collection", ```<br/>&emsp;```"collections" : [ ], ```<br/>&emsp;```"description" : "N/A", ```<br/>&emsp;```"label" : "Cars", ```<br/>&emsp;```"logo" : "https://localhost/assets/downloads/logos/Logo.png", ```<br/>&emsp;```"manifests" : [ { ```<br/>&emsp;&emsp;```"@id" : "http://localhost:8080/fedora/presentation/cars_pcdm_objects/vintage", ```<br/>&emsp;&emsp;```"@type" : "sc:Manifest", ```<br/>&emsp;&emsp;```"label" : "Vintage"```<br/>&emsp;```}, { ```<br/>&emsp;&emsp;```"@id" : "http://localhost:8080/fedora/presentation/cars_pcdm_objects/lamborghini", ```<br/>&emsp;&emsp;```"@type" : "sc:Manifest", ```<br/>&emsp;&emsp;```"label" : "Lamborghini"```<br/>&emsp;```}], ```<br/>&emsp;```"metadata" : [ ], ```<br/>&emsp;```"viewingHint" : "multi-part" ```<br/>&emsp;```}``` |
| **Success Response** | **Code:** 200 OK<br/>**Content:**<br/>```{}``` |
| **Error Response** | **Code:** 404 NOT_FOUND<br/>**Content:** ```Fedora PCDM RDF not found!``` |
| **Error Response** | **Code:** 503 SERVICE_UNAVAILABLE<br/>**Content:** ```[Exception message]``` |
| **Sample Request** | ```/fedora/collection/cars_pcdm``` |
| **Notes** | If the container is a root of a PCDM collection, the collection manifest will contain multiple manifests. If the container is an element of a collection within the PCDM model, the collection manifest will contain a single manifest. There is currently no way to generate a collection of collections. |
| **Notes** | If the container is a root of a PCDM collection, the collection manifest will contain multiple manifests. If the container is an element of a collection within the PCDM model, the collection manifest will contain a single manifest. |

| **Title** | Presentation |
| :-------- | :--------- |
| **Description** | Returns a generated or cached presentation manifest for the provided Fedora container. |
| **URL** | ```/fedora/presentation/**/*``` |
| **Method** | **GET** |
| **URL Parameters** | **Optional:**<br/>```update=[boolean]```<br/>```allow=[semicolon separated string of MIME types]```<br/>```disallow=[semicolon separated string of MIME types]``` |
| **Success Response** | **Code:** 200 OK<br/>**Content:**<br/>```{ ```<br/>&emsp;```"@context" : "http://iiif.io/api/presentation/2/context.json", ```<br/>&emsp;```"@id" : "http://localhost:9003/fedora/presentation/cars_pcdm_objects/vintage", ```<br/>&emsp;```"@type" : "sc:Manifest", ```<br/>&emsp;```"description" : "Vintage", ```<br/>&emsp;```"label" : "Vintage", ```<br/>&emsp;```"logo" : "https://localhost/assets/downloads/logos/Logo.png", ```<br/>&emsp;```"metadata" : [ { ```<br/>&emsp;&emsp;```"label" : "Title", ```<br/>&emsp;&emsp;```"value" : "Vintage" ```<br/>&emsp;```}, { ```<br/>&emsp;&emsp;```"label" : "Description", ```<br/>&emsp;&emsp;```"value" : "A vintage car" ```<br/>&emsp;```} ], ```<br/>&emsp;```"sequences" : [ { ```<br/>&emsp;&emsp;```"@id" : "http://localhost:9003/fedora/sequence/cars_pcdm_objects/vintage", ```<br/>&emsp;&emsp;```"@type" : "sc:Sequence", ```<br/>&emsp;&emsp;```"canvases" : [ { ```<br/>&emsp;&emsp;&emsp;```"@id" : "http://localhost:9003/fedora/canvas/cars_pcdm_objects/vintage/pages/page_0", ```<br/>&emsp;&emsp;&emsp;```"@type" : "sc:Canvas", ```<br/>&emsp;&emsp;&emsp;```"height" : 1080, ```<br/>&emsp;&emsp;&emsp;```"images" : [ { ```<br/>&emsp;&emsp;&emsp;&emsp;```"@id" : "http://localhost:8182/iiif/2/ZmVkb3JhOmNhcnNfcGNkbV9vYmplY3RzL3ZpbnRhZ2UvcGFnZXMvcGFnZV8wL2ZpbGVzL3ZpbnRhZ2UuanBn/info.json", ```<br/>&emsp;&emsp;&emsp;&emsp;```"@type" : "oa:Annotation", ```<br/>&emsp;&emsp;&emsp;&emsp;```"motivation" : "sc:painting", ```<br/>&emsp;&emsp;&emsp;&emsp;```"on" : "http://localhost:9003/fedora/canvas/cars_pcdm_objects/vintage/pages/page_0", ```<br/>&emsp;&emsp;&emsp;&emsp;```"resource" : { ```<br/>&emsp;&emsp;&emsp;&emsp;&emsp;```"@id" : "http://localhost:8182/iiif/2/ZmVkb3JhOmNhcnNfcGNkbV9vYmplY3RzL3ZpbnRhZ2UvcGFnZXMvcGFnZV8wL2ZpbGVzL3ZpbnRhZ2UuanBn/full/full/0/default.jpg", ```<br/>&emsp;&emsp;&emsp;&emsp;&emsp;```"@type" : "dctypes:Image", ```<br/>&emsp;&emsp;&emsp;&emsp;&emsp;```"format" : "image/jpeg", ```<br/>&emsp;&emsp;&emsp;&emsp;&emsp;```"height" : 1080, ```<br/>&emsp;&emsp;&emsp;&emsp;&emsp;```"service" : { ```<br/>&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;```"label" : "Fedora IIIF Image Resource Service", ```<br/>&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;```"profile" : "http://iiif.io/api/image/2/level0.json", ```<br/>&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;```"@context" : "http://iiif.io/api/image/2/context.json", ```<br/>&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;```"@id" : "http://localhost:8182/iiif/2/ZmVkb3JhOmNhcnNfcGNkbV9vYmplY3RzL3ZpbnRhZ2UvcGFnZXMvcGFnZV8wL2ZpbGVzL3ZpbnRhZ2UuanBn" ```<br/>&emsp;&emsp;&emsp;&emsp;&emsp;```}, ```<br/>&emsp;&emsp;&emsp;&emsp;&emsp;```"width" : 1920 ```<br/>&emsp;&emsp;&emsp;&emsp;```} ```<br/>&emsp;&emsp;&emsp;```} ], ```<br/>&emsp;&emsp;&emsp;```"label" : "Page 0", ```<br/>&emsp;&emsp;&emsp;```"metadata" : [ ], ```<br/>&emsp;&emsp;&emsp;```"width" : 1920 ```<br/>&emsp;&emsp;```} ], ```<br/>&emsp;&emsp;```"label" : "Vintage" ```<br/>&emsp;```} ], ```<br/>&emsp;```"thumbnail" : { ```<br/>&emsp;&emsp;```"@id" : "http://localhost:8182/iiif/2/ZmVkb3JhOmNhcnNfcGNkbV9vYmplY3RzL3ZpbnRhZ2UvcGFnZXMvcGFnZV8wL2ZpbGVzL3ZpbnRhZ2UuanBn/full/!100,100/0/default.jpg", ```<br/>&emsp;&emsp;```"services" : [ { ```<br/>&emsp;&emsp;&emsp;```"@context" : "http://iiif.io/api/image/2/context.json", ```<br/>&emsp;&emsp;&emsp;```"@id" : "http://localhost:8182/iiif/2/ZmVkb3JhOmNhcnNfcGNkbV9vYmplY3RzL3ZpbnRhZ2UvcGFnZXMvcGFnZV8wL2ZpbGVzL3ZpbnRhZ2UuanBn", ```<br/>&emsp;&emsp;&emsp;```"label" : "Fedora IIIF Image Resource Service", ```<br/>&emsp;&emsp;&emsp;```"profile" : "http://iiif.io/api/image/2/level0.json" ```<br/>&emsp;&emsp;```} ] ```<br/>&emsp;```} ```<br/>```}``` |
| **Success Response** | **Code:** 200 OK<br/>**Content:**<br/>```{}``` |
| **Error Response** | **Code:** 404 NOT_FOUND<br/>**Content:** ```Fedora PCDM RDF not found!``` |
| **Error Response** | **Code:** 503 SERVICE_UNAVAILABLE<br/>**Content:** ```[Exception message]``` |
| **Sample Request** | ```/fedora/presentation/cars_pcdm_objects/vintage``` |
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/edu/tamu/iiif/constants/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,9 @@ public class Constants {

public final static String PCDM_HAS_FILE_PREDICATE = PCDM_PREFIX + "hasFile";
public final static String PCDM_HAS_MEMBER_PREDICATE = PCDM_PREFIX + "hasMember";

public final static String PCDM_COLLECTION = PCDM_PREFIX + "Collection";
public final static String PCDM_FILE = PCDM_PREFIX + "File";

// IANA
public final static String IANA_PREFIX = "http://www.iana.org/assignments/relation/";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@
@ControllerAdvice
public class GlobalExceptionHandler {

private final static Logger LOG = LoggerFactory.getLogger(GlobalExceptionHandler.class);
private final static Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

@ExceptionHandler(IOException.class)
@ResponseStatus(value = SERVICE_UNAVAILABLE)
public @ResponseBody ResponseEntity<String> handleIOException(IOException exception) {
ResponseEntity<String> response;
if (StringUtils.containsIgnoreCase(ExceptionUtils.getRootCauseMessage(exception), "Broken pipe")) {
LOG.debug("Client has disconnected before completing request.", exception);
logger.debug("Client has disconnected before completing request.", exception);
response = null;
} else {
LOG.debug(exception.getMessage(), exception);
logger.debug(exception.getMessage(), exception);
response = new ResponseEntity<String>(exception.getMessage(), SERVICE_UNAVAILABLE);
}
return response;
Expand All @@ -43,21 +43,21 @@ public class GlobalExceptionHandler {
@ExceptionHandler(RiotException.class)
@ResponseStatus(value = SERVICE_UNAVAILABLE)
public @ResponseBody ResponseEntity<String> handleRiotException(RiotException exception) {
LOG.debug(exception.getMessage(), exception);
logger.debug(exception.getMessage(), exception);
return new ResponseEntity<String>(exception.getMessage(), SERVICE_UNAVAILABLE);
}

@ExceptionHandler(RedisConnectionFailureException.class)
@ResponseStatus(value = SERVICE_UNAVAILABLE)
public @ResponseBody ResponseEntity<String> handleRedisConnectionFailureException(RedisConnectionFailureException exception) {
LOG.debug(exception.getMessage(), exception);
logger.debug(exception.getMessage(), exception);
return new ResponseEntity<String>(exception.getMessage(), SERVICE_UNAVAILABLE);
}

@ExceptionHandler(NotFoundException.class)
@ResponseStatus(value = NOT_FOUND)
public @ResponseBody ResponseEntity<String> handleNotFoundException(NotFoundException exception) {
LOG.debug(exception.getMessage(), exception);
logger.debug(exception.getMessage(), exception);
return new ResponseEntity<String>(exception.getMessage(), NOT_FOUND);
}

Expand Down
17 changes: 0 additions & 17 deletions src/main/java/edu/tamu/iiif/model/rdf/RdfModel.java

This file was deleted.

43 changes: 33 additions & 10 deletions src/main/java/edu/tamu/iiif/model/rdf/RdfResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,21 @@
import org.apache.jena.rdf.model.ResIterator;
import org.apache.jena.rdf.model.Resource;
import org.apache.jena.rdf.model.Statement;
import org.apache.jena.rdf.model.StmtIterator;

public class RdfResource extends RdfModel {
public class RdfResource {

private final Model model;

private Resource resource;

public RdfResource(Model model) {
super(model);
this.model = model;
}

public RdfResource(Model model, String id) {
this(model);
this.resource = getModel().getResource(id);
}

public RdfResource(Model model, Resource resource) {
Expand All @@ -22,15 +30,15 @@ public RdfResource(Model model, Resource resource) {

public RdfResource(RdfResource rdfResource, String id) {
this(rdfResource.getModel());
this.resource = getModel().getResource(id);
this.resource = model.getResource(id);
}

public RdfResource(RdfResource rdfResource, Resource resource) {
this(rdfResource.getModel(), resource);
}

public Model getModel() {
return super.getModel();
return model;
}

public String getId() {
Expand All @@ -46,27 +54,42 @@ public void setResource(Resource resource) {
}

public Resource getResourceById(String id) {
return getModel().getResource(id);
return model.getResource(id);
}

public Property getProperty(String id) {
return getModel().getProperty(id);
return model.getProperty(id);
}

public Statement getStatementOfPropertyWithId(String id) {
return resource.getProperty(getProperty(id));
}

public StmtIterator getStatementsOfPropertyWithId(String id) {
return resource.listProperties(getProperty(id));
}

public NodeIterator getAllNodesOfPropertyWithId(String id) {
return getModel().listObjectsOfProperty(getProperty(id));
return model.listObjectsOfProperty(getProperty(id));
}

public NodeIterator getNodesOfPropertyWithId(String id) {
return getModel().listObjectsOfProperty(getResource(), getProperty(id));
return model.listObjectsOfProperty(resource, getProperty(id));
}

public ResIterator listResourcesWithPropertyWithId(String id) {
return getModel().listResourcesWithProperty(getProperty(id));
return model.listResourcesWithProperty(getProperty(id));
}

public boolean containsStatement(String propertyId, String value) {
StmtIterator stmtItr = getStatementsOfPropertyWithId(propertyId);
while (stmtItr.hasNext()) {
Statement stmnt = stmtItr.next();
if (stmnt.getResource().toString().equals(value)) {
return true;
}
}
return false;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@

public interface RedisManifestRepo extends CrudRepository<RedisManifest, String> {

public Optional<RedisManifest> findByPathAndTypeAndRepositoryAndAllowedAndDisallowed(String path, ManifestType type, String repository, String allowed, String disallowed);
public Optional<RedisManifest> findByPathAndTypeAndRepositoryAndAllowedAndDisallowed(String path, ManifestType type, String repository, String allowed, String disallowed);

}
Loading

0 comments on commit 14948cb

Please sign in to comment.