Quantcast
Channel: iridium » systems
Viewing all articles
Browse latest Browse all 5

SWORD v2 – From clueless to claymore

$
0
0

What follows is a summary of my steps along the path of investigating what the sword technology is, through to being able to actually start to code something useful; I should probably point out that the beginning of this post can be consumed by less technical persons as a quick overview, but the later section assumes that you…

  • Have some knowledge of coding java
  • Have worked with java server containers (e.g. tomcat) before
  • Can place the libraries in an IDE like netbeans/eclipse to do “something” with them

(Since my investigations centered around sword in conjunction with Sakai and e-science central my language of choice therefore is Java).

I should also declare that I still don’t fully understand all of the implementation but this should help you along your way if you’re just starting out!


Taking the Sword course

My first port of call was the SWORD website itself, which will point you to some useful videos and slides to give you insight into what the technology is and what it can be used for. In short, this is what the “Sword Course” will teach you…

An Introduction To SWORD (Video/Slides)

What it is:

The “Simple Webservice Offering Repository Deposit” technology (or SWORD for short) intentionally only deals with how to get data into a repository, nothing else, and is complementary to something like dublin core used to describe stuff in a repository; it also does not deal with packaging, metadata, authentication or authorisation

Existing implementations can be found in:

DSpace
Eprints
Fedora
Intralibrary
Zentity

SWORD Use Cases (Video/Slides)

Use cases sword is trying to enable:

  • Deposit from a desktop machine
  • Deposit to multiple repositories (For example to allow depositing once and ending up in an institution’s repository, funder’s repository and a subject specific repository)
  • Deposit from a piece of lab equipment (non-human depositing data)
  • Deposit from one repository to another (For example Institutional repository to National repository which may have differing visibility of the data…. Dark and light repositories, dark = can’t be seen private repositories, light = can be seen public repository)
  • Deposit from external publisher/publishing system to long term storage (For example from OJS to your own institution’s repository)

How SWORD Works (Video/Slides)

  • SWORD is in the form of an “XML REST webservice to put things in a repository”
  • It has built on the resource creation aspects of the ATOM Pub standard which is for publishing content to the web
  • SWORD is an extension, or “profile”, of the ATOM Pub spec and existing ATOM Pub clients can be made to work if the relevant extensions are added
  • SWORD version 2 now includes full CRUD

When you use a sword client, this is basically what happens…

  • The client asks a repository to describe itself
  • The server returns a “Service document” to describe what you can do with the repository and how to do that. The service document is typically hidden by Basic Auth Authentication (AM: I think this is crying out for an OAuth implementation!) but once authenticated the web service will customise the service document to what you are allowed to / should do with the system. The server can also describe what data formats you want to accept, where it will go and how long you will store it etc… this is your “collection policy”
  • The client then uses the service description to format your data and then deposit it

What sword adds to ATOM Pub:

  • Accept Packaging – Tells the client what types of data the server accepts
  • Mediated Deposit – Allows you to deposit “as” / “on behalf of” someone else, a repository can say whether it allows this or not
  • Developer features – You can state that you want verbose output to say what happened (v1.3 featured a dry run feature called “no-op” that does not actually deposit or do anything. NOTE: this does not appear to be in v2 anymore)
  • Nested Service document – Where there may be many repositories for the service, the top level document provides links to sub documents, instead of repeating the same or similar definitions in one enormous file.

SWORD clients (Video/Slides)

There are generally three sorts of client:

  • Machine to machine – for very specific automated deposits (lab equipment)
  • General – human would use, talks to any repository
  • Specific – for depositing certain data into certain repositories in a given way that has an extra context of a general client, i.e. depositing specific journals, depositing data for a particular project

Interesting possibilities for deposit scenarios:

Writing something useful

My next stop on the journey through sword looked at actual code, how it’s laid out and what you need to do in order to start doing something useful.

As mentioned previously, I have been basing my investigations around the Java client and server libraries but I’d strongly recommend you also get a good grounding in the workings of ATOM Pub (HTML Version) and the Sword Profile specifications themselves. If you’ve ever read specification documents before you’ll know they can make quite dry reading, however, since ATOM Pub and sword are relatively straight forward technologies and the specs only reach into 30-50 odd pages it’s really worth a browse through.

How SWORD works in java

