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+n34. 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=15. The client waits as suggested for the second output, then follows the redirect.
GET /examples/hello?poll=2 Host: sadiframework.org Accept: text/rdf+n36. 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!" .