Previous Up Next

Appendix F  CBserver Plug-Ins

An LPI plug-in ("logic plug-in") is a small Prolog program that is attached to the CBserver (which is implemented in Prolog) at startup-time. It extends the functionality of the CBserver, for example for user-defined builtin queries. A plug-in can also interface to the services of the operating system1.

You can create a file like myplugin.swi.lpi to provide the implementation for user-defined builtin queries or for call actions in active rules (see section 4). You can use the full range of functions provided by the underlying Prolog system (here: SWI-Prolog, link) and the functions of the CBserver to realize your implementation. You can consult the the CB-Forum for some examples at link. You find there examples for sending emails from active rules, and for extending the set of builtin queries and functions.

F.1  Defining the plug-in

Once you have coded your file myplugin.swi.lpi, there are two methods to attach it to the CBserver. The first method is to copy the file into an existing database directory created by the CBserver:

  cbserver -g exit -d MYDB 
  cp myplugin.swi.lpi MYDB

The first command creates the database directory MYDB if not already existing and initializes it with the pre-defined system classes and objects. The second command copies the LPI file to the database directory. This method makes the definitions only visible to a CBserver that loads the database MYDB.

The second method instructs ConceptBase to load your LPI code to any new database created by the CBserver. To do so, copy the LPI file into the system database directory that holds the definitions of predefined ConceptBase objects:

  cp myplugin.swi.lpi <CB_HOME>/lib/SystemDB

where CB_HOME is the directory into which you installed ConceptBase. The number of LPI files is not limited. You may define zero, one or any number of plug-in files.

A couple of useful LPI plug-ins are published via the CB-Forum, see link. Note that these plug-ins are copyrighted and typically come with their own license conditions that may be different to the license conditions of ConceptBase. If you plan to use the plugins for commercial purposes, you may have to acquire appropriate licenses from the plug-in’s authors.

F.2  Calling the plug-in

There are two ways to trigger the call of a procedure implemented by an LPI plugin.

  1. By explicitely calling a builtin query class (or function) whose code has been implemented by the LPI plugin. The LPI code would then look similar to the code in SYSTEM.SWI.builtin and you must have defined an instance of BuiltinQueryClass that matches the signature of the LPI code. The call to the builtin query class may be enacted from the user interface, or it may be included as an ASKquery call in an instance of AnswerFormat. Refer for more information to section 3.2.5 and to the directory Examples/BuiltinQueries in your ConceptBase installation directory.
  2. By calling the implemented function as a CALL action of an active rule. See section 4.2.2 for an example. In that case, there does not need to be a definition of a builtin query class (or function) to declare the signature of the procedure.

If the code of an LPI plugin realizes a ConceptBase function, e.g. selecting the first instance of a class, then you can use that function whereever functions are allowed. As an example, consider the definition of the LPI plugin selectfirst.swi.lpi from link:

  compute_selectfirst(_res,_class,_c1) :-
        nonvar(_class),                              
        cbserver:ask([In(_res,_class)]),             
        !.

  tell: 'selectfirst in Function isA Proposition with
  parameter class: Proposition end'.

The first clause is the Prolog code. The predicate must start with the prefix compute_ followed by the name of the function. The first argument is for the result of the function. Subsequently, each input parameter is represented by two arguments, one for the input parameter itself and a second as a placeholder of the type of the input parameter. The second clause tells the new function as ConceptBase object so that it can be used like any other ConceptBase function. For technical reasons, the ’tell’ clause may not span over more than 5 lines. Use long lines if the object to be defined is large. The function object (here: selectfirst) is stored in the System module of the database. This is the root module of the module hierarchy, thus functions defined in this way are visible and executable in all sub-modules. If you omit the ’tell’ clause in the LPI file, then you need to tell it manually to the database. This can also be done in a module different to System. In this case, the function can only be called in those modules where the function object is visible.