Firstly, it’s probably best to understand a few basic concepts you will need to deal with, the main outputted concepts/objects are:

  • IRI‘s – unique identifiers to a resource
  • Entry – A deposit, has IRI’s/metadata
  • Media - An entry for media (word docs/pdfs/images), can be linked to in an Entry
  • Collection Entries – ATOM Pub collection of entries (member resources)
  • Collections – A set of deposited entries represented by an Atom Feed document, you can distinguish a collection feed from “a.n.other feed” by the presence of a collection IRI in service document
  • Workspaces – A compartmentilisation concept for repositories, has a name but doesn’t have IRI’s or processing models
  • Service Documents – Groups “Collections” in “Workspaces”, can indicate accepted media types and categories of collections

Next, I found you learn the most by studying the server libraries, what you get is a bundle of java and some set up files for your container. We’ll firstly look at the setup in web.xml

Setup and Servlet mappings (web.xml)

The main servlets (i.e ultimately your rest endpoints) that are defined are…

servicedocument

collection

  • Class: org.swordapp.server.servlets.CollectionServletDefault
  • URL: http://<yourServer>/<yourWebapp>/collection/*
  • Purpose: Retrieveing and deposting to/from collections/feeds and entries

mediaresource

container

statement

The code makes heavy use of interfaces to allow the implementer more freedom to create functionality using the server library in the way they want to, in order to tell the server libraries what code we want to instantiate and what auth. mechanism we will be using, you must set some context parameters to define those settings and implementations used at runtime:

auth

  • param-name: authentication-method
  • param-value: Basic or None (default: “Basic”)

You can set an Authorization header and base64 encode user:password (e.g. try going to a basic auth encoding website and encode “user:password” in plain text box) and send as a header… “Authorization” “Basic dXNlcjpwYXNzd29yZA==”, or, if you prefer set the param-value to “None” for no authorisation. I found (at the time of writing) the default code actually has a bug which means turning the auth off doesn’t work correctly. I found the best way of correcting this was in my own war project (which includes the server libraries as a dependancy) I created a org.swordapp.sever package (where I was implementing the objects needed for the interfaces) and dropped in a copy of the SwordAPIEndpoint.java to override the implementation in the library, I then changed the getAuthCredentials to…

protected AuthCredentials getAuthCredentials(HttpServletRequest request, boolean allowUnauthenticated) throws SwordAuthException
{
   AuthCredentials auth = null;
   String authType = this.config.getAuthType();
   String obo = "";
   this.log.info("Auth type = "+authType);
   //If we are insisting on "a" form of authentication that is not of type "none"
   if(!allowUnauthenticated && !authType.equalsIgnoreCase("none"))
   {
      // Has the user passed authentication details
      String authHeader = request.getHeader("Authorization");
      // Is there an On-Behalf-Of header?
      obo = request.getHeader("On-Behalf-Of");
      // Which authentication scheme do we recognise (should only be Basic)
      boolean isBasic = authType.equalsIgnoreCase("basic");

      if(isBasic && (authHeader == null || authHeader.equals("")))
      {
         throw new SwordAuthException(true);
      }
      // decode the auth header and populate the authcredentials object for return
      String[] userPass = this.decodeAuthHeader(authHeader);
      auth = new AuthCredentials(userPass[0], userPass[1], obo);
   }
   else
   {
      log.debug("No Authentication Credentials supplied/required");
      auth = new AuthCredentials(null, null, obo);
   }
   return auth;
}

The following context parameters set the implementations of interfaces used to implement functionality in the endpoints. However, you are not given default implementations for each of these, so in your war you need to create a new class that implements the respective interface and fill in your functionality….

collection-list-impl

  • param-value: org.swordapp.server.CollectionListManagerImpl
  • Interface it implements: org.swordapp.server.CollectionListManager

service-document-impl

  • param-value: org.swordapp.server.ServiceDocumentManagerImpl
  • Interface it implements: org.swordapp.server.ServiceDocumentManager

collection-list-impl

  • param-value: org.swordapp.server.CollectionListManagerImpl
  • Interface it implements: org.swordapp.server.CollectionListManager

collection-deposit-impl

  • param-value: org.swordapp.server.CollectionDepositManagerImpl
  • Interface it implements: org.swordapp.server.CollectionDepositManager

media-resource-impl

  • param-value: org.swordapp.server.MediaResourceManagerImpl
  • Interface it implements: org.swordapp.server.MediaResourceManager

