Introduction
This thought experiment is intended to chip away at the question "What would happen if the Content CMA supported read/write service methods on Fedora objects"? Specifically, let us take a look at the Atom Publishing Protocol(APP) as a concrete example. Can we use a read/write CMA to do creative things with the APP? Do we like the end result? If so, do we have any useful suggestions for the design of a fully read/write CMA? Please note that this is just a thought experiment, and not a proposal.
The scenario
Suppose that we wish to build a repository that contained two different kinds of atom feeds. Let's say that we want first type of feed to allow us to see all datastreams (and versions) in an object, in much the same fashion as the current atom serialization. For the second, we want to represent a higher level concept: image collections. Clearly, there will be situations where some objects contain BOTH feeds. We want the feeds to be orthogonal so that this is not a problem. Let's go into each situation in a bit more detail:
Feed 1: Datastreams in Every Object
For feed 1, we want to represent the structure of a Fedora object, in terms of datastreams, as an Atom feed. In addition, we would like to use the atom publishing protocol to add, remove, or update datastreams to an object. Specifically, let's suppose that we would like the following characteristics from our datastreams feed:
- The atom feed for an object shall have a URI, and this shall be the collection feed URI as specified in the atom publishing protocol.
- All datastreams and datastream versions shall be represented in the atom feed for an object
- In accordance with the APP, new datastreams may be created, deleted, or updated in this feed
- The client should be able to specify the desired datastream ID when creating a new datastream
- Once a specific datastream version exists (e.g bound to the point in time it was created), it can be deleted, but not edited.
Feed 2: Atomistic Image Collection
For feed 2, we want to allow for the repository to contain multiple image collections, extending the functionality of the demo image collection. Each image collection is represented by its own object in the repository, as are individual image items. Collection membership is determined by the presence of an 'isMemberOf' relationship from an image to a collection. Specifically, let's suppose that we wanted the following characteristics:
- We want to extend the functionality provided by the demo image collection by allowing these collections to be modified using the atom publishing protocol.
- Existing demo SDefs and SDeps from the image collection remain unchanged
- Images added/modified via AtomPub are considered FULL_SIZE
- There exists a ServiceDocument that enumerates all the image collections in the repository and their collection feed URIs (as provided by the APP)
- Each image collection has a collection feed URI
- In accordance with the APP images may be added, removed, or replaced through through this feed
- Adding an object to the feed implies adding a new object to the repository - but the client shall have no awareness of this process
- Deleting an object from the feed implies purging the image object from the repository - but the client shall have no awareness of this process
A Possible Direction
General concepts
It appears that there may be three general 'types' of object that are relevant as far as our APP needs are concerned: Objects that represent collection feeds, objects that represent items in a collection, and objects that represent the set of all collections. Note: In Feed 1, since the model is not atomistic, we really only have objects that represent entire collections. Let's see if this matters later on. For now, though, let's represent these 'types' as SDefs, with the following structure:
Code Block |
---|
SDef pid: atom:Collection method: service # APP Service document for one collection (read only) method: feed # Atom Collection feed (read/write as per APP) |
Code Block |
---|
SDef pid: atom:Item method: media # Media Resource for this item # may or may not have content, read/write method: entry # Atom entry for this item (read/write) |
Code Block |
---|
SDef pid: atom:Domain method: feeds # APP Service document for all collections (read only) |
Additionally, let's assume that Fedora's REST API exposed URIs for the methods expressed in these SDefs as follows:
http://(fedora.server)/services/(object-pid)/(sdef-pid)/(method)[/additional]
...where [/additional] could be parameters, or additional path elements in the URI.
For example, one of our service methods above would look like http://example.org/fedora/services/demo:SmileyStuff/atom:Collection/entries
The service methods (exposed as URIs in the REST API) behave as follows. The intent is to provide a representation of many concepts detailed in the APP, so the descriptions here are abbreviated.
atom:Collection/service
GET - retrieves a Service Document that describes the single feed in the collection
atom:Collection/feed
GET - retrieves an atom feed of the items in the collection
POST - create a entry or media item
atom:Item/media
This is the URI of the media resource. According to the APP, there will be a corresponding media link entry in the feed. The edit-media link in the entry will point to this URI.
GET - Get the media for an item (image, text, etc). If the item does not have media, it makes sense for this to return a 404
PUT - Replace the media for this item
atom:Item/entry
This is the URI of an individual atom entry - the feed is composed of these.
GET - Get the atom entry
PUT - Modify the information contained in an atom entry
atom:Domain/feeds
GET - Retrieves a Service Document describing all the collection feeds for a particular domain (defined later). Essentially, this is a concatination of the atom:Collection/service
documents of all collections in the domain.
Domains
The APP provides Service Documents as a means of discovery for feeds. A Service Document URI enumerates all the feeds within (and possibly groups them into "workspaces"). In a repository environment as diverse as Fedora, it does not seem to make sense to have a single Service Document enumerating ALL the feeds. In our example, we have two mostly unrelated scenarios: atom feeds for every object, and atom feeds for image collection objects. The composition and semantics of these feeds differ. Thus, let us use the word "domain" to represent the different information spaces represented by these feeds.
If we want different atom-feed domains to remain separate, then we need to expand upon the general SDefs introduced above. Some possibly valid strategies include
- Define new SDefs for each domain, conforming to the signatures of atom:Collection and atom:Item, but having distinct identities (e.g. http://example.org/fedora/services/demo:X/atom:ImageCollection/feed)
- Add parameters to each method/URI indicating domain (e.g. http://example.org/fedora/services/demo:X/atom:Collection/feed?domain=ImageCollection)
- Add additional path elements to the URI indicating domain(e.g. http://example.org/fedora/services/demo:X/atom:Collection/feed/ImageCollection/)
Solving Our Scenerio
In continuing this thought experiment, let's proceed by defining domain-specific SDefs representing our two feeds, and see where that gets us.
Code Block |
---|
SDef pid: thought-exp:ImageCollection method: service # APP Service document for one image collection method: feed # atom Image Collection feed --- has model: atom:DomainModel # This model has Sdef atom:Domain, so this object # has method atom:Domain/feeds, which list all # image collection feeds |
Code Block |
---|
SDef pid: thought-exp:ImageItem method: media # Image Resource for this item method: entry # Atom entry for this item |
Code Block |
---|
SDef pid: thought-exp:Datastreams method: service # APP Service document for one object's datastream collection method: feed # atom feed listing all datastreams + versions --- has model: atom:DomainModel # This model has Sdef atom:Domain, so this object # has method atom:Domain/feeds, which list all # object feeds (may be dangerous!!!) |
Code Block |
---|
SDef pid: thought-exp:Datastream method: media # Datastream content Resource for this item method: entry # Atom entry for this datastream |
Code Block |
---|
SDef pid: atom:Domain method: feeds # APP Service document for all collections |
These objects would then define the following URIs and interaction model:
http://example.org/fedora/services/thought-exp:ImageCollection/atom:Domain/feeds
GET - Service Document containing all image collection feeds.
Code Block | ||||
---|---|---|---|---|
| ||||
<service xmlns="http://www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom"> <workspace> <atom:title>Image Collections</atom:title> <collection href="http://example.org/fedora/services/demo:SmileyStuff/thought-exp:ImageCollection/feed"> <atom:title>Smiley Stuff Images</atom:title> <accept>image/jpeg</accept> </collection> <collection href="http://example.org/fedora/services/demo:FrownyStuff/thought-exp:ImageCollection/feed" > <atom:title>Frowny stuff</atom:title> <accept>image/png</accept> <accept>image/jpeg</accept> <accept>image/gif</accept> </collection> </workspace> </service> |
http://example.org/fedora/services/thought-exp:Datastream/atom:Domain/feeds
GET - Service Document containing all object datastream feeds. This could possibly be a very large list, resulting in a ginormous Service Document Unlike atom feeds, I don't believe one can implement pagination for this document.
http://example.org/fedora/services/(PID)/thought-exp:ImageCollection/service
GET - Service document for one image collection. Service documents may enumerate the acceptable content types, so an image collection may list off supported image formats (such as image/png, image/jpeg, etc
Code Block | ||||
---|---|---|---|---|
| ||||
<service xmlns="http://www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom"> <workspace> <atom:title>Image Collections</atom:title> <collection href="http://example.org/fedora/services/demo:SmileyStuff/thought-exp:ImageCollection/feed"> <atom:title>Smiley Stuff Images</atom:title> <accept>image/jpeg</accept> </collection> </workspace> </service> |
http://example.org/fedora/services/(PID)/thought-exp:ImageCollection/feed
GET - Get an atom feed of all images in a collection represented by the PID object.
Code Block | ||||
---|---|---|---|---|
| ||||
<feed xmlns="http://www.w3.org/2005/Atom"> <title>Smiley Stuff Images</title> <link href="http://example.org/fedora/services/demo:SmileyStuff/thought-exp:ImageCollection/feed" rel="self"/> <updated>1980-03-20T09:15:02Z</updated> <author> <name>Chis Wilper</name> </author> <id>info:fedora/demo:SmileyStuff/thought-exp:ImageCollection</id> <entry> <title>Smiley Toilet Brush</title> <id>info:fedora/demo:SmileyToiletBrush/thought-exp:ImageItem</id> <updated>1979-10-07T17:17:08Z</updated> <content type="image/png" src="http://example.org/fedora/services/demo:SmileyToiletBrush/thought-exp:ImageItem/media"/> <link rel="edit-media" href="http://example.org/fedora/services/demo:SmileyToiletBrush/thought-exp:ImageItem/media" /> <link rel="edit" href="http://example.org/fedora/services/demo:SmileyToiletBrush/thought-exp:ImageItem/entry" /> </entry> </feed> |
POST - Accept only media items (i.e. images in supported formats). A POSTed image would cause the following to happen:
- Create Fedora object for new item created
- If the 'Slug' header is defined, possibly assign object a NEW_PID derived from that
- Return an ATOM entry found at: http://example.org/fedora/services/(NEW_PID)/thought-exp:ImageItem/entry
http://example.org/fedora/services/(PID)/thought-exp:ImageItem/media
GET - Get an image
PUT - Replace an image. The item's 'entry' link will reflect the update
http://example.org/fedora/services/(PID)/thought-exp:ImageItem/entry
GET - Get the entry
Code Block | ||||
---|---|---|---|---|
| ||||
<entry xmlns="http://www.w3.org/2005/Atom"> <title>Smiley Beer Glass</title> <id>info:fedora/demo:SmileyBeerGlass/thought-exp:ImageItem</id> <updated>1979-10-07T17:17:08Z</updated> <content type="image/png" src="http://example.org/fedora/services/demo:SmileyBeerGlass/thought-exp:ImageItem/media"/> <link rel="edit-media" href="http://example.org/fedora/services/demo:SmileyBeerGlass/thought-exp:ImageItem/media" /> <link rel="edit" href="http://example.org/fedora/services/demo:SmileyBeerGlass/thought-exp:ImageItem/entry" /> </entry> |
PUT - Replace entry metadata
http://example.org/fedora/services/(PID)/thought-exp:Datastreams/service
GET - Service document for one object. Service documents may enumerate the acceptable content types.. perhaps it makes sense to introspect in the object's models, but it may be more flexible not to enumerate anything at all, if that's possible.
http://example.org/fedora/services/(PID)/thought-exp:Datastreams/feed
GET - Get an atom feed of all datastreams and versions represented by the PID object.
GET - Get an atom feed of all images in a collection represented by the PID object.
Code Block | ||||
---|---|---|---|---|
| ||||
<feed xmlns="http://www.w3.org/2005/Atom"> <title>demo:SmileyBeerGlass datastreams</title> <link href="http://example.org/fedora/services/demo:SmileyBeerGlass/thought-exp:Datastreams/feed" rel="self"/> <updated>1980-03-20T09:15:02Z</updated> <author> <name>Chis Wilper</name> </author> <id>info:fedora/demo:SmileyBeerGlass/thought-exp:Datastreams</id> <entry> <title type="text">RELS-EXT</title> <id>info:fedora/demo:SmileyBeerGlass/thought-exp:Datastream/RELS-EXT</id> <updated>1979-10-07T17:17:08Z</updated> <content type="application/rdf+xml" src="http://example.org/fedora/services/demo:SmileyBeerGlass/thought-exp:Datastream/media?id=RELS-EXT"/> <link rel="edit-media" href="http://example.org/fedora/services/demo:SmileyBeerGlass/thought-exp:Datastream/media?id=RELS-EXT" /> <link rel="edit" href="http://example.org/fedora/services/demo:SmileyBeerGlass/thought-exp:Datastream/entry?id=RELS-EXT" /> </entry> <entry xmlns:thr="http://purl.org/syndication/thread/1.0"> <title type="text">RELS-EXT.0</title> <id>info:fedora/demo:SmileyBeerGlass/thought-exp:Datastream/RELS-EXT.0</id> <updated>1979-10-07T17:17:08Z</updated> <thr:in-reply-to ref="info:fedora/demo:SmileyBeerGlass/thought-exp:Datastream/RELS-EXT"/> <content type="application/rdf+xml" src="http://example.org/fedora/services/demo:SmileyBeerGlass/thought-exp:Datastream/media?id=RELS-EXT&version=1979-10-07T17:17:08Z"/> <link rel="edit" href="http://example.org/fedora/services/demo:SmileyBeerGlass/thought-exp:Datastream/entry?id=RELS-EXT&version=1979-10-07T17:17:08Z" /> </entry> </feed> |
POST - Accept datastream content. POSTed data would cause the following to happen:
- Create a new datastream
- If the 'Slug' header is defined, assign object a datastream ID based on (identical to?) that.
- Return an ATOM entry found at: http://example.org/fedora/services/(PID)/thought-exp:Datastream/entry?id=DSID?version=XYZ, where 'DSID' is the datastream ID and 'version' is the datastream's version. entry shall have edit-media link pointing to unversioned datastream media URI.
http://example.org/fedora/services/(PID)/thought-exp:Datastream/media?id=DSID(&version=2008...)
GET - Get the datastream content
PUT - Put new datastream content (return an error if version is specifed??)
http://example.org/fedora/services/(PID)/thought-exp:Datastream/entry?id=DSID(&version=2008...)
GET - Get entry medatata
PUT - Modify entry metadata (Not sure what consequences are...)
The Other objects...
So far, we've really only discussed the public "interfaces" of AtomPub in a writable CMA - the SDefs, URIs, and data formats. Let us now take a look at all CMA and instance objects together, and see if we can map our desired functionality on to them.
Image item model:
Code Block |
---|
pid: demo:DualResImage relationship.hasService: thought-exp:ImageItem # New Atom functionality relationship.hasService: demo:DualResolution # Original image collection functionality relationship.hasModel: fedora-system:ContentModel-3.0 DS-COMPOSITE-MODEL: datastream: FULL_SIZE # from original demo:DualResImage datastream: MEDIUM_SIZE # from original demo:DualResImage datastream: RELS-EXT: # from original demo:DualResImage datastream: DC # from original demo:DualResImage |
Image item instance (one per image)
Code Block |
---|
pid: [item PID] datastream: MEDIUM_SIZE datastream: FULL_SIZE datastream: DC relationship.isMemberOf [collection PID] # de facto, see image collection demo objects relationship.hasModel: demo:DualResImage |
Image item SDef:
Code Block |
---|
pid: thought-exp:ImageItem method: service # APP Service document for one image collection method: feed # atom Image Collection feed relationship.hasModel atom:DomainModel # to have atom:Domain/feeds relationship.hasModel fedora-system:ServiceDefinition-3.0 |
Image item SDep:
Code Block |
---|
pid: thought-exp:ImageItemInstanceImpl relationship.isDeploymentOf: thought-exp:ImageItem relationship.isContractorOf: demo:DualResImage relationship.hasModel: fedora-system:ServiceDeployment-3.0 |
Image collection model:
Code Block |
---|
pid: demo:DualResImageCollection relationship.hasService: thought-exp:ImageCollection # Adds atom collection functionality relationship.hasModel: fedora-system:ContentModel-3.0 DS-COMPOSITE-MODEL: datastream: thought-exp.imageCollection.ATOM_SERVICE datastream: QUERY # from original demo:DualResImageCollection datastream: XSLT # from original demo:DualResImageCollection datastream: LIST # from original demo:DualResImageCollection |
Image collection instance:
Code Block |
---|
pid: [collection PID] relationship.hasModel demo:DualResImageCollection datastream: QUERY datastream: XSLT datastream: LIST datastream: thought-exp.ImageCollection.ATOM_SERVICE |
atom image collection SDef:
Code Block |
---|
atom image collection SDep:
Code Block |
---|
page is unfinished. Need to put in discussion, questions/implications, and basic conclusion as to whether this makes sense, and if it truly adds any value to, say, an external APP service.