4 Service Discovery with SWSL-Rules

This example illustrates the use of SWSL-Rules for Web service discovery. The particular features of the language that this example relies on include frame-based representation, reification, and nonmonotonic Lloyd-Topor extensions. In addition, logical updates à la Transaction logic [Bonner98] are used in the discovery queries. Transaction Logic was mentioned in Section 3.15 as a possible extension for SWSL-Rules.

To make the example manageable, services are described only by their names and conditional effects. To discover a service, users must represent their goals using the goal ontology described below. These goals are described in terms of user requests, which represent formulas that the user wants to be true in the after-state of the service (i.e., the state that would result after the execution of the service).

User goals and services may be expressed in different ontologies and so mediators are needed to translate between those ontologies. This type of mediators is known as wgMediators [Bruijn05]. In this example, we assume that each service advertises the mediators that can be used to talk to this service though the attribute mediators.

Ontologies

Geographical ontology. To begin, we assume the following simple geographic taxonomy, which is shared by user goals and services. It defines several regions and subregions, such as America, USA, Europe, Tyrol. Each region is viewed as a class of cities. For instance, Innsbruck is a city in Tyrol and 'Stony Brook' is a town in the New York State (NYState).

    USA::America.
    Germany::Europe.
    Austria::Europe.
    France::Europe.
    Tyrol::Austria.
    NewYorkState::USA.
    StonyBrook:NewYorkState.
    NewYork:NewYorkState.
    Innsbruck:Tyrol.
    Lienz:Tyrol.
    Vienna:Austria.
    Bonn:Germany.
    Frankfurt:Germany.
    Paris:France.
    Nancy:France.

    Europe:Region.
    America:Region.

    ?Reg:Region :- ?Reg1:Region and ?Reg::?Reg1.
    ?Loc:Location :- ?Reg:Region and ?Loc:?Reg.
  

To make it easier to specify what is a region and what is not, we use a rule (the penultimate statement above) to say that a subclasses of a region are also regions and, therefore, such subclasses do not need to be explicitly declared as regions. The last rule simply says that any object that is a member of a geographical region is a location.

Goal ontology. Services write their descriptions to conform to specific ontologies. Likewise, clients describe their goals in terms of goal ontologies. Here we will not describe these ontologies, but rather the forms of the inputs and outputs that the services expect to produce and the structure of the user goals. Furthermore, since users and service designers are unlikely to be skilled knowledge engineers, we assume that the inputs, the outputs, and the goals are fairly simple and that most of the intelligence lies in the mediators.

We assume that there is one ontology for goals and two for services. Consequently, there are two mediators: one translating between the goal ontology and the first service ontology, and the other between the goal ontology and the second service ontology.

The goal ontology looks as follows:

    Goal[requestId *=> Request,
         request   *=> TravelSearchQuery,
         result    *=> Service
    ].
  

The classes Request and Service will be specified explicitly by placing specific object Ids in them. The class TravelSearchQuery consists of the following search queries:

    searchTrip(?From,?To):TravelSearchQuery :- 
                  ?From:(Region or Location) and ?To:(Region or Location).
    searchCitipass(?Loc):TravelSearchQuery :- ?Loc:(Region or Location).

The meaning of the query searchTrip(?X,?Y) depends on whether the parameters are regions or just locations. For location-parameters, the query is assumed to fetch the services that serve those locations. For region-parameters, the query is assumed to find services that service every location in the region that is known to the knowledge base. For instance, searchTrip(Paris, Germany) is a request for travel services that can sell a ticket from Paris to any city in Germany. Similarly, searchCitipass(NewYork) is interpreted as a search for travel services that can sell city passes for New York and the request searchCitipass(USA) is looking for services that can sell city passes for every location in USA. The result attribute is provided by the ontology as a place where the discovery mechanism is supposed to put the results.

Domain-specific service ontologies. A service ontology is intended to represent the inputs and outputs of the service as well as the effects of the service. Since the inputs are not generally provided in the user goal (since the user is not expected to know anything about such inputs), the job of translating goal queries into the inputs to the services lies with the mediator.

Service ontology #1 is defined as follows:

  // Service input
  search(?requestId,?fromLocation,?toLocation):ProcessInput :-
		   ?requestId:Request and
                   ?fromLocation:Location and ?toLocation:Location.
  search(?requestId,?city):ProcessInput  :-
		   ?requestId:Request and ?city:Location.
  // Service output
  ItineraryInfo::ServiceOutput.
  PassInfo::ServiceOutput.
  ItineraryInfo[from*=>Location, to*=>Location].
  PassInfo[city*=>Location].
  itinerary(?reqNumber):ItineraryInfo :- ?reqNumber:Request.
  pass(?reqNumber):PassInfo :- ?reqNumber:Request.