container-impl

  • param-value: org.swordapp.server.ContainerManagerImpl
  • Interface it implements:  org.swordapp.server.ContainerManager

statement-impl

  • param-value: org.swordapp.server.StatementManagerImpl
  • Interface it implements: org.swordapp.server.StatementManager

config-impl

  • param-value: org.swordapp.server.SwordConfigurationDefault (Yes, this one does have a default implementation in the library you can use)
  • Interface it implements: org.swordapp.server.SwordConfiguration

Endpoint Servlet classes (org.swordapp.server.servlets.*)

Let’s now have a look at the servlets themselves, each servlet contains interfaces which are implemented by loading the classes specified in the web.xml (see above).

All servlets used in the server library extend the “SwordServlet” which (obviously) extends the HttpServlet. Since the SwordServlet contains the server configuration object, all servlets (through inheritance) also hold an implementation of the server configuration (i.e. SwordConfiguration) and a method to allow servlets to load classes from the configuration….

SwordServlet encapsulates:

  • SwordConfiguration interface, instantiated using config-impl
  • loadImplClass() method used for loading implementing classes from tomcat context params

CollectionServletDefault extends SwordServlet and encapsulates:

  • CollectionListManager interface, instantied using collection-list-impl
  • CollectionDepositManager interface, instantied using collection-deposit-impl
  • CollectionAPI object

ServiceDocumentServletDefault extends SwordServlet and encapsulates:

  • ServiceDocumentManager interface, instantiated using service-document-impl
  • ServiceDocumentAPI object

MediaResourceServletDefault extends SwordServlet and encapsulates:

  • MediaResourceManager interface, instantiated using media-resource-impl
  • MediaResourceAPI object

ContainerServletDefault extends SwordServlet and encapsulates:

  • ContainerManager interface, instantiated using container-impl
  • StatementManager interface, instantiated using statement-impl
  • ContainerAPI object

StatementServletDefault extends SwordServlet and encapsulates:

  • StatementManager interface, instantiated using statement-impl
  • StatementAPI object

Endpoint Servlet dependant classes (org.swordapp.server.*)

Those with a keen eye will have noticed that each servlet is also holding an “API” object, these objects fill out the standard Get/Post/Put/Delete HttpServlet methods that the servlets override by taking the configuration object and any interfaces that have been implemented and combine them to do something useful. Similarly to the servlets, they all extend a Sword API super class called “SwordAPIEndpoint”, which holds a SwordConfiguration implementation. The hierarchy (and interfaces they encapsulate) looks like this…

SwordAPIEndpoint

  • SwordConfiguration interface

CollectionAPI extends SwordAPIEndpoint

  • CollectionListManager interface
  • CollectionDepositManager interface

ServiceDocumentAPI extends SwordAPIEndpoint

  • ServiceDocumentManager interface

MediaResourceAPI extends SwordAPIEndpoint

  • MediaResourceManager interface

ContainerAPI extends SwordAPIEndpoint

  • ContainerManager interface
  • StatementManager interface

StatementAPI extends SwordAPIEndpoint

  • StatementManager interface

Implementations of interfaces (org.swordapp.server.*)

I keep mentioning the objects that implement the interfaces, I thought it might be useful to go through in “slightly” more detail what the content of those objects are intended for. Apologies once again, this is not exhaustive as I have not worked my way through what all the methods are intended for:

SwordConfigurationDefault implements org.swordapp.server.SwordConfiguration

  • This is the default object which holds the configuration for the server

CollectionListManagerImpl implements org.swordapp.server.CollectionListManager

CollectionDepositManagerImpl implements org.swordapp.server.CollectionDepositManager

