-
Notifications
You must be signed in to change notification settings - Fork 7
Service Bus (Quick)
Service Ontology is the shared model between service requester and provider. It works as an anchor to the Ontological model. It also allows restrictions over the original model to make services more specific.
Service Ontologies are usually included in the related ontology domain module, but more can also be created in other modules or in the applications themselves. The module containing the Service Ontology must be available to both requester and provider.
A Service Ontology is in fact just a single concept that connects to the Ontology Model of the domain it is going to handle, by one or more properties. It inherits from the concept Service included in the Service Bus. It should be linked to the most “root-like” concept(s) of domain ontologies, thus allowing providers to handle all possible combinations of the domain concepts. The property (or properties) linking to the domain is usually named MANAGES, CONTROLS or HANDLES.
//Ontology concept code that represents the service ontology
// This class represents the Lighting service concept
public class Lighting extends Service {
public static final String MY_URI = LightingOntology.NAMESPACE + "Lighting";
public static final String PROP_CONTROLS = LightingOntology.NAMESPACE + "controls";
...
}
//Ontology code to create a service ontology for lighting services
// Lighting concept is a service controlling the lights. The property CONTROLS links it to LightSource concept
oci = createNewOntClassInfo(Lighting.MY_URI, factory, 4);
oci.addSuperClass(Service.MY_URI);
oci.addObjectProperty(Lighting.PROP_CONTROLS);
oci.addRestriction(MergedRestriction.getAllValuesRestriction(Lighting.PROP_CONTROLS, LightSource.MY_URI));
Service Callees are those applications that provide services of certain Service Ontology. They do so by registering Service Profiles.
Service Profiles are the equivalent to methods. They represent the operation to perform. Starting at the Service Ontology they describe arguments as a Path to a concept on which an Effect is expected.
An application can actually have more than one Callee. Each Callee can answer to multiple Profiles. The Profiles are registered when the Callee is constructed. It is also possible to add more or remove later.
A Callee can use its own instance of a Service Ontology to define its own restrictions to narrow its provided services. This is done by creating an extension of the Service Ontology, usually named Provided Service. The Service Profiles are usually defined in this Provided Service class too.
//Service Callee code with the method to receive calls
public class MyCallee extends ServiceCallee {
// Called if a request matches a profile (find out which)
public ServiceResponse handleCall(ServiceCall call) {
String operation = call.getProcessURI();
if(operation.startsWith(
ProvidedService. PROF2_TURNON)){
// Profile arguments can be taken from the call
Object input = call.getInputValue(
ProvidedService.INPUT_LAMP);
// Make the light turn on here... then return success
return new ServiceResponse(CallStatus.succeeded);
} } }
//Application code to create and register a callee
// Create (and register) the Service Callee with the profiles
MyCallee callee=new MyCallee(moduleContext,ProvidedService.profs);
A Provided Service class is the most usual way to define the provided services. On one hand, it can define the desired restrictions because it is an extension of the Service Ontology to be provided. On the other hand, it´s also used to define the Service Profiles that will be handled by the Callee, each identified by a URI. If no restrictions are needed then it could also be possible not to have this Provided Service class and use the Service Ontology directly instead, declaring the Profiles elsewhere.
Then there is the Service Callee itself. It is an extension of ServiceCallee from the Service Bus. When created, it must be passed the Service Profiles it will answer to. Whenever a call is received that matches one of the profiles, its method handleCall will be invoked, along with the call. The Callee must then identify which Service Profile was called by looking into the call´s requested URI. Then it can work with the call, extract the arguments it knows it has, do something with it and return a response (success, error, timeout...), along with outputs, if any.
A Profile is created by first defining which Service Ontology is being used as starting point. This is done by creating instances of the Provided Service. Then the arguments are added, as many as needed. The meaning of the operation is actually described by the combination of arguments and their restrictions.
//Provided Service code extending service ontology and defining restrictions and profiles
public class ProvidedService {
// Profiles and restrictions will be placed here
static final ServiceProfile[] profs = new ServiceProfile[2];
static {
// Define here the Service Profiles:
// This profile is for getting all controlled lights
ProvidedService prof1 = new ProvidedService(PROF1_GETALL);
prof1.addOutput(OUTPUT_LAMPS, LightSource.MY_URI, 0, 0, new String[] { Lighting.PROP_CONTROLS });
profs[0] = prof1.getProfile();
// This one is for turning on a lamp (set brightness 100%)
ProvidedService prof2 = new ProvidedService(PROF2_TURNON);
prof2.addFilteringInput(INPUT_LAMP, LightSource.MY_URI, 1, 1, new String[] { Lighting.PROP_CONTROLS });
prof2.getProfile().addChangeEffect(new String[]{Lighting.PROP_CONTROLS, LightSource.PROP_SOURCE_BRIGHTNESS }, new Integer(100));
profs[1] = prof2.getProfile();
...
}
}
The Path leads to a concept in the Ontological Model, starting from the Service Ontology. It is represented as an array of the successive properties to follow from the Service Ontology to the desired concept.
The Effect determines what is expected to happen with the concept at the end of the path. Possible effects are Add, Change, Remove, Filter and Output. These are set depending on the method of Service used to define the argument.
Service Callers are the applications that request the execution of a service. This is achieved by issuing Service Requests. The Requests are matched to registered Profiles and if they are ontologically equivalent, the Callee(s) that registered them will be called and will give an answer.
Service Requests are the counterpart of Service Profiles, built the same way but declare what the Caller wants to execute.
One Caller is enough for most applications, but there can be more. There is also a default implementation. Nothing is needed at construction.
//Service Caller code with the method to handle asynchronous responses
public class MyCaller extends ServiceCaller {
// Handles async. responses, when sendRequest(call) is used
public void handleResponse(String requestID, ServiceResponse response) {
// Handle the response
} }
The default implementation is enough to send requests and getting the response. But if this must be handled asynchronously, ServiceCaller must be extended, and responses handled in handleResponse method. In any case, requests are sent with call method of the Caller.
What is sent in the call is a Service Request, which is built the same way as the Service Profile it intends to call: First create it with the root Service Ontology, and then add arguments with paths and effects. But because services are semantic it doesn´t have to be identical to the Profile: just call what is needed. All services which Profiles match the Request will respond, which means that a response may have answers from many services. This is relevant if outputs are requested.
//Application code to create a request, create a caller, send the request and get outputs
// Create the caller, which doesn´t need extra info
MyCaller caller = new MyCaller(moduleContext);
// Call “get all lamps”. Using Lighting instead of Provided Service and not specifying light type still matches the profile.
ServiceRequest req = new ServiceRequest(new Lighting(), null);
req.addRequiredOutput(REQUIRED_OUTPUT_LAMPS, new String[] { Lighting.PROP_CONTROLS });
// Synchronous call. Asynchronous would be sendRequest(call)
ServiceResponse rsp=caller.call(req);
// Deal with response. There are other methods to get outputs.
if (rsp.getCallStatus() == CallStatus.succeeded) {
List lampList = rsp.getOutput(REQUIRED_OUTPUT_LAMPS, true);
}
Found a problem?
- Report suggestions, missing, outdated or wrong documentation creating an Issue with "documentation" tag
Support:
Found a problem?- Report suggestions, missing, outdated or wrong documentation creating an Issue with "documentation" tag