This document describes Semantic Automated Discovery and Integration (SADI), a set of design patterns defining the behavior of data retrieval and/or analysis resources that must interoperate on the Semantic Web. These behaviors facilitate the native use of Semantic Web data as input, facilitate the integration of output data back into the Semantic Web, and facilitate semantic discovery of the resource itself. With SADI, interoperability and Semantic Web integration depend on two features: (a) the shared use of predicate vocabularies, rather than shared Schemas or ad hoc data formats, and (b) requiring that the resource creates an explicit semantic relationship (predicate) connecting the input and output data. We present a specification that applies these design patterns to the case of Web Services. In this specification, OWL is used to describe service input and output datatypes, and RDF data is consumed and produced natively through service invocations via HTTP POST. SADI Web Services enable: i) automated discovery of services that provide data or computations of interest, and ii) automated matchmaking between local data and available services. By iterative application of these two capabilities, SADI enables semi-automated construction of arbitrarily complex workflows across independent service providers.
This document is governed by the 1 September 2015 W3C Process Document.
Adhering to these design patterns are the only requirements for SADI-compliance, and can be applied to any transaction-based resource. Additional utility can be gained by, for example, creating a searchable index of the resource descriptors; however, these are only a requirement for resources intended to be used by third-parties.
SADI Semantic Web Services defines a set of behaviors and design patterns for implementing stateless web services that natively consume RDF data as input and generate RDF data as output. Its primary purpose is to increase the interoperability of services across independent providers. Under SADI, the schemas for the input and output RDF data of a service are defined by the service's input OWL class and output OWL class, respectively. Provider-specified metadata about a service, including the URIs of the input and output OWL classes, is published as an RDF document that is retrievable by an HTTP GET on the service URL. Service invocation is accomplished by issuing an HTTP POST request to the service URL with an appropriate input RDF document as the request body. The input RDF document contains one or more instances of the input OWL class which represent independent inputs to the service, and in response the service returns an output RDF document with a corresponding number of instances of the output OWL class. Each output instance has the same root URI as its corresponding input instance, in order to ensure that the data consumed and generated by the service are explicitly linked.
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix hello: <http://sadiframework.org/examples/hello.owl#> .
@prefix input: <http://sadiframework.org/data/examples/hello-input.n3#> .
input:GuyIncognito
a hello:NamedIndividual;
foaf:name "Guy Incognito" .
input:HomerSimpson
a hello:NamedIndividual;
foaf:name "Homer Simpson" .
In response, the service generates the following output RDF document:
@prefix hello: <http://sadiframework.org/examples/hello.owl#> .
@prefix input: <http://sadiframework.org/data/examples/hello-input.n3#> .
input:GuyIncognito
a hello:GreetedIndividual;
hello:greeting "Hello, Guy Incognito!" .
input:HomerSimpson
a hello:GreetedIndividual;
hello:greeting "Hello, Homer Simpson!" .
In this example, the input RDF document contains two input instances
with the URIs input:GuyIncognito and input:HomerSimpson. Each input
instance is processed independently by the service, and so the same
result could be generated by invoking the service twice with the
input graphs for Guy Incognito and Homer Simpson separately, and
afterwords performing an RDF-merge on the two output RDF documents.
The input instances are identified by the service as those URIs
having an rdf:type matching the service's input OWL class (hello:
NamedIndividual). The purpose of the input OWL class is to describe
the expected structure of the input instances. More will be said
about the purpose and design of the input OWL class in The Input OWL
Class (Section XXXX).
@prefix protege-dc: <http://protege.stanford.edu/plugins/owl/dc/protege-dc.owl#> .
@prefix mygrid: <http://www.mygrid.org.uk/mygrid-moby-service#> .
@prefix hello: <http://sadiframework.org/examples/hello.owl#> .
@prefix test: <http://sadiframework.org/examples/t/> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
<http://sadiframework.org/examples/hello-param>
a mygrid:serviceDescription ;
#----------------------------------------
# Service Name
#----------------------------------------
mygrid:hasServiceNameText
"ParamaterizedHelloWorld"^^xsd:string ;
#----------------------------------------
# Service Description
#----------------------------------------
mygrid:hasServiceDescriptionText
"A \"Hello, world!\" service where the output language is
specified in a parameter"^^xsd:string ;
#----------------------------------------
# Contact E-mail Address, Authoritative Flag
#----------------------------------------
mygrid:providedBy
[ a mygrid:organisation ;
protege-dc:creator "person@organization.com"^^xsd:string ;
mygrid:authoritative "false"^^xsd:boolean
] ;
mygrid:hasOperation
[ a mygrid:operation ;
#----------------------------------------
# Input OWL Class
#----------------------------------------
mygrid:inputParameter
[ a mygrid:parameter ;
mygrid:objectType hello:NamedIndividual
] ;
#----------------------------------------
# Parameter OWL Class, Default Parameter Graph
#----------------------------------------
mygrid:inputParameter
[ a mygrid:secondaryParameter ;
mygrid:objectType hello:SecondaryParameters ;
mygrid:hasDefaultValue
[ a hello:SecondaryParameters ;
hello:lang "en"^^xsd:string
]
] ;
#----------------------------------------
# Output OWL Class
#----------------------------------------
mygrid:outputParameter
[ a mygrid:parameter ;
mygrid:objectType hello:GreetedIndividual
] ;
#----------------------------------------
# Unit Test
# (test input/output RDF included directly)
#----------------------------------------
mygrid:hasUnitTest
[ a mygrid:testCase ;
mygrid:exampleInput
[ a hello:InputClass ;
foaf:name "Guy Incognito"
] ;
mygrid:exampleOutput
[ a hello:OutputClass ;
hello:greeting "Hello, Guy Incognito!"
]
] ;
#----------------------------------------
# Unit Test
# (test input/output RDF in external documents)
#----------------------------------------
mygrid:hasUnitTest
[ a mygrid:testCase ;
mygrid:exampleInput test:hello-param-input.rdf ;
mygrid:exampleOutput test:hello-param-output.rdf
]
] .
<owl:Class rdf:ID="NamedIndividual">
<owl:equivalentClass>
<owl:Restriction>
<owl:onProperty rdf:resource="http://xmlns.com/foaf/0.1/name"/>
<owl:minCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#int">1</owl:minCardinality>
</owl:Restriction>
</owl:equivalentClass>
</owl:Class>
This class definition states that a URI is an instance of hello:
NamedIndividual if and only if it has one or more values for the
foaf:name property. As a result, each input instance for the "Hello,
World!" service is required to have at least one foaf:name property.
<owl:Class rdf:ID="NamedIndividual">
<rdfs:subClassOf>
<owl:Restriction>
<owl:onProperty rdf:resource="http://xmlns.com/foaf/0.1/name"/>
<owl:minCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#int">1</owl:minCardinality>
</owl:Restriction>
</rdfs:subClassOf>
</owl:Class>
<owl:Class rdf:ID="NamedIndividual">
<owl:equivalentClass>
<owl:Restriction>
<owl:onProperty rdf:resource="http://xmlns.com/foaf/0.1/name"/>
<owl:minCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#int">1</owl:minCardinality>
</owl:Restriction>
</owl:equivalentClass>
</owl:Class>
The first definition uses only a necessary condition. It states that
a URI has one or more foaf:name properties if it is an instance of
hello:NamedIndividual. (A URI is known to be an instance of the
hello:NamedIndividual if it has an rdf:type value of hello:
NamedIndividual.) From this rule, a reasoner cannot deduce that a
given URI is a member of hello:NamedIndividual based on its
properties.
<owl:Class rdf:ID="NamedIndividual">
<owl:equivalentClass>
<owl:Restriction>
<owl:onProperty rdf:resource="http://xmlns.com/foaf/0.1/name"/>
<owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#int">1</owl:cardinality>
</owl:Restriction>
</owl:equivalentClass>
</owl:Class>
This definition states that a URI is a member of the hello:
NamedIndividual class if and only if it has exactly one foaf:name
property. However, even if a URI (person) has exactly one value for
foaf:name in a particular data set, that same URI may possess any
number of additional foaf:name values in other RDF data sets on the
web. For this reason, a reasoner using the OWA can never confirm the
truth of an exact cardinality restriction by examining the known
properties of a URI. It can only prove the falsehood of the
cardinality restriction in cases where the URI is known to have a
greater number of distinct values for the property than desired.
<owl:Class rdf:ID="GreetedIndividual">
<owl:equivalentClass>
<owl:Restriction>
<owl:onProperty rdf:resource="#greeting"/>
<owl:minCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#int"></owl:minCardinality>
</owl:Restriction>
</owl:equivalentClass>
</owl:Class>
The definition for hello:GreetedIndividual indicates that one or more
hello:greeting properties will be attached to each input instance as
a result of invoking the service.
<owl:Class rdf:ID="SecondaryParameters">
<owl:equivalentClass>
<owl:Restriction>
<owl:onProperty rdf:resource="#lang"/>
<owl:minCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#int">1</owl:minCardinality>
</owl:Restriction>
</owl:equivalentClass>
</owl:Class>
Although the intention of the hello:SecondaryParameters class is to
describe a graph with a single value for hello:lang, a minimum
cardinality restriction is used to facilitate instance checking with
OWL reasoners that use the Open World Assumption (OWA). For a
detailed discussion of OWL class design as it pertains to instance
checking and the OWA, see Instance Checking and the Input OWL Class
(Section XXXXX).
@prefix hello: <http://sadiframework.org/examples/hello.owl#> .
[] a hello:SecondaryParameters;
hello:lang "en" .
This default parameter instance indicates that, unless otherwise
specified, greetings will be returned in English.
POST /examples/hello HTTP/1.1
Host: sadiframework.org
Accept: text/rdf+n3
Content-type: text/rdf+n3
@prefix hello: <http://sadiframework.org/examples/hello.owl#> .
@prefix input: <http://sadiframework.org/data/examples/hello-input.n3#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
input:GuyIncognito
a hello:NamedIndividual;
foaf:name "Guy Incognito" .
input:HomerSimpson
a hello:NamedIndividual;
foaf:name "Homer Simpson" .
2. The service sends a response containing the output. The body of
the response is an RDF document containing one or more instances of
the service's output OWL class. Each instance of the output OWL
class MUST have the same root URI as exactly one instance of the
input OWL class from the input RDF document. The serialization
format of the output RDF document MUST be specified in the Content-
type header of the response (one of application/rdf+xml or text/
rdf+n3).
HTTP/1.1 200 OK
Content-type: text/rdf+n3
@prefix hello: <http://sadiframework.org/examples/hello.owl#> .
@prefix input: <http://sadiframework.org/data/examples/hello-input.n3#> .
input:GuyIncognito
a hello:GreetedIndividual;
hello:greeting "Hello, Guy Incognito!" .
input:HomerSimpson
a hello:GreetedIndividual;
hello:greeting "Hello, Homer Simpson!" .
A synchronous service must finish processing its input before the TCP
connection between the client and the service times out. SADI
services that must run for longer periods of time should be
implemented as asynchronous services, as described in the next
section.
POST /examples/hello HTTP/1.1
Host: sadiframework.org
Accept: text/rdf+n3
Content-type: text/rdf+n3
@prefix hello: <http://sadiframework.org/examples/hello.owl#> .
@prefix input: <http://sadiframework.org/data/examples/hello-input.n3#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
input:GuyIncognito
a hello:NamedIndividual;
foaf:name "Guy Incognito" .
input:HomerSimpson
a hello:NamedIndividual;
foaf:name "Homer Simpson" .
2. The service sends a response with the HTTP response code 202
(accepted but incomplete). The body of the response is an RDF
document containing statements about the input instances. The
serialization format of the RDF document MUST be specified in the
Content-type header of the response (one of application/rdf+xml or
text/rdf+n3). The existence of additional data is indicated by rdfs:
isDefinedBy statements where the object is a URL the client must
fetch to receive the complete output. There MAY be multiple such
URLs and the initial output MAY contain only rdfs:isDefinedBy
statements.
HTTP/1.1 202 Accepted
Content-type: text/rdf+n3
@prefix hello: <http://sadiframework.org/examples/hello.owl#> .
@prefix input: <http://sadiframework.org/data/examples/hello-input.n3#> .
@prefix rdfs: <http://http://www.w3.org/2000/01/rdf-schema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
input:GuyIncognito
a hello:GreetedIndividual;
rdfs:isDefinedBy <http://sadiframework.org/examples/hello?poll=1> .
input:HomerSimpson
a hello:GreetedIndividual;
rdfs:isDefinedBy <http://sadiframework.org/examples/hello?poll=2> .
3. The client sends a GET request for each rdfs:isDefinedBy URL in
the initial response. The client SHOULD include an Accept header
indicating the desired RDF serialization format for the response,
which otherwise defaults to RDF/XML.
Request 1:
GET /examples/hello?poll=1
Host: sadiframework.org
Accept: text/rdf+n3
Request 2:
GET /examples/hello?poll=2
Host: sadiframework.org
Accept: text/rdf+n3
4. If the output for a given polling URL is ready, the service sends
a response with the output. The body of the response is an RDF
document containing statements that should be combined with the
initial output document. The serialization format of the RDF
document MUST be specified in the Content-type header of the response
(one of application/rdf+xml or text/rdf+n3). If the output is not
yet ready, the service sends an HTTP redirect with a Retry-after
header that contains the number of seconds that the client should
wait before trying again. In the example responses below, the
response for the first output is ready, but the response for the
second output is not yet ready.
Response for Request 1:
HTTP/1.1 200 OK
Content-type: text/rdf+n3
@prefix hello: <http://sadiframework.org/examples/hello.owl#> .
@prefix input: <http://sadiframework.org/data/examples/hello-input.n3#> .
input:GuyIncognito
a hello:GreetedIndividual;
hello:greeting "Hello, Guy Incognito!" .
Response for Request 2:
HTTP/1.1 302 Moved Temporarily
Pragma: sadi-please-wait = 5000
Location: http://sadiframework.org/examples/hello?poll=1
5. The client waits as suggested for the second output, then follows
the redirect.
GET /examples/hello?poll=2
Host: sadiframework.org
Accept: text/rdf+n3
6. If the second output is still not ready, the service sends
another HTTP redirect as above. When the second output is ready, the
service returns another RDF document:
HTTP/1.1 200 OK
Content-type: text/rdf+n3
@prefix hello: <http://sadiframework.org/examples/hello.owl#> .
@prefix input: <http://sadiframework.org/data/examples/hello-input.n3#> .
input:HomerSimpson
a hello:GreetedIndividual;
hello:greeting "Hello, Homer Simpson!" .
7. The client may perform an RDF-merge on the initial output
document and the output documents from each polling URL to create an
RDF document that contains a complete representation of all output
instances. The client SHOULD remove the rdfs:isDefinedBy statements
from the merged document, as the polling URLs MAY expire after
returning data or after a fixed period of time, at the discretion of
the service provider.
@prefix hello: <http://sadiframework.org/examples/hello.owl#> .
@prefix input: <http://sadiframework.org/data/examples/hello-input.n3#> .
input:GuyIncognito
a hello:GreetedIndividual;
hello:greeting "Hello, Guy Incognito!" .
input:HomerSimpson
a hello:GreetedIndividual;
hello:greeting "Hello, Homer Simpson!" .
Request:
POST /examples/hello HTTP/1.1
Host: sadiframework.org
Accept: text/rdf+n3
Content-type: text/rdf+n3
@prefix hello: <http://sadiframework.org/examples/hello.owl#> .
@prefix input: <http://sadiframework.org/data/examples/hello-input.n3#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
[] a hello:SecondaryParameters;
hello:lang "es" .
input:GuyIncognito
a hello:NamedIndividual;
foaf:name "Guy Incognito" .
input:HomerSimpson
a hello:NamedIndividual;
foaf:name "Homer Simpson" .
Response:
HTTP/1.1 200 OK
Content-type: text/rdf+n3
@prefix hello: <http://sadiframework.org/examples/hello.owl#> .
@prefix input: <http://sadiframework.org/data/examples/hello-input.n3#> .
input:GuyIncognito
a hello:GreetedIndividual;
hello:greeting "Hola, Guy Incognito!" .
input:HomerSimpson
a hello:GreetedIndividual;
hello:greeting "Hola, Homer Simpson!" .