ServiceDocumentManagerImpl implements org.swordpapp.server.ServerDocumentManager

  • Method: getServiceDocument()
  • Accessed via: GET http://<yourServer>/<yourWebapp>/servicedocument/
  • Returns: org.swordapp.server.ServiceDocument
  • Purpose: Serves service documents (xml that explains the contents and deposit policies for the repository(/ies)

MediaResourceManagerImpl implements org.swordapp.server.MediaResourceManager

  • Method: replaceMediaResource()
  • Accessed via: PUT http://<yourServer>/<yourWebapp>/edit-media/
  • Returns: org.swordapp.server.DepositReceipt
  • Purpose: Swap a media resource (pdf/doc etc….) in the repository with the one being “PUT’ed”

ContainerManagerImpl implements org.swordapp.server.ContainerManager

  • Method: replaceMetadataAndMediaResource()
  • Accessed via: PUT http://<yourServer>/<yourWebapp>/edit/
  • Returns: org.swordapp.server.DepositReceipt
  • Purpose: Replaces metadata and media associated with an entry
  • Method: addMetadataAndResources()
  • Accessed via: Does not appear to be “directly” accessible via any specific HTTP request
  • Returns: org.swordapp.server.DepositReceipt
  • Purpose: Not used by the ContainerAPI yet, but presumably it would be for adding a series of entries and associated metadata
  • Method: addResources()
  • Accessed via: Does not appear to be “directly” accessible
  • Returns: org.swordapp.server.DepositReceipt
  • Purpose: Not used by the ContainerAPI yet, but presumably it would be for adding a series of entries
  • Method: useHeaders()
  • Accessed via: POST http://<yourServer>/<yourWebapp>/edit/
  • Returns: org.swordapp.server.DepositReceipt
  • Purpose: Used when depositing only information specified in the HTTP headers, no entry/ies (i.e. no content body to the POST) will have been specified

StatementManagerImpl implements org.swordapp.server.StatementManager

And finally…

Once you have all that setup (and I’d recommend just creating skeleton override methods for the objects implementing interfaces for the time being whilst you figure the code out), you can then start coding the abdera / sword code and try make the client do something. The client itself comes with a handy cli driven (SwordCLI) interface that you can point at your newly created server instance and test the various example methods. I would recommend though, that you comment out the entire list of method references in the main method and go through the list iteratively to slowly make each part of your server work…

As a brief example, if we were to try and get a basic service document to work, try adding this code to your ServiceDocumentManagerImpl.java….

    public ServiceDocument getServiceDocument(String sdUri, AuthCredentials auth, SwordConfiguration config) throws SwordError, SwordServerException, SwordAuthException
    {
        //Our test service document
        ServiceDocument sd = new ServiceDocument();
        //sd.setVersion("2.0");
        sd.setMaxUploadSize(1000000000);

        //Our test workspace
        SwordWorkspace sw = new SwordWorkspace();
        sw.setTitle("TestWorkspace");

        //Our test collection
        SwordCollection sc = new SwordCollection();
        sc.setHref("http://<yourServer>/<yourWebapp>/collection/TestCollection");
        sc.setTitle("TestCollection");
        sc.addAccepts("*/*");
        sc.setCollectionPolicy("TestCollectionPolicy");
        sc.setAbstract("TestCollectionAbstract");
        sc.setMediation(false);
        sc.setTreatment("A human-readable statement describing treatment the deposited resource has received or a URI that dereferences to such a description.");
        sc.addAcceptPackaging("http://purl.org/net/sword/package/SimpleZip");
        sc.addAcceptPackaging("http://purl.org/net/sword/package/METSDSpaceSIP");
        sc.addAcceptPackaging("http://www.ncl.ac.uk/terms/package/html");
        sc.setLocation("http://<yourServer>/<yourWebapp>/collection/TestCollection");
        sc.setMediation(true);
        List iris = new ArrayList();
        iris.add(new IRI("http://<yourServer>/<yourWebapp>/collection/TestCollection/TestSubService"));
        sc.setSubServices(iris);

        //Add collection to workspace
        sw.addCollection(sc);
        //Add workspace to service document
        sd.addWorkspace(sw);

        return sd;
    }

Browsing to http://<yourServer>/<yourWebapp>/collection/ should return something, and removing your comment in your client for the line…

    cli.trySwordServiceDocument()

…should now yield some results (If you just get errors try temporarily turning off the auth. requirement on the server for the purposes of testing).

And that’s the basic principle, you then take the specs and implement the returning and unpackaging of ATOM using the abdera/SWORD objects and link what’s passed/returned to the content found in the system you are trying to integrate.

Further reading

Sword Specs
Brief history of Sword
More useful Sword docs
Another set of slides explaining sword

Andrew Martin
Research and Collaborative Services
Newcastle University


Filed under: blog report, dissemination, implementation, systems, tools

Viewing all articles
Browse latest Browse all 5

Latest Images

Trending Articles





Latest Images