Skip to content

stain/paq

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Providing provenance for a RESTful service

This is an example of how a a RESTful service, implemented using JAX-RS and CXF, can expose provenance of the resources it exposes.

There are two branches in this project on https://github.com/stain/paq:

  • master - REST service that can say hello, and return provenance of greeting
  • paq - REST service that also provides link between greeting and its provenance

To compile/run, you will need Java and Maven:

PS C:\users\stain\src\paq> mvn clean jetty:run
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Example PROV-AQ usage 0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
(..)    
2013-03-25 15:39:09.419:INFO::Started SelectChannelConnector@0.0.0.0:8080
[INFO] Started Jetty Server
[INFO] Starting scanner at interval of 10 seconds.

The base URI should be http://localhost:8080/paq/ unless you modified the port with mvn -Djetty.port=9999

Check the HelloWorld REST service is working using your favourite HTTP client (e.g. browser or curl in a new terminal window).

PS C:\Users\stain\src\paq> curl http://localhost:8080/paq/hello/Alice
Hello, Alice

You may replace Alice with any name, as long as it is URI escaped:

PS C:\Users\stain\src\paq> curl http://localhost:8080/paq/hello/Joe%20Bloggs
Hello, Joe Bloggs

Provenance resource

This example service provide provenance, using the PROV-N format:

PS C:\Users\stain\src\paq> curl -i http://localhost:8080/paq/provenance/hello/Alice
HTTP/1.1 200 OK
Content-Type: text/provenance-notation
Date: Mon, 25 Mar 2013 15:41:02 GMT
Content-Length: 305
Server: Jetty(6.1.26)

document
  prefix hello <http://localhost:8080/paq/hello/>
  prefix app <http://localhost:8080/paq/>
  entity(hello:Alice)
  wasDerivedFrom(hello:Alice, name)
  entity(name, [ prov:value="Alice" ])
  agent(app:hello, [ prov:type=prov:SoftwareAgent ])
  wasAttributedTo(hello:Alice, app:hello)
endDocument

Note that we used the -i parameter above to verify that the correct media-type text/provenance-notation was returned.

This provenance says that the resource http://localhost:8080/paq/hello/Alice was derived from a name with value "Alice", and made by the (web) service http://localhost:8080/paq/hello.

This PROV-N trace is generated by HelloWorld.helloProvenance() by filling in the URIs and name in the template src/main/resources/provTemplate.txt - a more detailed provenance trace might include things like timestamps and details about who provided the name.

Providing links to the provenance

A restful client who has requested http://localhost:8080/paq/hello/Alice will not magically know that there is a provenance trace at http://localhost:8080/paq/provenance/hello/Alice - the URI for the provenance resource could just as well have been say http://localhost:8080/about/history/1337.

Rather than for each publisher to invent specific ways to locate the provenance resource, the W3C PROV-AQ Note suggests a common way to find the provenance resource by using HTTP Link: headers according to RFC5988

Specifically, PAQ says that a resource accessed by HTTP can describe its provenance trace by adding a Link}} header with the relation "http://www.w3.org/ns/prov#has_provenance". So in our case, this can be achieved with:

Link: <http://localhost:8080/paq/provenance/hello/Alice>; rel="http://www.w3.org/ns/prov#has_provenance"

OK, so how do we provide this Link header? Our existing greeting is quite simple thanks to JAX-RS and CXF:

@GET
@Path("hello/{name}")
@Produces("text/plain")
public String hello(@PathParam("name") String name) {
    String greeting = "Hello, " + name + "\n";
    return greeting;
}

Our provenance method is a bit more complicated as it generates the absolute URIs for the greeting resource (depending on the name parameter) and then build the PROV-N trace - here using a simple MessageFormat template.

@GET
@Path("provenance/hello/{name}")
@Produces("text/provenance-notation")
public String helloProvenance(@PathParam("name") String name,
        @Context UriInfo ui) throws IOException {
    // Get our absolute URI
    // See http://cxf.apache.org/docs/jax-rs-basics.html#JAX-RSBasics-URIcalculationusingUriInfoandUriBuilder       
    UriBuilder appUri = ui.getBaseUriBuilder();
    // Absolute URIs for resources we are to give provenance about
    URI helloURI = appUri.path(getClass(), "hello").build(name);        
    
    // Prepare prefixes for PROV-N qualified names
    URI appURI = appUri.build("").resolve("../");       
    URI helloPrefix = helloURI.resolve("./");
    
    // The PROV-N qualified name for our /hello/{name} resource
    String helloEntity = "hello:" + helloPrefix.relativize(helloURI);
    
    // Simple PROV-N trace, see <http://www.w3.org/TR/prov-n/>
    // Here this is done in a naive way by loading a template 
    // from src/main/resources and do string-replace to insert
    // our URIs.
    String template = IOUtils.toString(getClass().getResourceAsStream("/provTemplate.txt"));
    String prov = MessageFormat.format(template, 
            helloPrefix, appURI, helloEntity, name);
    // Note: PROV-N should be be built using say the PROV Toolbox 
    // rather than this naive template approach!
    return prov;
}

So in order to provide the RESTful links we will need to insert Link: headers in the hello() response. As we need to return both the greeting and HTTP headers, we change our return to a Response:

@GET
@Path("hello/{name}")
@Produces("text/plain")
public Response hello(@PathParam("name") String name) {
    String greeting = "Hello, " + name + "\n";
    ResponseBuilder responseBuilder = Response.ok().entity(greeting);
    return responseBuilder.build();
}

We'll inject the same @Context UriInfo ui parameter as in
in order to find the absolute URI to calling the helloProvenance() method:

public Response hello(@PathParam("name") String name, @Context UriInfo ui) {
    URI provUri = ui.getBaseUriBuilder().path(getClass(), "helloProvenance").build(name);

and then build a new Link instance:

    Link provLink = Link.fromUri(provUri).rel(HAS_PROVENANCE).build();

This uses the fixed URI for the provenance relation:

private static final String HAS_PROVENANCE = "http://www.w3.org/ns/prov#has_provenance";

Finally we include the new Link header by adding it to the response builder before returning:

return responseBuilder.header(HttpHeaders.LINK, provLink).build();

The final version of hello() should then look something like:

@GET
@Path("hello/{name}")
@Produces("text/plain")
public Response hello(@PathParam("name") String name, @Context UriInfo ui) {
    String greeting = "Hello, " + name + "\n";
    ResponseBuilder responseBuilder = Response.ok().entity(greeting);
    // TODO: Could have used Link.fromResourceMethod but it seems to return wrong URI in CXF :(
    URI provUri = ui.getBaseUriBuilder().path(getClass(), "helloProvenance").build(name);
    Link provLink = Link.fromUri(provUri).rel(HAS_PROVENANCE).build();
    return responseBuilder.header(HttpHeaders.LINK, provLink).build();
}

You may check out the paq branch from https://github.com/stain/paq to see the final version.

Finding the provenance links

If you have not followed the tutorial above, make sure you check out and build the paq branch from https://github.com/stain/paq to include the PROV-AQ Link headers.

About

Example of using PROV-AQ

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages