Qorus Integration Engine®  4.0.3.p2_git
Qorus Services

Overview

Services are sets of logic defined in methods that are loaded from the database. A service consists of a set of one or more methods that can be called from the Qorus system itself, from Qorus workflows, from other Qorus services, from other applications through the system API exported through YAML-RPC, XML-RPC, JSON-RPC, or SOAP handlers, or even from the command-line using the ocmd program.

There are 2 types of services: system services and user services. Both kinds of services have their meta-data and actual method logic stored in the database.

System services are written by Qore Technologies, delivered with the system. User services are defined and written by Qorus programmers.

Because system services provide Qorus system functionality, and because services can be deleted and reloaded from the database at any time, this means that these parts of the Qorus system itself can be upgraded without taking the system down and restarting it.

Passive services without running threads that just export methods to be called either internally or externally (or both) are like named and versioned API sets. Services that implement actions in service threads can do anything that the available system APIs allows them to do.

Services and Service Methods

There are three service methods that have a special meaning to the Qorus system as listed in the following table. If any of these methods are defined, they will be called by the system itself when the service is loaded or unloaded.

Special Service Methods

Method Description
init() If an init() method exists, it is called automatically when the service is loaded. If this method is explicitly called, no action is taken by the system, therefore this method can be called to ensure that the service is loaded. If this method returns a value, it will be ignored by the system. Any lock attribute defined on this method will be ignored.
start() If a start() method exists, it is launched in the background in a separate thread automatically when the service is loaded, after the init() method is called. Like the init() method, if this method is explicitly called, no action is taken by the system, therefore this method can be called to ensure that the service is loaded and running in the background. If this method returns a value, it will be ignored by the system. Any lock attribute defined on this method will be ignored.
stop() If a start() method exists, then a stop() method must also be defined. The stop() method is only called by the system when the service is deleted. If an external call to a service's stop() method is attempted, it results in an error. Any lock attribute defined on this method will be ignored.

Important service attributes are depicted in the following graphic.

service-diagram.png
Service Diagram

Services have attributes as listed in the following table.

Service Attributes (table: SERVICES)

Attribute Description
serviceid The internal identifier for a service (primary key for this table)
name The service's name
version The version identifier for the service. The type, name and version together uniquely identify a service in the database and are associated with one service ID.
type Either "SYSTEM" or "USER". System services are delivered with the system by Qore Technologies, while user services are developed by Qorus programmers.
description The description of the service
autostart If this boolean flag is set to True, then the service will be automatically started when the system is started.
RWLock This is an internal attribute of each service (not stored in the database); it is a read-write lock that belongs to every service and is automatically applied to method calls depending on the method's lock type attribute
Tags A list of user-defined tags for the service, stored in the SERVICE_TAGS table

Additionally, the SERVICE_STATE_DATA table is used to store persistent data for service data to aid in recovering service actions in case of errors. This table has one row for each service. Service state can be stored using OMQ::UserApi::Service::svc_save_state_data() and retrieved with OMQ::UserApi::Service::svc_get_state_data().

Services then have one or more methods, which provide the logic for the service.

Service methods can be depicted as in the following graphic.

service-method.png
Service Method

Service methods have the attributes listed in the following table. Note that service methods do not have versions; their name must be unique within the scope of the parent service object.

Service Method Attributes (table: SERVICE_METHODS)

Attribute Description
service_methodid The unique ID of the method (primary key for the table)
serviceid The service the method belongs to
name The method's name, which must be unique within the service.
description An optional description for the service
locktype This can be OMQ::SLRead, OMQ::SLWrite, or OMQ::SLNone. Depending on this value the system will wrap calls to the method with the appropriate call to the parent service object's read-write lock object.
internal A Boolean flag that restricts calling the method to internal calls only. Any method with this flag set will not be exported through the HTTP server's web service protocol handlers.
writeflag A Boolean flag that can be used to restrict call access only to users with write access to the application
code This is the code that is loaded when the method is loaded along with the parent service object. This logic must contain a function with the same name as the service method.

Loading and Unloading Services

When services are loaded, all methods belonging to the service are loaded at the same time. Additionally, the Qorus system will automatically load the latest version of the service from the database.

As described above, if the service has init() or start() methods defined, they will be automatically called by the system when the service is loaded, before any other methods can be called. This allows the service to perform initialization before other methods are called and therefore ensure consistent results. Additionally, when a service is loaded, the system event SERVICE_START is raised.

When a service is deleted, if it has start() and stop() methods, or if any service threads were started by calling svc_start_thread() or svc_start_thread_args(), the stop() method is called and the system waits for all service threads to terminate. After all service threads terminate, the service is unloaded from the system immediately. When a service is unloaded, the system event SERVICE_STOP is raised. If any system errors occur related to loading or running a service (such as a service's thread terminating prematurely, for example), the system event SERVICE_ERROR is raised.

Service Calls

If a service call is made and the service referenced is not already loaded, the latest version of the service will be loaded from the Oracle database and initialized and started (if init() and/or start() methods exist, respectively).

The call will block until the service is loaded (and initialized and/or started if init() and/or start() methods exist).

In general, any calls to services while they are in transition states (being initialized/started or deleted) will block until the service exits the transition state. In the case of a service call to a service being deleted, the call will block until the service has been deleted, after which the latest version of the service will be reloaded (and initialized, etc if necessary) and then the call will be made.

Note
The fact that service calls are guaranteed to be completed even when services are being initialized or deleted (assuming that the service is valid can be loaded, initialized, and/or started) means that live service upgrades will not cause any lack of availability of the service being upgraded. Calls during these transition states may take longer to fulfill, but will still be completed after the service exits the transition state (i.e. the initialization or deletion has completed).