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.
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.
There are two ways to trigger the call of a procedure implemented by an LPI plugin.
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.
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.
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!