Note that services expect locations as part of their input and they know nothing about regions. In contrast, as we have seen, user goals can have region-wide requests. It is one of the responsibilities of the mediators to bridge this mismatch.

Service ontology #2 is similar to ontology #1 except that it understands only requests for citipasses and the formats for the input and the output are slightly different.

  // Service input
  discover(?requestId,?city):ProcessInput  :-
		   ?requestId:Request and ?city:Location.
  // Service output
  ServiceOutput[location*=>Location].
  ?reqNumber:ServiceOutput :- ?reqNumber:Request.

Shared core ontology for services. In addition, we need a core ontology that is shared by everyone in order to provide a common ground for the service infrastructure. In this example, the core ontology is represented by a single class Service, which is declared as follows:

   prefix xsd = "http://www.w3.org/2001/XMLSchema".
   Service[
      name        *=> xsd#string,
      process     *=> Process[effect(ProcessInput) *=> Formula],
      mediators   *=> Mediator
   ].

Note that the definition of the class Service belongs to the core ontology and therefore it is shared by everybody. The method effect represents the conditional effect of the service. It takes an input to the service as a parameter and returns a set of rules that specify the effects of the service for that input. Formula is a predefined class. The attribute mediators indicates the mediators that the service advertises for anywho would want to talk to that service.

Note that the class ProcessInput belongs to the core ontology, but it's extension (the set of objects that are members of that class) is defined by domain-specific ontologies.

Examples of Concrete Services

We now present instances of concrete services.

// This service uses ontology #1, and mediator med1 bridges it to the goal ontology
serv1:Service[
   name -> "Schwartz Travel, Inc.",

   // Input must be a request for ticket from somewhere in Germany to somewhere
   // in Austria  OR  a request for a city pass for a city in Tyrol
   // Depending on the input, output is either an itinerary object with Id
   // itinerary(requestId) or a citipass object with Id
   // pass(requestId).
   process ->
       _#[effect(?Input) -> ${
	       (itinerary(?Req)[from->?From,to->?To] :-
			   Input = search(?Req, ?From:Germany, ?To:Austria))
	       and
	       (pass(?Req)[city->?City] :- ?Input=search(?Req,?City:Tyrol))
			     }],
    mediators -> med1
].

// Another ontology #1 service
serv2:Service[
    name -> "Mueller Travel, Inc.",
    process ->
	_#[effect(?Input)-> ${
		itinerary(?Req)[from->?From, to->?To] :-
		     ?Input = search(?Req,?From:(France or Germany),?To:Austria)
			      }],
    mediators -> med1
].

// An ontology #2 service
serv3:Service[
    name -> "France Citeseeing, Inc.",
    process ->
	_#[effect(?Input)-> ${
	         ?Req[location->?City] :- ?Input=discover(?Req,?City:France)
		               }],
    mediators -> med2
].

// Another ontology #2 service
serv4:Service[
    name -> "Province Travel",
    process ->
        _#[effect(?Input)-> ${
	       ?Req[location->?City] :-
			?Input = discover(?Req,?City:France) and ?City != Paris
			      }],
    mediators -> med2
].

User Goals

Next we show examples of user goals. Note that the value of the attribute result is initially the empty set. When the goal is posed to the discovery engine, this value will be changed to contain the result of the discovery.

goal1:Goal[
   requestId -> _#:Request,
   request   -> searchTrip(Bonn,Innsbruck),
   result    -> {}
].

// search for services that serve all cities in France and Austria
goal2:Goal[
   requestId -> _#:Request,
   request   -> searchTrip(France,Austria),
   result    -> {}
].

goal3:Goal[
   requestId -> _#:Request,
   request   -> searchCitipass(Frankfurt),
   result    -> {}
].

goal4:Goal[
   requestId -> _#:Request,
   request   -> searchCitipass(Innsbruck),
   result    -> {}
].

// services that can sell citipasses for every city in France
goal5:Goal[
   requestId -> _#:Request,
   request   -> searchCitipass(France),
   result    -> {}
].

Mediators

Each of the two mediators, med1 and med2, consists of several main clauses. The first clause in each mediator takes a user goal and translates it into input (to services) that is appropriate for the corresponding domain-specific service ontology.

The remaining clauses define the mediator's method getResult. This method is supposed to be invoked in the after-state of the service execution. It takes as parameters the user goal and the service (in whose after-state the method is invoked). Depending on the form of the goal's request, getResult poses a query that is appropriate for that request and the service ontology of the service. For instance, if the request is searchCitipass(?City:Location), i.e., finding services that can sell citipasses for a specific location, then the query appropriate for services that use ontology #1 is pass(?)[city->?City] and the query for ontology #2 is ?[location->?City]. Finally, if the query yields results, the mediator constructs output that can be used to return results to the user and this output is compliant with our goal ontology.