If you just want to invoke the procedure defined in an LPI plugin via the CALL clause of an active rule, you do not need to include a ’tell’ clause. Consider for example the SENDMAIL plugin from link.

You need to be very careful with testing your code. Only use this feature for functions that cannot be realized by a regular query class or by active rules. LPI code has the full expressiveness of a programming language. Program errors may lead to crashes of the CBserver or to infinite loops or to harmful behavior such as deletion of files. Query classes, deductive rules and integrity constraints can never loop infinitely and can (to the best of our knowledge) only produce answer, not changes to your file system or interact with the operating system. Active rules could loop infinitely but also shall not change your file system and shall not interact with the operating system unless you call such code explicitely in the active rule.

You can disable the loading of LPI plugins with the CBserver option -g nolpi. The CBserver will then not load LPI plugins upon startup. This option might be useful for debugging or for disabling loading LPI plugins that are configured in the lib/SystemDB sub-directory of your ConceptBase installation.

F.3  Programming interface for the plug-ins

The CBserver plug-ins need to interface to the functionalities of the CBserver, the Prolog runtime, and possibly the operating system. To simplify the programming of the CBserver plug-ins, we document here the interface of the module cbserver. We assume that the code for the plug-ins is written in SWI-Prolog and the user is familiar with the SWI-Prolog system.

cbserver:ask(Q,Params,A)
asks the query Q with parameters Params to the CBserver. The answer is returned as a Prolog atom in A. The atom holds the answer represented in the answer format of Q, see also chapter 3.
Example: cbserver:ask(find_classes,[bill/objname],A)
cbserver:ask(Preds)
evaluates the predicates in list Preds. The predicate can backtrack and will bind in case of success the free variables in Preds. We currently support only the following predicates: In, A, AL, and Isa.
Example: cbserver:ask([In(X,Employee),A(X,salary,1000)])
cbserver:askAll(X,Preds,Set)
finds all objects X that satisfy the condition in Preds and puts the result into the list Set. Supported predicates in Preds are: In, A, AL, and Isa.
Example: cbserver:askAll(X,[In(X,Employee),A(X,salary,1000],S)
cbserver:tellFrames(F)
tells all frames contained in the atom F. The call will fail if there is any error in F.
Example: cbserver:tellFrames(’bill in Employee end’)
cbserver:makeName(Id,A)
converts an object identifier to a readable object name.
cbserver:makeId(A,Id)
converts an object name (Prolog atom) into an object identifier used by the CBserver to identify Telos objects. If A is already an object identifier, it is returned as well in Id.
cbserver:arg2val(E,V)
transforms an argument (either an object identifier or a functional expression) to a value (a number or a string). The value can then be used in Prolog style computations such as arithmetic expressions.
cbserver:val2arg(V,I)
transforms a Prolog value (number, string) to an object identifier, possibly by creating a new object for the value.
cbserver:concat(X,Y)
concats the strings (Prolog atoms) contained in the list X. The result is returned in Y.

Note that ConceptBase internally manages concepts by their object identifier. The programming interface instead addresses concepts (and objects) by their name, i.e. the label of the object or the Prolog value corresponding to the label. You may have to use the procedure makeName and makeId to switch between the two representations. The two procedures arg2val and val2arg are useful for defining new builtin functions on the basis of Prolog’s arithmetic functions. Assume for example, that the object identifier id123 has been created to correspond to the real number 1.5. Then, the following relations hold: makeName(id123,’1.5’), arg2val(id123,1.5). Hence, makeName returns the label ’1.5’ whereas arg2val returns the number 1.5.

The interface shall be extended in the future to provide more functionality. Be sure that you only use this feature if user-defined query classes cannot realize your requirements!


1
Since we currently supply the CBserver for Linux only, you need to run the CBserver on a local Linux computer in your network if you want to use the plug-ins. Note that we supply a ready-to-use virtual appliance that includes Linux and ConceptBase and that can be executed via a virtualization engine, see link for details.

Previous Up Next