Each form of the input has two cases: one assumes that the parameters are locations (e.g., searchCitipass(?City:Location)) and the other that they are regions (e.g., searchCitipass(?City:Region)). Therefore, for each form of the input our mediators have two clauses. Since ontology #2 understands only one input, med2 uses only two clauses to define getResult. The mediator for ontology #1, med1, needs four clauses to cover both forms of the input.

Finally, we remark that the clauses that deal with region-based requests have to construct more sophisticated queries to be asked in the after-state of the services. In our example, we use nonmonotonic Lloyd-Topor extensions to simplify such queries.

// mediator for ontology #1
med1:Mediator.
med1[constructInput(?Goal)->?Input] :- 
	?Goal[requestId->?ReqId, request->?Query] and
        if ?Query = searchTrip(?From,?To)
        then ?Input = search(?ReqId,?From1,?To1)
        else if ?Query = searchCitipass(?City)
        then ?Input = search(?ReqId,?City1).

med1[getResult(?Goal,?Serv) -> ${?Goal[result->?Serv]}] :-
	?Goal[request->searchCitipass(?City:Location)] and
        pass(?)[city->?City].
med1[getResult(?Goal,?Serv) -> ${?Goal[result->?Serv]}] :-
	?Goal[request->searchCitipass(?Region:Region)] and
        forall ?City (?City:?Region ==> pass(?)[city->?City]).

med1[getResult(?Goal,?Serv) -> ${?Goal[result->?Serv]}] :-
	?Goal[request->searchTrip(?From:Location,?To:Location)] and
        itinerary(?)[from->?From, to->?To].
med1[getResult(?Goal,?Serv) -> and ?Result = ${?Goal[result->?Serv]}] :-
	?Goal[request->searchTrip(?From:Region,?To:Region)] and
	forall ?From,?To (?City1:?FromReg and ?City2:?ToReg
                          ==> itinerary(?)[from->?City1, to->?City2]).

// mediator for ontology #2
med2:Mediator.
med2[constructInput(?Goal)->?Input] :- 
	?Goal[requestId->?ReqId, request->?Query] and
        if ?Query = searchCitipass(?City)
        then ?Input = discover(?ReqId,?City1).

med2[getResult(?Goal,?Serv) -> ${?Goal[result->?Serv]}] :-
	?Goal[request->searchCitipass(?City:Location)] and
        ?[location->?City].
med2[getResult(?Goal,?Serv) -> ${?Goal[result->?Serv]}] :-
	?Goal[request->searchCitipass(?Region:Region)] and
        forall ?City (?City:?Region ==> ?[location->?City]).

The Discovery Engine

The final piece of the puzzle is the actual engine that performs service discovery. It relies on the features, borrowed from Transaction Logic [Bonner98], which are currently not in SWSL-Language, but are considered for future extensions. These features include modifications to the current state of the knowledge base and hypothetical execution of such modifications.

findService(?Goal) :-
	?Serv[mediators -> ?Mediator] and
        ?Mediator[constructInput(?Goal) -> ?Input] and
	?Serv.process[effect(?Input) -> ?Effects] and
        hypothetically(
	    insert{?Effects} and
            ?Mediator[getResult(?Goal,?Serv) -> ?Result]
        ) and
	insert{?Result}.

The findService transaction performs the following tasks:

  1. For each service instance, s, it finds the mediator that the service advertises.
  2. It then uses the mediator to construct the input for that service based on the user goal.
  3. Using the input, it computes the effects that the service guarantees to be true in the after-state of the execution.
  4. It then hypothetically does the following:

    1. It inserts the effects (which are rules in this case) into the knowledge base. This simulates the execution of the service s and temporarily creates the after-state of the service execution.
    2. Using the mediator, it checks whether the user goal is true in the after-state of the service s and then returns the result.
  5. If the above hypothetical execution fails for a particular service, no result is returned and the subsequent insert operation is not executed. If the hypothetical execution succeeds, it means that the service s matches the goal. After the hypothetical execution, the state of the knowledge base returns to what was before the execution of the service, but the variable ?Result is now bound to a result, which is a formula of the form

     goal[result->s] 

    This is then inserted into the knowledge base. In this way, the set of answers to the goal is built as the value of the result attribute of the goal object.

For instance, if

 ?- findService(goal1). 

is executed then the following will become true:

 goal1[result -> {serv1,serv2}]

Similarly, executing

 ?- findService(goal2).

yields goal2[result -> serv2]. The third goal, goal3, matches none of the services listed above, so only goal3[result->{}] can be derived. Executing

 ?- findService(goal4).

yields goal4[result -> serv1].

A more interesting goal is goal5, because it requests citipasses for an entire region (France). Given the information available in our knowledge base, only serv3 should match. Note that serv4 does not match because it does not serve Paris, while the goal specifies only those services that can sell citipasses for every location in France.